632 lines
No EOL
28 KiB
Python
632 lines
No EOL
28 KiB
Python
# ---------------------
|
|
# Traffic Lights Models
|
|
# ---------------------
|
|
|
|
from django.contrib.gis.db import models
|
|
from django.urls import reverse
|
|
from django.contrib.contenttypes.models import ContentType
|
|
from django.utils.translation import gettext_lazy as _
|
|
from django.contrib.postgres.fields import ArrayField
|
|
|
|
from assets.models import AbstractLocation, AbstractAsset, AbstractGeoAsset, AbstractAssetModel
|
|
from common.models import Municipality, Document
|
|
from common.storages import PrivateMediaStorage
|
|
|
|
private_storage = PrivateMediaStorage()
|
|
|
|
TL_INTERSECTION_STATUS_CHOICES = {
|
|
'active': _('Actif'),
|
|
'in_preparation': _('En préparation'),
|
|
'under_repair': _('En cours de réparation'),
|
|
'voluntarily_out_of_service': _('Mis hors service volontairement'),
|
|
'inactive': _('Désactivé'),
|
|
'archived': _('Archivé'),
|
|
}
|
|
|
|
TL_VOLTAGE_CHOICES = {
|
|
'10V': '10 Volts',
|
|
'230V': '230 Volts',
|
|
'LED': 'LED',
|
|
}
|
|
|
|
|
|
class TrafficLightAbstractAsset(AbstractAsset):
|
|
def get_absolute_url(self):
|
|
content_type = ContentType.objects.get_for_model(self.__class__)
|
|
return reverse(
|
|
'assets:trafficlights_assets_detail',
|
|
kwargs={
|
|
'asset_model': content_type.model,
|
|
'asset_id': self.id
|
|
}
|
|
)
|
|
|
|
class Meta:
|
|
abstract = True
|
|
|
|
|
|
# Traffic Light Abstract Geo Asset
|
|
class TrafficLightAbstractGeoAsset(AbstractGeoAsset):
|
|
def get_absolute_url(self):
|
|
content_type = ContentType.objects.get_for_model(self.__class__)
|
|
return reverse(
|
|
'assets:trafficlights_assets_detail',
|
|
kwargs={
|
|
'asset_model': content_type.model,
|
|
'asset_id': self.id
|
|
}
|
|
)
|
|
|
|
class Meta:
|
|
abstract = True
|
|
|
|
|
|
# Traffic Light Intersection (crossroads, ...)
|
|
class TrafficLightIntersection(AbstractLocation):
|
|
geom = models.PointField(srid=3812, null=True,blank=True)
|
|
voltage_type = models.CharField(choices=TL_VOLTAGE_CHOICES, null=True,blank=True)
|
|
status = models.CharField(choices=TL_INTERSECTION_STATUS_CHOICES, default='active')
|
|
begin_time = models.DateTimeField(null=True,blank=True)
|
|
master = models.ForeignKey("self", on_delete=models.SET_NULL, null=True, blank=True, related_name="slaves")
|
|
municipality = models.ForeignKey(Municipality, on_delete=models.SET_NULL, null=True, blank=True, related_name="trafficlights_intersections")
|
|
current_index = models.CharField(max_length=100, null=True, blank=True)
|
|
is_radar = models.BooleanField(default=False, verbose_name=_("Est un radar ?"))
|
|
|
|
def get_active_assets(self):
|
|
# Retrieve associated poles
|
|
poles = list(self.poles.filter(status='active'))
|
|
radar_poles = list(self.radar_poles.filter(status='active'))
|
|
all_poles = poles + radar_poles
|
|
all_poles.sort(key=lambda p: p.code)
|
|
|
|
# Retrieve associated cables
|
|
cables = self.cables.filter(status='active').order_by('code')
|
|
|
|
# Retrieve associated lanterns via poles
|
|
lanterns = TrafficLightLantern.objects.filter(pole__intersection=self).filter(status='active').order_by('code')
|
|
|
|
# Retrieve associated controllers
|
|
controllers = self.controllers.filter(status='active').order_by('code')
|
|
|
|
# Retrieve associated detectors
|
|
detectors = self.detectors.filter(status='active').order_by('code')
|
|
|
|
# Retrieve associated electrical cabinets
|
|
electrical_cabinets = self.electrical_cabinets.filter(status='active').order_by('code')
|
|
|
|
# Retrieve associated radars
|
|
radars = self.radars.filter(status='active').order_by('code')
|
|
|
|
return {
|
|
"poles": all_poles,
|
|
"cables": cables,
|
|
"lanterns": lanterns,
|
|
"controllers": controllers,
|
|
"detectors": detectors,
|
|
"electrical_cabinets": electrical_cabinets,
|
|
"radars": radars,
|
|
}
|
|
|
|
def get_all_assets(self):
|
|
# Retrieve associated poles
|
|
poles = list(self.poles.all())
|
|
radar_poles = list(self.radar_poles.all())
|
|
all_poles = poles + radar_poles
|
|
all_poles.sort(key=lambda p: p.code)
|
|
|
|
# Retrieve associated cables
|
|
cables = self.cables.all().order_by('code')
|
|
|
|
# Retrieve associated lanterns via poles
|
|
lanterns = TrafficLightLantern.objects.filter(pole__intersection=self).all().order_by('code')
|
|
|
|
# Retrieve associated controllers
|
|
controllers = self.controllers.all().order_by('code')
|
|
|
|
# Retrieve associated detectors
|
|
detectors = self.detectors.all().order_by('code')
|
|
|
|
# Retrieve associated electrical cabinets
|
|
electrical_cabinets = self.electrical_cabinets.all().order_by('code')
|
|
|
|
# Retrieve associated radars
|
|
radars = self.radars.all().order_by('code')
|
|
|
|
return {
|
|
"poles": all_poles,
|
|
"cables": cables,
|
|
"lanterns": lanterns,
|
|
"controllers": controllers,
|
|
"detectors": detectors,
|
|
"electrical_cabinets": electrical_cabinets,
|
|
"radars": radars,
|
|
}
|
|
|
|
def get_direct_child_assets(self):
|
|
# Retrieve associated poles
|
|
poles = list(self.poles.all())
|
|
radar_poles = list(self.radar_poles.all())
|
|
all_poles = poles + radar_poles
|
|
all_poles.sort(key=lambda p: p.code)
|
|
|
|
# Retrieve associated cables
|
|
cables = self.cables.all().order_by('code')
|
|
|
|
# Retrieve associated lanterns via poles
|
|
lanterns = TrafficLightLantern.objects.filter(pole__intersection=self).all().order_by('code')
|
|
|
|
# Retrieve associated controllers
|
|
controllers = self.controllers.all().order_by('code')
|
|
|
|
# Retrieve associated detectors
|
|
detectors = self.detectors.all().order_by('code')
|
|
|
|
# Retrieve associated electrical cabinets
|
|
electrical_cabinets = self.electrical_cabinets.all().order_by('code')
|
|
|
|
# Retrieve associated radars
|
|
radars = self.radars.all().order_by('code')
|
|
|
|
return {
|
|
"poles": all_poles,
|
|
"cables": cables,
|
|
"lanterns": lanterns,
|
|
"controllers": controllers,
|
|
"detectors": detectors,
|
|
"electrical_cabinets": electrical_cabinets,
|
|
"radars": radars,
|
|
}
|
|
|
|
def get_controller_contract(self):
|
|
"""
|
|
Get the maintenance contract of the most recently installed active controller.
|
|
"""
|
|
latest_controller = self.controllers.filter(status='active').order_by('-installation_date').first()
|
|
return latest_controller.controller_maintenance_contract if latest_controller else None
|
|
|
|
@property
|
|
def is_connected_to_control_center(self):
|
|
"""
|
|
Is the most recently installed active controller connected to the control center.
|
|
"""
|
|
latest_controller = self.controllers.filter(status='active').order_by('-installation_date').first()
|
|
return latest_controller.connected_to_control_center if latest_controller else None
|
|
|
|
def get_absolute_url(self):
|
|
return reverse("assets:trafficlights_intersections_detail", kwargs={"intersection_id": self.id})
|
|
|
|
|
|
|
|
TL_DOCUMENT_TYPE_CHOICES = (
|
|
('as_built', 'As Built'),
|
|
('cross_plan', _('Plan de croix')),
|
|
('other', _('Autre')),
|
|
('programming_file', _('Fichier de programmation')),
|
|
('connexion_plan', _('Plan de raccordement')),
|
|
)
|
|
class TrafficLightIntersectionDocument(Document):
|
|
DOCUMENT_MANAGEMENT_SYNC = True
|
|
|
|
intersection = models.ForeignKey(TrafficLightIntersection, on_delete=models.CASCADE, related_name="documents", verbose_name="Associated Intersection")
|
|
programming = models.ForeignKey("TrafficLightProgramming", on_delete=models.SET_NULL, null=True, blank=True, related_name="documents", verbose_name=_("Programmation associée"))
|
|
file = models.FileField(upload_to="assets_documents/traffic_lights/", storage=private_storage, verbose_name="Fichier", max_length=255)
|
|
thumbnail = models.ImageField(upload_to="assets_documents/thumbnails/traffic_lights/", storage=private_storage, null=True, blank=True, max_length=255)
|
|
document_type = models.CharField(max_length=50, choices=TL_DOCUMENT_TYPE_CHOICES, default='other', verbose_name="Type de document")
|
|
|
|
def save(self, *args, **kwargs):
|
|
from assets.views.trafficlights import ensure_intersection_document_folder, generate_dxf_overlay_managed_document
|
|
|
|
# Determine if it's a new or changed file
|
|
is_dxf = self.file and self.file.name.lower().endswith('.dxf')
|
|
|
|
# Ensure the intersection folder exists
|
|
folder = ensure_intersection_document_folder(self.intersection)
|
|
|
|
# Call the parent save method
|
|
super().save(*args, **kwargs)
|
|
|
|
# Link the managed document to the intersection folder so it appears in the documents tree
|
|
if self.managed_document and folder:
|
|
self.managed_document.folders.add(folder)
|
|
|
|
# If it's a DXF, generate/update overlay in document management
|
|
if is_dxf and self.managed_document and not getattr(self, 'skip_overlay_generation', False):
|
|
try:
|
|
generate_dxf_overlay_managed_document(self.managed_document, user=self.uploaded_by)
|
|
except Exception as e:
|
|
import logging
|
|
logger = logging.getLogger(__name__)
|
|
logger.error(f"Error generating DXF overlay ManagedDocument: {e}")
|
|
|
|
|
|
|
|
class TrafficLightContract(models.Model):
|
|
intersection = models.ForeignKey(TrafficLightIntersection, on_delete=models.CASCADE, related_name="trafficlight_contracts")
|
|
contract = models.ForeignKey("contracts.Contract", on_delete=models.CASCADE, related_name="trafficlight_contracts")
|
|
status = models.CharField(max_length=50, choices=[('active', 'Actif'), ('inactive', 'Inactif'), ('archived', 'Archivé')], default='active')
|
|
maintenance_type = models.CharField(max_length=50, choices=[('infra', 'Infrastructure'), ('controller', 'Contrôleur de traffic'), ('other', 'Autre')], default='other')
|
|
|
|
def __str__(self):
|
|
return f"{self.intersection.code} - {self.contract.contract_number}"
|
|
|
|
class Meta:
|
|
verbose_name = "Traffic Lights Contract"
|
|
verbose_name_plural = "Traffic Lights Contracts"
|
|
unique_together = ('intersection', 'contract')
|
|
|
|
|
|
|
|
|
|
# Traffic Light Pole Model
|
|
class TrafficLightPoleModel(AbstractAssetModel):
|
|
colour = models.CharField(max_length=200, null=True,blank=True, verbose_name=_("Couleur"))
|
|
ral = models.CharField(max_length=200, null=True,blank=True, verbose_name=_("RAL"))
|
|
height = models.FloatField(null=True,blank=True, verbose_name=_("Hauteur (m)"))
|
|
|
|
# Traffic Light Pole
|
|
class TrafficLightPole(TrafficLightAbstractGeoAsset):
|
|
model = models.ForeignKey(TrafficLightPoleModel, on_delete=models.PROTECT)
|
|
intersection = models.ForeignKey(TrafficLightIntersection, related_name="poles", on_delete=models.PROTECT)
|
|
geom = models.PointField(srid=3812, null=True,blank=True)
|
|
arc_length = models.FloatField(null=True, blank=True, verbose_name=_("Longueur de l'arc (m)"))
|
|
extension = models.BooleanField(default=False, verbose_name=_("Extension"))
|
|
colour = models.CharField(max_length=200, null=True,blank=True, verbose_name=_("Couleur"))
|
|
ral = models.CharField(max_length=200, null=True,blank=True, verbose_name=_("RAL"))
|
|
|
|
@property
|
|
def sorted_lanterns(self):
|
|
return self.lanterns.order_by('code')
|
|
|
|
@property
|
|
def sorted_accessories(self):
|
|
return self.accessories.order_by('code')
|
|
|
|
@property
|
|
def sorted_radars(self):
|
|
return self.radars.order_by('code')
|
|
|
|
@property
|
|
def sorted_detectors(self):
|
|
return self.detectors.order_by('code')
|
|
|
|
@property
|
|
def sorted_signpanels(self):
|
|
return self.signpanels_attached.order_by('code')
|
|
|
|
def save(self, *args, **kwargs):
|
|
super().save(*args, **kwargs)
|
|
for radar in self.radars.all():
|
|
radar.save()
|
|
|
|
class Meta:
|
|
verbose_name = _("Traffic light pole")
|
|
verbose_name_plural = _("Traffic light poles")
|
|
|
|
|
|
# Traffic Light Cable Model
|
|
class TrafficLightCableModel(AbstractAssetModel):
|
|
number_of_strands = models.IntegerField(verbose_name=_("Nombre de brins"))
|
|
|
|
# Traffic Light Cable
|
|
class TrafficLightCable(TrafficLightAbstractGeoAsset):
|
|
model = models.ForeignKey(TrafficLightCableModel, on_delete=models.PROTECT)
|
|
intersection = models.ForeignKey(TrafficLightIntersection, related_name="cables", on_delete=models.PROTECT)
|
|
geom = models.LineStringField(srid=3812, null=True,blank=True)
|
|
|
|
class Meta:
|
|
verbose_name = _("Câble")
|
|
verbose_name_plural = _("Câbles")
|
|
|
|
|
|
# Traffic Light Lantern Model
|
|
class TrafficLightLanternModel(AbstractAssetModel):
|
|
voltage = models.IntegerField(verbose_name=_("Tension (V)"))
|
|
|
|
# Traffic Light Lantern
|
|
class TrafficLightLantern(TrafficLightAbstractAsset):
|
|
model = models.ForeignKey(TrafficLightLanternModel, on_delete=models.PROTECT)
|
|
pole = models.ForeignKey(TrafficLightPole, related_name="lanterns", on_delete=models.PROTECT)
|
|
orientation = models.FloatField(null=True, blank=True)
|
|
|
|
class Meta:
|
|
verbose_name = _("Lanterne")
|
|
verbose_name_plural = _("Lanternes")
|
|
|
|
|
|
# Traffic Light Controller Model
|
|
class TrafficLightControllerModel(AbstractAssetModel):
|
|
pass
|
|
|
|
# Traffic Light Controller
|
|
class TrafficLightController(TrafficLightAbstractGeoAsset):
|
|
model = models.ForeignKey(TrafficLightControllerModel, on_delete=models.PROTECT)
|
|
intersection = models.ForeignKey(TrafficLightIntersection, related_name="controllers", on_delete=models.PROTECT)
|
|
connected_to_control_center = models.BooleanField(default=False, verbose_name=_("Connecté à la centrale de contrôle"))
|
|
controller_maintenance_contract = models.ForeignKey("contracts.Contract", on_delete=models.SET_NULL, null=True, blank=True)
|
|
plan = models.CharField(max_length=255, null=True, blank=True, verbose_name=_("Plan"))
|
|
cabinet_type = models.CharField(max_length=255, null=True, blank=True, verbose_name=_("Type"))
|
|
power = models.CharField(max_length=255, null=True, blank=True, verbose_name=_("Puissance"))
|
|
firmware_version = models.CharField(max_length=255, null=True, blank=True, verbose_name=_("Version du firmware"))
|
|
rms = models.BooleanField(default=True, verbose_name=_("RMS"))
|
|
url = models.URLField(null=True, blank=True, verbose_name=_("URL"))
|
|
|
|
@property
|
|
def sorted_hardwares(self):
|
|
return self.hardwares.order_by('code')
|
|
|
|
@property
|
|
def sorted_detectors(self):
|
|
return self.detectors.order_by('code')
|
|
|
|
@property
|
|
def sorted_network_hardwares(self):
|
|
return self.network_hardwares.order_by('code')
|
|
|
|
@property
|
|
def sorted_voltage_hardwares(self):
|
|
return self.voltage_hardwares.order_by('code')
|
|
|
|
class Meta:
|
|
verbose_name = _("Contrôleur")
|
|
verbose_name_plural = _("Contrôleurs")
|
|
|
|
|
|
# Traffic Light Controller Hardware
|
|
class TrafficLightControllerHardware(TrafficLightAbstractGeoAsset):
|
|
controller = models.ForeignKey(TrafficLightController, related_name="hardwares", on_delete=models.CASCADE)
|
|
hardware = models.CharField(max_length=255, null=True, blank=True, verbose_name=_("Hardware"))
|
|
name = models.CharField(max_length=255, null=True, blank=True, verbose_name=_("Nom du matériel"))
|
|
description = models.TextField(null=True, blank=True, verbose_name=_("Description du matériel"))
|
|
|
|
class Meta:
|
|
verbose_name = _("Hardware contrôleur")
|
|
verbose_name_plural = _("Hardwares contrôleur")
|
|
|
|
|
|
# Traffic Light Controller Log File
|
|
class TrafficLightControllerLogFile(models.Model):
|
|
controller = models.ForeignKey(TrafficLightController, related_name="logs", on_delete=models.SET_NULL, null=True, blank=True)
|
|
file = models.FileField(upload_to="assets/traffic_lights/controller_logs/", storage=private_storage, verbose_name="Fichier de log", max_length=255)
|
|
filedate = models.DateTimeField(verbose_name=_("Date du log"), null=True, blank=True)
|
|
|
|
class Meta:
|
|
verbose_name = _("Fichier de log du contrôleur")
|
|
verbose_name_plural = _("Fichiers de log du contrôleur")
|
|
|
|
|
|
# Traffic Light Detector Model
|
|
class TrafficLightDetectorModel(AbstractAssetModel):
|
|
pass
|
|
|
|
# Traffic Light Detector
|
|
class TrafficLightDetector(TrafficLightAbstractGeoAsset):
|
|
name = models.CharField(max_length=255, null=True, blank=True, verbose_name=_("Nom du détecteur"))
|
|
hardware = models.CharField(max_length=255, null=True, blank=True, verbose_name=_("Hardware"))
|
|
model = models.ForeignKey(TrafficLightDetectorModel, on_delete=models.PROTECT)
|
|
intersection = models.ForeignKey(TrafficLightIntersection, related_name="detectors", on_delete=models.PROTECT)
|
|
controller = models.ForeignKey(TrafficLightController, related_name="detectors", on_delete=models.PROTECT, null=True, blank=True)
|
|
pole = models.ForeignKey(TrafficLightPole, related_name="detectors", on_delete=models.PROTECT, null=True, blank=True)
|
|
phases = ArrayField(models.CharField(max_length=50), null=True, blank=True, verbose_name=_("Phases associées"))
|
|
mentionned_on_plan = models.BooleanField(default=False, verbose_name=_("Mentionné sur le plan"))
|
|
|
|
class Meta:
|
|
verbose_name = _("Détecteur")
|
|
verbose_name_plural = _("Détecteurs")
|
|
|
|
|
|
# Traffic Light Network Hardware
|
|
class TrafficLightNetworkHardware(TrafficLightAbstractGeoAsset):
|
|
name = models.CharField(max_length=255, null=True, blank=True, verbose_name=_("Nom du matériel"))
|
|
hardware = models.CharField(max_length=255, null=True, blank=True, verbose_name=_("Hardware"))
|
|
intersection = models.ForeignKey(TrafficLightIntersection, related_name="network_hardwares", on_delete=models.PROTECT)
|
|
controller = models.ForeignKey(TrafficLightController, related_name="network_hardwares", on_delete=models.PROTECT, null=True, blank=True)
|
|
mentionned_on_plan = models.BooleanField(default=False, verbose_name=_("Mentionné sur le plan"))
|
|
|
|
def __str__(self):
|
|
return self.name or f"Matériel {self.id}"
|
|
|
|
class Meta:
|
|
verbose_name = _("Network Hardware")
|
|
verbose_name_plural = _("Network Hardwares")
|
|
|
|
|
|
# Traffic Light Voltage Hardware
|
|
class TrafficLightVoltageHardware(TrafficLightAbstractGeoAsset):
|
|
name = models.CharField(max_length=255, null=True, blank=True, verbose_name=_("Nom du matériel"))
|
|
hardware = models.CharField(max_length=255, null=True, blank=True, verbose_name=_("Hardware"))
|
|
intersection = models.ForeignKey(TrafficLightIntersection, related_name="voltage_hardwares", on_delete=models.PROTECT)
|
|
controller = models.ForeignKey(TrafficLightController, related_name="voltage_hardwares", on_delete=models.PROTECT, null=True, blank=True)
|
|
mentionned_on_plan = models.BooleanField(default=False, verbose_name=_("Mentionné sur le plan"))
|
|
|
|
def __str__(self):
|
|
return self.name or f"Matériel {self.id}"
|
|
|
|
class Meta:
|
|
verbose_name = _("Voltage Hardware")
|
|
verbose_name_plural = _("Voltage Hardwares")
|
|
|
|
|
|
|
|
# Traffic Light Electrical Cabinet Model
|
|
class TrafficLightElectricalCabinetModel(AbstractAssetModel):
|
|
width = models.FloatField(null=True, blank=True, verbose_name=_("Largeur (cm)"))
|
|
height = models.FloatField(null=True, blank=True, verbose_name=_("Hauteur (cm)"))
|
|
depth = models.FloatField(null=True, blank=True, verbose_name=_("Profondeur (cm)"))
|
|
ip_rating = models.CharField(max_length=10, null=True, blank=True, verbose_name=_("Indice de protection IP"))
|
|
|
|
|
|
CABINET_TYPE_CHOICES = (
|
|
('traffic_lights', 'Feux de signalisation'),
|
|
('mixed', 'Mixte'),
|
|
('other', 'Autre'),
|
|
)
|
|
|
|
KEY_TYPE_CHOICES = (
|
|
('bks', 'BKS'),
|
|
('bur16', 'BUR 16'),
|
|
('eu4', 'EU 4'),
|
|
('siemens', 'Siemens'),
|
|
('yale5', 'Yale 5'),
|
|
('58335', '58335'),
|
|
('no_key', 'Pas de clé'),
|
|
('other', 'Autre'),
|
|
)
|
|
|
|
# Traffic Light Electrical Cabinet
|
|
class TrafficLightElectricalCabinet(TrafficLightAbstractGeoAsset):
|
|
model = models.ForeignKey(TrafficLightElectricalCabinetModel, on_delete=models.SET_NULL, null=True, blank=True)
|
|
intersection = models.ForeignKey(TrafficLightIntersection, related_name="electrical_cabinets", on_delete=models.PROTECT)
|
|
geom = models.PointField(srid=3812, null=True, blank=True)
|
|
ean_code = models.CharField(max_length=50, null=True, blank=True, verbose_name=_("Code EAN"))
|
|
power_supply = models.CharField(max_length=100, null=True, blank=True, verbose_name=_("Alimentation électrique"))
|
|
meter_number = models.CharField(max_length=100, null=True, blank=True, verbose_name=_("Numéro de compteur"))
|
|
publicity = models.BooleanField(default=False, verbose_name=_("Publicité"))
|
|
key_type = models.CharField(max_length=100, null=True, blank=True, choices=KEY_TYPE_CHOICES, verbose_name=_("Type de clé"))
|
|
cabinet_type = models.CharField(max_length=100, null=True, blank=True, choices=CABINET_TYPE_CHOICES, verbose_name=_("Type d'armoire"), default='traffic_lights')
|
|
managed_by = models.CharField(max_length=255, null=True, blank=True, verbose_name=_("Géré par"))
|
|
meter_inside = models.BooleanField(default=False, verbose_name=_("Présence Compteur"))
|
|
differential_switch = models.BooleanField(default=False, verbose_name=_("Interrupteur différentiel"))
|
|
differential_switch_caliber = models.CharField(max_length=50, null=True, blank=True, verbose_name=_("Calibre du disjoncteur différentiel"))
|
|
differential_breaker = models.BooleanField(default=False, verbose_name=_("Disjoncteur différentiel"))
|
|
differential_breaker_caliber = models.CharField(max_length=50, null=True, blank=True, verbose_name=_("Calibre du disjoncteur"))
|
|
differential_caliber = models.CharField(max_length=50, null=True, blank=True, verbose_name=_("Calibre différentiel"))
|
|
departure_towards = models.CharField(max_length=50, null=True, blank=True, verbose_name=_("Départ vers"))
|
|
visual_conformity = models.CharField(max_length=255, null=True, blank=True, verbose_name=_("Conformité visuelle"))
|
|
cleanliness = models.CharField(max_length=255, null=True, blank=True, verbose_name=_("Propreté"))
|
|
|
|
class Meta:
|
|
verbose_name = _("Armoire")
|
|
verbose_name_plural = _("Armoires")
|
|
|
|
|
|
class TrafficLightElectricalCabinetContentModel(AbstractAssetModel):
|
|
pass
|
|
|
|
class TrafficLightElectricalCabinetContent(TrafficLightAbstractGeoAsset):
|
|
cabinet = models.ForeignKey(TrafficLightElectricalCabinet, related_name="contents", on_delete=models.CASCADE)
|
|
model = models.ForeignKey(TrafficLightElectricalCabinetContentModel, on_delete=models.SET_NULL, null=True, blank=True)
|
|
description = models.CharField(max_length=255, null=True, blank=True, verbose_name=_("Description du contenu"))
|
|
number = models.CharField(max_length=100, null=True, blank=True, verbose_name=_("Numéro de référence"))
|
|
ean = models.CharField(max_length=100, null=True, blank=True, verbose_name=_("Code EAN"))
|
|
|
|
class Meta:
|
|
verbose_name = _("Contenu d'armoire")
|
|
verbose_name_plural = _("Contenus d'armoires")
|
|
|
|
|
|
# Traffic Light Accessory Model
|
|
class TrafficLightAccessoryModel(AbstractAssetModel):
|
|
pass
|
|
|
|
# Traffic Light Accessory
|
|
class TrafficLightAccessory(TrafficLightAbstractGeoAsset):
|
|
model = models.ForeignKey(TrafficLightAccessoryModel, on_delete=models.PROTECT, null=True, blank=True)
|
|
pole = models.ForeignKey(TrafficLightPole, related_name="accessories", on_delete=models.PROTECT, null=True, blank=True)
|
|
intersection = models.ForeignKey(TrafficLightIntersection, related_name="accessories", on_delete=models.PROTECT, null=True, blank=True)
|
|
orientation = models.FloatField(null=True, blank=True)
|
|
|
|
class Meta:
|
|
verbose_name = _("Accessoire")
|
|
verbose_name_plural = _("Accessoires")
|
|
|
|
|
|
|
|
# Treffic Light Programmation
|
|
class TrafficLightProgramming(TrafficLightAbstractAsset):
|
|
intersection = models.ForeignKey(TrafficLightIntersection, on_delete=models.CASCADE, related_name="programmations")
|
|
commissioning_date = models.DateTimeField(null=True, blank=True, verbose_name=_("Date de mise en service"))
|
|
uncommissioning_date = models.DateTimeField(null=True, blank=True, verbose_name=_("Date de mise hors service"))
|
|
plan = models.CharField(max_length=255, null=True, blank=True, verbose_name=_("Plan"))
|
|
index = models.CharField(max_length=100, verbose_name=_("Index"))
|
|
info = models.TextField(null=True, blank=True, verbose_name=_("Informations"))
|
|
|
|
def __str__(self):
|
|
return f"{self.index} - {self.intersection.code}"
|
|
|
|
class Meta:
|
|
ordering = ['-commissioning_date']
|
|
|
|
|
|
# Traffic Light Radar Pole Model
|
|
class TrafficLightRadarPoleModel(AbstractAssetModel):
|
|
pass
|
|
|
|
# Traffic Light Radar Pole
|
|
class TrafficLightRadarPole(TrafficLightAbstractGeoAsset):
|
|
model = models.ForeignKey(TrafficLightRadarPoleModel, on_delete=models.SET_NULL, null=True, blank=True)
|
|
intersection = models.ForeignKey(TrafficLightIntersection, related_name="radar_poles", on_delete=models.PROTECT)
|
|
colour = models.CharField(max_length=200, null=True,blank=True, verbose_name=_("Couleur"))
|
|
ral = models.CharField(max_length=200, null=True,blank=True, verbose_name=_("RAL"))
|
|
|
|
@property
|
|
def sorted_lanterns(self):
|
|
return []
|
|
|
|
@property
|
|
def sorted_accessories(self):
|
|
return []
|
|
|
|
@property
|
|
def sorted_radars(self):
|
|
return self.radars.order_by('code')
|
|
|
|
@property
|
|
def sorted_signpanels(self):
|
|
return []
|
|
|
|
def save(self, *args, **kwargs):
|
|
super().save(*args, **kwargs)
|
|
for radar in self.radars.all():
|
|
radar.save()
|
|
|
|
class Meta:
|
|
verbose_name = _("Radar Pole")
|
|
verbose_name_plural = _("Radar Poles")
|
|
|
|
# Traffic Light Radar Model
|
|
class TrafficLightRadarModel(AbstractAssetModel):
|
|
class Meta:
|
|
verbose_name = _("Traffic Light Radar Model")
|
|
verbose_name_plural = _("Traffic Light Radar Models")
|
|
|
|
# Traffic Light Radar
|
|
class TrafficLightRadar(TrafficLightAbstractGeoAsset):
|
|
model = models.ForeignKey(TrafficLightRadarModel, on_delete=models.PROTECT)
|
|
intersection = models.ForeignKey(TrafficLightIntersection, related_name="radars", on_delete=models.PROTECT)
|
|
|
|
# Physical Supports
|
|
pole = models.ForeignKey(TrafficLightRadarPole, verbose_name=_("Radar Pole (Support)"), related_name="radars", on_delete=models.SET_NULL, null=True, blank=True)
|
|
publiclighting_pole = models.ForeignKey('assets.PublicLightingPole', verbose_name=_("Public Lighting Pole (Support)"), related_name="radars", on_delete=models.SET_NULL, null=True, blank=True)
|
|
trafficlights_pole = models.ForeignKey(TrafficLightPole, verbose_name=_("Traffic Light Pole (Support)"), related_name="radars", on_delete=models.SET_NULL, null=True, blank=True)
|
|
|
|
# Electrical Connections / Power Sources
|
|
power_publiclighting_pole = models.ForeignKey('assets.PublicLightingPole', verbose_name=_("Public Lighting Pole (Power)"), related_name="powered_radars", on_delete=models.SET_NULL, null=True, blank=True)
|
|
power_trafficlights_pole = models.ForeignKey(TrafficLightPole, verbose_name=_("Traffic Light Pole (Power)"), related_name="powered_radars", on_delete=models.SET_NULL, null=True, blank=True)
|
|
|
|
class Meta:
|
|
verbose_name = _("Radar")
|
|
verbose_name_plural = _("Radars")
|
|
|
|
def clean(self):
|
|
super().clean()
|
|
support = self.pole or self.publiclighting_pole or self.trafficlights_pole
|
|
if support and support.geom:
|
|
self.geom = support.geom
|
|
if hasattr(support, 'lon'):
|
|
self.lon = support.lon
|
|
if hasattr(support, 'lat'):
|
|
self.lat = support.lat
|
|
if hasattr(support, 'geojson'):
|
|
self.geojson = support.geojson
|
|
|
|
def save(self, *args, **kwargs):
|
|
support = self.pole or self.publiclighting_pole or self.trafficlights_pole
|
|
if support and support.geom:
|
|
self.geom = support.geom
|
|
if hasattr(support, 'lon'):
|
|
self.lon = support.lon
|
|
if hasattr(support, 'lat'):
|
|
self.lat = support.lat
|
|
if hasattr(support, 'geojson'):
|
|
self.geojson = support.geojson
|
|
super().save(*args, **kwargs) |