summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorÉtienne Loks <etienne.loks@peacefrogs.net>2012-09-18 01:21:02 +0200
committerÉtienne Loks <etienne.loks@peacefrogs.net>2012-09-18 01:21:02 +0200
commit919f1da7d1afac6c39bc12f86b979272b0dd5483 (patch)
tree9239f3ee7af9289fa3b417d6494948c7bd210c3b
parent432e3ee5db3745502e47091199d9677f00d69339 (diff)
downloadChimère-919f1da7d1afac6c39bc12f86b979272b0dd5483.tar.bz2
Chimère-919f1da7d1afac6c39bc12f86b979272b0dd5483.zip
Work on CSV export
-rw-r--r--chimere/admin.py15
-rw-r--r--chimere/models.py12
-rw-r--r--chimere/utils.py156
-rw-r--r--example_project/settings.py1
4 files changed, 178 insertions, 6 deletions
diff --git a/chimere/admin.py b/chimere/admin.py
index 3e2bb16..d215d84 100644
--- a/chimere/admin.py
+++ b/chimere/admin.py
@@ -39,7 +39,8 @@ from chimere.forms import MarkerAdminForm, RouteAdminForm, AreaAdminForm,\
from chimere.models import Category, Icon, SubCategory, Marker, \
PropertyModel, News, Route, Area, ColorTheme, Color, RouteFile,\
MultimediaType, MultimediaFile, PictureFile, Importer, Layer, AreaLayers
-from chimere.utils import unicode_normalize, ShapefileManager, KMLManager
+from chimere.utils import unicode_normalize, ShapefileManager, KMLManager,\
+ CSVManager
from chimere.widgets import TextareaWidget
def get_areas_for_user(user):
@@ -86,6 +87,16 @@ def export_to_shapefile(modeladmin, request, queryset):
return response
export_to_shapefile.short_description = _(u"Export to Shapefile")
+def export_to_csv(modeladmin, request, queryset):
+ u"""
+ Export data to CSV
+ """
+ filename, result = CSVManager.export(queryset)
+ response = HttpResponse(result, mimetype='text/csv')
+ response['Content-Disposition'] = 'attachment; filename=%s' % filename
+ return response
+export_to_csv.short_description = _(u"Export to CSV")
+
class PictureInline(admin.TabularInline):
model = PictureFile
extra = 1
@@ -101,7 +112,7 @@ class MarkerAdmin(admin.ModelAdmin):
search_fields = ("name",)
list_display = ('name', 'status')
list_filter = ('status', 'categories')
- actions = [validate, export_to_kml, export_to_shapefile]
+ actions = [validate, export_to_kml, export_to_shapefile, export_to_csv]
exclude = ['submiter_session_key', 'import_key', 'import_version',
'available_date']
readonly_fields = ['submiter_email', 'submiter_comment', 'import_source',
diff --git a/chimere/models.py b/chimere/models.py
index 19c99eb..0db9899 100644
--- a/chimere/models.py
+++ b/chimere/models.py
@@ -291,6 +291,10 @@ class GeographicItem(models.Model):
self.import_key = new_keys
self.save()
+ @classmethod
+ def properties(cls):
+ return [pm for pm in PropertyModel.objects.filter(available=True)]
+
class Marker(GeographicItem):
'''Marker for a POI
'''
@@ -312,8 +316,7 @@ class Marker(GeographicItem):
super(Marker, self).__init__(*args, **kwargs)
# add read attributes for properties
for property in self.getProperties():
- attr_name = defaultfilters.slugify(property.propertymodel.name)
- attr_name = re.sub(r'-','_', attr_name)
+ attr_name = property.propertymodel.getAttrName()
if not hasattr(self, attr_name):
setattr(self, attr_name, property.python_value)
@@ -1049,6 +1052,11 @@ class PropertyModel(models.Model):
ordering = ('order',)
verbose_name = _("Property model")
+ def getAttrName(self):
+ attr_name = defaultfilters.slugify(self.name)
+ attr_name = re.sub(r'-','_', attr_name)
+ return attr_name
+
def getNamedId(self):
'''Get the name used as named id (easily sortable)
'''
diff --git a/chimere/utils.py b/chimere/utils.py
index 60b2dac..a24e22f 100644
--- a/chimere/utils.py
+++ b/chimere/utils.py
@@ -21,6 +21,7 @@
Utilitaries
"""
+import csv
import datetime
import os
import re
@@ -429,10 +430,161 @@ class ShapefileManager(ImportManager):
buff.close()
return filename, zip_stream
-RE_HOOK = re.compile('\[([^\]]*)\]')
+class ImportManager:
+ u"""
+ Generic class for specific importers
+ """
+ default_source = None
+ def __init__(self, importer_instance):
+ self.importer_instance = importer_instance
+ self.default_name = " - ".join([cat.name
+ for cat in self.importer_instance.categories.order_by('name').all()])
+
+ def get(self):
+ pass
+
+ def put(self):
+ pass
+
+ def create_or_update_item(self, cls, values, import_key, version=None):
+ updated, created, item = False, False, None
+ if import_key:
+ dct_import = {
+ 'import_key__icontains':'%s:%s;' % (
+ self.importer_instance.importer_type,
+ import_key),
+ 'import_source':self.importer_instance.source}
+ try:
+ item = cls.objects.get(**dct_import)
+ if version and item.import_version == int(version):
+ # no update since the last import
+ return item, None, None
+ for k in values:
+ setattr(item, k, values[k])
+ try:
+ item.save()
+ except TypeError:
+ # error on data source
+ return None, False, False
+ updated = True
+ except ObjectDoesNotExist:
+ pass
+ if not item:
+ values.update({
+ 'import_source':self.importer_instance.source})
+ values['status'] = 'I'
+ try:
+ item = cls.objects.create(**values)
+ except TypeError:
+ # error on data source
+ return None, False, False
+ created = True
+ if import_key:
+ item.set_key(self.importer_instance.importer_type,
+ import_key)
+ item.categories.clear()
+ for cat in self.importer_instance.categories.all():
+ item.categories.add(cat)
+ return item, updated, created
+ @classmethod
+ def get_files_inside_zip(cls, zippedfile, suffixes, dest_dir=None):
+ try:
+ flz = zipfile.ZipFile(zippedfile)
+ except zipfile.BadZipfile:
+ return [], _(u"Bad zip file")
+ namelist = flz.namelist()
+ filenames = []
+ for suffix in suffixes:
+ current_file_name = None
+ for name in namelist:
+ if name.endswith(suffix) \
+ or name.endswith(suffix.lower()) \
+ or name.endswith(suffix.upper()):
+ current_file_name = name
+ filenames.append(current_file_name)
+ files = []
+ for filename in filenames:
+ if filename:
+ if dest_dir:
+ files.append(filename)
+ flz.extract(filename, dest_dir)
+ else:
+ files.append(flz.open(filename))
+ else:
+ files.append(None)
+ return files
+
+ def get_source_file(self, source, suffixes, dest_dir=None,
+ extra_url=None):
+ if not hasattr(source, 'read'):
+ if not source:
+ source = self.importer_instance.source \
+ if self.importer_instance.source else self.default_source
+ try:
+ url = source
+ if extra_url:
+ url += extra_url
+ remotehandle = urllib2.urlopen(url)
+ source = StringIO.StringIO(remotehandle.read())
+ remotehandle.close()
+ except ValueError:
+ # assume it is a local file
+ try:
+ source = open(source)
+ except IOError, msg:
+ return (None, msg)
+ except urllib2.URLError as error:
+ return (None, error.message)
+ if self.importer_instance.zipped:
+ try:
+ files = self.get_files_inside_zip(source, suffixes, dest_dir)
+ except zipfile.BadZipfile:
+ return (None, _(u"Bad zip file"))
+ if not files or None in files:
+ return (None,
+ _(u"Missing file(s) inside the zip file"))
+ source = files[0] if len(suffixes) == 1 else files
+ return (source, None)
+
+class CSVManager(ImportManager):
+ u"""
+ CSV importer
+ """
+ _COLS = [("Id", 'pk'), (_(u"Name"), 'name'),
+ (_(u"Categories"), lambda obj:", ".join(
+ [c.name for c in obj.categories.all()])),
+ (_(u"State"), 'status')]
+ COLS = {'marker':_COLS+[(_(u"Description"), 'description'),
+ (_(u"Localisation"), lambda obj: obj.point.wkt)],
+ 'route':_COLS+[(_(u"Localisation"), lambda obj: obj.route.wkt)]}
+
+ @classmethod
+ def export(cls, queryset):
+ dct = {'description':unicode(datetime.date.today()), 'data':[]}
+ cls_name = queryset.model.__name__.lower()
+ cols = cls.COLS[cls_name][:]
+ for pm in queryset.model.properties():
+ cols.append((pm.name, pm.getAttrName()))
+ header = [lbl for lbl, attr in cols]
+ dct['data'].append(header)
+ for item in queryset.all():
+ data = []
+ for lbl, attr in cols:
+ if callable(attr):
+ data.append(attr(item))
+ print lbl, attr(item)
+ else:
+ data.append(getattr(item, attr))
+ dct['data'].append(data)
+ filename = unicode_normalize(settings.PROJECT_NAME + dct['description']\
+ + '.csv')
+ result = render_to_response('chimere/export_%s.csv' % cls_name, dct)
+ return filename, result
+
+RE_HOOK = re.compile('\[([^\]]*)\]')
-# manage deleted item from OSM
+# TODO: manage deleted item from OSM
class OSMManager(ImportManager):
u"""
diff --git a/example_project/settings.py b/example_project/settings.py
index dec2827..6aaa577 100644
--- a/example_project/settings.py
+++ b/example_project/settings.py
@@ -76,6 +76,7 @@ CHIMERE_SHAPEFILE_ENCODING = 'ISO-8859-1'
CHIMERE_THUMBS_SCALE_HEIGHT=250
CHIMERE_THUMBS_SCALE_WIDTH=None
+CHIMERE_CSV_ENCODING = 'ISO-8859-1'
ADMINS = (
# ('Your Name', 'your_email@domain.com'),