# ---------------------- # Roads Models # ---------------------- from django.contrib.gis.db import models from django.contrib.postgres.fields import ArrayField from django.urls import reverse from django.utils.text import slugify from django.utils.translation import get_language from django.utils.translation import gettext_lazy as _ import os from assets.models import AbstractLocation, AbstractAsset, AbstractGeoAsset, AbstractAssetModel from common.models import Municipality, Document from common.storages import PrivateMediaStorage from observations.models import Observation private_storage = PrivateMediaStorage() # Road Street class RoadStreet(AbstractLocation): geom = models.MultiPolygonField(srid=3812, null=True,blank=True) municipality = models.ForeignKey(Municipality, on_delete=models.SET_NULL, null=True, blank=True, related_name="road_streets") road_code = models.CharField(max_length=50, null=True, blank=True) section_code = models.CharField(max_length=50, null=True, blank=True) street_level = models.CharField(max_length=50, null=True, blank=True) def get_active_assets(self): roadways = self.roadways.filter(status='active') sidewalks = self.sidewalks.filter(status='active') furnitures = self.furnitures.filter(status='active') return { "roadways": roadways, "sidewalks": sidewalks, "furnitures": furnitures, } def get_all_assets(self): roadways = self.roadways.all() sidewalks = self.sidewalks.all() furnitures = self.furnitures.all() return { "roadways": roadways, "sidewalks": sidewalks, "furnitures": furnitures, } def get_direct_child_assets(self): roadways = self.roadways.all() sidewalks = self.sidewalks.all() furnitures = self.furnitures.all() return { "roadways": roadways, "sidewalks": sidewalks, "furnitures": furnitures, } def get_absolute_url(self): return reverse("assets:roads_streets_detail", kwargs={"street_id": self.id}) def upload_to_roadstreet(instance, filename): location = instance.street code = slugify(location.code or "unknown") # Fallback si code est None return os.path.join("assets_documents", "roads", code, filename) ROAD_DOCUMENT_TYPE_CHOICES = ( ('as_built', 'As Built'), ('other', 'Autre'), ) class RoadStreetDocument(Document): street = models.ForeignKey(RoadStreet, on_delete=models.CASCADE, related_name="documents", verbose_name="Associated Street") file = models.FileField(upload_to=upload_to_roadstreet, storage=private_storage, verbose_name="Fichier", max_length=255) thumbnail = models.ImageField(upload_to=upload_to_roadstreet, storage=private_storage, null=True, blank=True, max_length=255) document_type = models.CharField(max_length=50, choices=ROAD_DOCUMENT_TYPE_CHOICES, default='other', verbose_name="Type de document") QUALITY_LEVEL_CHOICES = ( (1, '1 - Très mauvais'), (2, '2 - Mauvais'), (3, '3 - Bon'), (4, '4 - Très bon'), (5, '5 - Excellent'), ) # Roadway Model class RoadwayModel(AbstractAssetModel): speed_limit = models.IntegerField(null=True,blank=True) ROAD_COATING_TYPE_CHOICES = ( ('asphalt', 'Asphalt'), ('concrete', 'Concrete'), ('cobblestone', 'Cobblestone'), ('gravel', 'Gravel'), ('dirt', 'Dirt'), ('other', 'Other'), ) # Roadway class Roadway(AbstractGeoAsset): coating_type = models.CharField(max_length=200, null=True,blank=True, choices=ROAD_COATING_TYPE_CHOICES) model = models.ForeignKey(RoadwayModel, on_delete=models.SET_NULL, null=True, blank=True) street = models.ForeignKey(RoadStreet, related_name="roadways", on_delete=models.PROTECT) quality_level = models.IntegerField(null=True, blank=True, choices=QUALITY_LEVEL_CHOICES) lanes = models.IntegerField(null=True, blank=True) area = models.FloatField(null=True, blank=True) geom = models.MultiPolygonField(srid=3812, null=True,blank=True) class Meta: verbose_name = "Roadway" verbose_name_plural = "Roadways" def get_absolute_url(self): return reverse( "assets:roads_assets_detail", kwargs={"asset_model": self._meta.model_name, "asset_id": self.id}, ) # Sidewalk Model class RoadSidewalkModel(AbstractAssetModel): pass SW_COATING_TYPE_CHOICES = ( ('concrete', 'Concrete'), ('paving_stones', 'Paving Stones'), ('tiles20', 'Tiles 20x20'), ('tiles30', 'Tiles 30x30'), ('tiles40', 'Tiles 40x40'), ('tiles50', 'Tiles 50x50'), ('tiles', 'Tiles'), ('asphalt', 'Asphalt'), ('dolomite','Dolomie'), ('gravel', 'Gravel'), ('dirt', 'Dirt'), ('other', 'Other'), ) # Sidewalk class RoadSidewalk(AbstractGeoAsset): coating_type = models.CharField(max_length=200, null=True,blank=True, choices=SW_COATING_TYPE_CHOICES) model = models.ForeignKey(RoadSidewalkModel, on_delete=models.SET_NULL, null=True, blank=True) street = models.ForeignKey(RoadStreet, related_name="sidewalks", on_delete=models.PROTECT) quality_level = models.IntegerField(null=True, blank=True, choices=QUALITY_LEVEL_CHOICES) area = models.FloatField(null=True, blank=True) geom = models.MultiPolygonField(srid=3812, null=True,blank=True) class Meta: verbose_name = "Sidewalk" verbose_name_plural = "Sidewalks" def get_absolute_url(self): return reverse( "assets:roads_assets_detail", kwargs={"asset_model": self._meta.model_name, "asset_id": self.id}, ) # Street Furniture Model class RoadFurnitureModel(AbstractAssetModel): color = models.CharField(max_length=200, null=True,blank=True) ral = models.CharField(max_length=200, null=True,blank=True) height = models.FloatField(null=True,blank=True) width = models.FloatField(null=True,blank=True) material = models.CharField(max_length=200, null=True,blank=True) # Street Furniture class RoadFurniture(AbstractGeoAsset): model = models.ForeignKey(RoadFurnitureModel, on_delete=models.SET_NULL, null=True, blank=True) street = models.ForeignKey(RoadStreet, related_name="furnitures", on_delete=models.PROTECT) class Meta: verbose_name = "Street Furniture" verbose_name_plural = "Street Furnitures" def get_absolute_url(self): return reverse( "assets:roads_assets_detail", kwargs={"asset_model": self._meta.model_name, "asset_id": self.id}, ) ROAD_FUNCTION_CHOICES = ( ('masterful', 'Magistrale'), ('plus', 'Plus'), ('confort', 'Confort'), ('other', 'Autre'), ) # Roadway Functions class RoadwayFunction(models.Model): roadway = models.ForeignKey(Roadway, on_delete=models.CASCADE, related_name="functions") pedestrian_function = models.CharField(max_length=200, null=True, blank=True, choices=ROAD_FUNCTION_CHOICES) bicycle_function = models.CharField(max_length=200, null=True, blank=True, choices=ROAD_FUNCTION_CHOICES) public_transport_function = models.CharField(max_length=200, null=True, blank=True, choices=ROAD_FUNCTION_CHOICES) private_motorized_function = models.CharField(max_length=200, null=True, blank=True, choices=ROAD_FUNCTION_CHOICES) class Meta: verbose_name = "Roadway Function" verbose_name_plural = "Roadway Functions" CONCRETE_BLOCK_FUNCTION_CHOICES = ( ('protection', _('Protection Asset')), ('antiterrorist', _('Antiterroriste')), ('parking_neutralization', _('Neutralisation stationnement')), ('road_safety', _('Sécurité routière')), ('public_space', _('Espace public')), ('construction', _('Chantier')), ('unidentified', _('Non identifié')), ) CONCRETE_BLOCK_ACTION_CHOICES = ( ('keep', _('Maintenir')), ('remove', _('Enlever')), ('replace', _('Remplacer')), ) # Road Concrete blocks Set class RoadConcreteBlockSet(AbstractGeoAsset): address = models.CharField(max_length=2048, null=True, blank=True) main_function = models.CharField(max_length=200, null=True, blank=True, choices=CONCRETE_BLOCK_FUNCTION_CHOICES) secondary_functions = ArrayField(models.CharField(max_length=200, choices=CONCRETE_BLOCK_FUNCTION_CHOICES), null=True, blank=True) number_of_blocks = models.IntegerField(null=True, blank=True) length = models.FloatField(null=True, blank=True) recommended_action = models.CharField(max_length=2048, null=True, blank=True, choices=CONCRETE_BLOCK_ACTION_CHOICES) priority_level = models.IntegerField(null=True, blank=True) targeted_action_date = models.DateField(null=True, blank=True) observation_source = models.ForeignKey(Observation, on_delete=models.SET_NULL, null=True, blank=True, related_name="concrete_block_sets") def get_absolute_url(self): return reverse( 'assets:roads_assets_detail', kwargs={'asset_model': self._meta.model_name, 'asset_id': self.id}, ) def __str__(self): return self.address if self.address else _("Road Concrete Block Set") DRAIN_SOIL_TYPE_CHOICES = ( ('concrete', _('Béton')), ('asphalt', _('Asphalte')), ('stone', _('Pierre')), ('concrete_slabs', _('Dalles en béton')), ('concrete_paving', _('Pavés en béton')), ('stone_slabs', _('Dalles en pierre')), ('stone_paving', _('Pavés en pierre')), ('concrete_grass', _('Béton/Gazon')), ('gravel', _('Gravier')), ('grass', _('Gazon')), ('earth', _('Terre')), ('other', _('Autre')), ('na', _('N/A')), ) DRAIN_LOCATION_TYPE_CHOICES = ( ('waterway', _("Filet d'eau")), ('sidewalk', _('Trottoir')), ('berme', _('Berme')), ('sidewalk_facade_to_road', _('Trottoir façade vers chaussée')), ('square', _('Place')), ('along_sidewalk', _('Le long du trottoir')), ('middle_road', _('Milieu chaussée')), ('other', _('Autre')), ('na', _('N/A')), ) DRAIN_DEFECT_TYPE_CHOICES = ( ('obstructed', _('Obstrué')), ('smell_cut_missing', _('Coupe-odeur manquant')), ('grate_broken', _('Grille cassée')), ('grate_missing', _('Grille manquante')), ('poor_water_flow', _('Mauvais écoulement')), ('other', _('Autre')), ) # Road Drain Model class RoadDrainModel(AbstractAssetModel): pass # Road Drain class RoadDrain(AbstractGeoAsset): street = models.ForeignKey(RoadStreet, related_name="drains", on_delete=models.SET_NULL, null=True, blank=True) model = models.ForeignKey(RoadDrainModel, on_delete=models.SET_NULL, null=True, blank=True) soil_type = models.CharField(max_length=200, null=True, blank=True, choices=DRAIN_SOIL_TYPE_CHOICES) positionning_source = models.CharField(max_length=2048, null=True, blank=True) positionning_note = models.TextField(null=True, blank=True) positionning_date = models.DateField(null=True, blank=True) drain_location_type = models.CharField(max_length=200, null=True, blank=True, choices=DRAIN_LOCATION_TYPE_CHOICES) nearby_vegetation = models.BooleanField(null=True, blank=True) possible_rooting = models.BooleanField(null=True, blank=True) is_defect = models.BooleanField(null=True, blank=True) defect_types = ArrayField(models.CharField(max_length=200, choices=DRAIN_DEFECT_TYPE_CHOICES), null=True, blank=True) defect_note = models.TextField(null=True, blank=True) related_image_type = models.CharField(max_length=1000, null=True, blank=True) altitude = models.FloatField(null=True, blank=True) geom = models.PointField(srid=3812, null=True, blank=True, dim=3) def get_absolute_url(self): return reverse( 'assets:roads_assets_detail', kwargs={'asset_model': self._meta.model_name, 'asset_id': self.id}, ) def __str__(self): return self.code if self.code else _("Road Drain")