Ejemplo n.º 1
0
class UbidViewTests(TestCase):
    def setUp(self):
        user_details = {
            'username': '******',
            'password': '******',
        }
        self.user = User.objects.create_superuser(email='*****@*****.**',
                                                  **user_details)
        self.org, _, _ = create_organization(self.user)
        self.client.login(**user_details)

        self.property_state_factory = FakePropertyStateFactory(
            organization=self.org)
        self.taxlot_state_factory = FakeTaxLotStateFactory(
            organization=self.org)
        self.property_view_factory = FakePropertyViewFactory(
            organization=self.org)
        self.taxlot_view_factory = FakeTaxLotViewFactory(organization=self.org)

    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))

    def test_decode_ubid_results_returns_a_summary_dictionary(self):
        property_none_details = self.property_state_factory.get_details()
        property_none_details["organization_id"] = self.org.id
        property_none = PropertyState(**property_none_details)
        property_none.save()

        property_none_view = self.property_view_factory.get_property_view(
            state=property_none)

        property_correctly_populated_details = self.property_state_factory.get_details(
        )
        property_correctly_populated_details["organization_id"] = self.org.id
        property_correctly_populated_details['ubid'] = '86HJPCWQ+2VV-1-3-2-3'
        property_correctly_populated_details['bounding_box'] = (
            "POLYGON ((-87.56021875000002 41.74504999999999, "
            "-87.56021875000002 41.74514999999997, "
            "-87.56043749999996 41.74514999999997, "
            "-87.56043749999996 41.74504999999999, "
            "-87.56021875000002 41.74504999999999))")
        property_correctly_populated_details['centroid'] = (
            "POLYGON ((-87.56031249999999 41.74509999999998, "
            "-87.56031249999999 41.74512499999997, "
            "-87.56034374999999 41.74512499999997, "
            "-87.56034374999999 41.74509999999998, "
            "-87.56031249999999 41.74509999999998))")
        property_correctly_populated = PropertyState(
            **property_correctly_populated_details)
        property_correctly_populated.save()

        property_correctly_populated_view = self.property_view_factory.get_property_view(
            state=property_correctly_populated)

        property_not_decoded_details = self.property_state_factory.get_details(
        )
        property_not_decoded_details["organization_id"] = self.org.id
        property_not_decoded_details['ubid'] = '86HJPCWQ+2VV-1-3-2-3'
        # bounding_box could be populated from a GeoJSON import
        property_not_decoded_details['bounding_box'] = (
            "POLYGON ((-87.56021875000002 41.74504999999999, "
            "-87.56021875000002 41.74514999999997, "
            "-87.56043749999996 41.74514999999997, "
            "-87.56043749999996 41.74504999999999, "
            "-87.56021875000002 41.74504999999999))")
        property_not_decoded = PropertyState(**property_not_decoded_details)
        property_not_decoded.save()

        property_not_decoded_view = self.property_view_factory.get_property_view(
            state=property_not_decoded)

        url = reverse(
            'api:v3:ubid-decode-results') + '?organization_id=%s' % self.org.pk
        post_params = {
            'property_view_ids': [
                property_none_view.id, property_correctly_populated_view.id,
                property_not_decoded_view.id
            ]
        }

        result = self.client.post(url, post_params)
        result_dict = ast.literal_eval(result.content.decode("utf-8"))

        expectation = {
            "ubid_unpopulated": 1,
            "ubid_successfully_decoded": 1,
            "ubid_not_decoded": 1,
            "ulid_unpopulated": 0,
            "ulid_successfully_decoded": 0,
            "ulid_not_decoded": 0
        }

        self.assertEqual(result_dict, expectation)

    def test_decode_ulid_results_returns_a_summary_dictionary(self):
        taxlot_none_details = self.taxlot_state_factory.get_details()
        taxlot_none_details["organization_id"] = self.org.id
        taxlot_none = TaxLotState(**taxlot_none_details)
        taxlot_none.save()

        taxlot_none_view = self.taxlot_view_factory.get_taxlot_view(
            state=taxlot_none)

        taxlot_correctly_populated_details = self.taxlot_state_factory.get_details(
        )
        taxlot_correctly_populated_details["organization_id"] = self.org.id
        taxlot_correctly_populated_details['ulid'] = '86HJPCWQ+2VV-1-3-2-3'
        taxlot_correctly_populated_details['bounding_box'] = (
            "POLYGON ((-87.56021875000002 41.74504999999999, "
            "-87.56021875000002 41.74514999999997, "
            "-87.56043749999996 41.74514999999997, "
            "-87.56043749999996 41.74504999999999, "
            "-87.56021875000002 41.74504999999999))")
        taxlot_correctly_populated_details['centroid'] = (
            "POLYGON ((-87.56031249999999 41.74509999999998, "
            "-87.56031249999999 41.74512499999997, "
            "-87.56034374999999 41.74512499999997, "
            "-87.56034374999999 41.74509999999998, "
            "-87.56031249999999 41.74509999999998))")
        taxlot_correctly_populated = TaxLotState(
            **taxlot_correctly_populated_details)
        taxlot_correctly_populated.save()

        taxlot_correctly_populated_view = self.taxlot_view_factory.get_taxlot_view(
            state=taxlot_correctly_populated)

        taxlot_not_decoded_details = self.taxlot_state_factory.get_details()
        taxlot_not_decoded_details["organization_id"] = self.org.id
        taxlot_not_decoded_details['ulid'] = '86HJPCWQ+2VV-1-3-2-3'
        # bounding_box could be populated from a GeoJSON import
        taxlot_not_decoded_details['bounding_box'] = (
            "POLYGON ((-87.56021875000002 41.74504999999999, "
            "-87.56021875000002 41.74514999999997, "
            "-87.56043749999996 41.74514999999997, "
            "-87.56043749999996 41.74504999999999, "
            "-87.56021875000002 41.74504999999999))")
        taxlot_not_decoded = TaxLotState(**taxlot_not_decoded_details)
        taxlot_not_decoded.save()

        taxlot_not_decoded_view = self.taxlot_view_factory.get_taxlot_view(
            state=taxlot_not_decoded)

        url = reverse(
            'api:v3:ubid-decode-results') + '?organization_id=%s' % self.org.pk
        post_params = {
            'taxlot_view_ids': [
                taxlot_none_view.id, taxlot_correctly_populated_view.id,
                taxlot_not_decoded_view.id
            ]
        }

        result = self.client.post(url, post_params)
        result_dict = ast.literal_eval(result.content.decode("utf-8"))

        expectation = {
            "ubid_unpopulated": 0,
            "ubid_successfully_decoded": 0,
            "ubid_not_decoded": 0,
            "ulid_unpopulated": 1,
            "ulid_successfully_decoded": 1,
            "ulid_not_decoded": 1
        }

        self.assertEqual(result_dict, expectation)
Ejemplo n.º 2
0
class UbidUtilMethods(TestCase):
    def setUp(self):
        user_details = {
            'username': '******',
            'password': '******',
        }
        self.user = User.objects.create_superuser(email='*****@*****.**',
                                                  **user_details)
        self.org, _, _ = create_organization(self.user)
        self.org.save()

        self.property_state_factory = FakePropertyStateFactory(
            organization=self.org)
        self.taxlot_state_factory = FakeTaxLotStateFactory(
            organization=self.org)

    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)

    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)

    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))

    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))

    def test_decode_ubids_doesnt_throw_an_error_if_an_invalid_ubid_is_provided(
            self):
        property_details = self.property_state_factory.get_details()
        property_details['organization_id'] = self.org.id
        property_details['ubid'] = 'invalidubid'

        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))

    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))
Ejemplo n.º 3
0
class GeocodeAddresses(TestCase):
    def setUp(self):
        user_details = {
            'username': '******',
            'password': '******',
        }
        self.user = User.objects.create_superuser(
            email='*****@*****.**', **user_details
        )
        self.org, _, _ = create_organization(self.user)
        self.org.mapquest_api_key = test_key
        self.org.save()

        self.property_state_factory = FakePropertyStateFactory(organization=self.org)
        self.tax_lot_state_factory = FakeTaxLotStateFactory(organization=self.org)

    def test_geocode_buildings_successful_when_real_fields_provided(self):
        with base_vcr.use_cassette('seed/tests/data/vcr_cassettes/geocode_base_case.yaml'):
            property_details = self.property_state_factory.get_details()
            property_details['organization_id'] = self.org.id
            property_details['address_line_1'] = "3001 Brighton Blvd"
            property_details['address_line_2'] = "suite 2693"
            property_details['city'] = "Denver"
            property_details['state'] = "Colorado"
            property_details['postal_code'] = "80216"

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

            tax_lot_details = self.tax_lot_state_factory.get_details()
            tax_lot_details['organization_id'] = self.org.id
            tax_lot_details['address_line_1'] = "2020 Lawrence St"
            tax_lot_details['address_line_2'] = "unit A"
            tax_lot_details['city'] = "Denver"
            tax_lot_details['state'] = "Colorado"
            tax_lot_details['postal_code'] = "80205"

            tax_lot = TaxLotState(**tax_lot_details)
            tax_lot.save()
            tax_lots = TaxLotState.objects.filter(pk=tax_lot.id)

            geocode_buildings(properties)
            geocode_buildings(tax_lots)

            refreshed_property = PropertyState.objects.get(pk=property.id)
            refreshed_tax_lot = TaxLotState.objects.get(pk=tax_lot.id)

            self.assertEqual('POINT (-104.986138 39.765251)', long_lat_wkt(refreshed_property))
            self.assertEqual('High (P1AAA)', refreshed_property.geocoding_confidence)
            self.assertEqual(-104.986138, refreshed_property.longitude)
            self.assertEqual(39.765251, refreshed_property.latitude)

            self.assertEqual('POINT (-104.991046 39.752396)', long_lat_wkt(refreshed_tax_lot))
            self.assertEqual('High (P1AAA)', refreshed_tax_lot.geocoding_confidence)

    def test_geocode_buildings_returns_no_data_when_provided_address_is_ambigious(self):
        with base_vcr.use_cassette('seed/tests/data/vcr_cassettes/geocode_low_geocodequality.yaml'):
            state_zip_only_details = self.property_state_factory.get_details()
            state_zip_only_details['organization_id'] = self.org.id
            state_zip_only_details['address_line_1'] = ""
            state_zip_only_details['address_line_2'] = ""
            state_zip_only_details['city'] = ""
            state_zip_only_details['state'] = "Colorado"
            state_zip_only_details['postal_code'] = "80202"

            state_zip_only_property = PropertyState(**state_zip_only_details)
            state_zip_only_property.save()

            wrong_state_zip_details = self.property_state_factory.get_details()
            wrong_state_zip_details['organization_id'] = self.org.id
            wrong_state_zip_details['address_line_1'] = "3001 Brighton Blvd"
            wrong_state_zip_details['address_line_2'] = "suite 2693"
            wrong_state_zip_details['city'] = "Denver"
            wrong_state_zip_details['state'] = "New Jersey"
            wrong_state_zip_details['postal_code'] = "08081"

            wrong_state_zip_property = PropertyState(**wrong_state_zip_details)
            wrong_state_zip_property.save()

            ids = [state_zip_only_property.id, wrong_state_zip_property.id]

            properties = PropertyState.objects.filter(id__in=ids)

            geocode_buildings(properties)

            state_zip_only_property = PropertyState.objects.get(pk=state_zip_only_property.id)
            wrong_state_zip_property = PropertyState.objects.get(pk=wrong_state_zip_property.id)

            self.assertIsNone(state_zip_only_property.long_lat)
            self.assertIsNone(state_zip_only_property.longitude)
            self.assertIsNone(state_zip_only_property.latitude)
            self.assertEqual("Missing address components (N/A)", state_zip_only_property.geocoding_confidence)

            self.assertIsNone(wrong_state_zip_property.long_lat)
            self.assertIsNone(wrong_state_zip_property.longitude)
            self.assertIsNone(wrong_state_zip_property.latitude)
            self.assertEqual("Low - check address (B3BCA)", wrong_state_zip_property.geocoding_confidence)

    def test_geocode_buildings_is_successful_even_if_two_buildings_have_same_address(self):
        with base_vcr.use_cassette('seed/tests/data/vcr_cassettes/geocode_dup_addresses.yaml'):
            property_details = self.property_state_factory.get_details()
            property_details['organization_id'] = self.org.id
            property_details['address_line_1'] = "3001 Brighton Blvd"
            property_details['address_line_2'] = "suite 2693"
            property_details['city'] = "Denver"
            property_details['state'] = "Colorado"
            property_details['postal_code'] = "80216"

            property_1 = PropertyState(**property_details)
            property_2 = PropertyState(**property_details)
            property_1.save()
            property_2.save()

            ids = [property_1.id, property_2.id]

            properties = PropertyState.objects.filter(id__in=ids)

            geocode_buildings(properties)

            refreshed_properties = PropertyState.objects.filter(id__in=ids)

            for property in refreshed_properties:
                self.assertEqual('POINT (-104.986138 39.765251)', long_lat_wkt(property))
                self.assertEqual('High (P1AAA)', property.geocoding_confidence)
                self.assertEqual(-104.986138, property.longitude)
                self.assertEqual(39.765251, property.latitude)

    def test_geocode_buildings_is_successful_with_over_100_properties(self):
        with batch_vcr.use_cassette('seed/tests/data/vcr_cassettes/geocode_101_unique_addresses.yaml', match_on=['uri_length']):
            property_details = self.property_state_factory.get_details()
            property_details['organization_id'] = self.org.id
            property_details['address_line_2'] = ""
            property_details['city'] = "Denver"
            property_details['state'] = "Colorado"
            property_details['postal_code'] = "80202"

            ids = []
            for n in range(101):
                street_number = n + 1600
                property_details['address_line_1'] = str(street_number) + " Larimer Street"

                property = PropertyState(**property_details)
                property.save()
                ids.append(property.id)

            properties = PropertyState.objects.filter(id__in=ids).order_by('id')

            geocode_buildings(properties)

            refreshed_properties = PropertyState.objects.filter(id__in=ids).order_by('id')

            long_lats = [
                property.long_lat
                for property
                in refreshed_properties
                if property.long_lat is not None
            ]
            geocode_confidence_results = [
                property.geocoding_confidence
                for property
                in refreshed_properties
                if property.geocoding_confidence is not None
            ]

            self.assertTrue(len(long_lats) > 0)
            self.assertTrue(len(geocode_confidence_results) == 101)

    def test_geocode_buildings_is_unsuccessful_when_the_API_key_is_invalid_or_expired(self):
        with base_vcr.use_cassette('seed/tests/data/vcr_cassettes/geocode_invalid_or_expired_key.yaml'):
            self.org_fake_key, _, _ = create_organization(self.user)
            self.org_fake_key.mapquest_api_key = 'fakeapikey'
            self.org_fake_key.save()

            self.property_state_factory_fake_key = FakePropertyStateFactory(organization=self.org_fake_key)

            property_details_fake_key = self.property_state_factory_fake_key.get_details()
            property_details_fake_key['organization_id'] = self.org_fake_key.id
            property_details_fake_key['address_line_1'] = "3001 Brighton Blvd"
            property_details_fake_key['address_line_2'] = "suite 2693"
            property_details_fake_key['city'] = "Denver"
            property_details_fake_key['state'] = "Colorado"
            property_details_fake_key['postal_code'] = "80216"

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

            properties = PropertyState.objects.filter(pk=property.id)

            with self.assertRaises(MapQuestAPIKeyError):
                geocode_buildings(properties)

    def test_geocode_buildings_doesnt_run_an_api_request_when_an_API_key_is_not_provided(self):
        self.org_no_key, _, _ = create_organization(self.user)
        self.property_state_factory_no_key = FakePropertyStateFactory(organization=self.org_no_key)
        property_details_no_key = self.property_state_factory_no_key.get_details()

        property_details_no_key['organization_id'] = self.org_no_key.id
        property_details_no_key['address_line_1'] = "3001 Brighton Blvd"
        property_details_no_key['address_line_2'] = "suite 2693"
        property_details_no_key['city'] = "Denver"
        property_details_no_key['state'] = "Colorado"
        property_details_no_key['postal_code'] = "80216"

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

        properties = PropertyState.objects.filter(pk=property.id)

        self.assertIsNone(geocode_buildings(properties))

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

        self.assertIsNone(refreshed_property.long_lat)
        self.assertIsNone(refreshed_property.geocoding_confidence)

    def test_geocode_address_can_handle_addresses_with_reserved_and_unsafe_characters(self):
        with base_vcr.use_cassette('seed/tests/data/vcr_cassettes/geocode_reserved_and_unsafe_characters.yaml'):
            property_details = self.property_state_factory.get_details()
            property_details['organization_id'] = self.org.id
            property_details['address_line_1'] = r'3001 Brighton Blvd;/?:@=&<>#%{}|"\^~[]`'
            property_details['address_line_2'] = "suite 2693"
            property_details['city'] = "Denver"
            property_details['state'] = "Colorado"
            property_details['postal_code'] = "80216"

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

            properties = PropertyState.objects.filter(pk=property.id)

            geocode_buildings(properties)

            refreshed_properties = PropertyState.objects.filter(pk=property.id)

            self.assertEqual('POINT (-104.986138 39.765251)', long_lat_wkt(refreshed_properties[0]))

    def test_geocode_address_can_use_prepopulated_lat_and_long_fields(self):
        property_details = self.property_state_factory.get_details()
        property_details['organization_id'] = self.org.id
        property_details['latitude'] = 39.765251
        property_details['longitude'] = -104.986138

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

        properties = PropertyState.objects.filter(pk=property.id)

        geocode_buildings(properties)

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

        self.assertEqual('POINT (-104.986138 39.765251)', long_lat_wkt(refreshed_property))
        self.assertEqual("Manually geocoded (N/A)", refreshed_property.geocoding_confidence)

    def test_geocode_address_can_handle_receiving_no_buildings(self):
        self.assertIsNone(geocode_buildings(PropertyState.objects.none()))

    def test_geocoding_an_address_again_after_successful_geocode_executes_successfully(self):
        with base_vcr.use_cassette('seed/tests/data/vcr_cassettes/geocode_same_record_twice.yaml'):
            property_details = self.property_state_factory.get_details()
            property_details['organization_id'] = self.org.id
            property_details['address_line_1'] = "3001 Brighton Blvd"
            property_details['address_line_2'] = "suite 2693"
            property_details['city'] = "Denver"
            property_details['state'] = "Colorado"
            property_details['postal_code'] = "80216"
            property = PropertyState(**property_details)
            property.save()

            properties = PropertyState.objects.filter(pk=property.id)
            geocode_buildings(properties)

            refreshed_property = PropertyState.objects.get(pk=property.id)
            refreshed_property.address_line_1 = "2020 Lawrence St"
            refreshed_property.address_line_2 = "unit A"
            refreshed_property.postal_code = "80205"
            refreshed_property.save()

            refreshed_properties = PropertyState.objects.filter(pk=refreshed_property.id)
            geocode_buildings(refreshed_properties)

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

            self.assertEqual('POINT (-104.991046 39.752396)', long_lat_wkt(refreshed_updated_property))
            self.assertEqual('High (P1AAA)', refreshed_updated_property.geocoding_confidence)
            self.assertEqual(-104.991046, refreshed_updated_property.longitude)
            self.assertEqual(39.752396, refreshed_updated_property.latitude)

    def test_geocoded_fields_are_changed_appropriately_if_a_user_manually_updates_latitude_or_longitude_of_ungeocoded_property(self):
        property_details = self.property_state_factory.get_details()
        property_details['organization_id'] = self.org.id
        property = PropertyState(**property_details)
        property.save()

        refreshed_property = PropertyState.objects.get(pk=property.id)
        self.assertIsNone(long_lat_wkt(refreshed_property))
        self.assertIsNone(refreshed_property.geocoding_confidence)

        refreshed_property.latitude = 39.765251
        refreshed_property.save()

        refreshed_property = PropertyState.objects.get(pk=property.id)
        self.assertEqual(39.765251, refreshed_property.latitude)
        self.assertIsNone(long_lat_wkt(refreshed_property))
        self.assertIsNone(refreshed_property.geocoding_confidence)

        refreshed_property.longitude = -104.986138
        refreshed_property.save()

        refreshed_property = PropertyState.objects.get(pk=property.id)
        self.assertEqual(-104.986138, refreshed_property.longitude)
        self.assertEqual('POINT (-104.986138 39.765251)', long_lat_wkt(refreshed_property))
        self.assertEqual("Manually geocoded (N/A)", refreshed_property.geocoding_confidence)

    def test_geocoded_fields_are_changed_appropriately_if_a_user_manually_updates_latitude_or_longitude_of_geocoded_property(self):
        property_details = self.property_state_factory.get_details()
        property_details['organization_id'] = self.org.id
        property_details['latitude'] = 39.765251
        property_details['longitude'] = -104.986138
        property_details['long_lat'] = 'POINT (-104.986138 39.765251)'
        property_details['geocoding_confidence'] = 'High (P1AAA)'
        property = PropertyState(**property_details)
        property.save()

        # Make sure geocoding_confidence isn't overriden to be Manual given latitude and longitude are updated
        refreshed_property = PropertyState.objects.get(pk=property.id)
        self.assertEqual('High (P1AAA)', refreshed_property.geocoding_confidence)

        # Try updating latitude
        refreshed_property.latitude = 39.81
        refreshed_property.save()

        refreshed_property = PropertyState.objects.get(pk=property.id)
        self.assertEqual(39.81, refreshed_property.latitude)
        self.assertEqual('POINT (-104.986138 39.81)', long_lat_wkt(refreshed_property))
        self.assertEqual("Manually geocoded (N/A)", refreshed_property.geocoding_confidence)

        # If latitude or longitude are not there long_lat and geocoding_confidence should be empty
        refreshed_property.latitude = None
        refreshed_property.save()

        self.assertIsNone(refreshed_property.latitude)
        self.assertIsNone(long_lat_wkt(refreshed_property))
        self.assertIsNone(refreshed_property.geocoding_confidence)
Ejemplo n.º 4
0
class GeocodeViewTests(TestCase):
    def setUp(self):
        user_details = {
            'username': '******',
            'password': '******',
        }
        self.user = User.objects.create_superuser(email='*****@*****.**',
                                                  **user_details)
        self.org, _, _ = create_organization(self.user)
        self.client.login(**user_details)

        self.property_state_factory = FakePropertyStateFactory(
            organization=self.org)
        self.tax_lot_state_factory = FakeTaxLotStateFactory(
            organization=self.org)

    def test_geocode_endpoint_base_with_prepopulated_lat_long_no_api_request(
            self):
        property_details = self.property_state_factory.get_details()
        property_details['organization_id'] = self.org.id
        property_details['latitude'] = 39.765251
        property_details['longitude'] = -104.986138

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

        url = reverse('api:v2:geocode-geocode-by-ids')
        post_params = {
            'organization_id': self.org.pk,
            'property_ids': [property.id],
            'taxlot_ids': []
        }

        self.client.post(url, post_params)

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

        self.assertEqual('POINT (-104.986138 39.765251)',
                         long_lat_wkt(refreshed_property))

    def test_geocode_confidence_summary_returns_summary_dictionary(self):
        property_none_details = self.property_state_factory.get_details()
        property_none_details["organization_id"] = self.org.id
        property_none = PropertyState(**property_none_details)
        property_none.save()

        property_high_details = self.property_state_factory.get_details()
        property_high_details["organization_id"] = self.org.id
        property_high_details["geocoding_confidence"] = "High (P1AAA)"
        property_high = PropertyState(**property_high_details)
        property_high.save()

        property_low_details = self.property_state_factory.get_details()
        property_low_details["organization_id"] = self.org.id
        property_low_details["geocoding_confidence"] = "Low (P1CCC)"
        property_low = PropertyState(**property_low_details)
        property_low.save()

        property_manual_details = self.property_state_factory.get_details()
        property_manual_details["organization_id"] = self.org.id
        property_manual_details[
            "geocoding_confidence"] = "Manually geocoded (N/A)"
        property_manual = PropertyState(**property_manual_details)
        property_manual.save()

        property_missing_details = self.property_state_factory.get_details()
        property_missing_details["organization_id"] = self.org.id
        property_missing_details[
            "geocoding_confidence"] = "Missing address components (N/A)"
        property_missing = PropertyState(**property_missing_details)
        property_missing.save()

        tax_lot_none_details = self.tax_lot_state_factory.get_details()
        tax_lot_none_details["organization_id"] = self.org.id
        tax_lot_none = TaxLotState(**tax_lot_none_details)
        tax_lot_none.save()

        tax_lot_high_details = self.tax_lot_state_factory.get_details()
        tax_lot_high_details["organization_id"] = self.org.id
        tax_lot_high_details["geocoding_confidence"] = "High (P1AAA)"
        tax_lot_high = TaxLotState(**tax_lot_high_details)
        tax_lot_high.save()

        tax_lot_low_details = self.tax_lot_state_factory.get_details()
        tax_lot_low_details["organization_id"] = self.org.id
        tax_lot_low_details["geocoding_confidence"] = "Low (P1CCC)"
        tax_lot_low = TaxLotState(**tax_lot_low_details)
        tax_lot_low.save()

        tax_lot_missing_details = self.tax_lot_state_factory.get_details()
        tax_lot_missing_details["organization_id"] = self.org.id
        tax_lot_missing_details[
            "geocoding_confidence"] = "Missing address components (N/A)"
        tax_lot_missing = TaxLotState(**tax_lot_missing_details)
        tax_lot_missing.save()

        url = reverse('api:v2:geocode-confidence-summary')
        post_params = {
            'organization_id':
            self.org.pk,
            'property_ids': [
                property_none.id, property_high.id, property_low.id,
                property_manual.id, property_missing.id
            ],
            'tax_lot_ids': [
                tax_lot_none.id, tax_lot_high.id, tax_lot_low.id,
                tax_lot_missing.id
            ]
        }

        result = self.client.post(url, post_params)
        result_dict = ast.literal_eval(result.content.decode("utf-8"))

        expectation = {
            "properties": {
                "not_geocoded": 1,
                "high_confidence": 1,
                "low_confidence": 1,
                "manual": 1,
                "missing_address_components": 1
            },
            "tax_lots": {
                "not_geocoded": 1,
                "high_confidence": 1,
                "low_confidence": 1,
                "missing_address_components": 1
            }
        }

        self.assertEqual(result_dict, expectation)

    def test_api_key_endpoint_returns_true_or_false_if_org_has_api_key(self):
        url = reverse('api:v2:geocode-api-key-exists')

        post_params_false = {'organization_id': self.org.pk}
        false_result = self.client.get(url, post_params_false)

        self.assertEqual(b'false', false_result.content)

        org_with_key, _, _ = create_organization(self.user)
        org_with_key.mapquest_api_key = "somekey"
        org_with_key.save()
        post_params_true = {'organization_id': org_with_key.id}
        true_result = self.client.get(url, post_params_true)

        self.assertEqual(b'true', true_result.content)
Ejemplo n.º 5
0
class GeocodeAddresses(TestCase):
    def setUp(self):
        user_details = {
            'username': '******',
            'password': '******',
        }
        self.user = User.objects.create_superuser(email='*****@*****.**',
                                                  **user_details)
        self.org, _, _ = create_organization(self.user)
        self.org.mapquest_api_key = test_key
        self.org.save()

        self.property_state_factory = FakePropertyStateFactory(
            organization=self.org)
        self.tax_lot_state_factory = FakeTaxLotStateFactory(
            organization=self.org)

    def test_geocode_buildings_successful_when_real_fields_provided(self):
        with base_vcr.use_cassette(
                'seed/tests/data/vcr_cassettes/geocode_base_case.yaml'):
            property_details = self.property_state_factory.get_details()
            property_details['organization_id'] = self.org.id
            property_details['address_line_1'] = "3001 Brighton Blvd"
            property_details['address_line_2'] = "suite 2693"
            property_details['city'] = "Denver"
            property_details['state'] = "Colorado"
            property_details['postal_code'] = "80216"

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

            tax_lot_details = self.tax_lot_state_factory.get_details()
            tax_lot_details['organization_id'] = self.org.id
            tax_lot_details['address_line_1'] = "2020 Lawrence St"
            tax_lot_details['address_line_2'] = "unit A"
            tax_lot_details['city'] = "Denver"
            tax_lot_details['state'] = "Colorado"
            tax_lot_details['postal_code'] = "80205"

            tax_lot = TaxLotState(**tax_lot_details)
            tax_lot.save()
            tax_lots = TaxLotState.objects.filter(pk=tax_lot.id)

            geocode_buildings(properties)
            geocode_buildings(tax_lots)

            refreshed_property = PropertyState.objects.get(pk=property.id)
            refreshed_tax_lot = TaxLotState.objects.get(pk=tax_lot.id)

            self.assertEqual('POINT (-104.986138 39.765251)',
                             long_lat_wkt(refreshed_property))
            self.assertEqual('High (P1AAA)',
                             refreshed_property.geocoding_confidence)
            self.assertEqual(-104.986138, refreshed_property.longitude)
            self.assertEqual(39.765251, refreshed_property.latitude)

            self.assertEqual('POINT (-104.991046 39.752396)',
                             long_lat_wkt(refreshed_tax_lot))
            self.assertEqual('High (P1AAA)',
                             refreshed_tax_lot.geocoding_confidence)

    def test_geocode_properties_with_custom_fields(self):
        with base_vcr.use_cassette(
                'seed/tests/data/vcr_cassettes/geocode_property_custom_fields.yaml'
        ):
            property_details = self.property_state_factory.get_details()
            property_details['organization_id'] = self.org.id
            property_details['pm_parent_property_id'] = "3001 Brighton Blvd"
            property_details['pm_property_id'] = "suite 2693"
            property_details['property_name'] = None  # can handle empty DB col
            property_details['state'] = "Colorado"
            property_details['extra_data'] = {
                'ed_city': "Denver",
                'ed_zip': 80216,  # can handle numbers
                'ed_empty': None,  # can handle empty extra_data col
            }

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

            # Activate and order geocoding columns
            self.org.column_set.filter(
                column_name='pm_parent_property_id',
                table_name="PropertyState").update(geocoding_order=1)
            self.org.column_set.filter(
                column_name='pm_property_id',
                table_name="PropertyState").update(geocoding_order=2)
            self.org.column_set.create(column_name='ed_city',
                                       is_extra_data=True,
                                       table_name='PropertyState',
                                       geocoding_order=3)
            self.org.column_set.filter(
                column_name='state',
                table_name="PropertyState").update(geocoding_order=4)
            self.org.column_set.create(column_name='ed_zip',
                                       is_extra_data=True,
                                       table_name='PropertyState',
                                       geocoding_order=5)
            self.org.column_set.create(column_name='ed_empty',
                                       is_extra_data=True,
                                       table_name='PropertyState',
                                       geocoding_order=6)
            self.org.column_set.filter(
                column_name='property_name',
                table_name="PropertyState").update(geocoding_order=7)

            # Deactivate default geocoding columns
            self.org.column_set.filter(
                column_name__in=[
                    'address_line_1', 'address_line_2', 'city', 'postal_code'
                ],
                table_name="PropertyState").update(geocoding_order=0)

            geocode_buildings(properties)

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

            self.assertEqual('POINT (-104.986138 39.765251)',
                             long_lat_wkt(refreshed_property))
            self.assertEqual('High (P1AAA)',
                             refreshed_property.geocoding_confidence)
            self.assertEqual(-104.986138, refreshed_property.longitude)
            self.assertEqual(39.765251, refreshed_property.latitude)

    def test_geocode_taxlots_with_custom_fields(self):
        with base_vcr.use_cassette(
                'seed/tests/data/vcr_cassettes/geocode_taxlots_custom_fields.yaml'
        ):
            taxlot_details = self.tax_lot_state_factory.get_details()
            taxlot_details['organization_id'] = self.org.id
            taxlot_details['jurisdiction_tax_lot_id'] = "3001 Brighton Blvd"
            taxlot_details['block_number'] = "suite 2693"
            taxlot_details['custom_id_1'] = None  # can handle empty DB col
            taxlot_details['state'] = "Colorado"
            taxlot_details['extra_data'] = {
                'ed_city': "Denver",
                'ed_zip': 80216,  # can handle numbers
                'ed_empty': None,  # can handle empty extra_data col
            }

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

            # Activate and order geocoding columns
            self.org.column_set.filter(
                column_name='jurisdiction_tax_lot_id',
                table_name="TaxLotState").update(geocoding_order=1)
            self.org.column_set.filter(
                column_name='block_number',
                table_name="TaxLotState").update(geocoding_order=2)
            self.org.column_set.create(column_name='ed_city',
                                       is_extra_data=True,
                                       table_name='TaxLotState',
                                       geocoding_order=3)
            self.org.column_set.filter(
                column_name='state',
                table_name="TaxLotState").update(geocoding_order=4)
            self.org.column_set.create(column_name='ed_zip',
                                       is_extra_data=True,
                                       table_name='TaxLotState',
                                       geocoding_order=5)
            self.org.column_set.create(column_name='ed_empty',
                                       is_extra_data=True,
                                       table_name='TaxLotState',
                                       geocoding_order=6)
            self.org.column_set.filter(
                column_name='custom_id_1',
                table_name="TaxLotState").update(geocoding_order=7)

            # Deactivate default geocoding columns
            self.org.column_set.filter(
                column_name__in=[
                    'address_line_1', 'address_line_2', 'city', 'postal_code'
                ],
                table_name="TaxLotState").update(geocoding_order=0)

            geocode_buildings(taxlots)

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

            self.assertEqual('POINT (-104.986138 39.765251)',
                             long_lat_wkt(refreshed_taxlot))
            self.assertEqual('High (P1AAA)',
                             refreshed_taxlot.geocoding_confidence)
            self.assertEqual(-104.986138, refreshed_taxlot.longitude)
            self.assertEqual(39.765251, refreshed_taxlot.latitude)

    def test_not_enough_geocoding_fields_for_org_leads_to_no_geocoding(self):
        property_details = self.property_state_factory.get_details()
        property_details['organization_id'] = self.org.id
        property_details['address_line_1'] = "3001 Brighton Blvd"
        property_details['address_line_2'] = "suite 2693"
        property_details['city'] = "Denver"
        property_details['state'] = "Colorado"
        property_details['postal_code'] = "80216"

        # Deactivate all PropertyState geocoding columns
        self.org.column_set.filter(table_name="PropertyState").update(
            geocoding_order=0)

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

        self.assertIsNone(geocode_buildings(properties))

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

        self.assertIsNone(refreshed_property.long_lat)
        self.assertIsNone(refreshed_property.geocoding_confidence)

    def test_not_enough_geocoding_fields_for_record_leads_to_no_geocoding(
            self):
        property_details = self.property_state_factory.get_details()
        property_details['organization_id'] = self.org.id
        property_details['address_line_1'] = ""
        property_details['address_line_2'] = ""
        property_details['city'] = ""
        property_details['state'] = ""
        property_details['postal_code'] = ""

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

        self.assertIsNone(geocode_buildings(properties))

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

        self.assertIsNone(refreshed_property.long_lat)
        self.assertEqual(refreshed_property.geocoding_confidence,
                         "Missing address components (N/A)")

    def test_geocode_buildings_returns_no_data_when_provided_address_is_ambigious(
            self):
        with base_vcr.use_cassette(
                'seed/tests/data/vcr_cassettes/geocode_low_geocodequality.yaml'
        ):
            # 1st Property
            state_zip_only_details = self.property_state_factory.get_details()
            state_zip_only_details['organization_id'] = self.org.id
            state_zip_only_details['address_line_1'] = ""
            state_zip_only_details['address_line_2'] = ""
            state_zip_only_details['city'] = ""
            state_zip_only_details['state'] = "Colorado"
            state_zip_only_details['postal_code'] = "80202"

            state_zip_only_property = PropertyState(**state_zip_only_details)
            state_zip_only_property.save()

            properties = PropertyState.objects.filter(
                id__in=[state_zip_only_property.id])

            geocode_buildings(properties)

            state_zip_only_property = PropertyState.objects.get(
                pk=state_zip_only_property.id)

            self.assertIsNone(state_zip_only_property.long_lat)
            self.assertIsNone(state_zip_only_property.longitude)
            self.assertIsNone(state_zip_only_property.latitude)
            self.assertEqual("Low - check address (Z1XAA)",
                             state_zip_only_property.geocoding_confidence)

            # 2nd Property
            wrong_state_zip_details = self.property_state_factory.get_details()
            wrong_state_zip_details['organization_id'] = self.org.id
            wrong_state_zip_details['address_line_1'] = ""
            wrong_state_zip_details['address_line_2'] = ""
            wrong_state_zip_details['city'] = "Denver"
            wrong_state_zip_details['state'] = "Colorado"
            wrong_state_zip_details['postal_code'] = ""

            wrong_state_zip_property = PropertyState(**wrong_state_zip_details)
            wrong_state_zip_property.save()

            properties = PropertyState.objects.filter(
                id__in=[wrong_state_zip_property.id])

            geocode_buildings(properties)

            wrong_state_zip_property = PropertyState.objects.get(
                pk=wrong_state_zip_property.id)

            self.assertIsNone(wrong_state_zip_property.long_lat)
            self.assertIsNone(wrong_state_zip_property.longitude)
            self.assertIsNone(wrong_state_zip_property.latitude)
            self.assertEqual("Low - check address (A5XAX)",
                             wrong_state_zip_property.geocoding_confidence)

    def test_geocode_buildings_returns_no_data_when_provided_address_returns_multiple_results(
            self):
        with base_vcr.use_cassette(
                'seed/tests/data/vcr_cassettes/geocode_multiple_results.yaml'):
            # 1st Property
            wrong_state_details = self.property_state_factory.get_details()
            wrong_state_details['organization_id'] = self.org.id
            wrong_state_details['address_line_1'] = "101 Market Street"
            wrong_state_details['address_line_2'] = ""
            wrong_state_details['city'] = "Denver"
            wrong_state_details['state'] = "California"
            wrong_state_details['postal_code'] = ""

            wrong_state_property = PropertyState(**wrong_state_details)
            wrong_state_property.save()

            properties = PropertyState.objects.filter(
                id__in=[wrong_state_property.id])

            geocode_buildings(properties)

            wrong_state_property = PropertyState.objects.get(
                pk=wrong_state_property.id)

            self.assertIsNone(wrong_state_property.long_lat)
            self.assertIsNone(wrong_state_property.longitude)
            self.assertIsNone(wrong_state_property.latitude)
            self.assertEqual("Low - check address (Ambiguous)",
                             wrong_state_property.geocoding_confidence)

            # 2nd Property
            not_specific_enough_details = self.property_state_factory.get_details(
            )
            not_specific_enough_details['organization_id'] = self.org.id
            not_specific_enough_details['address_line_1'] = "101 Market Street"
            not_specific_enough_details['address_line_2'] = ""
            not_specific_enough_details['city'] = ""
            not_specific_enough_details['state'] = "California"
            not_specific_enough_details['postal_code'] = ""

            not_specific_enough_property = PropertyState(
                **not_specific_enough_details)
            not_specific_enough_property.save()

            properties = PropertyState.objects.filter(
                id__in=[not_specific_enough_property.id])

            geocode_buildings(properties)

            not_specific_enough_property = PropertyState.objects.get(
                pk=not_specific_enough_property.id)

            self.assertIsNone(not_specific_enough_property.long_lat)
            self.assertIsNone(not_specific_enough_property.longitude)
            self.assertIsNone(not_specific_enough_property.latitude)
            self.assertEqual("Low - check address (Ambiguous)",
                             not_specific_enough_property.geocoding_confidence)

    def test_geocode_buildings_is_successful_even_if_two_buildings_have_same_address(
            self):
        with base_vcr.use_cassette(
                'seed/tests/data/vcr_cassettes/geocode_dup_addresses.yaml'):
            property_details = self.property_state_factory.get_details()
            property_details['organization_id'] = self.org.id
            property_details['address_line_1'] = "3001 Brighton Blvd"
            property_details['address_line_2'] = "suite 2693"
            property_details['city'] = "Denver"
            property_details['state'] = "Colorado"
            property_details['postal_code'] = "80216"

            property_1 = PropertyState(**property_details)
            property_2 = PropertyState(**property_details)
            property_1.save()
            property_2.save()

            ids = [property_1.id, property_2.id]

            properties = PropertyState.objects.filter(id__in=ids)

            geocode_buildings(properties)

            refreshed_properties = PropertyState.objects.filter(id__in=ids)

            for property in refreshed_properties:
                self.assertEqual('POINT (-104.986138 39.765251)',
                                 long_lat_wkt(property))
                self.assertEqual('High (P1AAA)', property.geocoding_confidence)
                self.assertEqual(-104.986138, property.longitude)
                self.assertEqual(39.765251, property.latitude)

    def test_geocode_buildings_is_successful_with_over_100_properties(self):
        with batch_vcr.use_cassette(
                'seed/tests/data/vcr_cassettes/geocode_101_unique_addresses.yaml',
                match_on=['uri_length']):
            property_details = self.property_state_factory.get_details()
            property_details['organization_id'] = self.org.id
            property_details['address_line_2'] = ""
            property_details['city'] = "Denver"
            property_details['state'] = "Colorado"
            property_details['postal_code'] = "80202"

            ids = []
            for n in range(101):
                street_number = n + 1600
                property_details['address_line_1'] = str(
                    street_number) + " Larimer Street"

                property = PropertyState(**property_details)
                property.save()
                ids.append(property.id)

            properties = PropertyState.objects.filter(
                id__in=ids).order_by('id')

            geocode_buildings(properties)

            refreshed_properties = PropertyState.objects.filter(
                id__in=ids).order_by('id')

            long_lats = [
                property.long_lat for property in refreshed_properties
                if property.long_lat is not None
            ]
            geocode_confidence_results = [
                property.geocoding_confidence
                for property in refreshed_properties
                if property.geocoding_confidence is not None
            ]

            self.assertTrue(len(long_lats) > 0)
            self.assertTrue(len(geocode_confidence_results) == 101)

    def test_geocode_buildings_is_unsuccessful_when_the_API_key_is_invalid_or_expired(
            self):
        with base_vcr.use_cassette(
                'seed/tests/data/vcr_cassettes/geocode_invalid_or_expired_key.yaml'
        ):
            self.org_fake_key, _, _ = create_organization(self.user)
            self.org_fake_key.mapquest_api_key = 'fakeapikey'
            self.org_fake_key.save()

            self.property_state_factory_fake_key = FakePropertyStateFactory(
                organization=self.org_fake_key)

            property_details_fake_key = self.property_state_factory_fake_key.get_details(
            )
            property_details_fake_key['organization_id'] = self.org_fake_key.id
            property_details_fake_key['address_line_1'] = "3001 Brighton Blvd"
            property_details_fake_key['address_line_2'] = "suite 2693"
            property_details_fake_key['city'] = "Denver"
            property_details_fake_key['state'] = "Colorado"
            property_details_fake_key['postal_code'] = "80216"

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

            properties = PropertyState.objects.filter(pk=property.id)

            with self.assertRaises(MapQuestAPIKeyError):
                geocode_buildings(properties)

    def test_geocode_buildings_doesnt_run_an_api_request_when_an_API_key_is_not_provided(
            self):
        self.org_no_key, _, _ = create_organization(self.user)
        self.property_state_factory_no_key = FakePropertyStateFactory(
            organization=self.org_no_key)
        property_details_no_key = self.property_state_factory_no_key.get_details(
        )

        property_details_no_key['organization_id'] = self.org_no_key.id
        property_details_no_key['address_line_1'] = "3001 Brighton Blvd"
        property_details_no_key['address_line_2'] = "suite 2693"
        property_details_no_key['city'] = "Denver"
        property_details_no_key['state'] = "Colorado"
        property_details_no_key['postal_code'] = "80216"

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

        properties = PropertyState.objects.filter(pk=property.id)

        self.assertIsNone(geocode_buildings(properties))

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

        self.assertIsNone(refreshed_property.long_lat)
        self.assertIsNone(refreshed_property.geocoding_confidence)

    def test_geocode_address_can_handle_addresses_with_reserved_and_unsafe_characters(
            self):
        with base_vcr.use_cassette(
                'seed/tests/data/vcr_cassettes/geocode_reserved_and_unsafe_characters.yaml'
        ):
            property_details = self.property_state_factory.get_details()
            property_details['organization_id'] = self.org.id
            property_details[
                'address_line_1'] = r'3001 Brighton Blvd;/?:@=&<>#%{}|"\^~[]`'
            property_details['address_line_2'] = "suite 2693"
            property_details['city'] = "Denver"
            property_details['state'] = "Colorado"
            property_details['postal_code'] = "80216"

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

            properties = PropertyState.objects.filter(pk=property.id)

            geocode_buildings(properties)

            refreshed_properties = PropertyState.objects.filter(pk=property.id)

            self.assertEqual('POINT (-104.986138 39.765251)',
                             long_lat_wkt(refreshed_properties[0]))

    def test_geocode_address_can_use_prepopulated_lat_and_long_fields(self):
        property_details = self.property_state_factory.get_details()
        property_details['organization_id'] = self.org.id
        property_details['latitude'] = 39.765251
        property_details['longitude'] = -104.986138

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

        properties = PropertyState.objects.filter(pk=property.id)

        geocode_buildings(properties)

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

        self.assertEqual('POINT (-104.986138 39.765251)',
                         long_lat_wkt(refreshed_property))
        self.assertEqual("Manually geocoded (N/A)",
                         refreshed_property.geocoding_confidence)

    def test_geocode_address_can_handle_receiving_no_buildings(self):
        self.assertIsNone(geocode_buildings(PropertyState.objects.none()))

    def test_geocoding_an_address_again_after_successful_geocode_executes_successfully(
            self):
        with base_vcr.use_cassette(
                'seed/tests/data/vcr_cassettes/geocode_same_record_twice.yaml'
        ):
            property_details = self.property_state_factory.get_details()
            property_details['organization_id'] = self.org.id
            property_details['address_line_1'] = "3001 Brighton Blvd"
            property_details['address_line_2'] = "suite 2693"
            property_details['city'] = "Denver"
            property_details['state'] = "Colorado"
            property_details['postal_code'] = "80216"
            property = PropertyState(**property_details)
            property.save()

            properties = PropertyState.objects.filter(pk=property.id)
            geocode_buildings(properties)

            refreshed_property = PropertyState.objects.get(pk=property.id)
            refreshed_property.address_line_1 = "2020 Lawrence St"
            refreshed_property.address_line_2 = "unit A"
            refreshed_property.postal_code = "80205"
            refreshed_property.save()

            refreshed_properties = PropertyState.objects.filter(
                pk=refreshed_property.id)
            geocode_buildings(refreshed_properties)

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

            self.assertEqual('POINT (-104.991046 39.752396)',
                             long_lat_wkt(refreshed_updated_property))
            self.assertEqual('High (P1AAA)',
                             refreshed_updated_property.geocoding_confidence)
            self.assertEqual(-104.991046, refreshed_updated_property.longitude)
            self.assertEqual(39.752396, refreshed_updated_property.latitude)

    def test_geocoded_fields_are_changed_appropriately_if_a_user_manually_updates_latitude_or_longitude_of_ungeocoded_property(
            self):
        property_details = self.property_state_factory.get_details()
        property_details['organization_id'] = self.org.id
        property = PropertyState(**property_details)
        property.save()

        refreshed_property = PropertyState.objects.get(pk=property.id)
        self.assertIsNone(long_lat_wkt(refreshed_property))
        self.assertIsNone(refreshed_property.geocoding_confidence)

        refreshed_property.latitude = 39.765251
        refreshed_property.save()

        refreshed_property = PropertyState.objects.get(pk=property.id)
        self.assertEqual(39.765251, refreshed_property.latitude)
        self.assertIsNone(long_lat_wkt(refreshed_property))
        self.assertIsNone(refreshed_property.geocoding_confidence)

        refreshed_property.longitude = -104.986138
        refreshed_property.save()

        refreshed_property = PropertyState.objects.get(pk=property.id)
        self.assertEqual(-104.986138, refreshed_property.longitude)
        self.assertEqual('POINT (-104.986138 39.765251)',
                         long_lat_wkt(refreshed_property))
        self.assertEqual("Manually geocoded (N/A)",
                         refreshed_property.geocoding_confidence)

    def test_geocoded_fields_are_changed_appropriately_if_a_user_manually_updates_latitude_or_longitude_of_geocoded_property(
            self):
        property_details = self.property_state_factory.get_details()
        property_details['organization_id'] = self.org.id
        property_details['latitude'] = 39.765251
        property_details['longitude'] = -104.986138
        property_details['long_lat'] = 'POINT (-104.986138 39.765251)'
        property_details['geocoding_confidence'] = 'High (P1AAA)'
        property = PropertyState(**property_details)
        property.save()

        # Make sure geocoding_confidence isn't overriden to be Manual given latitude and longitude are updated
        refreshed_property = PropertyState.objects.get(pk=property.id)
        self.assertEqual('High (P1AAA)',
                         refreshed_property.geocoding_confidence)

        # Try updating latitude
        refreshed_property.latitude = 39.81
        refreshed_property.save()

        refreshed_property = PropertyState.objects.get(pk=property.id)
        self.assertEqual(39.81, refreshed_property.latitude)
        self.assertEqual('POINT (-104.986138 39.81)',
                         long_lat_wkt(refreshed_property))
        self.assertEqual("Manually geocoded (N/A)",
                         refreshed_property.geocoding_confidence)

        # If latitude or longitude are not there long_lat and geocoding_confidence should be empty
        refreshed_property.latitude = None
        refreshed_property.save()

        self.assertIsNone(refreshed_property.latitude)
        self.assertIsNone(long_lat_wkt(refreshed_property))
        self.assertIsNone(refreshed_property.geocoding_confidence)