summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--chimere/admin.py17
-rw-r--r--chimere/forms.py2
-rw-r--r--chimere/migrations/0029_auto__add_field_marker_modified_since_import__add_field_marker_not_for.py249
-rw-r--r--chimere/models.py55
-rw-r--r--chimere/tasks.py3
-rw-r--r--chimere/templates/chimere/export_marker.csv2
-rw-r--r--chimere/utils.py221
-rw-r--r--example_project/settings.py1
8 files changed, 524 insertions, 26 deletions
diff --git a/chimere/admin.py b/chimere/admin.py
index e86bcea..d497da5 100644
--- a/chimere/admin.py
+++ b/chimere/admin.py
@@ -41,7 +41,8 @@ from chimere.models import Category, Icon, SubCategory, Marker, \
PropertyModel, News, Route, Area, ColorTheme, Color, RouteFile,\
MultimediaType, MultimediaFile, PictureFile, Importer, Layer, AreaLayers,\
PropertyModelChoice, MultimediaExtension, Page
-from chimere.utils import unicode_normalize, ShapefileManager, KMLManager
+from chimere.utils import unicode_normalize, ShapefileManager, KMLManager,\
+ CSVManager
def get_areas_for_user(user):
"""
@@ -87,6 +88,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
@@ -102,7 +113,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', 'ref_item']
readonly_fields = ['submiter_email', 'submiter_comment', 'import_source',
@@ -199,7 +210,7 @@ class ImporterAdmin(admin.ModelAdmin):
list_display = ('importer_type', 'source', 'state', 'filtr')
list_filter = ('importer_type',)
readonly_fields = ('state',)
- actions = [importing, cancel_import, cancel_export]
+ actions = [importing, cancel_import, export_to_osm, cancel_export]
admin.site.register(Importer, ImporterAdmin)
class PageAdmin(admin.ModelAdmin):
diff --git a/chimere/forms.py b/chimere/forms.py
index 7315775..5cf2c63 100644
--- a/chimere/forms.py
+++ b/chimere/forms.py
@@ -137,7 +137,7 @@ class MarkerAdminFormBase(forms.ModelForm):
"""
Main form for marker
"""
- description = forms.CharField(widget=TextareaWidget, required=None)
+ description = forms.CharField(widget=TextareaWidget, required=False)
class Meta:
model = Marker
diff --git a/chimere/migrations/0029_auto__add_field_marker_modified_since_import__add_field_marker_not_for.py b/chimere/migrations/0029_auto__add_field_marker_modified_since_import__add_field_marker_not_for.py
new file mode 100644
index 0000000..72d718c
--- /dev/null
+++ b/chimere/migrations/0029_auto__add_field_marker_modified_since_import__add_field_marker_not_for.py
@@ -0,0 +1,249 @@
+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+ # Adding field 'Marker.modified_since_import'
+ db.add_column('chimere_marker', 'modified_since_import',
+ self.gf('django.db.models.fields.BooleanField')(default=False),
+ keep_default=False)
+
+ # Adding field 'Marker.not_for_osm'
+ db.add_column('chimere_marker', 'not_for_osm',
+ self.gf('django.db.models.fields.BooleanField')(default=False),
+ keep_default=False)
+
+ # Adding field 'Route.modified_since_import'
+ db.add_column('chimere_route', 'modified_since_import',
+ self.gf('django.db.models.fields.BooleanField')(default=False),
+ keep_default=False)
+
+ # Adding field 'Route.not_for_osm'
+ db.add_column('chimere_route', 'not_for_osm',
+ self.gf('django.db.models.fields.BooleanField')(default=False),
+ keep_default=False)
+
+
+ def backwards(self, orm):
+ # Deleting field 'Marker.modified_since_import'
+ db.delete_column('chimere_marker', 'modified_since_import')
+
+ # Deleting field 'Marker.not_for_osm'
+ db.delete_column('chimere_marker', 'not_for_osm')
+
+ # Deleting field 'Route.modified_since_import'
+ db.delete_column('chimere_route', 'modified_since_import')
+
+ # Deleting field 'Route.not_for_osm'
+ db.delete_column('chimere_route', 'not_for_osm')
+
+
+ models = {
+ 'chimere.area': {
+ 'Meta': {'ordering': "('order', 'name')", 'object_name': 'Area'},
+ 'available': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'default': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+ 'default_subcategories': ('chimere.widgets.SelectMultipleField', [], {'to': "orm['chimere.SubCategory']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'dynamic_categories': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+ 'external_css': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'layers': ('chimere.widgets.SelectMultipleField', [], {'symmetrical': 'False', 'related_name': "'areas'", 'blank': 'True', 'through': "orm['chimere.AreaLayers']", 'to': "orm['chimere.Layer']"}),
+ 'lower_right_corner': ('django.contrib.gis.db.models.fields.PointField', [], {'default': "'POINT(0 0)'"}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {}),
+ 'restrict_to_extent': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'subcategories': ('chimere.widgets.SelectMultipleField', [], {'symmetrical': 'False', 'related_name': "'areas'", 'blank': 'True', 'db_table': "'chimere_subcategory_areas'", 'to': "orm['chimere.SubCategory']"}),
+ 'upper_left_corner': ('django.contrib.gis.db.models.fields.PointField', [], {'default': "'POINT(0 0)'"}),
+ 'urn': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50', 'blank': 'True'}),
+ 'welcome_message': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
+ },
+ 'chimere.arealayers': {
+ 'Meta': {'ordering': "('order',)", 'object_name': 'AreaLayers'},
+ 'area': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.Area']"}),
+ 'default': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'layer': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.Layer']"}),
+ 'order': ('django.db.models.fields.IntegerField', [], {})
+ },
+ 'chimere.category': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'Category'},
+ 'available': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {})
+ },
+ 'chimere.color': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'Color'},
+ 'code': ('django.db.models.fields.CharField', [], {'max_length': '6'}),
+ 'color_theme': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.ColorTheme']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {})
+ },
+ 'chimere.colortheme': {
+ 'Meta': {'object_name': 'ColorTheme'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'})
+ },
+ 'chimere.icon': {
+ 'Meta': {'object_name': 'Icon'},
+ 'height': ('django.db.models.fields.IntegerField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'width': ('django.db.models.fields.IntegerField', [], {})
+ },
+ 'chimere.importer': {
+ 'Meta': {'object_name': 'Importer'},
+ 'categories': ('chimere.widgets.SelectMultipleField', [], {'to': "orm['chimere.SubCategory']", 'symmetrical': 'False'}),
+ 'filtr': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'importer_type': ('django.db.models.fields.CharField', [], {'max_length': '4'}),
+ 'source': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'srid': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'state': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'zipped': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
+ },
+ 'chimere.layer': {
+ 'Meta': {'object_name': 'Layer'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'layer_code': ('django.db.models.fields.TextField', [], {'max_length': '300'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'})
+ },
+ 'chimere.marker': {
+ 'Meta': {'ordering': "('status', 'name')", 'object_name': 'Marker'},
+ 'available_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'categories': ('chimere.widgets.SelectMultipleField', [], {'to': "orm['chimere.SubCategory']", 'symmetrical': 'False'}),
+ 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'end_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'import_key': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'import_source': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'import_version': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'modified_since_import': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'not_for_osm': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'point': ('chimere.widgets.PointField', [], {}),
+ 'ref_item': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'submited_marker'", 'null': 'True', 'to': "orm['chimere.Marker']"}),
+ 'route': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'associated_marker'", 'null': 'True', 'to': "orm['chimere.Route']"}),
+ 'start_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+ 'status': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
+ 'submiter_comment': ('django.db.models.fields.TextField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'submiter_email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}),
+ 'submiter_name': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'}),
+ 'submiter_session_key': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'})
+ },
+ 'chimere.multimediafile': {
+ 'Meta': {'object_name': 'MultimediaFile'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'marker': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'multimedia_files'", 'to': "orm['chimere.Marker']"}),
+ 'miniature': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'multimedia_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.MultimediaType']"}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '1'}),
+ 'url': ('django.db.models.fields.URLField', [], {'max_length': '200'})
+ },
+ 'chimere.multimediatype': {
+ 'Meta': {'object_name': 'MultimediaType'},
+ 'available': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'iframe': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'media_type': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
+ 'mime_type': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True', 'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'})
+ },
+ 'chimere.news': {
+ 'Meta': {'object_name': 'News'},
+ 'available': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'content': ('django.db.models.fields.TextField', [], {}),
+ 'date': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '150'})
+ },
+ 'chimere.picturefile': {
+ 'Meta': {'object_name': 'PictureFile'},
+ 'height': ('django.db.models.fields.IntegerField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'marker': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'pictures'", 'to': "orm['chimere.Marker']"}),
+ 'miniature': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '1'}),
+ 'picture': ('django.db.models.fields.files.ImageField', [], {'max_length': '100'}),
+ 'thumbnailfile': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+ 'thumbnailfile_height': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'thumbnailfile_width': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'width': ('django.db.models.fields.IntegerField', [], {})
+ },
+ 'chimere.property': {
+ 'Meta': {'object_name': 'Property'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'marker': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.Marker']"}),
+ 'propertymodel': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.PropertyModel']"}),
+ 'value': ('django.db.models.fields.TextField', [], {})
+ },
+ 'chimere.propertymodel': {
+ 'Meta': {'ordering': "('order',)", 'object_name': 'PropertyModel'},
+ 'available': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'mandatory': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {}),
+ 'subcategories': ('chimere.widgets.SelectMultipleField', [], {'symmetrical': 'False', 'related_name': "'properties'", 'blank': 'True', 'to': "orm['chimere.SubCategory']"}),
+ 'type': ('django.db.models.fields.CharField', [], {'max_length': '1'})
+ },
+ 'chimere.route': {
+ 'Meta': {'ordering': "('status', 'name')", 'object_name': 'Route'},
+ 'associated_file': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.RouteFile']", 'null': 'True', 'blank': 'True'}),
+ 'categories': ('chimere.widgets.SelectMultipleField', [], {'to': "orm['chimere.SubCategory']", 'symmetrical': 'False'}),
+ 'end_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+ 'height': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'import_key': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'import_source': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'import_version': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'modified_since_import': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'not_for_osm': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'picture': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+ 'ref_item': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'submited_route'", 'null': 'True', 'to': "orm['chimere.Route']"}),
+ 'route': ('chimere.widgets.RouteField', [], {}),
+ 'start_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+ 'status': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
+ 'submiter_comment': ('django.db.models.fields.TextField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'submiter_email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}),
+ 'submiter_name': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'}),
+ 'submiter_session_key': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'}),
+ 'width': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'})
+ },
+ 'chimere.routefile': {
+ 'Meta': {'ordering': "('name',)", 'object_name': 'RouteFile'},
+ 'file_type': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'raw_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
+ 'simplified_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'})
+ },
+ 'chimere.subcategory': {
+ 'Meta': {'ordering': "['category', 'order']", 'object_name': 'SubCategory'},
+ 'available': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'category': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.Category']"}),
+ 'color_theme': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.ColorTheme']", 'null': 'True', 'blank': 'True'}),
+ 'icon': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.Icon']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'item_type': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {})
+ },
+ 'chimere.tinyurl': {
+ 'Meta': {'object_name': 'TinyUrl'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'parameters': ('django.db.models.fields.CharField', [], {'max_length': '500'})
+ }
+ }
+
+ complete_apps = ['chimere'] \ No newline at end of file
diff --git a/chimere/models.py b/chimere/models.py
index d51cd5b..123b2fa 100644
--- a/chimere/models.py
+++ b/chimere/models.py
@@ -34,7 +34,7 @@ from django.contrib import admin
from django.core.files import File
from django.core.exceptions import ValidationError
from django.core.urlresolvers import reverse
-from django.db.models.signals import post_save
+from django.db.models.signals import post_save, pre_save
from django import forms
from django.template import defaultfilters
from django.utils.translation import ugettext_lazy as _
@@ -330,6 +330,10 @@ class GeographicItem(models.Model):
blank=True, null=True)
import_source = models.CharField(_(u"Source"), max_length=200,
blank=True, null=True)
+ modified_since_import = models.BooleanField(_(u"Modified since last import"),
+ default=False)
+ not_for_osm = models.BooleanField(_(u"Not to be imported inside OSM"),
+ default=False)
if settings.CHIMERE_DAYS_BEFORE_EVENT:
start_date = models.DateField(_(u"Start date"), blank=True, null=True,
help_text=_(u"Not mandatory. Set it for dated item such as event. "\
@@ -363,6 +367,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
'''
@@ -386,8 +394,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)
@@ -521,6 +528,33 @@ class Marker(GeographicItem):
url = reverse('chimere:tiny', args=[area_name, urn])
return url
+pre_save_marker_values = {}
+def marker_pre_save(sender, **kwargs):
+ if not kwargs['instance']:
+ return
+ instance = kwargs['instance']
+ pre_save_marker_values[instance.pk] = (instance.name, instance.point,
+ instance.import_version)
+pre_save.connect(marker_pre_save, sender=Marker)
+
+def geometry_post_save(pre_save_geom_values):
+ def geom_post_save(sender, **kwargs):
+ if not kwargs['instance']:
+ return
+ instance = kwargs['instance']
+ name, geometry, import_version = pre_save_geom_values[instance]
+ if (instance.import_version != import_version
+ and instance.modified_since_import):
+ instance.modified_since_import = False
+ instance.save()
+ return
+ if instance.modified_since_import:
+ return
+ if instance.name != name or instance.geometry != geometry:
+ instance.modified_since_import = True
+ return geom_post_save
+post_save.connect(geometry_post_save(pre_save_marker_values), sender=Marker)
+
class MultimediaType(models.Model):
MEDIA_TYPES = (('A', _(u"Audio")),
('V', _(u"Video")),
@@ -874,6 +908,16 @@ class Route(GeographicItem):
self.categories[0].id)
return TinyUrl.getUrnByParameters(parameters)
+pre_save_route_values = {}
+def route_pre_save(sender, **kwargs):
+ if not kwargs['instance']:
+ return
+ instance = kwargs['instance']
+ pre_save_route_values[instance.pk] = (instance.name, instance.route,
+ instance.import_version)
+pre_save.connect(route_pre_save, sender=Route)
+post_save.connect(geometry_post_save(pre_save_route_values), sender=Route)
+
def getDateCondition():
'''
Return an SQL condition for apparition of dates
@@ -1105,6 +1149,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/tasks.py b/chimere/tasks.py
index 1d55d2d..2d55bfd 100644
--- a/chimere/tasks.py
+++ b/chimere/tasks.py
@@ -22,6 +22,7 @@ if 'kombu.transport.django' in settings.INSTALLED_APPS \
and 'djcelery' in settings.INSTALLED_APPS:
from celery.decorators import task
+from django.core.cache import cache
from django.core.exceptions import ObjectDoesNotExist
from django.utils.translation import ugettext_lazy as _
@@ -44,7 +45,7 @@ if 'task' in globals():
release_lock = lambda: cache.delete(lock_id)
if acquire_lock():
try:
- func()
+ func(*args, **kwargs)
finally:
release_lock()
return wrapper
diff --git a/chimere/templates/chimere/export_marker.csv b/chimere/templates/chimere/export_marker.csv
new file mode 100644
index 0000000..619b872
--- /dev/null
+++ b/chimere/templates/chimere/export_marker.csv
@@ -0,0 +1,2 @@
+{% for row in data %}{% for dat in row %}{% if forloop.counter0 %};{% endif %}"{{ dat|safe|addslashes }}"{% endfor %}
+{% endfor %}
diff --git a/chimere/utils.py b/chimere/utils.py
index d61ad19..a24e22f 100644
--- a/chimere/utils.py
+++ b/chimere/utils.py
@@ -21,6 +21,7 @@
Utilitaries
"""
+import csv
import datetime
import os
import re
@@ -429,9 +430,161 @@ class ShapefileManager(ImportManager):
buff.close()
return filename, zip_stream
-RE_NODE = re.compile('node\[([^\]]*)\]')
+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
-# manage deleted item from OSM
+ 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('\[([^\]]*)\]')
+
+# TODO: manage deleted item from OSM
class OSMManager(ImportManager):
u"""
@@ -554,26 +707,58 @@ class OSMManager(ImportManager):
password=settings.CHIMERE_OSM_PASSWORD)
api.ChangesetCreate({u"comment": u"Import from Chimère %s" % \
get_version()})
- tag = RE_NODE.finddall(self.importer_instance.filtr)
- if not tag:
+ hooks = RE_HOOK.findall(self.importer_instance.filtr)
+ if not hooks:
return 0, _(u"Bad param")
- tag = tag[0].split('=')
- default_dct = {'tag':{tag[0]:tag[1]},
+ tags = {}
+ bbox = []
+ for hook in hooks:
+ key, value = hook.split('=')
+ if '*' in value or '|' in key or '|' in value:
+ continue
+ if key == 'bbox':
+ x1, y1, x2, y2 = [float(val) for val in value.split(',')]
+ bbox = GEOSGeometry(
+ 'POLYGON((%f %f,%f %f,%f %f,%f %f,%f %f))' % (
+ x1, y1, x2, y1, x2, y2, x1, y2, x1, y1), srid=4326)
+ continue
+ tags[key] = value
+ if not tags:
+ return 0, _(u"No non ambigious tag is defined in the XAPI request")
+ if not bbox:
+ return 0, _(u"No bounding box is defined in the XAPI request."\
+ u"If you are sure to manage the entire planet set the bounding box"\
+ u" to -180,-90,180,90")
+ default_dct = {'tag':tags,
'import_source':self.importer_instance.source}
- for idx, item in Marker.objects.filter(status='A',
- categories=self.importer_instance.categories.all()):
- dct = default_dct.update({
- 'name':item.name,
- 'lon':item.point.lon,
- 'lat':item.point.lat})
+ idx = -1
+ for idx, item in enumerate(Marker.objects.filter(status='A',
+ point__contained=bbox,
+ categories=self.importer_instance.categories.all(),
+ not_for_osm=False, modified_since_import=True).all()):
+ dct = default_dct.copy()
+ dct.update({'lon':item.point.x,
+ 'lat':item.point.y})
+ dct['tag']['name'] = item.name
node = None
- import_key = marker.get_key('OSM')
- if not import_key:
- node = OsmApi.NodeCreate(dct)
+ import_key = item.get_key('OSM')
+ updated = False
+ if import_key:
+ try:
+ dct['id'] = import_key
+ dct['version'] = item.import_version
+ node = api.NodeUpdate(dct)
+ updated = True
+ except OsmApi.ApiError, error:
+ if error.status == 404:
+ dct.pop('id')
+ dct.pop('version')
+ pass # if the node doesn't exist it is created
+ else:
+ raise
+ if not updated:
+ node = api.NodeCreate(dct)
item.set_key('OSM', node['id'])
- else:
- dct['id'] = import_key
- node = OsmApi.NodeUpdate(dct)
item.import_version = node['version']
item.save()
api.ChangesetClose()
diff --git a/example_project/settings.py b/example_project/settings.py
index f67ceeb..372f1e6 100644
--- a/example_project/settings.py
+++ b/example_project/settings.py
@@ -105,6 +105,7 @@ CHIMERE_THUMBS_SCALE_WIDTH=None
CHIMERE_SHORT_DESC_LENGTH = 400
CHIMERE_MODIF_EMAIL = _(u"""Hello, I would like to propose you a modification about this item: """)
+CHIMERE_CSV_ENCODING = 'ISO-8859-1'
ADMINS = (
# ('Your Name', 'your_email@domain.com'),