Пример #1
0
    def test_decode_ubids_is_successful_when_valid_UBID_provided(self):
        property_details = self.property_state_factory.get_details()
        property_details['organization_id'] = self.org.id
        property_details['ubid'] = '86HJPCWQ+2VV-1-3-2-3'

        property = PropertyState(**property_details)
        property.save()
        properties = PropertyState.objects.filter(pk=property.id)

        decode_unique_ids(properties)
        refreshed_property = PropertyState.objects.get(pk=property.id)
        property_bounding_box_wkt = (
            "POLYGON ((-87.56021875000002 41.74504999999999, "
            "-87.56021875000002 41.74514999999997, "
            "-87.56043749999996 41.74514999999997, "
            "-87.56043749999996 41.74504999999999, "
            "-87.56021875000002 41.74504999999999))")
        property_centroid_wkt = (
            "POLYGON ((-87.56031249999999 41.74509999999998, "
            "-87.56031249999999 41.74512499999997, "
            "-87.56034374999999 41.74512499999997, "
            "-87.56034374999999 41.74509999999998, "
            "-87.56031249999999 41.74509999999998))")
        self.assertEqual(property_bounding_box_wkt,
                         bounding_box_wkt(refreshed_property))
        self.assertEqual(property_centroid_wkt,
                         centroid_wkt(refreshed_property))
        self.assertEqual(refreshed_property.latitude, 41.7451)
        self.assertEqual(refreshed_property.longitude, -87.560328125)
Пример #2
0
    def test_ubid_decode_by_id_endpoint_base(self):
        property_details = self.property_state_factory.get_details()
        property_details['organization_id'] = self.org.id
        property_details['ubid'] = '86HJPCWQ+2VV-1-3-2-3'

        property = PropertyState(**property_details)
        property.save()

        property_view = self.property_view_factory.get_property_view(
            state=property)

        url = reverse(
            'api:v3:ubid-decode-by-ids') + '?organization_id=%s' % self.org.pk
        post_params = {'property_view_ids': [property_view.id]}

        self.client.post(url, post_params)

        refreshed_property = PropertyState.objects.get(pk=property.id)

        property_bounding_box_wkt = (
            "POLYGON ((-87.56021875000002 41.74504999999999, "
            "-87.56021875000002 41.74514999999997, "
            "-87.56043749999996 41.74514999999997, "
            "-87.56043749999996 41.74504999999999, "
            "-87.56021875000002 41.74504999999999))")
        property_centroid_wkt = (
            "POLYGON ((-87.56031249999999 41.74509999999998, "
            "-87.56031249999999 41.74512499999997, "
            "-87.56034374999999 41.74512499999997, "
            "-87.56034374999999 41.74509999999998, "
            "-87.56031249999999 41.74509999999998))")
        self.assertEqual(property_bounding_box_wkt,
                         bounding_box_wkt(refreshed_property))
        self.assertEqual(property_centroid_wkt,
                         centroid_wkt(refreshed_property))
Пример #3
0
    def test_decode_ulids_is_successful_when_valid_ULID_provided(self):
        taxlot_details = self.taxlot_state_factory.get_details()
        taxlot_details['organization_id'] = self.org.id
        taxlot_details['ulid'] = '86HJPCWQ+2VV-1-3-2-3'

        taxlot = TaxLotState(**taxlot_details)
        taxlot.save()
        taxlots = TaxLotState.objects.filter(pk=taxlot.id)

        decode_unique_ids(taxlots)
        refreshed_taxlot = TaxLotState.objects.get(pk=taxlot.id)
        taxlot_bounding_box_wkt = (
            "POLYGON ((-87.56021875000002 41.74504999999999, "
            "-87.56021875000002 41.74514999999997, "
            "-87.56043749999996 41.74514999999997, "
            "-87.56043749999996 41.74504999999999, "
            "-87.56021875000002 41.74504999999999))")
        taxlot_centroid_wkt = (
            "POLYGON ((-87.56031249999999 41.74509999999998, "
            "-87.56031249999999 41.74512499999997, "
            "-87.56034374999999 41.74512499999997, "
            "-87.56034374999999 41.74509999999998, "
            "-87.56031249999999 41.74509999999998))")
        self.assertEqual(taxlot_bounding_box_wkt,
                         bounding_box_wkt(refreshed_taxlot))
        self.assertEqual(taxlot_centroid_wkt, centroid_wkt(refreshed_taxlot))
        self.assertEqual(refreshed_taxlot.latitude, 41.7451)
        self.assertEqual(refreshed_taxlot.longitude, -87.560328125)
Пример #4
0
    def test_decode_ubids_is_successful_when_v2_format_C_NW_SE_UBID_provided(
            self):
        property_details = self.property_state_factory.get_details()
        property_details['organization_id'] = self.org.id
        property_details['ubid'] = '849VQJH6+95J-849VQJH5+VGW-849VQJG6+XV8'

        property = PropertyState(**property_details)
        property.save()
        properties = PropertyState.objects.filter(pk=property.id)

        decode_unique_ids(properties)
        refreshed_property = PropertyState.objects.get(pk=property.id)
        property_bounding_box_wkt = (
            "POLYGON ((-122.38778125 37.77740000000001, "
            "-122.38778125 37.77975000000001, "
            "-122.3911875 37.77975000000001, "
            "-122.3911875 37.77740000000001, "
            "-122.38778125 37.77740000000001))")
        property_centroid_wkt = ("POLYGON ((-122.38959375 37.77845, "
                                 "-122.38959375 37.778475, "
                                 "-122.389625 37.778475, "
                                 "-122.389625 37.77845, "
                                 "-122.38959375 37.77845))")
        self.assertEqual(property_bounding_box_wkt,
                         bounding_box_wkt(refreshed_property))
        self.assertEqual(property_centroid_wkt,
                         centroid_wkt(refreshed_property))
        self.assertEqual(refreshed_property.latitude, 37.778575)
        self.assertEqual(refreshed_property.longitude, -122.389484375)
Пример #5
0
    def test_decode_ulids_does_nothing_if_no_ULID_provided(self):
        taxlot_details = self.taxlot_state_factory.get_details()
        taxlot_details['organization_id'] = self.org.id

        taxlot = TaxLotState(**taxlot_details)
        taxlot.save()
        taxlots = PropertyState.objects.filter(pk=taxlot.id)

        decode_unique_ids(taxlots)
        refreshed_taxlot = TaxLotState.objects.get(pk=taxlot.id)

        self.assertIsNone(bounding_box_wkt(refreshed_taxlot))
        self.assertIsNone(centroid_wkt(refreshed_taxlot))
Пример #6
0
    def test_decode_ubids_does_nothing_if_no_UBID_provided(self):
        property_details = self.property_state_factory.get_details()
        property_details['organization_id'] = self.org.id

        property = PropertyState(**property_details)
        property.save()
        properties = PropertyState.objects.filter(pk=property.id)

        decode_unique_ids(properties)
        refreshed_property = PropertyState.objects.get(pk=property.id)

        self.assertIsNone(bounding_box_wkt(refreshed_property))
        self.assertIsNone(centroid_wkt(refreshed_property))
Пример #7
0
    def test_centroid_wkt_takes_a_state_and_returns_the_WKT_string_or_None(
            self):
        property_details = self.property_state_factory.get_details()
        property_details['organization_id'] = self.org.id

        no_centroid_property = PropertyState(**property_details)
        no_centroid_property.save()

        property_details['centroid'] = 'POLYGON ((0 0, 4 0, 4 4, 0 4, 0 0))'

        centroid_property = PropertyState(**property_details)
        centroid_property.save()

        no_centroid_record = PropertyState.objects.get(
            pk=no_centroid_property.id)
        geocoded_record = PropertyState.objects.get(pk=centroid_property.id)

        self.assertIsNone(no_centroid_record.centroid)
        self.assertIsNone(centroid_wkt(no_centroid_record))

        self.assertIsInstance(geocoded_record.centroid, Polygon)
        self.assertEqual('POLYGON ((0 0, 4 0, 4 4, 0 4, 0 0))',
                         centroid_wkt(centroid_property))
Пример #8
0
    def test_decode_ulids_doesnt_throw_an_error_if_an_invalid_ulid_is_provided(
            self):
        taxlot_details = self.taxlot_state_factory.get_details()
        taxlot_details['organization_id'] = self.org.id
        taxlot_details['ulid'] = 'invalidulid'

        taxlot = TaxLotState(**taxlot_details)
        taxlot.save()
        taxlots = TaxLotState.objects.filter(pk=taxlot.id)

        decode_unique_ids(taxlots)

        refreshed_taxlot = TaxLotState.objects.get(pk=taxlot.id)

        self.assertIsNone(bounding_box_wkt(refreshed_taxlot))
        self.assertIsNone(centroid_wkt(refreshed_taxlot))
Пример #9
0
    def get_related(cls, object_list, show_columns, columns_from_database):
        """
        This method takes a list of TaxLotViews or PropertyViews and returns the data along
        with the related TaxLotView or PropertyView.

        The columns are the items as seen by the front end. This means that the columns
        are prepended with tax_ or property_ if they are the related columns.

        This method is just a copy/abstraction from the _get_filtered_results in the
        Property/TaxLot viewset.  In the future this should become a serializer. For now it is
        here so that we can use this method to create the data for exporting to CSV on the backend.

        :param object_list: list
        :param show_columns: list, columns (as defined by backend), Pass None to default to all columns excluding extra
                             data
        :param columns_from_database: list, columns from the database as list of dict
        :return: list
        """
        results = []

        if len(object_list) == 0:
            return results

        Note = apps.get_model('seed', 'Note')

        if object_list[0].__class__.__name__ == 'PropertyView':
            lookups = {
                'obj_class': 'PropertyView',
                'obj_query_in': 'property_view_id__in',
                'obj_state_id': 'property_state_id',
                'obj_view_id': 'property_view_id',
                'obj_id': 'property_id',
                'centroid': 'centroid',
                'bounding_box': 'bounding_box',
                'long_lat': 'long_lat',
                'related_class': 'TaxLotView',
                'related_query_in': 'taxlot_view_id__in',
                'select_related': 'taxlot',
                'related_view': 'taxlot_view',
                'related_view_id': 'taxlot_view_id',
                'related_state_id': 'taxlot_state_id',
            }
        else:
            lookups = {
                'obj_class': 'TaxLotView',
                'obj_query_in': 'taxlot_view_id__in',
                'obj_state_id': 'taxlot_state_id',
                'obj_view_id': 'taxlot_view_id',
                'obj_id': 'taxlot_id',
                'centroid': 'centroid',
                'bounding_box': 'bounding_box',
                'long_lat': 'long_lat',
                'related_class': 'PropertyView',
                'related_query_in': 'property_view_id__in',
                'select_related': 'property',
                'related_view': 'property_view',
                'related_view_id': 'property_view_id',
                'related_state_id': 'property_state_id',
            }

        # Ids of views to look up in m2m
        ids = [obj.pk for obj in object_list]
        joins = TaxLotProperty.objects.filter(**{lookups['obj_query_in']: ids}).select_related(lookups['related_view'])

        # Get all ids of related views on these joins
        related_ids = [getattr(j, lookups['related_view_id']) for j in joins]

        # Get all related views from the related_class
        related_views = apps.get_model('seed', lookups['related_class']).objects.select_related(
            lookups['select_related'], 'state', 'cycle').filter(pk__in=related_ids)

        related_columns = []
        related_column_name_mapping = {}
        obj_columns = []
        obj_column_name_mapping = {}
        for column in columns_from_database:
            if column['related']:
                related_columns.append(column)
                related_column_name_mapping[column['column_name']] = column['name']
            else:
                obj_columns.append(column)
                obj_column_name_mapping[column['column_name']] = column['name']

        related_map = {}

        if show_columns is None:
            filtered_fields = set([col['column_name'] for col in related_columns if not col['is_extra_data']])
        else:
            filtered_fields = set([col['column_name'] for col in related_columns if not col['is_extra_data']
                                   and col['id'] in show_columns])
            filtered_extra_data_fields = set([col['column_name'] for col in related_columns if col['is_extra_data']
                                              and col['id'] in show_columns])

        for related_view in related_views:
            related_dict = TaxLotProperty.model_to_dict_with_mapping(
                related_view.state,
                related_column_name_mapping,
                fields=filtered_fields,
                exclude=['extra_data']
            )

            related_dict[lookups['related_state_id']] = related_view.state.id

            # Add GIS stuff to the related dict
            # (I guess these are special fields not in columns and not directly JSON serializable...)
            related_dict[lookups['bounding_box']] = bounding_box_wkt(related_view.state)
            related_dict[lookups['long_lat']] = long_lat_wkt(related_view.state)
            related_dict[lookups['centroid']] = centroid_wkt(related_view.state)

            # custom handling for when it is TaxLotView
            if lookups['obj_class'] == 'TaxLotView':
                if 'campus' in filtered_fields:
                    related_dict[related_column_name_mapping['campus']] = related_view.property.campus
                # Do not make these timestamps naive. They persist correctly.
                if 'updated' in filtered_fields:
                    related_dict[related_column_name_mapping['updated']] = related_view.property.updated
                if 'created' in filtered_fields:
                    related_dict[related_column_name_mapping['created']] = related_view.property.created
                # Replace the enumerations
                if 'analysis_state' in filtered_fields:
                    related_dict[
                        related_column_name_mapping['analysis_state']] = related_view.state.get_analysis_state_display()
            elif lookups['obj_class'] == 'PropertyView':
                # Do not make these timestamps naive. They persist correctly.
                if 'updated' in filtered_fields:
                    related_dict[related_column_name_mapping['updated']] = related_view.taxlot.updated
                if 'created' in filtered_fields:
                    related_dict[related_column_name_mapping['created']] = related_view.taxlot.created

            # Only add extra data columns if a settings profile was used
            if show_columns is not None:
                related_dict.update(
                    TaxLotProperty.extra_data_to_dict_with_mapping(
                        related_view.state.extra_data,
                        related_column_name_mapping,
                        fields=filtered_extra_data_fields
                    ).items()
                )
            related_map[related_view.pk] = related_dict

            # Replace taxlot_view id with taxlot id
            related_map[related_view.pk]['id'] = getattr(related_view, lookups['select_related']).id

        # Not sure what this code is really doing, but it only exists for TaxLotViews
        if lookups['obj_class'] == 'TaxLotView':
            # Get whole taxlotstate table:
            tuple_prop_to_jurisdiction_tl = tuple(
                TaxLotProperty.objects.values_list('property_view_id', 'taxlot_view__state__jurisdiction_tax_lot_id')
            )

            # create a mapping that defaults to an empty list
            prop_to_jurisdiction_tl = defaultdict(list)

            # populate the mapping
            for name, pth in tuple_prop_to_jurisdiction_tl:
                prop_to_jurisdiction_tl[name].append(pth)

        join_note_counts = {x[0]: x[1] for x in Note.objects.filter(**{lookups['related_query_in']: related_ids})
                            .values_list(lookups['related_view_id']).order_by().annotate(Count(lookups['related_view_id']))}

        # A mapping of object's view pk to a list of related state info for a related view
        join_map = {}
        for join in joins:
            # Another taxlot specific view
            if lookups['obj_class'] == 'TaxLotView':
                jurisdiction_tax_lot_ids = prop_to_jurisdiction_tl[join.property_view_id]

                # Filter out associated tax lots that are present but which do not have preferred
                none_in_jurisdiction_tax_lot_ids = None in jurisdiction_tax_lot_ids
                jurisdiction_tax_lot_ids = list(filter(lambda x: x is not None, jurisdiction_tax_lot_ids))

                if none_in_jurisdiction_tax_lot_ids:
                    jurisdiction_tax_lot_ids.append('Missing')

                join_dict = related_map[getattr(join, lookups['related_view_id'])].copy()
                join_dict.update({
                    # 'primary': 'P' if join.primary else 'S',
                    # 'calculated_taxlot_ids': '; '.join(jurisdiction_tax_lot_ids),
                    lookups['related_view_id']: getattr(join, lookups['related_view_id'])
                })

            else:
                join_dict = related_map[getattr(join, lookups['related_view_id'])].copy()
                join_dict.update({
                    # 'primary': 'P' if join.primary else 'S',
                    lookups['related_view_id']: getattr(join, lookups['related_view_id'])
                })

            join_dict['notes_count'] = join_note_counts.get(join.id, 0)

            # remove the measures from this view for now
            if join_dict.get('measures'):
                del join_dict['measures']

            try:
                join_map[getattr(join, lookups['obj_view_id'])].append(join_dict)
            except KeyError:
                join_map[getattr(join, lookups['obj_view_id'])] = [join_dict]

        if show_columns is None:
            filtered_fields = set([col['column_name'] for col in obj_columns if not col['is_extra_data']])
        else:
            filtered_fields = set([col['column_name'] for col in obj_columns if not col['is_extra_data']
                                   and col['id'] in show_columns])
            filtered_extra_data_fields = set([col['column_name'] for col in obj_columns if col['is_extra_data']
                                              and col['id'] in show_columns])

        obj_note_counts = {x[0]: x[1] for x in Note.objects.filter(**{lookups['obj_query_in']: ids})
                           .values_list(lookups['obj_view_id']).order_by().annotate(Count(lookups['obj_view_id']))}

        for obj in object_list:
            # Each object in the response is built from the state data, with related data added on.
            obj_dict = TaxLotProperty.model_to_dict_with_mapping(obj.state,
                                                                 obj_column_name_mapping,
                                                                 fields=filtered_fields,
                                                                 exclude=['extra_data'])

            # Only add extra data columns if a settings profile was used
            if show_columns is not None:
                obj_dict.update(
                    TaxLotProperty.extra_data_to_dict_with_mapping(
                        obj.state.extra_data,
                        obj_column_name_mapping,
                        fields=filtered_extra_data_fields
                    ).items()
                )

            # Use property_id instead of default (state_id)
            obj_dict['id'] = getattr(obj, lookups['obj_id'])
            obj_dict['notes_count'] = obj_note_counts.get(obj.id, 0)

            obj_dict[lookups['obj_state_id']] = obj.state.id
            obj_dict[lookups['obj_view_id']] = obj.id

            # bring in GIS data
            obj_dict[lookups['bounding_box']] = bounding_box_wkt(obj.state)
            obj_dict[lookups['long_lat']] = long_lat_wkt(obj.state)

            # store the property / taxlot data to the object dictionary as well. This is hacky.
            if lookups['obj_class'] == 'PropertyView':
                # bring in property-specific GIS data
                obj_dict[lookups['centroid']] = centroid_wkt(obj.state)

                if 'campus' in filtered_fields:
                    obj_dict[obj_column_name_mapping['campus']] = obj.property.campus
                # Do not make these timestamps naive. They persist correctly.
                if 'created' in filtered_fields:
                    obj_dict[obj_column_name_mapping['created']] = obj.property.created
                if 'updated' in filtered_fields:
                    obj_dict[obj_column_name_mapping['updated']] = obj.property.updated
                if 'analysis_state' in filtered_fields:
                    obj_dict[obj_column_name_mapping['analysis_state']] = obj.state.get_analysis_state_display()
            elif lookups['obj_class'] == 'TaxLotView':
                # Do not make these timestamps naive. They persist correctly.
                if 'updated' in filtered_fields:
                    obj_dict[obj_column_name_mapping['updated']] = obj.taxlot.updated
                if 'created' in filtered_fields:
                    obj_dict[obj_column_name_mapping['created']] = obj.taxlot.created

            # All the related tax lot states.
            obj_dict['related'] = join_map.get(obj.pk, [])

            # remove the measures from this view for now
            if obj_dict.get('measures'):
                del obj_dict['measures']

            results.append(obj_dict)

        return results