Esempio n. 1
0
    def test_get_state_attrs(self):
        # create the column for data_1
        Column.objects.create(
            column_name=u'data_1',
            table_name=u'TaxLotState',
            organization=self.org,
            is_extra_data=True,
        )
        tlv1 = self.taxlot_view_factory.get_taxlot_view(
            extra_data={"data_1": "value_1"})
        tlv2 = self.taxlot_view_factory.get_taxlot_view(
            extra_data={"data_1": "value_2"})

        self.assertEqual(tlv1.state.extra_data['data_1'], 'value_1')
        self.assertEqual(tlv2.state.extra_data['data_1'], 'value_2')

        res = get_state_attrs([tlv1.state, tlv2.state])
        self.assertEqual(res['custom_id_1'], {
            tlv2.state: None,
            tlv1.state: None
        })
        self.assertEqual(res['postal_code'], {
            tlv2.state: tlv2.state.postal_code,
            tlv1.state: tlv1.state.postal_code
        })
        self.assertTrue('data_1' not in res.keys())
Esempio n. 2
0
    def merge(self, request):
        """
        Merge multiple tax lot records into a single new record
        ---
        parameters:
            - name: organization_id
              description: The organization_id for this user's organization
              required: true
              paramType: query
            - name: state_ids
              description: Array containing tax lot state ids to merge
              paramType: body
        """
        body = request.data

        state_ids = body.get('state_ids', [])
        organization_id = int(request.query_params.get('organization_id',
                                                       None))

        # Check the number of state_ids to merge
        if len(state_ids) < 2:
            return JsonResponse(
                {
                    'status': 'error',
                    'message': 'At least two ids are necessary to merge'
                },
                status=status.HTTP_400_BAD_REQUEST)

        # Make sure the state isn't already matched
        for state_id in state_ids:
            if ImportFileViewSet.has_coparent(state_id, 'properties'):
                return JsonResponse(
                    {
                        'status':
                        'error',
                        'message':
                        'Source state [' + state_id + '] is already matched'
                    },
                    status=status.HTTP_400_BAD_REQUEST)

        audit_log = TaxLotAuditLog
        inventory = TaxLot
        label = apps.get_model('seed', 'TaxLot_labels')
        state = TaxLotState
        view = TaxLotView

        index = 1
        merged_state = None
        while index < len(state_ids):
            # state 1 is the base, state 2 is merged on top of state 1
            # Use index 0 the first time through, merged_state from then on
            if index == 1:
                state1 = state.objects.get(id=state_ids[index - 1])
            else:
                state1 = merged_state
            state2 = state.objects.get(id=state_ids[index])

            merged_state = state.objects.create(
                organization_id=organization_id)
            merged_state = merging.merge_state(merged_state,
                                               state1,
                                               state2,
                                               merging.get_state_attrs(
                                                   [state1, state2]),
                                               default=state2)

            state_1_audit_log = audit_log.objects.filter(state=state1).first()
            state_2_audit_log = audit_log.objects.filter(state=state2).first()

            audit_log.objects.create(organization=state1.organization,
                                     parent1=state_1_audit_log,
                                     parent2=state_2_audit_log,
                                     parent_state1=state1,
                                     parent_state2=state2,
                                     state=merged_state,
                                     name='Manual Match',
                                     description='Automatic Merge',
                                     import_filename=None,
                                     record_type=AUDIT_IMPORT)

            # Set the merged_state to merged
            merged_state.data_state = DATA_STATE_MATCHING
            merged_state.merge_state = MERGE_STATE_MERGED
            merged_state.save()
            state1.merge_state = MERGE_STATE_UNKNOWN
            state1.save()
            state2.merge_state = MERGE_STATE_UNKNOWN
            state2.save()

            # Delete existing views and inventory records
            views = view.objects.filter(state_id__in=[state1.id, state2.id])
            view_ids = list(views.values_list('id', flat=True))

            # Find unique notes
            notes = list(
                Note.objects.values(
                    'name', 'note_type', 'text', 'log_data', 'created',
                    'updated', 'organization_id',
                    'user_id').filter(taxlot_view_id__in=view_ids).distinct())

            cycle_id = views.first().cycle_id
            label_ids = []
            # Get paired view ids
            paired_view_ids = list(
                TaxLotProperty.objects.filter(
                    taxlot_view_id__in=view_ids).order_by('property_view_id').
                distinct('property_view_id').values_list('property_view_id',
                                                         flat=True))
            for v in views:
                label_ids.extend(
                    list(v.taxlot.labels.all().values_list('id', flat=True)))
                v.taxlot.delete()
            label_ids = list(set(label_ids))

            # Create new inventory record
            inventory_record = inventory(organization_id=organization_id)
            inventory_record.save()

            # Create new labels and view
            for label_id in label_ids:
                label(taxlot_id=inventory_record.id,
                      statuslabel_id=label_id).save()
            new_view = view(cycle_id=cycle_id,
                            state_id=merged_state.id,
                            taxlot_id=inventory_record.id)
            new_view.save()

            # Assign notes to the new view
            for note in notes:
                note['taxlot_view'] = new_view
                n = Note(**note)
                n.save()
                # Correct the created and updated times to match the original note
                Note.objects.filter(id=n.id).update(created=note['created'],
                                                    updated=note['updated'])

            # Delete existing pairs and re-pair all to new view
            # Probably already deleted by cascade
            TaxLotProperty.objects.filter(taxlot_view_id__in=view_ids).delete()
            for paired_view_id in paired_view_ids:
                TaxLotProperty(primary=True,
                               cycle_id=cycle_id,
                               property_view_id=paired_view_id,
                               taxlot_view_id=new_view.id).save()

            index += 1

        return {'status': 'success'}
Esempio n. 3
0
    def process(self, organization_id, cycle, property_view=None):
        """
        Process the building file that was uploaded and create the correct models for the object

        :param organization_id: integer, ID of organization
        :param cycle: object, instance of cycle object
        :param property_view: Existing property view of the building file that will be updated from merging the property_view.state
        :return: list, [status, (PropertyState|None), (PropertyView|None), messages]
        """

        Parser = self.BUILDING_FILE_PARSERS.get(self.file_type, None)
        if not Parser:
            acceptable_file_types = ', '.join(
                map(dict(self.BUILDING_FILE_TYPES).get, self.BUILDING_FILE_PARSERS.keys())
            )
            return False, None, None, "File format was not one of: {}".format(acceptable_file_types)

        parser = Parser()
        parser.import_file(self.file.path)
        parser_args = []
        parser_kwargs = {}
        if self.file_type == self.BUILDINGSYNC:
            parser_args.append(BuildingSync.BRICR_STRUCT)
        data, errors, messages = parser.process(*parser_args, **parser_kwargs)

        if errors or not data:
            return False, None, None, messages

        # sub-select the data that are needed to create the PropertyState object
        db_columns = Column.retrieve_db_field_table_and_names_from_db_tables()
        create_data = {"organization_id": organization_id}
        extra_data = {}
        for k, v in data.items():
            # Skip the keys that are for measures and reports and process later
            if k in ['measures', 'reports', 'scenarios']:
                continue

            # Check if the column exists, if not, then create one.

            if ('PropertyState', k) in db_columns:
                create_data[k] = v
            else:
                extra_data[k] = v

        # always create the new object, then decide if we need to merge it.
        # create a new property_state for the object and promote to a new property_view
        property_state = PropertyState.objects.create(**create_data)
        property_state.extra_data = extra_data
        property_state.save()

        Column.save_column_names(property_state)

        PropertyAuditLog.objects.create(
            organization_id=organization_id,
            state_id=property_state.id,
            name='Import Creation',
            description='Creation from Import file.',
            import_filename=self.file.path,
            record_type=AUDIT_IMPORT
        )

        # set the property_state_id so that we can list the building files by properties
        self.property_state_id = property_state.id
        self.save()

        # add in the measures
        for m in data.get('measures', []):
            # Find the measure in the database
            try:
                measure = Measure.objects.get(
                    category=m['category'], name=m['name'], organization_id=organization_id,
                )
            except Measure.DoesNotExist:
                # TODO: Deal with it
                continue

            # Add the measure to the join table.
            # Need to determine what constitutes the unique measure for a property
            join, _ = PropertyMeasure.objects.get_or_create(
                property_state_id=self.property_state_id,
                measure_id=measure.pk,
                implementation_status=PropertyMeasure.str_to_impl_status(m['implementation_status']),
                application_scale=PropertyMeasure.str_to_application_scale(
                    m.get('application_scale_of_application',
                          PropertyMeasure.SCALE_ENTIRE_FACILITY)
                ),
                category_affected=PropertyMeasure.str_to_category_affected(
                    m.get('system_category_affected', PropertyMeasure.CATEGORY_OTHER)
                ),
                recommended=m.get('recommended', 'false') == 'true',
            )
            join.description = m.get('description')
            join.property_measure_name = m.get('property_measure_name')
            join.cost_mv = m.get('mv_cost')
            join.cost_total_first = m.get('measure_total_first_cost')
            join.cost_installation = m.get('measure_installation_cost')
            join.cost_material = m.get('measure_material_cost')
            join.cost_capital_replacement = m.get('measure_capital_replacement_cost')
            join.cost_residual_value = m.get('measure_residual_value')
            join.save()

        # add in scenarios
        for s in data.get('scenarios', []):
            # measures = models.ManyToManyField(PropertyMeasure)

            # {'reference_case': u'Baseline', 'annual_savings_site_energy': None,
            #  'measures': [], 'id': u'Baseline', 'name': u'Baseline'}

            scenario, _ = Scenario.objects.get_or_create(
                name=s.get('name'),
                property_state_id=self.property_state_id,
            )
            scenario.description = s.get('description')
            scenario.annual_site_energy_savings = s.get('annual_site_energy_savings')
            scenario.annual_source_energy_savings = s.get('annual_source_energy_savings')
            scenario.annual_cost_savings = s.get('annual_cost_savings')
            scenario.summer_peak_load_reduction = s.get('summer_peak_load_reduction')
            scenario.winter_peak_load_reduction = s.get('winter_peak_load_reduction')
            scenario.hdd = s.get('hdd')
            scenario.hdd_base_temperature = s.get('hdd_base_temperature')
            scenario.cdd = s.get('cdd')
            scenario.cdd_base_temperature = s.get('cdd_base_temperature')

            # temporal_status = models.IntegerField(choices=TEMPORAL_STATUS_TYPES,
            #                                       default=TEMPORAL_STATUS_CURRENT)

            if s.get('reference_case'):
                ref_case = Scenario.objects.filter(
                    name=s.get('reference_case'),
                    property_state_id=self.property_state_id,
                )
                if len(ref_case) == 1:
                    scenario.reference_case = ref_case.first()

            # set the list of measures
            for measure_name in s['measures']:
                # find the join measure in the database
                measure = None
                try:
                    measure = PropertyMeasure.objects.get(
                        property_state_id=self.property_state_id,
                        property_measure_name=measure_name,
                    )
                except PropertyMeasure.DoesNotExist:
                    # PropertyMeasure is not in database, skipping silently
                    continue

                scenario.measures.add(measure)

            scenario.save()

        if property_view:
            # create a new blank state to merge the two together
            merged_state = PropertyState.objects.create(organization_id=organization_id)

            # assume the same cycle id as the former state.
            # should merge_state also copy/move over the relationships?
            merged_state = merge_state(merged_state,
                                       property_view.state,
                                       property_state,
                                       get_state_attrs([property_view.state, property_state]))

            # log the merge
            # Not a fan of the parent1/parent2 logic here, seems error prone, what this
            # is also in here: https://github.com/SEED-platform/seed/blob/63536e99cf5be3a9a86391c5cead6dd4ff74462b/seed/data_importer/tasks.py#L1549
            PropertyAuditLog.objects.create(
                organization_id=organization_id,
                parent1=PropertyAuditLog.objects.filter(state=property_view.state).first(),
                parent2=PropertyAuditLog.objects.filter(state=property_state).first(),
                parent_state1=property_view.state,
                parent_state2=property_state,
                state=merged_state,
                name='System Match',
                description='Automatic Merge',
                import_filename=None,
                record_type=AUDIT_IMPORT
            )

            property_view.state = merged_state
            property_view.save()

            merged_state.merge_state = MERGE_STATE_MERGED
            merged_state.save()

            # set the property_state to the new one
            property_state = merged_state
        elif not property_view:
            property_view = property_state.promote(cycle)
        else:
            # invalid arguments, must pass both or neither
            return False, None, None, "Invalid arguments passed to BuildingFile.process()"

        return True, property_state, property_view, messages