Skip to content

Commit

Permalink
Implements #611 -- Customizable location and relationship types
Browse files Browse the repository at this point in the history
  • Loading branch information
oliverroick committed May 16, 2017
1 parent 992117a commit 9a69328
Show file tree
Hide file tree
Showing 51 changed files with 745 additions and 286 deletions.
37 changes: 37 additions & 0 deletions cadasta/core/form_mixins.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from django.utils.translation import get_language
from django.forms import Form, ModelForm, MultipleChoiceField
from jsonattrs.mixins import template_xlang_labels
from jsonattrs.forms import form_field_from_name
Expand All @@ -9,6 +10,41 @@
from .widgets import XLangSelect, XLangSelectMultiple


def get_types(question_name, default, questionnaire_id=None,
include_labels=False):
types = []
if questionnaire_id:
try:
question = Question.objects.get(
name=question_name,
questionnaire=questionnaire_id)
except Question.DoesNotExist:
pass
else:
types = []
options = QuestionOption.objects.filter(
question=question).values_list('name', 'label_xlat')

lang = get_language()
default_lang = question.questionnaire.default_language

for o in options:
label = o[1]

if isinstance(label, dict):
label = label.get(lang, label.get(default_lang))

types.append((o[0], label))

if not types:
types = default

if include_labels:
return types
else:
return [t[0] for t in types]


class SuperUserCheck:

def __init__(self, *args, **kwargs):
Expand Down Expand Up @@ -48,6 +84,7 @@ def set_standard_field(self, name, empty_choice=None, field_name=None):

choices = ([('', empty_choice)] + list(choices)
if empty_choice else list(choices))
self.fields[field_name].choices = choices
self.fields[field_name].widget = XLangSelect(
attrs=self.fields[field_name].widget.attrs,
choices=choices,
Expand Down
3 changes: 0 additions & 3 deletions cadasta/core/management/commands/loadstatic.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
from core.management.commands import loadsite
from accounts.management.commands import loadpolicies
from geography.management.commands import loadcountries
from party.management.commands import loadtenurereltypes
from jsonattrs.management.commands import loadattrtypes


Expand Down Expand Up @@ -33,5 +32,3 @@ def handle(self, *args, **options):
loadpolicies.Command().handle(force=options['force'])
print('LOADING ATTRIBUTE TYPES\n')
loadattrtypes.Command().handle(force=options['force'])
print('LOADING TENURE RELATIONSHIP TYPES\n')
loadtenurereltypes.Command().handle(force=options['force'])
137 changes: 125 additions & 12 deletions cadasta/core/tests/test_form_mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@
from organization.tests.factories import ProjectFactory
from party.models import Party
from party.tests.factories import PartyFactory
from party.choices import TENURE_RELATIONSHIP_TYPES as default_types
from questionnaires.tests import factories as q_factories

from ..form_mixins import AttributeForm, AttributeFormMixin, AttributeModelForm
from .. import form_mixins
from ..mixins import SchemaSelectorMixin
from ..widgets import XLangSelect


class MockAttributeForm(AttributeForm):
class MockAttributeForm(form_mixins.AttributeForm):

def __init__(self, project, *args, **kwargs):
super().__init__(*args, **kwargs)
Expand All @@ -24,7 +25,7 @@ def __init__(self, project, *args, **kwargs):
self.add_attribute_fields(content_type)


class MockAttributeModelForm(AttributeModelForm):
class MockAttributeModelForm(form_mixins.AttributeModelForm):

attributes_field = 'attributes'

Expand Down Expand Up @@ -107,7 +108,7 @@ def setUp(self):
)

def test_create_model_fields(self):
form_mixin = AttributeFormMixin()
form_mixin = form_mixins.AttributeFormMixin()
schema_mixin = SchemaSelectorMixin()
form_mixin.fields = {}
attributes = schema_mixin.get_model_attributes(
Expand Down Expand Up @@ -141,7 +142,7 @@ def test_create_model_fields(self):
assert number.initial == '0'

def test_create_model_fields_with_new_item(self):
form_mixin = AttributeFormMixin()
form_mixin = form_mixins.AttributeFormMixin()
schema_mixin = SchemaSelectorMixin()
form_mixin.fields = {}
attributes = schema_mixin.get_model_attributes(
Expand Down Expand Up @@ -170,7 +171,7 @@ def test_process_attributes(self):
'party::in::number_of_something': '12',
'party::in::homeowner': True
}
form_mixin = AttributeFormMixin()
form_mixin = form_mixins.AttributeFormMixin()
schema_mixin = SchemaSelectorMixin()
form_mixin.fields = {}
attributes = schema_mixin.get_model_attributes(
Expand All @@ -186,7 +187,7 @@ def test_process_attributes(self):
assert processed_attributes['number_of_something'] == '12'

def test_set_standard_field(self):
form_mixin = AttributeFormMixin()
form_mixin = form_mixins.AttributeFormMixin()
form_mixin.project = self.project
form_mixin.fields = {'party_name': CharField()}
questionnaire = q_factories.QuestionnaireFactory(project=self.project)
Expand All @@ -200,7 +201,7 @@ def test_set_standard_field(self):
assert 'de="Name"' in form_mixin.fields['party_name'].labels_xlang

def test_set_standard_field_set_field_name(self):
form_mixin = AttributeFormMixin()
form_mixin = form_mixins.AttributeFormMixin()
form_mixin.project = self.project
form_mixin.fields = {'name': CharField()}
questionnaire = q_factories.QuestionnaireFactory(project=self.project)
Expand All @@ -214,7 +215,7 @@ def test_set_standard_field_set_field_name(self):
assert 'de="Name"' in form_mixin.fields['name'].labels_xlang

def test_set_standard_field_no_question(self):
form_mixin = AttributeFormMixin()
form_mixin = form_mixins.AttributeFormMixin()
form_mixin.project = self.project
form_mixin.fields = {'name': CharField()}
questionnaire = q_factories.QuestionnaireFactory(project=self.project)
Expand All @@ -227,7 +228,7 @@ def test_set_standard_field_no_question(self):
assert hasattr(form_mixin.fields['name'], 'labels_xlang') is False

def test_set_standard_field_with_options(self):
form_mixin = AttributeFormMixin()
form_mixin = form_mixins.AttributeFormMixin()
form_mixin.project = self.project
form_mixin.fields = {'building': ChoiceField(
choices=(('barn', 'Barn'), ('house', 'House')))}
Expand Down Expand Up @@ -260,7 +261,7 @@ def test_set_standard_field_with_options(self):
}

def test_set_standard_field_with_empty_choice(self):
form_mixin = AttributeFormMixin()
form_mixin = form_mixins.AttributeFormMixin()
form_mixin.project = self.project
form_mixin.fields = {'building': ChoiceField(
choices=(('barn', 'Barn'), ('house', 'House')))}
Expand Down Expand Up @@ -298,7 +299,7 @@ def test_set_standard_field_with_empty_choice(self):
}

def test_set_standard_field_with_single_lang(self):
form_mixin = AttributeFormMixin()
form_mixin = form_mixins.AttributeFormMixin()
form_mixin.project = self.project
form_mixin.fields = {'building': ChoiceField(
choices=(('barn', 'Barn'), ('house', 'House')))}
Expand Down Expand Up @@ -386,3 +387,115 @@ def test_add_attribute_fields(self):
attr = form.fields.get('party::gr::homeowner')
assert attr is not None
assert attr.label == 'Homeowner'


class GetTypesTest(TestCase):
def test_get_types_no_questionnaire(self):
types = form_mixins.get_types('tenure_type',
default_types,
include_labels=True)
assert types == default_types

def test_get_types_no_question(self):
types = form_mixins.get_types('tenure_type',
default_types,
questionnaire_id='abc',
include_labels=True)
assert types == default_types

def test_get_types_questionnaire_default_label(self):
questionnaire = q_factories.QuestionnaireFactory.create()
question = q_factories.QuestionFactory.create(
type='S1',
name='tenure_type',
questionnaire=questionnaire)
q_factories.QuestionOptionFactory.create(
question=question,
name='AA',
label='AA Label')
q_factories.QuestionOptionFactory.create(
question=question,
name='BB',
label='BB Label')

types = form_mixins.get_types('tenure_type',
default_types,
questionnaire_id=questionnaire.id,
include_labels=True)
assert list(types) == [('AA', 'AA Label'), ('BB', 'BB Label')]

def test_get_types_questionnaire_multilang_label(self):
questionnaire = q_factories.QuestionnaireFactory.create(
default_language='kar')
question = q_factories.QuestionFactory.create(
type='S1',
name='tenure_type',
label={'kar': 'Karen Type', 'cs': 'Czech Type'},
questionnaire=questionnaire)
q_factories.QuestionOptionFactory.create(
question=question,
name='AA',
label={'kar': 'AA Karen', 'cs': 'AA Czech'})
q_factories.QuestionOptionFactory.create(
question=question,
name='BB',
label={'kar': 'BB Karen', 'cs': 'BB Czech'})

types = form_mixins.get_types('tenure_type',
default_types,
questionnaire_id=questionnaire.id,
include_labels=True)
assert list(types) == [('AA', 'AA Karen'), ('BB', 'BB Karen')]

def test_get_types_no_questionnaire_no_labels(self):
types = form_mixins.get_types('tenure_type',
default_types)
assert types == [t[0] for t in default_types]

def test_get_types_no_question_no_labels(self):
types = form_mixins.get_types('tenure_type',
default_types,
questionnaire_id='abc')
assert types == [t[0] for t in default_types]

def test_get_types_questionnaire_default_label_no_labels(self):
questionnaire = q_factories.QuestionnaireFactory.create()
question = q_factories.QuestionFactory.create(
type='S1',
name='tenure_type',
questionnaire=questionnaire)
q_factories.QuestionOptionFactory.create(
question=question,
name='AA',
label='AA Label')
q_factories.QuestionOptionFactory.create(
question=question,
name='BB',
label='BB Label')

types = form_mixins.get_types('tenure_type',
default_types,
questionnaire_id=questionnaire.id)
assert list(types) == ['AA', 'BB']

def test_get_types_questionnaire_multilang_label_no_labels(self):
questionnaire = q_factories.QuestionnaireFactory.create(
default_language='kar')
question = q_factories.QuestionFactory.create(
type='S1',
name='tenure_type',
label={'kar': 'Karen Type', 'cs': 'Czech Type'},
questionnaire=questionnaire)
q_factories.QuestionOptionFactory.create(
question=question,
name='AA',
label={'kar': 'AA Karen', 'cs': 'AA Czech'})
q_factories.QuestionOptionFactory.create(
question=question,
name='BB',
label={'kar': 'BB Karen', 'cs': 'BB Czech'})

types = form_mixins.get_types('tenure_type',
default_types,
questionnaire_id=questionnaire.id)
assert list(types) == ['AA', 'BB']
2 changes: 0 additions & 2 deletions cadasta/core/tests/test_management_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
from spatial.models import SpatialUnit, SpatialRelationship
from core.management.commands import loadsite
from jsonattrs.models import create_attribute_types
from party.models import load_tenure_relationship_types


class FixturesTest(TestCase):
Expand All @@ -23,7 +22,6 @@ def test_fixture_setup(self):
# Just for test coverage...
PolicyFactory.load_policies(force=True)
create_attribute_types()
load_tenure_relationship_types()
data.add_test_users_and_roles()
data.add_test_projects()
data.add_test_spatial_units()
Expand Down
2 changes: 0 additions & 2 deletions cadasta/core/tests/utils/cases.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,13 @@
from buckets.test.storage import FakeS3Storage
from core.tests.factories import PolicyFactory
from jsonattrs.models import create_attribute_types
from party.models import load_tenure_relationship_types


class UserTestCase:
def setUp(self):
super().setUp()
PolicyFactory.load_policies()
create_attribute_types()
load_tenure_relationship_types(force=True)


class FileStorageTestCase:
Expand Down
4 changes: 2 additions & 2 deletions cadasta/organization/download/shape.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ def write_relationships(self, filename):
content_type = ContentType.objects.get(app_label='party',
model='tenurerelationship')
self.write_items(filename, relationships, content_type,
('id', 'party_id', 'spatial_unit_id',
'tenure_type.id', 'tenure_type.label'))
('id', 'party_id', 'spatial_unit_id', 'tenure_type',
'tenure_type_label'))

def write_parties(self, filename):
parties = self.project.parties.all()
Expand Down
4 changes: 2 additions & 2 deletions cadasta/organization/download/xls.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ def write_relationships(self):
content_type = ContentType.objects.get(app_label='party',
model='tenurerelationship')
self.write_items(worksheet, relationships, content_type,
['party_id', 'spatial_unit_id', 'tenure_type.id',
'tenure_type.label'])
['party_id', 'spatial_unit_id', 'tenure_type',
'tenure_type_label'])

def make_download(self, f_name):
path = os.path.join(settings.MEDIA_ROOT, 'temp/{}.xlsx'.format(f_name))
Expand Down
8 changes: 4 additions & 4 deletions cadasta/organization/importers/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from django.conf import settings
from django.core.exceptions import ValidationError
from django.db import transaction
from party.models import Party, TenureRelationship, TenureRelationshipType
from party.models import Party, TenureRelationship
from spatial.models import SpatialUnit

from . import exceptions, validators
Expand Down Expand Up @@ -157,6 +157,7 @@ def get_attribute_map(self, type, entity_types, flatten=False):
def _import(self, config, csvfile):
attributes = config.get('attributes', None)
entity_types = config.get('entity_types', None)

type = config.get('type', None)
(attr_map,
extra_attrs, extra_headers) = self.get_attribute_map(
Expand Down Expand Up @@ -205,7 +206,7 @@ def _import(self, config, csvfile):
raise exceptions.DataImportError(
e.messages[0], line_num=reader.line_num)

def _create_models(self, type, headers, row, content_types, tenure_type):
def _create_models(self, type, headers, row, content_types, tenure):

party_ct = content_types['party.party']
spatial_ct = content_types['spatial.spatialunit']
Expand Down Expand Up @@ -256,10 +257,9 @@ def _create_models(self, type, headers, row, content_types, tenure_type):
self._parties_created[party_id] = party.pk

if party_ct and spatial_ct:
tt = TenureRelationshipType.objects.get(id=tenure_type)
content_types['party.tenurerelationship']['party'] = party
content_types['party.tenurerelationship']['spatial_unit'] = su
content_types['party.tenurerelationship']['tenure_type'] = tt
content_types['party.tenurerelationship']['tenure_type'] = tenure
TenureRelationship.objects.create(
**content_types['party.tenurerelationship']
)
Expand Down
Loading

0 comments on commit 9a69328

Please sign in to comment.