From dea96941b781391df895a2c708ef7c83531407c0 Mon Sep 17 00:00:00 2001 From: Lindsey Jacks Date: Tue, 12 Jul 2016 10:30:19 -0400 Subject: [PATCH] removed location name from model and updated geoodk api updated fixtures changed all of the tests that referenced spatialunit.name --- cadasta/core/fixtures.py | 59 +++++------- .../core/management/commands/loadfixtures.py | 1 - .../core/tests/test_management_commands.py | 2 - .../tests/test_views_default_projects.py | 19 ++-- cadasta/party/models.py | 2 +- cadasta/party/tests/test_models.py | 2 +- .../tests/test_views_api_relationship_list.py | 6 +- .../test_views_api_tenure_relationships.py | 10 +- .../standard_cadasta_questionnaire_0.5.xlsx | Bin 0 -> 17283 bytes .../files/test_standard_questionnaire.xlsx | Bin 17152 -> 17283 bytes .../questionnaires/tests/test_attr_schemas.py | 4 +- cadasta/spatial/migrations/0001_initial.py | 4 +- ...708_2001.py => 0002_auto_20160712_1513.py} | 2 +- cadasta/spatial/models.py | 10 +- cadasta/spatial/serializers.py | 6 +- cadasta/spatial/tests/factories.py | 1 - cadasta/spatial/tests/test_models.py | 70 +++++++------- cadasta/spatial/tests/test_serializers.py | 29 ++---- .../test_views_api_spatial_relationships.py | 24 +++-- .../tests/test_views_api_spatial_units.py | 55 +++++------ cadasta/spatial/views/api.py | 2 - cadasta/xforms/mixins/model_helper.py | 7 +- cadasta/xforms/serializers.py | 4 +- cadasta/xforms/tests/attr_schemas.py | 87 +----------------- cadasta/xforms/tests/files/test_resources.py | 42 ++------- cadasta/xforms/tests/test_renderers.py | 0 cadasta/xforms/tests/test_views_api.py | 19 ++-- 27 files changed, 161 insertions(+), 306 deletions(-) create mode 100644 cadasta/questionnaires/tests/files/standard_cadasta_questionnaire_0.5.xlsx rename cadasta/spatial/migrations/{0002_auto_20160708_2001.py => 0002_auto_20160712_1513.py} (90%) delete mode 100644 cadasta/xforms/tests/test_renderers.py diff --git a/cadasta/core/fixtures.py b/cadasta/core/fixtures.py index ddeb50e7d..8eeaa62ce 100644 --- a/cadasta/core/fixtures.py +++ b/cadasta/core/fixtures.py @@ -11,7 +11,7 @@ from spatial.tests.factories import ( SpatialUnitFactory, SpatialRelationshipFactory ) -from spatial.models import SpatialUnit + from tutelary.models import Policy, Role @@ -293,8 +293,19 @@ def add_test_spatial_units(self): project = Project.objects.get( name__contains='Pekapuran Laut Test Project') + # add attribute schema + content_type = ContentType.objects.get( + app_label='spatial', model='spatialunit') + sch = Schema.objects.create( + content_type=content_type, + selectors=(project.organization.pk, project.pk)) + attr_type = AttributeType.objects.get(name="text") + Attribute.objects.create( + schema=sch, name='name', long_name='Name', + required=False, index=1, attr_type=attr_type + ) + su1 = SpatialUnitFactory( - name='Building Unit (Test)', geometry=GEOSGeometry('{"type": "Polygon",' '"coordinates": [[' '[-245.3920519351959, -3.3337982265513184],' @@ -305,20 +316,10 @@ def add_test_spatial_units(self): '}' ), project=project, - type='BU') - - # add attribute schema - content_type = ContentType.objects.get( - app_label='spatial', model='spatialunit') - sch = Schema.objects.create(content_type=content_type, selectors=()) - attr_type = AttributeType.objects.get(name="text") - Attribute.objects.create( - schema=sch, name='testing', long_name='Testing', - required=False, index=1, attr_type=attr_type - ) + type='BU', + attributes={'name': 'Building Unit (Test)'}) su2 = SpatialUnitFactory( - name='Apartment Unit (Test)', geometry=GEOSGeometry('{"type": "Polygon",' '"coordinates": [[' '[-245.39200901985168, -3.333808937230755],' @@ -330,13 +331,12 @@ def add_test_spatial_units(self): ), project=project, type='AP', - attributes={"testing": "attributes"}) + attributes={"name": "Apartment Unit (Test)"}) SpatialRelationshipFactory( su1=su1, su2=su2, type='C', project=project) su3 = SpatialUnitFactory( - name='Parcel (Test)', geometry=GEOSGeometry('{"type": "Polygon",' '"coordinates": [[' '[-245.39088249206543, -3.333262692430284],' @@ -347,23 +347,23 @@ def add_test_spatial_units(self): ']}' ), project=project, - type='PA') + type='PA', + attributes={ + 'name': 'Parcel (Test)', + }) su4 = SpatialUnitFactory( - name='Point Inside Parcel (Test)', geometry=GEOSGeometry('{"type": "Point",' '"coordinates": [' '-245.39034605026242, -3.333294824485769]}' ), project=project, - type='PA', - attributes={"testing": "attributes"}) + type='PA') SpatialRelationshipFactory( su1=su3, su2=su4, type='C', project=project) SpatialUnitFactory( - name='Line (Test)', geometry=GEOSGeometry('{"type": "LineString",' '"coordinates": [' '[-245.3934037685394, -3.334258785662196],' @@ -374,32 +374,23 @@ def add_test_spatial_units(self): type='RW') SpatialUnitFactory( - name='Uncontained Point (Test)', geometry=GEOSGeometry('{"type": "Point",' '"coordinates": [' '-245.39366126060483, -3.334130257559935]}' ), project=project, - type='MI') + type='MI', + attributes={"name": 'Uncontained Point (Test)'}) SpatialUnitFactory( - name='Kibera Test Spatial Unit (Test)', geometry=GEOSGeometry('{"type": "Point",' '"coordinates": [' '-4.9383544921875,' '7.833452408875349' ']}'), project=Project.objects.get(name='Kibera Test Project'), - type='MI') - - def delete_test_spatial_units(self): - sus = SpatialUnit.objects.filter(name__contains='Test') - for su in sus: - su.delete() - content_type = ContentType.objects.get( - app_label='spatial', model='spatialunit' - ) - Schema.objects.filter(content_type=content_type, selectors=()).delete() + type='MI', + attributes={}) def delete_test_organizations(self): orgs = Organization.objects.filter(name__contains='Test') diff --git a/cadasta/core/management/commands/loadfixtures.py b/cadasta/core/management/commands/loadfixtures.py index 14c658aac..c9671c6c2 100644 --- a/cadasta/core/management/commands/loadfixtures.py +++ b/cadasta/core/management/commands/loadfixtures.py @@ -21,7 +21,6 @@ def handle(self, *args, **options): data = FixturesData() if options['delete']: - data.delete_test_spatial_units() data.delete_test_projects() data.delete_test_users() data.delete_test_organizations() diff --git a/cadasta/core/tests/test_management_commands.py b/cadasta/core/tests/test_management_commands.py index 635fc4e1d..2c181d39a 100644 --- a/cadasta/core/tests/test_management_commands.py +++ b/cadasta/core/tests/test_management_commands.py @@ -15,7 +15,6 @@ class FixturesTest(TestCase): def test_fixture_setup(self): data = FixturesData() - data.delete_test_spatial_units() data.delete_test_users() data.delete_test_organizations() data.delete_test_projects() @@ -34,7 +33,6 @@ def test_fixture_setup(self): assert SpatialUnit.objects.count() == 7 assert SpatialRelationship.objects.count() == 2 - data.delete_test_spatial_units() data.delete_test_users() data.delete_test_organizations() data.delete_test_projects() diff --git a/cadasta/organization/tests/test_views_default_projects.py b/cadasta/organization/tests/test_views_default_projects.py index 3bf0488dd..9f37453ae 100644 --- a/cadasta/organization/tests/test_views_default_projects.py +++ b/cadasta/organization/tests/test_views_default_projects.py @@ -16,7 +16,6 @@ from buckets.test.storage import FakeS3Storage from core.tests.base_test_case import UserTestCase -from core.tests.factories import RoleFactory from accounts.tests.factories import UserFactory from organization.models import OrganizationRole, Project, ProjectRole from questionnaires.tests.factories import QuestionnaireFactory @@ -231,13 +230,15 @@ def _get_private(self, status=None, user=None, make_org_member=False, OrganizationRole.objects.create(organization=other_org, user=user) return self._get(prj, user=user, status=status), prj - def _check_render(self, response, project, assign_context=False): + def _check_render(self, response, project, + assign_context=False, is_superuser=False): content = response.render().content.decode('utf-8') context = RequestContext(self.request) context['object'] = project context['project'] = project context['geojson'] = '{"type": "FeatureCollection", "features": []}' + context['is_superuser'] = is_superuser expected = render_to_string( 'organization/project_dashboard.html', @@ -261,13 +262,10 @@ def test_get_with_unauthorized_user(self): def test_get_with_superuser(self): superuser = UserFactory.create() - superuser_pol = Policy.objects.get(name='superuser') - self.superuser_role = RoleFactory.create( - name='superuser', policies=[superuser_pol] - ) + self.superuser_role = Role.objects.get(name='superuser') superuser.assign_policies(self.superuser_role) response = self._get(self.project1, user=superuser, status=200) - self._check_render(response, self.project1,) + self._check_render(response, self.project1, is_superuser=True) def test_get_non_existent_project(self): setattr(self.request, 'user', self.user) @@ -313,15 +311,12 @@ def test_get_private_project_on_org_membership_removal(self): def test_get_private_project_with_superuser(self): superuser = UserFactory.create() - superuser_pol = Policy.objects.get(name='superuser') - self.superuser_role = RoleFactory.create( - name='superuser', policies=[superuser_pol] - ) + self.superuser_role = Role.objects.get(name='superuser') superuser.assign_policies(self.superuser_role) response, prj = self._get_private( user=superuser, status=200 ) - self._check_render(response, prj) + self._check_render(response, prj, is_superuser=True) class ProjectAddTest(UserTestCase): diff --git a/cadasta/party/models.py b/cadasta/party/models.py index a6e9468a1..9b2b27407 100644 --- a/cadasta/party/models.py +++ b/cadasta/party/models.py @@ -269,7 +269,7 @@ class TutelaryMeta: def __str__(self): return " {type} <{su}>>".format( - party=self.party.name, su=self.spatial_unit.name, + party=self.party.name, su=self.spatial_unit.get_type_display(), type=self.tenure_type.label) def __repr__(self): diff --git a/cadasta/party/tests/test_models.py b/cadasta/party/tests/test_models.py index f49e28357..d21638a7f 100644 --- a/cadasta/party/tests/test_models.py +++ b/cadasta/party/tests/test_models.py @@ -132,7 +132,7 @@ def test_str(self): party__project=project, party__name='Family', spatial_unit__project=project, - spatial_unit__name='Parcel', + spatial_unit__type='PA', tenure_type=tenure_type) assert str(relationship) == ( " Leasehold >") diff --git a/cadasta/party/tests/test_views_api_relationship_list.py b/cadasta/party/tests/test_views_api_relationship_list.py index 6cd96d936..db166b9ec 100644 --- a/cadasta/party/tests/test_views_api_relationship_list.py +++ b/cadasta/party/tests/test_views_api_relationship_list.py @@ -25,9 +25,9 @@ def _test_objs(self, access='public'): org = OrganizationFactory.create(slug='namati') prj = ProjectFactory.create( slug='test-project', organization=org, access=access) - su1 = SpatialUnitFactory.create(project=prj, name='Parcel') - su2 = SpatialUnitFactory.create(project=prj, name='House') - su3 = SpatialUnitFactory.create(project=prj, name='Village') + su1 = SpatialUnitFactory.create(project=prj) + su2 = SpatialUnitFactory.create(project=prj) + su3 = SpatialUnitFactory.create(project=prj) self.su1 = su1 self.su2 = su2 self.su3 = su3 diff --git a/cadasta/party/tests/test_views_api_tenure_relationships.py b/cadasta/party/tests/test_views_api_tenure_relationships.py index af8dbc4bb..7bc44013e 100644 --- a/cadasta/party/tests/test_views_api_tenure_relationships.py +++ b/cadasta/party/tests/test_views_api_tenure_relationships.py @@ -34,8 +34,8 @@ def _test_objs(self, access='public'): slug='test-project', organization=org, access=access) party1 = PartyFactory.create(project=prj, name='Landowner') party2 = PartyFactory.create(project=prj, name='Family') - su1 = SpatialUnitFactory.create(project=prj, name='Parcel') - su2 = SpatialUnitFactory.create(project=prj, name='House') + su1 = SpatialUnitFactory.create(project=prj, type='PA') + su2 = SpatialUnitFactory.create(project=prj, type='PA') TR = TenureRelationshipFactory rel1 = TR.create(project=prj, party=party1, spatial_unit=su1) TR.create(project=prj, party=party1, spatial_unit=su2) @@ -100,8 +100,8 @@ def _test_objs(self, access='public'): slug='test-project', organization=org, access=access) party = PartyFactory.create(project=prj, name='Landowner') party2 = PartyFactory.create(project=prj, name='Family') - spatial_unit = SpatialUnitFactory.create(project=prj, name='Parcel') - spatial_unit2 = SpatialUnitFactory.create(project=prj, name='House') + spatial_unit = SpatialUnitFactory.create(project=prj, type='PA') + spatial_unit2 = SpatialUnitFactory.create(project=prj, type='PA') rel = TenureRelationshipFactory.create( project=prj, party=party, spatial_unit=spatial_unit) self.party2 = party2 @@ -132,7 +132,7 @@ def check_for_updated(self, content): def check_for_unchanged(self, content): assert content['party']['name'] == "Landowner" - assert content['spatial_unit']['properties']['name'] == "Parcel" + assert content['spatial_unit']['properties']['type'] == "PA" # Additional tests diff --git a/cadasta/questionnaires/tests/files/standard_cadasta_questionnaire_0.5.xlsx b/cadasta/questionnaires/tests/files/standard_cadasta_questionnaire_0.5.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..2e85bb2c72b354e7023be466503ab1e2cc2cd348 GIT binary patch literal 17283 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 diff --git a/cadasta/questionnaires/tests/test_attr_schemas.py b/cadasta/questionnaires/tests/test_attr_schemas.py index 20bef7b92..d9748b8b1 100644 --- a/cadasta/questionnaires/tests/test_attr_schemas.py +++ b/cadasta/questionnaires/tests/test_attr_schemas.py @@ -98,7 +98,7 @@ def test_spatial_unit_attribute_schema(self): project=project, dict=location_xform_group, content_type=content_type, errors=[]) spatial_unit = SpatialUnitFactory.create( - name='Test', project=project, + project=project, attributes={ 'quality': 'polygon_high' } @@ -124,7 +124,7 @@ def test_spatial_unit_invalid_attribute(self): assert 1 == Schema.objects.all().count() with pytest.raises(KeyError): SpatialUnitFactory.create( - name='TestLocation', project=project, + project=project, attributes={ 'invalid_attribute': 'yes', } diff --git a/cadasta/spatial/migrations/0001_initial.py b/cadasta/spatial/migrations/0001_initial.py index 05493830c..9c19266f4 100644 --- a/cadasta/spatial/migrations/0001_initial.py +++ b/cadasta/spatial/migrations/0001_initial.py @@ -23,7 +23,6 @@ class Migration(migrations.Migration): name='HistoricalSpatialUnit', fields=[ ('id', models.CharField(db_index=True, max_length=24)), - ('name', models.CharField(max_length=200)), ('geometry', django.contrib.gis.db.models.fields.GeometryField(null=True, srid=4326)), ('type', models.CharField(choices=[('PA', 'Parcel'), ('CB', 'Community boundary'), ('BU', 'Building'), ('AP', 'Apartment'), ('PX', 'Project extent'), ('RW', 'Right-of-way'), ('UC', 'Utility corridor'), ('NP', 'National park boundary'), ('MI', 'Miscellaneous')], default='PA', max_length=2)), ('attributes', jsonattrs.fields.JSONAttributeField(default=jsonattrs.fields.JSONAttributes)), @@ -61,14 +60,13 @@ class Migration(migrations.Migration): name='SpatialUnit', fields=[ ('id', models.CharField(max_length=24, primary_key=True, serialize=False)), - ('name', models.CharField(max_length=200)), ('geometry', django.contrib.gis.db.models.fields.GeometryField(null=True, srid=4326)), ('type', models.CharField(choices=[('PA', 'Parcel'), ('CB', 'Community boundary'), ('BU', 'Building'), ('AP', 'Apartment'), ('PX', 'Project extent'), ('RW', 'Right-of-way'), ('UC', 'Utility corridor'), ('NP', 'National park boundary'), ('MI', 'Miscellaneous')], default='PA', max_length=2)), ('attributes', jsonattrs.fields.JSONAttributeField(default=jsonattrs.fields.JSONAttributes)), ('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='spatial_units', to='organization.Project')), ], options={ - 'ordering': ('name',), + 'ordering': ('type',), }, ), migrations.CreateModel( diff --git a/cadasta/spatial/migrations/0002_auto_20160708_2001.py b/cadasta/spatial/migrations/0002_auto_20160712_1513.py similarity index 90% rename from cadasta/spatial/migrations/0002_auto_20160708_2001.py rename to cadasta/spatial/migrations/0002_auto_20160712_1513.py index 08fb0af33..50e7eeace 100644 --- a/cadasta/spatial/migrations/0002_auto_20160708_2001.py +++ b/cadasta/spatial/migrations/0002_auto_20160712_1513.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Generated by Django 1.9.6 on 2016-07-08 20:01 +# Generated by Django 1.9.6 on 2016-07-12 15:13 from __future__ import unicode_literals from django.db import migrations diff --git a/cadasta/spatial/models.py b/cadasta/spatial/models.py index 532d41235..fd56b654b 100644 --- a/cadasta/spatial/models.py +++ b/cadasta/spatial/models.py @@ -31,10 +31,6 @@ class SpatialUnit(ResourceModelMixin, RandomIDModel): project = models.ForeignKey(Project, on_delete=models.CASCADE, related_name='spatial_units') - # All spatial units have a name used to provide - # a human-readable label for it. - name = models.CharField(max_length=200) - # Spatial unit type: used to manage range of allowed attributes. type = models.CharField(max_length=2, choices=TYPE_CHOICES, default='PA') @@ -59,7 +55,7 @@ class SpatialUnit(ResourceModelMixin, RandomIDModel): history = HistoricalRecords() class Meta: - ordering = ('name',) + ordering = ('type',) class TutelaryMeta: perm_type = 'spatial' @@ -88,7 +84,7 @@ class TutelaryMeta: ) def __str__(self): - return "".format(self.name) + return "".format(self.get_type_display()) def __repr__(self): return str(self) @@ -223,7 +219,7 @@ class TutelaryMeta: def __str__(self): return " {type} <{su2}>>".format( - su1=self.su1.name, su2=self.su2.name, + su1=self.su1.get_type_display(), su2=self.su2.get_type_display(), type=dict(self.TYPE_CHOICES).get(self.type)) def __repr__(self): diff --git a/cadasta/spatial/serializers.py b/cadasta/spatial/serializers.py index cb83f5544..efc85941d 100644 --- a/cadasta/spatial/serializers.py +++ b/cadasta/spatial/serializers.py @@ -31,7 +31,7 @@ class Meta: context_key = 'project' geo_field = 'geometry' id_field = False - fields = ('id', 'name', + fields = ('id', 'geometry', 'type', 'attributes', 'relationships', 'project',) read_only_fields = ('id', 'project',) @@ -66,8 +66,8 @@ def get_type(self, location): class SpatialRelationshipReadSerializer(serializers.ModelSerializer): - su1 = SpatialUnitSerializer(fields=('id', 'name', 'geometry', 'type')) - su2 = SpatialUnitSerializer(fields=('id', 'name', 'geometry', 'type')) + su1 = SpatialUnitSerializer(fields=('id', 'geometry', 'type')) + su2 = SpatialUnitSerializer(fields=('id', 'geometry', 'type')) rel_class = serializers.SerializerMethodField() class Meta: diff --git a/cadasta/spatial/tests/factories.py b/cadasta/spatial/tests/factories.py index 1dba64dac..db5b5bf09 100644 --- a/cadasta/spatial/tests/factories.py +++ b/cadasta/spatial/tests/factories.py @@ -9,7 +9,6 @@ class SpatialUnitFactory(ExtendedFactory): class Meta: model = SpatialUnit - name = factory.Sequence(lambda n: "Location #{}".format(n)) project = factory.SubFactory(ProjectFactory) diff --git a/cadasta/spatial/tests/test_models.py b/cadasta/spatial/tests/test_models.py index 7b8ccfabd..1a31b04e7 100644 --- a/cadasta/spatial/tests/test_models.py +++ b/cadasta/spatial/tests/test_models.py @@ -12,9 +12,9 @@ class SpatialUnitTest(UserTestCase): def test_str(self): - spatial_unit = SpatialUnitFactory.create(name='Disneyland') - assert str(spatial_unit) == '' - assert repr(spatial_unit) == '' + spatial_unit = SpatialUnitFactory.create(type='PA') + assert str(spatial_unit) == '' + assert repr(spatial_unit) == '' def test_has_random_id(self): spatial_unit = SpatialUnitFactory.create() @@ -89,28 +89,28 @@ def test_str(self): relationship = SpatialRelationshipFactory( project=self.project, su1__project=self.project, - su1__name='Los Angeles', + su1__type='PA', su2__project=self.project, - su2__name='California', + su2__type='CB', type='C') assert str(relationship) == ( " is-contained-in >" + " is-contained-in >" ) assert repr(relationship) == ( " is-contained-in >" + " is-contained-in >" ) def test_relationships_creation(self): relationship = SpatialRelationshipFactory( project=self.project, su1__project=self.project, - su1__name='Disneyworld', + su1__type='PA', su2__project=self.project, - su2__name='Disneyland') - su2_name = str(relationship.su1.relationships.all()[0]) - assert su2_name == '' + su2__type='CB') + su2_type = str(relationship.su1.relationships.all()[0]) + assert su2_type == '' def test_relationship_type(self): relationship = SpatialRelationshipFactory(type='S') @@ -129,16 +129,16 @@ def test_adding_attributes(self): required=False, index=1, attr_type=attr_type ) relationship = SpatialRelationshipFactory( - su1__name='Disneyworld', - su2__name='Disneyland', + su1__type='BU', + su2__type='AP', attributes={'test': 'Partner amusement parks.'}) assert relationship.attributes['test'] == 'Partner amusement parks.' def test_traversing_contained_spatial_unit(self): relationship = SpatialRelationshipFactory( - su1__name='Building', - su2__name='Apartment', + su1__type='BU', + su2__type='AP', type='C') su1_contains = str(relationship.su1.relationships.all()[0]) su2_is_contained_in = str(relationship.su2.relationships_set.all()[0]) @@ -149,31 +149,31 @@ def test_traversing_contained_spatial_unit(self): def test_traversing_split_spatial_unit(self): relationship = SpatialRelationshipFactory( - su1__name='Parent Property', - su2__name='Inheritance', + su1__type='BU', + su2__type='AP', type='S') su1_split_into = str(relationship.su1.relationships.all()[0]) su2_split_from = str(relationship.su2.relationships_set.all()[0]) assert relationship.get_type_display() == 'is-split-of' - assert su1_split_into == '' - assert su2_split_from == '' + assert su1_split_into == '' + assert su2_split_from == '' def test_traversing_merged_spatial_unit(self): relationship = SpatialRelationshipFactory( - su1__name='Married Property', - su2__name='Individual Property', + su1__type='BU', + su2__type='AP', type='M') su1_merged_from = str(relationship.su1.relationships.all()[0]) su2_merged_into = str(relationship.su2.relationships_set.all()[0]) assert relationship.get_type_display() == 'is-merge-of' - assert su1_merged_from == '' - assert su2_merged_into == '' + assert su1_merged_from == '' + assert su2_merged_into == '' def test_spatial_unit_contains_anothers_geometry(self): relationship = SpatialRelationshipFactory( - su1__name='Building', + su1__type='BU', su1__geometry='SRID=4326;POLYGON((' '-91.9947 34.7994, ' '-91.9950 34.7846, ' @@ -182,7 +182,7 @@ def test_spatial_unit_contains_anothers_geometry(self): '-91.9174 34.7627, ' '-91.9153 34.8032, ' '-91.9947 34.7994))', - su2__name='Apartment', + su2__type='AP', su2__geometry='SRID=4326;POLYGON((' '-91.9320 34.7918, ' '-91.9335 34.7846, ' @@ -195,7 +195,7 @@ def test_spatial_unit_contains_anothers_geometry(self): def test_relationship_fails_if_contained_unit_expands_outside_parent(self): with pytest.raises(Exception): SpatialRelationshipFactory( - su1__name='Building', + su1__type='BU', su2__geometry='SRID=4326;POLYGON((' '-91.9947 34.7994, ' '-91.9950 34.7846, ' @@ -204,7 +204,7 @@ def test_relationship_fails_if_contained_unit_expands_outside_parent(self): '-91.9174 34.7627, ' '-91.9153 34.8032, ' '-91.9947 34.7994))', - su2__name='Apartment', + su2__type='AP', su1__geometry='SRID=4326;POLYGON((' '-91.9320 34.7918, ' '-91.9335 34.7846, ' @@ -216,14 +216,14 @@ def test_relationship_fails_if_contained_unit_expands_outside_parent(self): def test_spatial_unit_does_not_contain_anothers_geometry(self): with pytest.raises(Exception): SpatialRelationshipFactory( - su1__name='Building', + su1__type='BU', su1__geometry='SRID=4326;POLYGON((' '-91.9960 34.7850, ' '-91.9960 34.8016, ' '-91.9785 34.8016, ' '-91.9785 34.7850, ' '-91.9960 34.7850))', - su2__name='Apartment', + su2__type='AP', su2__geometry='SRID=4326;POLYGON((' '11.36667 47.25000, ' '11.41667 47.25000, ' @@ -234,7 +234,7 @@ def test_spatial_unit_does_not_contain_anothers_geometry(self): def test_spatial_unit_contains_a_point(self): relationship = SpatialRelationshipFactory( - su1__name='Building', + su1__type='BU', su1__geometry='SRID=4326;POLYGON((' '-109.0461 40.2617, ' '-108.6039 40.2459,' @@ -243,7 +243,7 @@ def test_spatial_unit_contains_a_point(self): '-108.8841 40.7836, ' '-109.0434 40.8657, ' '-109.0461 40.2617))', - su2__name='Apartment', + su2__type='AP', su2__geometry='SRID=4326;POINT(' '-108.7536 40.5054)', type='C') @@ -252,7 +252,7 @@ def test_spatial_unit_contains_a_point(self): def test_spatial_unit_does_not_contain_point(self): with pytest.raises(Exception): SpatialRelationshipFactory( - su1__name='Building', + su1__type='BU', su1__geometry='SRID=4326;POLYGON((' '-109.0461 40.2617, ' '-108.6039 40.2459,' @@ -261,17 +261,17 @@ def test_spatial_unit_does_not_contain_point(self): '-108.8841 40.7836, ' '-109.0434 40.8657, ' '-109.0461 40.2617))', - su2__name='Apartment', + su2__type='AP', su2__geometry='SRID=4326;POINT(' '-108.0972 40.9508)', type='C') def test_spatial_unit_point_contains_relationship_still_created(self): relationship = SpatialRelationshipFactory( - su1__name='Building', + su1__type='BU', su1__geometry='SRID=4326;POINT(' '-108.7536 40.5054)', - su2__name='Apartment', + su2__type='AP', su2__geometry='SRID=4326;POLYGON((' '-109.0461 40.2617, ' '-108.6039 40.2459,' diff --git a/cadasta/spatial/tests/test_serializers.py b/cadasta/spatial/tests/test_serializers.py index 7d8ca40d5..1627a4801 100644 --- a/cadasta/spatial/tests/test_serializers.py +++ b/cadasta/spatial/tests/test_serializers.py @@ -40,29 +40,13 @@ def test_project_detail_not_serialized(self): serializer = serializers.SpatialUnitSerializer(spatial_data) assert 'description' not in serializer.data['properties']['project'] - def test_create_without_name(self): - project = ProjectFactory.create() - spatial_data = { - 'properties': { - 'name': '', - 'project': project - } - } - - serializer = serializers.SpatialUnitSerializer( - data=spatial_data, - context={'project': project} - ) - with pytest.raises(ValidationError): - serializer.is_valid(raise_exception=True) - def test_update_spatial_unit(self): project = ProjectFactory.create() - su = SpatialUnitFactory.create(name='Test Spatial Unit', + su = SpatialUnitFactory.create(type='BU', project=project) spatial_data = { 'properties': { - 'name': 'Updated Spatial Unit', + 'type': 'AP', 'project': project } } @@ -77,15 +61,15 @@ def test_update_spatial_unit(self): serializer.save() su_update = SpatialUnit.objects.get(id=su.id) - assert su_update.name == 'Updated Spatial Unit' + assert su_update.type == 'AP' def test_update_spatial_unit_fails(self): project = ProjectFactory.create() - su = SpatialUnitFactory.create(name='Test Spatial Unit', + su = SpatialUnitFactory.create(type='BU', project=project) spatial_data = { 'properties': { - 'name': '' + 'type': '' } } serializer = serializers.SpatialUnitSerializer( @@ -100,8 +84,7 @@ def test_update_spatial_unit_fails(self): def test_update_spatial_unit_project_fails(self): project = ProjectFactory.create(name='Original Project') - su = SpatialUnitFactory.create(name='Test Spatial Unit', - project=project) + su = SpatialUnitFactory.create(project=project) ProjectFactory.create(name='New Project') spatial_data = { 'properties': { diff --git a/cadasta/spatial/tests/test_views_api_spatial_relationships.py b/cadasta/spatial/tests/test_views_api_spatial_relationships.py index eb1e8494d..f6fccc981 100644 --- a/cadasta/spatial/tests/test_views_api_spatial_relationships.py +++ b/cadasta/spatial/tests/test_views_api_spatial_relationships.py @@ -31,9 +31,9 @@ def _test_objs(self, access='public'): org = OrganizationFactory.create(slug='namati') prj = ProjectFactory.create( slug='test-project', organization=org, access=access) - su1 = SpatialUnitFactory.create(project=prj, name="House") - su2 = SpatialUnitFactory.create(project=prj, name="Parcel") - su3 = SpatialUnitFactory.create(project=prj, name="Bungalow") + su1 = SpatialUnitFactory.create(project=prj, type='AP') + su2 = SpatialUnitFactory.create(project=prj, type='BU') + su3 = SpatialUnitFactory.create(project=prj, type='CB') SpatialRelationshipFactory.create(project=prj, su1=su1, su2=su2) SpatialRelationshipFactory.create(project=prj, su1=su2, su2=su3) SpatialRelationshipFactory.create(project=prj, su1=su1, su2=su3) @@ -55,6 +55,10 @@ def default_create_data(self): 'type': 'C' } + def check_for_unchanged(self, content): + assert content['su1']['properties']['type'] == "AP" + assert content['su2']['properties']['type'] == "BU" + # Additional tests def test_create_invalid_record_with_dupe_su(self): @@ -73,7 +77,7 @@ def test_create_invalid_record_with_dupe_su(self): def test_create_invalid_record_with_different_project(self): org, prj = self._test_objs() other_prj = ProjectFactory.create(slug='other', organization=org) - other_su = SpatialUnitFactory.create(project=other_prj, name="Other") + other_su = SpatialUnitFactory.create(project=other_prj) invalid_data = { 'su1': self.su1.id, 'su2': other_su.id, @@ -105,9 +109,9 @@ def _test_objs(self, access='public'): org = OrganizationFactory.create(slug='namati') prj = ProjectFactory.create( slug='test-project', organization=org, access=access) - su1 = SpatialUnitFactory.create(project=prj, name="House") - su2 = SpatialUnitFactory.create(project=prj, name="Parcel") - su3 = SpatialUnitFactory.create(project=prj, name="Bungalow") + su1 = SpatialUnitFactory.create(project=prj, type='AP') + su2 = SpatialUnitFactory.create(project=prj, type='BU') + su3 = SpatialUnitFactory.create(project=prj) rel = SpatialRelationshipFactory.create( project=prj, su1=su1, su2=su2) self.su1 = su1 @@ -137,8 +141,8 @@ def check_for_updated(self, content): assert content['su2'] == self.su3.id def check_for_unchanged(self, content): - assert content['su1']['properties']['name'] == "House" - assert content['su2']['properties']['name'] == "Parcel" + assert content['su1']['properties']['type'] == "AP" + assert content['su2']['properties']['type'] == "BU" # Additional tests @@ -156,7 +160,7 @@ def test_update_invalid_record_with_different_project(self): other_org = OrganizationFactory.create(slug='other') other_prj = ProjectFactory.create(slug='other', organization=other_org) - other_su = SpatialUnitFactory.create(project=other_prj, name="Other") + other_su = SpatialUnitFactory.create(project=other_prj) def get_invalid_data(): return {'su2': other_su.id} diff --git a/cadasta/spatial/tests/test_views_api_spatial_units.py b/cadasta/spatial/tests/test_views_api_spatial_units.py index a3c26d415..ad7f1a283 100644 --- a/cadasta/spatial/tests/test_views_api_spatial_units.py +++ b/cadasta/spatial/tests/test_views_api_spatial_units.py @@ -30,10 +30,10 @@ def _test_objs(self, access='public'): org = OrganizationFactory.create(slug='namati') prj = ProjectFactory.create( slug='test-project', organization=org, access=access) - SpatialUnitFactory.create(project=prj, name='Top of the world') - SpatialUnitFactory.create(project=prj, name='South Pole') + SpatialUnitFactory.create(project=prj, type='AP') + SpatialUnitFactory.create(project=prj, type='BU') SpatialUnitFactory.create( - project=prj, name='Center of the earth', type='RW') + project=prj, type='RW') self.num_records = 3 return (org, prj) @@ -47,26 +47,26 @@ def test_full_list(self): content = self._get( org_slug=org.slug, prj_slug=prj.slug, status=status_code.HTTP_200_OK, length=self.num_records) - assert extra_record.name not in ( - [u['properties']['name'] for u in content['features']]) - - def test_search_filter(self): - org, prj = self._test_objs() - content = self._get( - org_slug=org.slug, prj_slug=prj.slug, - status=status_code.HTTP_200_OK, length=1, query='search=earth') - assert all( - record['properties']['name'] == 'Center of the earth' for - record in content['features']) + assert extra_record.id not in ( + [u['properties']['id'] for u in content['features']]) + + # def test_search_filter(self): + # org, prj = self._test_objs() + # content = self._get( + # org_slug=org.slug, prj_slug=prj.slug, + # status=status_code.HTTP_200_OK, length=1, query='search=AP') + # assert all( + # record['properties']['type'] == 'AP' for + # record in content['features']) def test_ordering(self): org, prj = self._test_objs() content = self._get( org_slug=org.slug, prj_slug=prj.slug, status=status_code.HTTP_200_OK, length=self.num_records, - query='ordering=name') + query='ordering=type') names = [ - record['properties']['name'] for record in content['features']] + record['properties']['type'] for record in content['features']] assert names == sorted(names) def test_reverse_ordering(self): @@ -74,9 +74,9 @@ def test_reverse_ordering(self): content = self._get( org_slug=org.slug, prj_slug=prj.slug, status=status_code.HTTP_200_OK, length=self.num_records, - query='ordering=-name') + query='ordering=-type') names = [ - record['properties']['name'] for record in content['features']] + record['properties']['type'] for record in content['features']] assert names == sorted(names, reverse=True) def test_type_filter(self): @@ -94,7 +94,7 @@ class SpatialUnitCreateAPITest(SpatialUnitListTestCase, default_create_data = { 'properties': { - 'name': "Small world" + 'type': "AP" }, 'geometry': { 'type': 'Point', @@ -107,6 +107,7 @@ class SpatialUnitCreateAPITest(SpatialUnitListTestCase, def test_create_invalid_spatial_unit(self): org, prj = self._test_objs() invalid_data = { + 'type': '', 'geometry': { 'type': 'Point', 'coordinates': [100, 0] @@ -115,12 +116,12 @@ def test_create_invalid_spatial_unit(self): content = self._post( org_slug=org.slug, prj_slug=prj.slug, data=invalid_data, status=status_code.HTTP_400_BAD_REQUEST) - assert content['name'][0] == _("This field is required.") + assert content['type'][0] == _('"" is not a valid choice.') def test_create_spatial_unit_with_invalid_geometry(self): org, prj = self._test_objs() invalid_data = { - 'name': "Cat points!", + 'type': "BU", 'geometry': { 'type': 'Cats', 'coordinates': [100, 0] @@ -149,7 +150,7 @@ def _test_objs(self, access='public'): org = OrganizationFactory.create(slug='namati') prj = ProjectFactory.create( slug='test-project', organization=org, access=access) - su = SpatialUnitFactory.create(project=prj, name="House") + su = SpatialUnitFactory.create(project=prj, type='AP') self.su = su return (su, org) @@ -165,24 +166,24 @@ class SpatialUnitUpdateAPITest(SpatialUnitDetailTestCase, RecordUpdateAPITest): def get_valid_updated_data(self): - return {'name': "Way Cooler Name"} + return {'type': "BU"} def check_for_updated(self, content): data = self.get_valid_updated_data() - assert content['properties']['name'] == data['name'] + assert content['properties']['type'] == data['type'] def check_for_unchanged(self, content): - assert content['properties']['name'] == self.su.name + assert content['properties']['type'] == self.su.type # Additional tests def test_update_with_invalid_data(self): - def get_invalid_data(): return {'name': ''} + def get_invalid_data(): return {'type': ''} content = self._test_patch_public_record( get_invalid_data, status_code.HTTP_400_BAD_REQUEST) - assert content['name'][0] == _('This field may not be blank.') + assert content['type'][0] == _('"" is not a valid choice.') def test_update_with_invalid_geometry(self): diff --git a/cadasta/spatial/views/api.py b/cadasta/spatial/views/api.py index 7157ae63b..a47ab76cf 100644 --- a/cadasta/spatial/views/api.py +++ b/cadasta/spatial/views/api.py @@ -15,8 +15,6 @@ class SpatialUnitList(APIPermissionRequiredMixin, filters.SearchFilter, filters.OrderingFilter,) filter_fields = ('type',) - search_fields = ('name',) - ordering_fields = ('name',) permission_required = { 'GET': 'spatial.list', diff --git a/cadasta/xforms/mixins/model_helper.py b/cadasta/xforms/mixins/model_helper.py index 62c6b3fec..c2d1d188d 100644 --- a/cadasta/xforms/mixins/model_helper.py +++ b/cadasta/xforms/mixins/model_helper.py @@ -36,7 +36,6 @@ def add_data_to_party(self, data, project): def add_data_to_spatial_unit(self, data, project): location = SpatialUnit.objects.create( project=project, - name=data['location_name'], type=data['location_type'], geometry=self._format_geometry(data['location_geometry']), attributes=data['location_attributes'] @@ -57,7 +56,7 @@ def add_data_to_resource(self, data, user, project, content_object=None): Storage = get_storage_class() storage = Storage() url = storage.save('resources/' + data.name, data.file.read()) - # if not Resource.objects.filter(name=data.name).exists(): + Resource.objects.create( name=data.name, file=url, @@ -119,10 +118,10 @@ def upload_files(self, data, survey): if i != 'xml_submission_file': if i == survey['location_photo']: content_object = SpatialUnit.objects.get( - id=survey['location_name']) + id=survey['location']) elif i == survey['party_photo']: content_object = Party.objects.get( - id=survey['party_name']) + id=survey['party']) self.add_data_to_resource(data=data[i], user=user, diff --git a/cadasta/xforms/serializers.py b/cadasta/xforms/serializers.py index c87f9aba9..25c947c29 100644 --- a/cadasta/xforms/serializers.py +++ b/cadasta/xforms/serializers.py @@ -52,7 +52,7 @@ def create(self, request, *args, **kwargs): 'instanceID': survey['meta']['instanceID'], 'project': create_models['project'], 'location_photo': survey['location_photo'], - 'location_name': create_models['location'].id, + 'location': create_models['location'].id, 'party_photo': survey['party_photo'], - 'party_name': create_models['party'].id + 'party': create_models['party'].id } diff --git a/cadasta/xforms/tests/attr_schemas.py b/cadasta/xforms/tests/attr_schemas.py index f39bb3dd2..8d9a85652 100644 --- a/cadasta/xforms/tests/attr_schemas.py +++ b/cadasta/xforms/tests/attr_schemas.py @@ -66,91 +66,10 @@ "label": "Location Attributes", "children": [ { - "default": "none", - "choices": [ - { - "name": "none", - "label": "No data" - }, - { - "name": "text", - "label": "Textual" - }, - { - "name": "point", - "label": "Point data" - }, - { - "name": "polygon_low", - "label": "Low quality polygon" - }, - { - "name": "polygon_high", - "label": "High quality polygon" - } - ], - "hint": "Quality of parcel geometry", - "name": "quality", - "type": "select one", - "label": "Spatial Unit Quality" - }, - { - "label": "Acquired when", - "name": "acquired_when", - "type": "date" - }, - { - "label": "Acquired how", - "name": "acquired_how", - "type": "select one", - "choices": [ - { - "name": "CS", - "label": "Contractual Share Crop" - }, - { - "name": "CA", - "label": "Customary Arrangement" - }, - { - "name": "GF", - "label": "Gift" - }, - { - "name": "HS", - "label": "Homestead" - }, - { - "name": "IO", - "label": "Informal Occupant" - }, - { - "name": "IN", - "label": "Inheritance" - }, - { - "name": "LH", - "label": "Leasehold" - }, - { - "name": "PF", - "label": "Purchased Freehold" - }, - { - "name": "RN", - "label": "Rental" - }, - { - "name": "OT", - "label": "Other" - } - ], - }, - { - "label": "Notes", - "name": "notes", + "label": "Name", + "name": "name", "type": "text", - "hint": "Additional Notes" + "hint": "Name of the location" } ] } diff --git a/cadasta/xforms/tests/files/test_resources.py b/cadasta/xforms/tests/files/test_resources.py index ebfbea609..b50c56ca9 100644 --- a/cadasta/xforms/tests/files/test_resources.py +++ b/cadasta/xforms/tests/files/test_resources.py @@ -54,17 +54,13 @@ <party_type>IN</party_type> <party_name>Bilbo Baggins</party_name> - <location_name>Middle Earth</location_name> <location_geometry>40.6890612 -73.9925067 0.0 0.0;</location_geometry> <location_type>MI</location_type> <location_photo>test_image.png</location_photo> <party_photo /> <tenure_type>LH</tenure_type> <location_attributes> - <quality>none</quality> - <acquired_how>LH</acquired_how> - <acquired_when>2016-07-07</acquired_when> - <notes>Locaiton attribute notes.</notes> + <name>Middle Earth</name> </location_attributes> <party_attributes_default> <notes>Party attribute default notes.</notes> @@ -94,17 +90,13 @@ <title /> <party_type>IN</party_type> <party_name>Bilbo Baggins</party_name> - <location_name>Middle Earth</location_name> <location_geometry /> <location_type>MI</location_type> <location_photo>test_image.png</location_photo> <party_photo>this-is-fine.png</party_photo> <tenure_type>LH</tenure_type> <location_attributes> - <quality>none</quality> - <acquired_how>LH</acquired_how> - <acquired_when>2016-07-07</acquired_when> - <notes>Locaiton attribute notes.</notes> + <name>Middle Earth</name> </location_attributes> <party_attributes_default> <notes>Party attribute default notes.</notes> @@ -134,17 +126,13 @@ <title /> <party_type>IN</party_type> <party_name></party_name> - <location_name>Null Island</location_name> <location_geometry>40.6890612 -73.9925067 0.0 0.0;</location_geometry> <location_type>MI</location_type> <location_photo>test_image.png</location_photo> <party_photo /> <tenure_type>LH</tenure_type> <location_attributes> - <quality>none</quality> - <acquired_how>LH</acquired_how> - <acquired_when>2016-07-07</acquired_when> - <notes>Locaiton attribute notes.</notes> + <name>Null Island</name> </location_attributes> <party_attributes_default> <notes>Party attribute default notes.</notes> @@ -174,7 +162,6 @@ <title /> <party_type>IN</party_type> <party_name>Peggy Carter</party_name> - <location_name>Polygon</location_name> <location_geometry>40.6890612 -73.9925067 0.0 0.0; 41.6890612 -73.9925067 0.0 0.0;41.6890612 -72.9925067 0.0 0.0; 40.6890612 -72.9925067 0.0 0.0;40.6890612 -73.9925067 0.0 0.0; @@ -184,10 +171,7 @@ <party_photo /> <tenure_type>LH</tenure_type> <location_attributes> - <quality>none</quality> - <acquired_how>LH</acquired_how> - <acquired_when>2016-07-07</acquired_when> - <notes>Locaiton attribute notes.</notes> + <name>Polygon</name> </location_attributes> <party_attributes_default> <notes>Party attribute default notes.</notes> @@ -217,7 +201,6 @@ <title /> <party_type>IN</party_type> <party_name>Buckey Barnes</party_name> - <location_name>Line</location_name> <location_geometry>45.56342779158167 -122.67650283873081 0.0 0.0; 45.56176327330353 -122.67669159919024 0.0 0.0; 45.56151562182025 -122.67490658909082 0.0 0.0; @@ -229,10 +212,7 @@ <party_photo /> <tenure_type>LH</tenure_type> <location_attributes> - <quality>none</quality> - <acquired_how>LH</acquired_how> - <acquired_when>2016-07-07</acquired_when> - <notes>Locaiton attribute notes.</notes> + <name>Line</name> </location_attributes> <party_attributes_default> <notes>Party attribute default notes.</notes> @@ -262,7 +242,6 @@ <title /> <party_type>IN</party_type> <party_name>Stever Rogers</party_name> - <location_name>Missing Semi</location_name> <location_geometry>40.6890612 -73.9925067 0.0 0.0 </location_geometry> <location_type>MI</location_type> @@ -270,10 +249,7 @@ <party_photo /> <tenure_type>LH</tenure_type> <location_attributes> - <quality>none</quality> - <acquired_how>LH</acquired_how> - <acquired_when>2016-07-07</acquired_when> - <notes>Locaiton attribute notes.</notes> + <name>Missing Semi</name> </location_attributes> <party_attributes_default> <notes>Party attribute default notes.</notes> @@ -303,7 +279,6 @@ <title /> <party_type>IN</party_type> <party_name>Scrooge McDuck</party_name> - <location_name>Bank</location_name> <location_geometry>40.6890612 -73.9925067 0.0 0.0; 41.6890612 -73.9925067 0.0 0.0;41.6890612 -72.9925067 0.0 0.0; 40.6890612 -72.9925067 0.0 0.0;40.6890612 -73.9925067 0.0 0.0; @@ -313,10 +288,7 @@ <party_photo /> <tenure_type>LH</tenure_type> <location_attributes> - <quality>none</quality> - <acquired_how>LH</acquired_how> - <acquired_when>2016-07-07</acquired_when> - <notes>Locaiton attribute notes.</notes> + <name>Bank</name> </location_attributes> <party_attributes_default> <notes>Party attribute default notes.</notes> diff --git a/cadasta/xforms/tests/test_renderers.py b/cadasta/xforms/tests/test_renderers.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/cadasta/xforms/tests/test_views_api.py b/cadasta/xforms/tests/test_views_api.py index e8ea0af34..97076e954 100644 --- a/cadasta/xforms/tests/test_views_api.py +++ b/cadasta/xforms/tests/test_views_api.py @@ -227,10 +227,11 @@ def test_survey_upload(self): image=images['test_image'], status=201) assert Party.objects.filter(name='Bilbo Baggins').exists() - assert SpatialUnit.objects.filter(name='Middle Earth').exists() + assert SpatialUnit.objects.filter( + attributes={'name': 'Middle Earth'}).exists() party = Party.objects.get(name='Bilbo Baggins') - location = SpatialUnit.objects.get(name='Middle Earth') + location = SpatialUnit.objects.get(attributes={'name': 'Middle Earth'}) assert location in party.tenure_relationships.all() assert Resource.objects.filter(name__contains='test_image').exists() @@ -243,10 +244,11 @@ def test_multiple_resource_upload(self): image=images, status=201) assert Party.objects.filter(name='Bilbo Baggins').exists() - assert SpatialUnit.objects.filter(name='Middle Earth').exists() + assert SpatialUnit.objects.filter( + attributes={'name': 'Middle Earth'}).exists() party = Party.objects.get(name='Bilbo Baggins') - location = SpatialUnit.objects.get(name='Middle Earth') + location = SpatialUnit.objects.get(attributes={'name': 'Middle Earth'}) assert location in party.tenure_relationships.all() assert Resource.objects.filter(name__contains='test_image').exists() @@ -259,15 +261,16 @@ def test_multiple_resource_upload(self): def test_invalid_resource_upload(self): # testing submitting with a missing xml_submission_file self._post(form=images['test_image'], status=400, valid=False) - assert not SpatialUnit.objects.filter(name='Null Island').exists() + assert not SpatialUnit.objects.filter( + attributes={'name': 'Null Island'}).exists() def test_geometry_upload(self): self._post(form='line_form', status=201) self._post(form='poly_form', status=201) self._post(form='missing_semi_form', status=201) - polygon = SpatialUnit.objects.get(name='Polygon') - line = SpatialUnit.objects.get(name='Line') - point = SpatialUnit.objects.get(name='Missing Semi') + polygon = SpatialUnit.objects.get(attributes={'name': 'Polygon'}) + line = SpatialUnit.objects.get(attributes={'name': 'Line'}) + point = SpatialUnit.objects.get(attributes={'name': 'Missing Semi'}) assert polygon.geometry.geom_type == 'Polygon' assert line.geometry.geom_type == 'LineString'