diff options
author | Étienne Loks <etienne.loks@peacefrogs.net> | 2012-03-29 16:55:17 +0200 |
---|---|---|
committer | Étienne Loks <etienne.loks@peacefrogs.net> | 2012-03-29 16:55:17 +0200 |
commit | 953310b7e18bdb012dae2fcc0184bc63f1c30c0c (patch) | |
tree | 98c7b6d2460060dd1a2a71c3b303e221d4fa258c | |
parent | a46efae7bf7a9a0f8a2ccaffdfe0d18ffcb2a0d2 (diff) | |
download | Chimère-953310b7e18bdb012dae2fcc0184bc63f1c30c0c.tar.bz2 Chimère-953310b7e18bdb012dae2fcc0184bc63f1c30c0c.zip |
Work on OSM export - simplify KML import
-rw-r--r-- | chimere/__init__.py | 7 | ||||
-rw-r--r-- | chimere/admin.py | 20 | ||||
l--------- | chimere/external_utils | 1 | ||||
-rw-r--r-- | chimere/migrations/0011_auto__del_field_importer_source_url__add_field_importer_source.py | 195 | ||||
-rw-r--r-- | chimere/models.py | 27 | ||||
-rw-r--r-- | chimere/tasks.py | 49 | ||||
-rw-r--r-- | chimere/tests.py | 14 | ||||
-rw-r--r-- | chimere/utils.py | 106 | ||||
-rw-r--r-- | debian/control | 2 | ||||
-rw-r--r-- | example_project/settings.py.example | 12 | ||||
-rw-r--r-- | requirements.txt | 1 |
11 files changed, 393 insertions, 41 deletions
diff --git a/chimere/__init__.py b/chimere/__init__.py index 1b2fc61..a7083c7 100644 --- a/chimere/__init__.py +++ b/chimere/__init__.py @@ -7,3 +7,10 @@ from django.utils.translation import ugettext as _ # templates/chimere/edit.html, templates/chimere/edit_route.html _(u"Multimedia files") _(u"Picture files") + +VERSION = (2, 0) + +def get_version(): + return u'.'.join((unicode(num) for num in VERSION)) + +__version__ = get_version() diff --git a/chimere/admin.py b/chimere/admin.py index afc818c..42d536e 100644 --- a/chimere/admin.py +++ b/chimere/admin.py @@ -152,10 +152,24 @@ def cancel_import(modeladmin, request, queryset): importer.save() cancel_import.short_description = _(u"Cancel import") +def cancel_export(modeladmin, request, queryset): + for importer in queryset: + importer.state = tasks.IMPORT_MESSAGES['export_cancel'][0] + importer.save() +cancel_export.short_description = _(u"Cancel export") + +def export_to_osm(modeladmin, request, queryset): + importers = modeladmin.model.objects.filter(importer_type='OSM') + for importer in queryset: + importer.state = unicode(tasks.IMPORT_MESSAGES['export_pending'][0]) + importer.save() + tasks.exporting(importer.pk) +export_to_osm.short_description = _(u"Export to osm") + class ImporterAdmin(admin.ModelAdmin): - list_display = ('importer_type', 'source_url', 'state', 'filtr') - list_filter = ('importer_type', 'source_url') - actions = [importing, cancel_import] + list_display = ('importer_type', 'source', 'state', 'filtr') + list_filter = ('importer_type', 'source') + actions = [importing, cancel_import, cancel_export] class NewsAdmin(admin.ModelAdmin): """ diff --git a/chimere/external_utils b/chimere/external_utils new file mode 120000 index 0000000..19985ba --- /dev/null +++ b/chimere/external_utils @@ -0,0 +1 @@ +../utils/
\ No newline at end of file diff --git a/chimere/migrations/0011_auto__del_field_importer_source_url__add_field_importer_source.py b/chimere/migrations/0011_auto__del_field_importer_source_url__add_field_importer_source.py new file mode 100644 index 0000000..31ce4d8 --- /dev/null +++ b/chimere/migrations/0011_auto__del_field_importer_source_url__add_field_importer_source.py @@ -0,0 +1,195 @@ +# encoding: 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): + + # Deleting field 'Importer.source_url' + db.delete_column('chimere_importer', 'source_url') + + # Adding field 'Importer.source' + db.add_column('chimere_importer', 'source', self.gf('django.db.models.fields.CharField')(max_length=200, null=True, blank=True), keep_default=False) + + + def backwards(self, orm): + + # Adding field 'Importer.source_url' + db.add_column('chimere_importer', 'source_url', self.gf('django.db.models.fields.CharField')(max_length=200, null=True, blank=True), keep_default=False) + + # Deleting field 'Importer.source' + db.delete_column('chimere_importer', 'source') + + + models = { + 'chimere.area': { + 'Meta': {'ordering': "('order', 'name')", 'object_name': 'Area'}, + 'available': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + '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', [], {}), + 'upper_left_corner': ('django.contrib.gis.db.models.fields.PointField', [], {'default': "'POINT(0 0)'"}), + 'urn': ('django.db.models.fields.SlugField', [], {'db_index': 'True', 'unique': 'True', 'max_length': '50', 'blank': 'True'}) + }, + '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'}), + 'state': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}) + }, + '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'}), + 'multimedia_files': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'marker'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['chimere.MultimediaFile']"}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}), + 'pictures': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'marker'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['chimere.PictureFile']"}), + '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.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), + 'submiter_email': ('django.db.models.fields.EmailField', [], {'max_length': '75', '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'}), + 'multimedia_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['chimere.MultimediaType']"}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '150', 'null': 'True', 'blank': 'True'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '1'}), + 'url': ('django.db.models.fields.CharField', [], {'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'}), + 'miniature': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '150', 'null': 'True', 'blank': 'True'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '1'}), + 'picture': ('django.db.models.fields.files.ImageField', [], {'max_length': '100'}), + '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'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}), + 'order': ('django.db.models.fields.IntegerField', [], {}), + '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'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}), + '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.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), + 'submiter_email': ('django.db.models.fields.EmailField', [], {'max_length': '75', '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'}, + 'areas': ('chimere.widgets.SelectMultipleField', [], {'symmetrical': 'False', 'related_name': "'areas'", 'blank': 'True', 'to': "orm['chimere.Area']"}), + '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'] diff --git a/chimere/models.py b/chimere/models.py index c93d271..3556e52 100644 --- a/chimere/models.py +++ b/chimere/models.py @@ -204,8 +204,8 @@ class Importer(models.Model): importer_type = models.CharField(_(u"Importer type"), max_length=4, choices=IMPORTER_CHOICES) # URL of a KML file or a XAPI service for OSM - source_url = models.CharField(_(u"Source URL"), max_length=200, - blank=True, null=True) + source = models.CharField(_(u"Source"), max_length=200, + blank=True, null=True) filtr = models.CharField(_(u"Filter"), max_length=200, blank=True, null=True) categories = SelectMultipleField(SubCategory, @@ -253,6 +253,29 @@ class GeographicItem(models.Model): class Meta: abstract = True + def get_key(self, key): + key_vals = self.import_key.split(';') + for k_v in key_vals: + if k_v.startswith(key+':'): + return k_v.split(':')[1] + + def set_key(self, key, value): + new_keys, _set = '', None + key_vals = self.import_key.split(';') if self.import_key else [] + for k_v in key_vals: + if ':' not in k_v: + continue + k, v = k_v.split(':') + if k == key: + _set = True + new_keys += '%s:%s;' % (k, value) + else: + new_keys += '%s:%s;' % (k, v) + if not _set: + new_keys += '%s:%s;' % (key, value) + self.import_key = new_keys + self.save() + class Marker(GeographicItem): '''Marker for a POI ''' diff --git a/chimere/tasks.py b/chimere/tasks.py index ba88304..58af162 100644 --- a/chimere/tasks.py +++ b/chimere/tasks.py @@ -24,12 +24,33 @@ from django.utils.translation import ugettext_lazy as _ from chimere.models import Importer -IMPORT_MESSAGES = {'import_pending':[_(u"Import pending")], +def single_instance_task(timeout): + def task_exc(func): + def wrapper(*args, **kwargs): + lock_id = "celery-single-instance-" + func.__name__ + acquire_lock = lambda: cache.add(lock_id, "true", timeout) + release_lock = lambda: cache.delete(lock_id) + if acquire_lock(): + try: + func() + finally: + release_lock() + return wrapper + return task_exc + +IMPORT_MESSAGES = { + 'import_pending':[_(u"Import pending")], 'import_process':[_(u"Import processing")], 'import_done':[_(u"Import successfuly done"), _(u" %(new)d new item(s), %(updated)d updated item(s)")], 'import_failed':[_(u"Import failed"), "%s"], - 'import_cancel':[_(u"Import canceled")] + 'import_cancel':[_(u"Import canceled")], + 'export_pending':[_(u"Export pending")], + 'export_process':[_(u"Export processing")], + 'export_done':[_(u"Export successfuly done"), + _(u" %(updated)d updated item(s)")], + 'export_failed':[_(u"Export failed"), "%s"], + 'export_cancel':[_(u"Export canceled")] } @task() @@ -55,3 +76,27 @@ def importing(importer_pk): 'updated':updated_item} importer.save() return True + +@task() +@single_instance_task(60*10) +def exporting(importer_pk): + try: + importer = Importer.objects.get(pk=importer_pk) + except ObjectDoesNotExist: + # importer doesn't exists anymore: too late! + return + if importer.state != IMPORT_MESSAGES['export_pending'][0]: + # import canceled or done + return + importer.state = unicode(IMPORT_MESSAGES['export_process'][0]) + importer.save() + updated_item, error = importer.manager.put() + if error: + importer.state = unicode(IMPORT_MESSAGES['export_failed'][0]) \ + + u" - " + unicode(IMPORT_MESSAGES['export_failed'][1]) % error + importer.save() + return + importer.state = unicode(IMPORT_MESSAGES['export_done'][0]) + u" - " \ + + unicode(IMPORT_MESSAGES['export_done'][1]) % {'updated':updated_item} + importer.save() + return True diff --git a/chimere/tests.py b/chimere/tests.py index 7a3960f..7d84394 100644 --- a/chimere/tests.py +++ b/chimere/tests.py @@ -65,19 +65,19 @@ class KMLImporterTest(TestCase, ImporterTest): def setUp(self): subcategory_1, subcategory_2 = self._baseSetUp() importer1 = Importer.objects.create(importer_type='KML', - source_url=test_dir_path+'sample.kml', - filtr="//kml:Folder/kml:name[text()='Category 1']/../kml:Placemark") + source=test_dir_path+'sample.kml', + filtr="Category 1") importer1.categories.add(subcategory_1) importer2 = Importer.objects.create(importer_type='KML', - source_url=test_dir_path+'sample.kml', - filtr="//kml:Folder/kml:name[text()='Subcategory 1']/../kml:Placemark") + source=test_dir_path+'sample.kml', + filtr="Subcategory 1") importer2.categories.add(subcategory_1) importer2.categories.add(subcategory_2) importer3 = Importer.objects.create(importer_type='KML', - source_url=test_path+'sample.kml', - filtr="//kml:Folder/kml:name[text()='Subcategory 3']/../kml:Placemark") + source=test_path+'sample.kml', + filtr="Subcategory 3") importer3.categories.add(subcategory_2) self.marker_importers = [(importer1, 1), (importer2, 2), (importer3, 0)] @@ -86,7 +86,7 @@ class OSMImporterTest(TestCase, ImporterTest): def setUp(self): subcategory_1, subcategory_2 = self._baseSetUp() importer1 = Importer.objects.create(importer_type='OSM', - source_url=settings.CHIMERE_XAPI_URL, + source='OSM', filtr="node[amenity=pub]"\ "[bbox=-77.041579,38.885851,-77.007247,38.900881]") importer1.categories.add(subcategory_1) diff --git a/chimere/utils.py b/chimere/utils.py index 62d3786..b2c8ccc 100644 --- a/chimere/utils.py +++ b/chimere/utils.py @@ -21,10 +21,14 @@ Utilitaries """ -import urllib2 +import urllib2, re +from external_utils import OsmApi from lxml import etree +from chimere import get_version +from django.conf import settings from django.core.exceptions import ObjectDoesNotExist +from django.utils.translation import ugettext_lazy as _ class ImportManager: u""" @@ -42,11 +46,10 @@ class ImportManager: class KMLManager(ImportManager): u""" KML importer - The filtr argument has to be defined as a correct Xpath string pointing - to Placemark nodes. - A typical XPath string looks like: - "//kml:Folder/kml:name[text()='Subcategory 1']/../kml:Placemark" + The filtr argument has to be defined as the exact name of the folder to be + imported """ + XPATH = '//kml:Folder/kml:name[text()="%s"]/../kml:Placemark' def __init__(self, importer_instance, ns=''): self.importer_instance = importer_instance self.ns = ns @@ -62,11 +65,11 @@ class KMLManager(ImportManager): from models import Marker new_item, updated_item, msg = 0, 0, '' try: - source = urllib2.urlopen(self.importer_instance.source_url) + source = urllib2.urlopen(self.importer_instance.source) except ValueError: # assume it is a local file try: - source = open(self.importer_instance.source_url) + source = open(self.importer_instance.source) except IOError, msg: return (new_item, updated_item, msg) except urllib2.URLError as error: @@ -75,10 +78,11 @@ class KMLManager(ImportManager): # try to get default namespace if not self.ns: self.ns = tree.getroot().nsmap[None] - for placemark in tree.xpath(self.importer_instance.filtr, + for placemark in tree.xpath(self.XPATH % self.importer_instance.filtr, namespaces={'kml':self.ns}): name, point, linestring = None, None, None pl_id = placemark.attrib.get('id') + pl_key = 'kml-%d' % self.importer_instance.pk ns = '{%s}' % self.ns for item in placemark: if item.tag == ns + 'name': @@ -97,8 +101,8 @@ class KMLManager(ImportManager): m = None if pl_id: dct_import = { - 'import_key':pl_id, - 'import_source':self.importer_instance.source_url} + 'import_key__icontains':'%s:%s;' % (pl_key, pl_id), + 'import_source':self.importer_instance.source} try: m = Marker.objects.get(**dct_import) for k in dct: @@ -107,16 +111,23 @@ class KMLManager(ImportManager): updated_item += 1 except ObjectDoesNotExist: m = None - dct.update(dct_import) + dct.update({ + 'import_source':self.importer_instance.source}) if not m: dct['status'] = 'I' m = Marker.objects.create(**dct) new_item += 1 + if pl_id: + m.set_key(pl_key, pl_id) m.categories.clear() for cat in self.importer_instance.categories.all(): m.categories.add(cat) return (new_item, updated_item, msg) +RE_NODE = re.compile('node\[([^\]]*)\]') + +# manage deleted item from OSM + class OSMManager(ImportManager): u""" OSM importer @@ -124,23 +135,25 @@ class OSMManager(ImportManager): The filtr argument is XAPI args or empty if it is an OSM file. """ - def get(self): + def get(self, get_items=None): u""" Get data from the source Return a tuple with: - - number of new item ; - - number of item updated ; + - new items or number of new items if nb_only = True; + - updated items or number of updated items if nb_only = True; - error detail on error """ from models import Marker - new_item, updated_item, msg = 0, 0, '' + new_item, updated_item = 0 , 0 + items = [] + msg = '' try: - source = urllib2.urlopen(self.importer_instance.source_url+ + source = urllib2.urlopen(settings.CHIMERE_XAPI_URL+ self.importer_instance.filtr) except ValueError: # assume it is a local file try: - source = open(self.importer_instance.source_url) + source = open(self.importer_instance.source) except IOError, msg: return (new_item, updated_item, msg) except urllib2.URLError as error: @@ -163,10 +176,11 @@ class OSMManager(ImportManager): m = None if node_id: dct_import = { - 'import_key':node_id, - 'import_source':self.importer_instance.source_url} + 'import_key__icontains':'OSM:%s;' % (node_id), + 'import_source':self.importer_instance.source} try: m = Marker.objects.get(**dct_import) + items.append(m) if version and m.import_version == int(version): # no update since the last import continue @@ -176,12 +190,62 @@ class OSMManager(ImportManager): updated_item += 1 except ObjectDoesNotExist: m = None - dct.update(dct_import) + dct.update({ + 'import_source':self.importer_instance.source}) if not m: dct['status'] = 'I' m = Marker.objects.create(**dct) new_item += 1 + items.append(m) + if node_id: + m.set_key('OSM', node_id) m.categories.clear() for cat in self.importer_instance.categories.all(): m.categories.add(cat) - return (new_item, updated_item, msg) + if not get_items: + return (new_item, updated_item, msg) + return (items, new_item, updated_item, msg) + + def put(self): + # first of all: reimport in order to verify that no changes has been + # made since the last import + from models import Marker + items, new_item, updated_item, msg = self.get(get_items=True) + # check if import is possible + if msg: + return 0, msg + if new_item: + return 0, _(u"New items imported - validate them before exporting") + if Marker.objects.filter(status='I').count(): + return 0, _(u"There are items from a former import not yet " + u"validated - validate them before exporting") + # start import + api = OsmApi.OsmApi(api=settings.CHIMERE_OSM_API_URL, + username=settings.CHIMERE_OSM_USER, + 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: + return 0, _(u"Bad param") + tag = tag[0].split('=') + default_dct = {'tag':{tag[0]:tag[1]}, + '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}) + node = None + import_key = marker.get_key('OSM') + if not import_key: + node = OsmApi.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() + return idx+1, None diff --git a/debian/control b/debian/control index 9fe58d9..b3cd3dc 100644 --- a/debian/control +++ b/debian/control @@ -5,5 +5,5 @@ Depends: python-django (>=1.3), python-gdal, python-psycopg2, libjs-jquery-ui, libjs-jquery-ui-theme-base, postgresql-9.1, postgresql-9.1-postgis, gettext, python-simplejson -Recommends: tinymce, gpsbabel, python-django-celery, rabbitmq-server +Recommends: tinymce, gpsbabel, python-django-celery, python-kombu Suggests: libjs-jquery-ui-theme-south-street diff --git a/example_project/settings.py.example b/example_project/settings.py.example index dc49768..d9afa6e 100644 --- a/example_project/settings.py.example +++ b/example_project/settings.py.example @@ -96,11 +96,12 @@ DATABASES = { # celery import djcelery djcelery.setup_loader() -BROKER_HOST = "localhost" -BROKER_PORT = 5672 -BROKER_USER = "guest" -BROKER_PASSWORD = "guest" -BROKER_VHOST = "/" +BROKER_URL = 'django://' +#BROKER_HOST = "localhost" +#BROKER_PORT = 5672 +#BROKER_USER = "guest" +#BROKER_PASSWORD = "guest" +#BROKER_VHOST = "/" # Local time zone for this installation. Choices can be found here: # http://www.postgresql.org/docs/8.1/static/datetime-keywords.html#DATETIME-TIMEZONE-SET-TABLE @@ -191,6 +192,7 @@ INSTALLED_APPS = ( 'django.contrib.sites', 'django.contrib.gis', 'django.contrib.staticfiles', + 'kombu.transport.django', 'djcelery', 'south', 'chimere', diff --git a/requirements.txt b/requirements.txt index 8ca59a9..f976fb9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,3 +6,4 @@ lxml south==0.7.3 simplejson django-celery==2.4.2 +kombu |