loko/streetup/assets/models/structures.py
2026-07-22 14:48:40 +02:00

180 lines
6.7 KiB
Python

# -----------------------------
# Engineering Structures Models
# -----------------------------
from django.contrib.gis.db import models
from django.urls import reverse
from django.utils.text import slugify
from django.utils.translation import get_language
from django.core.exceptions import ValidationError
import os
from assets.models import AbstractLocation, AbstractAsset, AbstractGeoAsset, AbstractAssetModel
from common.models import Municipality, Document
from common.storages import PrivateMediaStorage
private_storage = PrivateMediaStorage()
STRUCTURE_TYPE_CHOICES = (
('tunnel', 'Tunnnel'),
('bridge', 'Pont'),
('viaduct', 'Viaduc'),
('overpass', 'Passerelle'),
('underpass', 'Passage souterrain'),
('gallery', 'Galerie technique'),
('atypical', 'Ouvrage atypique'),
)
# Structure locations
class StructureLocation(AbstractLocation):
geom = models.MultiPolygonField(srid=3812, null=True,blank=True)
parent_location = models.ForeignKey('self', on_delete=models.SET_NULL, null=True, blank=True, related_name="child_locations")
is_main_location = models.BooleanField(default=False, verbose_name="Main Location")
structure = models.ForeignKey('Structure', on_delete=models.SET_NULL, null=True, blank=True, related_name="all_locations")
def get_active_assets(self):
def collect_assets(location):
assets = list(location.assets.filter(status='active'))
for child in location.child_locations.all():
assets += collect_assets(child)
return assets
return collect_assets(self)
def get_all_assets(self):
def collect_assets(location):
assets = list(location.assets.all())
for child in location.child_locations.all():
assets += collect_assets(child)
return assets
return collect_assets(self)
def get_direct_child_assets(self):
return self.assets.all()
def clean(self):
if self.is_main_location and StructureLocation.objects.filter(structure=self.structure, is_main_location=True).exclude(pk=self.pk).exists():
raise ValidationError("Cette structure a déjà une localisation principale.")
def get_absolute_url(self):
return reverse(
'assets:structures_locations_detail',
kwargs={
# 'asset_model': content_type.model,
'location_id': self.id
}
)
class Meta:
verbose_name = "Structure Location"
verbose_name_plural = "Structure Locations"
ordering = ["code"]
class Structure(models.Model):
code = models.CharField(max_length=50, unique=True)
name_fr = models.CharField(max_length=200,null=True,blank=True)
name_nl = models.CharField(max_length=200,null=True,blank=True)
structure_type = models.CharField(max_length=200, choices=STRUCTURE_TYPE_CHOICES, null=True, blank=True)
main_location = models.OneToOneField(StructureLocation, on_delete=models.SET_NULL, null=True, blank=True, related_name="main_structure")
description = models.TextField(null=True, blank=True)
linked_folder = models.CharField(max_length=1024, null=True, blank=True)
structure_number = models.IntegerField(null=True, blank=True, verbose_name="Numéro de l'ouvrage")
def get_name(self):
lang = get_language()
name = getattr(self, f"name_{lang}", self.name_fr) or "" # Fallback to name_fr
return name
def get_active_assets(self):
return self.assets.filter(status='active')
def get_all_assets(self):
return self.assets.all()
def get_all_locations(self):
return self.all_locations.all()
def get_main_location(self):
return self.main_location
def __str__(self):
return self.code + " - " + self.get_name()
class Meta:
ordering = ["code"]
def upload_to_structure(instance, filename):
location = instance.structure
code = slugify(location.code or "unknown") # Fallback si code est None
return os.path.join("assets_documents", "structures", code, filename)
TU_DOCUMENT_TYPE_CHOICES = (
('as_built', 'As Built'),
('cme', "CME"),
('inspection_ce', 'Inspection G.C.'),
('inspection_em', 'Inspection E.M.'),
('pis', 'PIS'),
('other', 'Autre'),
)
# Structure Location Document
class StructureDocument(Document):
structure = models.ForeignKey(Structure, on_delete=models.CASCADE, related_name="documents", verbose_name="Associated Structure")
file = models.FileField(upload_to=upload_to_structure, storage=private_storage, verbose_name="Fichier", max_length=255)
thumbnail = models.ImageField(upload_to=upload_to_structure, storage=private_storage, null=True, blank=True, max_length=255)
document_type = models.CharField(max_length=50, choices=TU_DOCUMENT_TYPE_CHOICES, default='other', verbose_name="Type de document")
SC_MAINTENANCE_TYPE_CHOICES = (
('electromeca', 'Eléctromécanique'),
('civil_engineering', 'Génie civil'),
('urgent_cleaning', 'Nettoyage urgent'),
('other', 'Autre'),
)
class StructureContract(models.Model):
structure = models.ForeignKey(Structure, on_delete=models.CASCADE, related_name="contracts")
contract = models.ForeignKey("contracts.Contract", on_delete=models.CASCADE, related_name="structure_contracts")
status = models.CharField(max_length=50, choices=[('active', 'Actif'), ('inactive', 'Inactif'), ('archived', 'Archivé')], default='active')
maintenance_type = models.CharField(max_length=50, choices=SC_MAINTENANCE_TYPE_CHOICES, default='other')
def __str__(self):
return f"{self.structure.code} - {self.contract.contract_number}"
class Meta:
verbose_name = "Structure Contract"
verbose_name_plural = "Structure Contracts"
unique_together = ('structure', 'contract')
class StructureAssetModel(AbstractAssetModel):
class Meta:
verbose_name = "Structure Asset Model"
verbose_name_plural = "Structure Asset Models"
class StructureGeoAsset(AbstractGeoAsset):
model = models.ForeignKey(StructureAssetModel, on_delete=models.PROTECT)
location = models.ForeignKey(StructureLocation, related_name="assets", on_delete=models.SET_NULL, null=True, blank=True)
structure = models.ForeignKey(Structure, on_delete=models.SET_NULL, related_name="assets", null=True, blank=True)
parent_asset = models.ForeignKey('self', on_delete=models.SET_NULL, null=True, blank=True, related_name="child_assets")
class Meta:
indexes = [
models.Index(fields=["code"]),
]
def get_absolute_url(self):
return reverse('assets:structures_assets_detail', kwargs={'asset_model': 'structuregeoasset', 'asset_id':self.id})