from django.core.management.base import BaseCommand from django.db import transaction from django.contrib.contenttypes.models import ContentType from assets.models import ( ITSGeoAsset, ITSGeoAssetContract, ITSAssetModel, TrafficLightIntersection, TrafficLightRadarModel, TrafficLightRadar, TrafficLightContract, AssetCategory ) from common.models import Thematic from interventions.models import ( Intervention, InterventionAsset, InterventionLocation ) class Command(BaseCommand): help = "Transfer prevention radars from the ITS thematic to the Traffic Lights thematic." def handle(self, *args, **options): # 1. Retrieve all prevention radars in ITSGeoAsset its_radars = ITSGeoAsset.objects.filter(code__startswith='RAD_PREV_') total_radars = its_radars.count() self.stdout.write(f"Found {total_radars} prevention radars starting with 'RAD_PREV_' in ITSGeoAsset.") if total_radars == 0: self.stdout.write(self.style.WARNING("No prevention radars to transfer. Exiting.")) return # 2. Get necessary content types and thematics its_geo_asset_ct = ContentType.objects.get_for_model(ITSGeoAsset) traffic_light_radar_ct = ContentType.objects.get_for_model(TrafficLightRadar) traffic_light_intersection_ct = ContentType.objects.get_for_model(TrafficLightIntersection) try: trafficlights_thematic = Thematic.objects.get(code='trafficlights') except Thematic.DoesNotExist: self.stdout.write(self.style.ERROR("Thematic 'trafficlights' not found. Exiting.")) return try: radar_category = AssetCategory.objects.get(code='TL_RADAR_PREVENTIF') except AssetCategory.DoesNotExist: try: radar_category = AssetCategory.objects.get(id=20) except AssetCategory.DoesNotExist: radar_category = AssetCategory.objects.filter(name_fr='Radar Préventif').first() if not radar_category: self.stdout.write(self.style.ERROR("AssetCategory 'Radar Préventif' (code TL_RADAR_PREVENTIF) not found. Exiting.")) return # We will keep track of models mapped, locations created/retrieved, radars created, interventions updated, contracts migrated transferred_count = 0 locations_created = 0 models_mapped = 0 interventions_updated = 0 contracts_migrated = 0 # Run everything in a transaction block with transaction.atomic(): for old_radar in its_radars: # A. Map the location (TrafficLightIntersection) intersection_code = old_radar.code.replace('RAD_PREV_', '').strip() intersection, created = TrafficLightIntersection.objects.get_or_create( code=intersection_code, defaults={ 'name_fr': f"Radar {intersection_code}", 'name_nl': f"Radar {intersection_code}", 'lon': old_radar.lon, 'lat': old_radar.lat, 'geom': old_radar.geom, 'geojson': old_radar.geojson, 'status': 'active', 'is_radar': True } ) if created: locations_created += 1 elif not intersection.is_radar: intersection.is_radar = True intersection.save(update_fields=['is_radar']) # B. Map the model (TrafficLightRadarModel) its_model = old_radar.model tl_model, created_model = TrafficLightRadarModel.objects.get_or_create( code=its_model.code, defaults={ 'name_fr': its_model.name_fr, 'name_nl': its_model.name_nl, 'description': its_model.description, 'brand': its_model.brand, 'category': radar_category } ) if created_model: models_mapped += 1 # C. Recreate/map the radar to TrafficLightRadar new_radar = TrafficLightRadar.objects.create( code=old_radar.code, name_fr=old_radar.name_fr, name_nl=old_radar.name_nl, status=old_radar.status, obsolescence=old_radar.obsolescence, installation_date=old_radar.installation_date, uninstallation_date=old_radar.uninstallation_date, serial_number=old_radar.serial_number, warranty_duration=old_radar.warranty_duration, last_inspection_date=old_radar.last_inspection_date, last_preventive_date=old_radar.last_preventive_date, last_corrective_date=old_radar.last_corrective_date, last_survey_date=old_radar.last_survey_date, brand=old_radar.brand, replaced_by=None, marked_as_removed_date=old_radar.marked_as_removed_date, marked_as_removed_by=old_radar.marked_as_removed_by, note=old_radar.note, validation_status=old_radar.validation_status, updated_by=old_radar.updated_by, lon=old_radar.lon, lat=old_radar.lat, geom=old_radar.geom, geojson=old_radar.geojson, model=tl_model, intersection=intersection, category=radar_category ) transferred_count += 1 # D. Migrate associated Interventions # Find all InterventionAssets referencing this old radar int_assets = InterventionAsset.objects.filter(content_type=its_geo_asset_ct, object_id=old_radar.id) for int_asset in int_assets: # Update generic foreign key pointing to the new radar int_asset.content_type = traffic_light_radar_ct int_asset.object_id = new_radar.id int_asset.save() intervention = int_asset.intervention # Update thematic of intervention intervention.thematic = trafficlights_thematic # Update asset category of intervention if it points to ITS Radars (ID 82) if intervention.asset_category and intervention.asset_category.id == 82: intervention.asset_category = radar_category # Update location_code field if empty if not intervention.location_code: intervention.location_code = intersection.code intervention.save() # Create InterventionLocation pointing to the TrafficLightIntersection InterventionLocation.objects.get_or_create( intervention=intervention, content_type=traffic_light_intersection_ct, object_id=intersection.id ) interventions_updated += 1 # E. Migrate associated Contracts # For each ITSGeoAssetContract linked to this old radar old_contracts = ITSGeoAssetContract.objects.filter(asset=old_radar) for old_contract_link in old_contracts: # Create corresponding TrafficLightContract for the intersection TrafficLightContract.objects.get_or_create( intersection=intersection, contract=old_contract_link.contract, defaults={ 'status': old_contract_link.status, 'maintenance_type': 'other' } ) contracts_migrated += 1 # Clean up old contract link old_contract_link.delete() # F. Delete old ITSGeoAsset radar old_radar.delete() self.stdout.write(self.style.SUCCESS( f"Successfully transferred {transferred_count}/{total_radars} prevention radars.\n" f"Created {locations_created} TrafficLightIntersections.\n" f"Mapped {models_mapped} TrafficLightRadarModels.\n" f"Updated {interventions_updated} associated interventions/assets/locations.\n" f"Migrated {contracts_migrated} contract links." ))