diff --git a/seed/data_importer/match.py b/seed/data_importer/match.py index f2c6231ed4..f117e6ebe6 100644 --- a/seed/data_importer/match.py +++ b/seed/data_importer/match.py @@ -10,6 +10,7 @@ from celery import shared_task from celery.utils.log import get_task_logger +from django.apps import apps from django.contrib.postgres.aggregates.general import ArrayAgg from django.db import IntegrityError, transaction from django.db.models import Subquery @@ -31,6 +32,8 @@ PropertyAuditLog, PropertyState, PropertyView, + PropertyViewLabel, + StatusLabel, TaxLotAuditLog, TaxLotState, TaxLotView, @@ -793,6 +796,19 @@ def link_states(states, ViewClass, cycle, highest_ali, sub_progress_key, tuple_v state.raw_access_level_instance = ali view = state.promote(cycle=cycle) + # assign incoming labels to view + if view and state.incoming_labels: + incoming_label_names = state.incoming_labels.split(",") + for incoming_label_name in incoming_label_names: + incoming_label, _ = StatusLabel.objects.get_or_create( + name=incoming_label_name, super_organization=cycle.organization, show_in_list=True + ) + if isinstance(view, PropertyView): + PropertyViewLabel.objects.get_or_create(statuslabel=incoming_label, propertyview=view) + elif isinstance(view, TaxLotView): + TaxLotViewLabel = apps.get_model("seed", "TaxLotView_labels") + TaxLotViewLabel.objects.get_or_create(statuslabel=incoming_label, taxlotview=view) + # link state link_count = _link_matches([*existing_views_matches, view], cycle.organization_id, view, ViewClass) if link_count == 0: diff --git a/seed/data_importer/tasks.py b/seed/data_importer/tasks.py index aed76f8eac..7f25636c90 100644 --- a/seed/data_importer/tasks.py +++ b/seed/data_importer/tasks.py @@ -416,7 +416,11 @@ def map_row_chunk(ids, file_pk, source_type, prog_key, **kwargs): if footprint_details.get("obj_field") and getattr(map_model_obj, footprint_details["obj_field"]) is None: _store_raw_footprint_and_create_rule(footprint_details, table, org, import_file, original_row, map_model_obj) - # There was an error with a field being too long [> 255 chars]. + # Store the incoming label names in state.incoming_labels. -ViewLabels will be created once a view is attatched + label_key = "Property Labels" if isinstance(map_model_obj, PropertyState) else "Tax Lot Labels" + if incoming_labels := map_model_obj.extra_data.pop(label_key, None): + map_model_obj.incoming_labels = incoming_labels + map_model_obj.save() # if importing BuildingSync create a BuildingFile for the property diff --git a/seed/lib/merging/merging.py b/seed/lib/merging/merging.py index edacd79e84..d68f2b876b 100644 --- a/seed/lib/merging/merging.py +++ b/seed/lib/merging/merging.py @@ -155,6 +155,13 @@ def _merge_extra_data(ed1, ed2, priorities, recognize_empty_columns, ignore_merg return extra_data +def _merge_incoming_labels(state1, state2, merged_state): + state1_labels = state1.incoming_labels.split(",") if state1.incoming_labels else [] + state2_labels = state2.incoming_labels.split(",") if state2.incoming_labels else [] + combined_labels = set(state1_labels + state2_labels) + merged_state.incoming_labels = ",".join(combined_labels) if combined_labels else None + + def merge_state(merged_state, state1, state2, priorities, ignore_merge_protection=False): """ Set attributes on our Canonical model, saving differences. @@ -217,6 +224,8 @@ def merge_state(merged_state, state1, state2, priorities, ignore_merge_protectio table_name=state2.__class__.__name__, recognize_empty=True, is_extra_data=True ).values_list("column_name", flat=True) + _merge_incoming_labels(state1, state2, merged_state) + merged_state.extra_data = _merge_extra_data( state1.extra_data, state2.extra_data, diff --git a/seed/migrations/0248_state_incoming_labels.py b/seed/migrations/0248_state_incoming_labels.py new file mode 100644 index 0000000000..a5a956cabc --- /dev/null +++ b/seed/migrations/0248_state_incoming_labels.py @@ -0,0 +1,22 @@ +# Generated by Django 3.2.25 on 2024-12-12 18:14 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("seed", "0247_aggregatemetersystem"), + ] + + operations = [ + migrations.AddField( + model_name="propertystate", + name="incoming_labels", + field=models.TextField(blank=True, null=True), + ), + migrations.AddField( + model_name="taxlotstate", + name="incoming_labels", + field=models.TextField(blank=True, null=True), + ), + ] diff --git a/seed/models/columns.py b/seed/models/columns.py index 39e42d35b3..11b1ada924 100644 --- a/seed/models/columns.py +++ b/seed/models/columns.py @@ -69,6 +69,7 @@ class Column(models.Model): "gross_floor_area_orig", "conditioned_floor_area_orig", "source_eui_weather_normalized_orig", + "incoming_labels", ] QUANTITY_UNIT_COLUMNS = [ @@ -1425,9 +1426,9 @@ def retrieve_db_field_name_for_hash_comparison(inventory_type, organization_id): f.name for f in inventory_type._meta.fields if ( - (f.get_internal_type() != "ForeignKey") - and (f.name not in Column.COLUMN_EXCLUDE_FIELDS) - and (f.name not in excluded_columns) + f.get_internal_type() != "ForeignKey" + and (f.name not in Column.COLUMN_EXCLUDE_FIELDS or f.name == "incoming_labels") + and f.name not in excluded_columns ) ] diff --git a/seed/models/properties.py b/seed/models/properties.py index cdeb56b5cc..ed687de586 100644 --- a/seed/models/properties.py +++ b/seed/models/properties.py @@ -174,6 +174,7 @@ class PropertyState(models.Model): merge_state = models.IntegerField(choices=MERGE_STATE, default=MERGE_STATE_UNKNOWN, null=True) raw_access_level_instance = models.ForeignKey(AccessLevelInstance, null=True, on_delete=models.SET_NULL) raw_access_level_instance_error = models.TextField(null=True) + incoming_labels = models.TextField(null=True, blank=True) jurisdiction_property_id = models.TextField(null=True, blank=True, db_collation="natural_sort") diff --git a/seed/models/tax_lots.py b/seed/models/tax_lots.py index ea79abe646..16c3890ea5 100644 --- a/seed/models/tax_lots.py +++ b/seed/models/tax_lots.py @@ -72,6 +72,7 @@ class TaxLotState(models.Model): merge_state = models.IntegerField(choices=MERGE_STATE, default=MERGE_STATE_UNKNOWN, null=True) raw_access_level_instance = models.ForeignKey(AccessLevelInstance, null=True, on_delete=models.SET_NULL) raw_access_level_instance_error = models.TextField(null=True) + incoming_labels = models.TextField(null=True, blank=True) custom_id_1 = models.CharField(max_length=255, null=True, blank=True, db_collation="natural_sort") diff --git a/seed/serializers/properties.py b/seed/serializers/properties.py index 4cf24ea833..45e1178b25 100644 --- a/seed/serializers/properties.py +++ b/seed/serializers/properties.py @@ -243,6 +243,7 @@ class PropertyStatePromoteWritableSerializer(serializers.ModelSerializer): import_file_id = serializers.IntegerField(allow_null=True, read_only=True) organization_id = serializers.IntegerField() raw_access_level_instance_id = serializers.IntegerField() + incoming_labels = serializers.IntegerField(read_only=True) # read-only core fields id = serializers.IntegerField(read_only=True) diff --git a/seed/tests/test_columns.py b/seed/tests/test_columns.py index b48a3108ad..0dced31bff 100644 --- a/seed/tests/test_columns.py +++ b/seed/tests/test_columns.py @@ -1096,6 +1096,7 @@ def test_retrieve_db_field_name_from_db_tables(self): "generation_date", "gross_floor_area", "home_energy_score_id", + "incoming_labels", "indoor_water_use", "indoor_wui", "jurisdiction_property_id", diff --git a/seed/views/v3/import_files.py b/seed/views/v3/import_files.py index 387af65680..00275f68b9 100644 --- a/seed/views/v3/import_files.py +++ b/seed/views/v3/import_files.py @@ -409,7 +409,6 @@ def map(self, request, pk=None): if not import_file.exists(): return {"status": "error", "message": f"ImportFile {pk} does not exist"} - # return remap_data(import_file_id) return JsonResponse(map_data(pk, remap, mark_as_done)) @swagger_auto_schema_org_query_param