Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #935: Conditional attributes not exported during download process #1083

Merged
merged 1 commit into from
Feb 9, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions cadasta/core/mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,14 @@ def get_model_attributes(self, project, content_type):
attributes_for_models = self.get_attributes(project)
return attributes_for_models[content_type]

def get_conditional_selector(self, content_type):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method is an example of how I feel the code seems to currently cater to the party entity being the only one right now to have a conditional selector. Maybe it is a good database design to have the conditional selector be a field of the model instead of a foreign field, but I kinda feel iffy with basing the decision on whether we have a conditional selector hinge on the presence of a period on the last schema selector.

Note that this is just an observation and should not be a blocker for the PR's approval.

content_type_to_selectors = self._get_content_types_to_selectors()
selectors = list(content_type_to_selectors[content_type])
if '.' in selectors[-1]:
return None
else:
return selectors[-1]

def _get_content_types_to_selectors(self):
content_type_to_selectors = dict()
for k, v in settings.JSONATTRS_SCHEMA_SELECTORS.items():
Expand Down
53 changes: 22 additions & 31 deletions cadasta/organization/download/base.py
Original file line number Diff line number Diff line change
@@ -1,52 +1,43 @@
from jsonattrs.models import Schema
from collections import OrderedDict
from core.mixins import SchemaSelectorMixin
from django.contrib.contenttypes.models import ContentType


class Exporter():
class Exporter(SchemaSelectorMixin):
def __init__(self, project):
self.project = project
self._schema_attrs = {}

def get_schema_attrs(self, content_type):
content_type_key = '{}.{}'.format(content_type.app_label,
content_type.model)

if content_type_key not in self._schema_attrs.keys():
selectors = [
self.project.organization.id,
self.project.id,
self.project.current_questionnaire
]
schemas = Schema.objects.lookup(
content_type=content_type, selectors=selectors
)

attrs = []
if schemas:
attrs = [
a for s in schemas
for a in s.attributes.all() if not a.omit
]
self._schema_attrs[content_type_key] = attrs

return self._schema_attrs[content_type_key]
label = '{0}.{1}'.format(content_type.app_label, content_type.model)
if self._schema_attrs == {}:
self._schema_attrs = self.get_attributes(self.project)
return self._schema_attrs[label]

def get_values(self, item, model_attrs, schema_attrs):
values = []
values = OrderedDict()
for attr in model_attrs:
if '.' in attr:
attr_items = attr.split('.')
value = None
for a in attr_items:
value = (getattr(item, a)
if not value else getattr(value, a))
values.append(value)
values[attr] = value
else:
values.append(getattr(item, attr))

for attr in schema_attrs:
attr_value = item.attributes.get(attr.name, '')
values[attr] = getattr(item, attr)

content_type = ContentType.objects.get_for_model(item)
conditional_selector = self.get_conditional_selector(content_type)
if conditional_selector:
entity_type = getattr(item, conditional_selector)
attributes = schema_attrs.get(entity_type, {})
else:
attributes = schema_attrs.get('DEFAULT', {})
for key, attr in attributes.items():
attr_value = item.attributes.get(key, '')
if type(attr_value) == list:
attr_value = (', ').join(attr_value)
values.append(attr_value)
values[key] = attr_value

return values
78 changes: 47 additions & 31 deletions cadasta/organization/download/shape.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import os
import csv
from osgeo import ogr, osr
import os
from collections import OrderedDict
from zipfile import ZipFile

from osgeo import ogr, osr

from django.conf import settings
from django.contrib.contenttypes.models import ContentType
from django.template.loader import render_to_string
Expand All @@ -12,59 +15,72 @@


class ShapeExporter(Exporter):

def write_items(self, filename, queryset, content_type, model_attrs):
schema_attrs = self.get_schema_attrs(content_type)
fields = list(model_attrs) + [a.name for a in schema_attrs]

# build column labels
attr_columns = OrderedDict()
for a in model_attrs:
attr_columns[a] = ''
for _, attrs in schema_attrs.items():
for a in attrs.values():
if a.name not in attr_columns.keys():
attr_columns[a.name] = None

with open(filename, 'w+', newline='') as csvfile:
csvwriter = csv.writer(csvfile)
csvwriter.writerow(fields)
csvwriter.writerow(attr_columns.keys())

for item in queryset:
values = self.get_values(item, model_attrs, schema_attrs)
csvwriter.writerow(values)
data = attr_columns.copy()
data.update(values)
csvwriter.writerow(data.values())

def write_relationships(self, filename):
relationships = self.project.tenure_relationships.all()
if relationships.count() == 0:
return

content_type = ContentType.objects.get(app_label='party',
model='tenurerelationship')
self.write_items(filename,
self.project.tenure_relationships.all(),
content_type,
self.write_items(filename, relationships, content_type,
('id', 'party_id', 'spatial_unit_id',
'tenure_type.label'))
'tenure_type.id', 'tenure_type.label'))

def write_parties(self, filename):
parties = self.project.parties.all()
if parties.count() == 0:
return

content_type = ContentType.objects.get(app_label='party',
model='party')
self.write_items(filename,
self.project.parties.all(),
content_type,
self.write_items(filename, parties, content_type,
('id', 'name', 'type'))

def write_features(self, layers, filename):
spatial_units = self.project.spatial_units.all()
if spatial_units.count() == 0:
return

content_type = ContentType.objects.get(app_label='spatial',
model='spatialunit')
model_attrs = ('id', 'type')
schema_attrs = self.get_schema_attrs(content_type)

with open(filename, 'w+', newline='') as csvfile:
csvwriter = csv.writer(csvfile)
csvwriter.writerow(list(model_attrs) +
[a.name for a in schema_attrs])

for su in self.project.spatial_units.all():
geom = ogr.CreateGeometryFromWkt(su.geometry.wkt)
layer_type = geom.GetGeometryType() - 1
layer = layers[layer_type]

feature = ogr.Feature(layer.GetLayerDefn())
feature.SetGeometry(ogr.CreateGeometryFromWkt(su.geometry.wkt))
feature.SetField('id', su.id)
layer.CreateFeature(feature)
feature.Destroy()

values = self.get_values(su, model_attrs, schema_attrs)
csvwriter.writerow(values)
self.write_items(
filename, spatial_units, content_type, model_attrs)

for su in spatial_units:
geom = ogr.CreateGeometryFromWkt(su.geometry.wkt)
layer_type = geom.GetGeometryType() - 1
layer = layers[layer_type]

feature = ogr.Feature(layer.GetLayerDefn())
feature.SetGeometry(ogr.CreateGeometryFromWkt(su.geometry.wkt))
feature.SetField('id', su.id)
layer.CreateFeature(feature)
feature.Destroy()

def create_datasource(self, dst_dir):
if not os.path.exists(dst_dir):
Expand Down
27 changes: 17 additions & 10 deletions cadasta/organization/download/xls.py
Original file line number Diff line number Diff line change
@@ -1,32 +1,42 @@
import os
from openpyxl import Workbook
from collections import OrderedDict

from django.conf import settings
from django.contrib.contenttypes.models import ContentType
from openpyxl import Workbook

from .base import Exporter


MIME_TYPE = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'


class XLSExporter(Exporter):

def write_items(self, worksheet, queryset, content_type, model_attrs):
schema_attrs = self.get_schema_attrs(content_type)

# write column labels
worksheet.append(model_attrs + [a.name for a in schema_attrs])
# build column labels
attr_columns = OrderedDict()
for a in model_attrs:
attr_columns[a] = ''
for _, attrs in schema_attrs.items():
for a in attrs.values():
if a.name not in attr_columns.keys():
attr_columns[a.name] = ''
worksheet.append(list(attr_columns.keys()))

# write data
for i, item in enumerate(queryset):
values = self.get_values(item, model_attrs, schema_attrs)
worksheet.append(values)
data = attr_columns.copy()
data.update(values)
worksheet.append(list(data.values()))

def write_locations(self):
locations = self.project.spatial_units.all()
if locations.count() == 0:
return
worksheet = self.workbook.create_sheet(title='locations')

content_type = ContentType.objects.get(app_label='spatial',
model='spatialunit')
self.write_items(worksheet, locations, content_type,
Expand All @@ -37,7 +47,6 @@ def write_parties(self):
if parties.count() == 0:
return
worksheet = self.workbook.create_sheet(title='parties')

content_type = ContentType.objects.get(app_label='party',
model='party')
self.write_items(worksheet, parties, content_type,
Expand All @@ -48,7 +57,6 @@ def write_relationships(self):
if relationships.count() == 0:
return
worksheet = self.workbook.create_sheet(title='relationships')

content_type = ContentType.objects.get(app_label='party',
model='tenurerelationship')
self.write_items(worksheet, relationships, content_type,
Expand All @@ -57,8 +65,7 @@ def write_relationships(self):

def make_download(self, f_name):
path = os.path.join(settings.MEDIA_ROOT, 'temp/{}.xlsx'.format(f_name))
self.workbook = Workbook()
self.workbook.remove_sheet(self.workbook['Sheet'])
self.workbook = Workbook(write_only=True)

self.write_locations()
self.write_parties()
Expand Down
Loading