summaryrefslogtreecommitdiff
path: root/chimere/utils.py
diff options
context:
space:
mode:
Diffstat (limited to 'chimere/utils.py')
-rw-r--r--chimere/utils.py272
1 files changed, 202 insertions, 70 deletions
diff --git a/chimere/utils.py b/chimere/utils.py
index 4cd6f45..75c0ad4 100644
--- a/chimere/utils.py
+++ b/chimere/utils.py
@@ -23,6 +23,7 @@ Utilitaries
import csv
import datetime
+import feedparser
import os
import re
import StringIO
@@ -69,16 +70,21 @@ class ImportManager:
def put(self):
pass
- def create_or_update_item(self, cls, values, import_key, version=None):
+ def create_or_update_item(self, cls, values, import_key, version=None,
+ key='', pk=None):
updated, created, item = False, False, None
- if import_key:
+ import_key = unicode(import_key).replace(':', '^')
+ if not key:
+ key = self.importer_instance.importer_type
+ if import_key or pk:
dct_import = {
- 'import_key__icontains':'%s:%s;' % (
- self.importer_instance.importer_type,
- import_key),
+ 'import_key__icontains':'%s:%s;' % (key, import_key),
'import_source':self.importer_instance.source}
try:
- item = cls.objects.get(**dct_import)
+ if pk:
+ item = cls.objects.get(pk=pk)
+ else:
+ item = cls.objects.get(**dct_import)
if version and item.import_version == int(version):
# no update since the last import
return item, None, None
@@ -103,8 +109,7 @@ class ImportManager:
return None, False, False
created = True
if import_key:
- item.set_key(self.importer_instance.importer_type,
- import_key)
+ item.set_key(key, import_key)
item.categories.clear()
for cat in self.importer_instance.categories.all():
item.categories.add(cat)
@@ -138,8 +143,9 @@ class ImportManager:
files.append(None)
return files
- def get_source_file(self, source, suffixes, dest_dir=None,
+ def get_source_file(self, suffixes, dest_dir=None,
extra_url=None):
+ source = self.importer_instance.source_file
if not hasattr(source, 'read'):
if not source:
source = self.importer_instance.source \
@@ -157,7 +163,7 @@ class ImportManager:
source = open(source)
except IOError, msg:
return (None, msg)
- except urllib2.URLError as error:
+ except (urllib2.URLError, AttributeError) as error:
return (None, error.message)
if self.importer_instance.zipped:
try:
@@ -182,21 +188,18 @@ class KMLManager(ImportManager):
self.importer_instance = importer_instance
self.ns = ns
- def get(self, source=None):
+ def get(self):
u"""
- Get data from the source
- Args:
- - source (None): input file if not provided get it from the distant
- source provided in the importer instance.
+ Get data from a KML source
Return a tuple with:
- number of new item ;
- number of item updated ;
- error detail on error
"""
- from models import Marker
+ from models import Marker, Route
new_item, updated_item, msg = 0, 0, ''
- source, msg = self.get_source_file(source, ['.kml'])
+ source, msg = self.get_source_file(['.kml'])
if msg:
return (0, 0, msg)
doc = source
@@ -207,7 +210,10 @@ class KMLManager(ImportManager):
if line.strip():
break
doc = StringIO.StringIO("\n".join(splitted[idx:]))
- tree = etree.parse(doc)
+ try:
+ tree = etree.parse(doc)
+ except:
+ return (0, 0, _(u"Bad XML file"))
# try to get default namespace
if not self.ns:
self.ns = tree.getroot().nsmap[None]
@@ -215,7 +221,7 @@ class KMLManager(ImportManager):
if self.importer_instance.filtr else self.DEFAULT_XPATH
for placemark in tree.xpath(xpath,
namespaces={'kml':self.ns}):
- name, point = None, None
+ name, point, line = None, None, None
pl_id = placemark.attrib.get('id')
pl_key = 'kml-%d' % self.importer_instance.pk
ns = '{%s}' % self.ns
@@ -232,34 +238,32 @@ class KMLManager(ImportManager):
if coord.tag == ns + 'coordinates':
x, y, z = coord.text.split(',')
point = 'SRID=4326;POINT(%s %s)' % (x, y)
+ elif item.tag == ns + 'LineString':
+ for coord in item:
+ if coord.tag == ns + 'coordinates':
+ points = coord.text.replace('\n', ' ').split(' ')
+ points = ",".join([" ".join(p.split(',')[:2])
+ for p in points if p])
+ line = 'SRID=4326;LINESTRING(%s)' % points
+ cls = None
+ dct = {'description':description,
+ 'name':name,
+ 'origin':self.importer_instance.origin,
+ 'license':self.importer_instance.license}
if point:
- dct = {'point':point,
- 'description':description,
- 'name':name,}
- m = None
- if pl_id:
- dct_import = {
- '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:
- setattr(m, k, dct[k])
- m.save()
- updated_item += 1
- except ObjectDoesNotExist:
- m = None
- dct.update({
- 'import_source':self.importer_instance.source})
- if not m:
- dct['status'] = 'I'
- m = Marker.objects.create(**dct)
+ dct['point'] = point
+ cls = Marker
+ if line:
+ dct['route'] = line
+ dct.pop('description')
+ cls = Route
+ if cls:
+ item, updated, created = self.create_or_update_item(
+ cls, dct, pl_id, key=pl_key)
+ if updated:
+ updated_item += 1
+ if created:
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)
@classmethod
@@ -277,12 +281,9 @@ class ShapefileManager(ImportManager):
u"""
Shapefile importer
"""
- def get(self, source=None):
+ def get(self):
u"""
- Get data from the source
- Args:
- - source (None): input file if not provided get it from the distant
- source provided in the importer instance.
+ Get data from a Shapefile source
Return a tuple with:
- number of new item ;
@@ -292,8 +293,7 @@ class ShapefileManager(ImportManager):
from models import Marker, Route
new_item, updated_item, msg = 0, 0, ''
tmpdir = tempfile.mkdtemp()
- sources, msg = self.get_source_file(source,
- ['.shp', '.dbf', '.prj', '.shx'],
+ sources, msg = self.get_source_file(['.shp', '.dbf', '.prj', '.shx'],
dest_dir=tmpdir)
if msg:
return (0, 0, msg)
@@ -350,13 +350,18 @@ class ShapefileManager(ImportManager):
name.decode(settings.CHIMERE_SHAPEFILE_ENCODING))
except:
continue
- geoms = [feat.geom.wkt]
+ try:
+ geoms = [feat.geom.wkt]
+ except:
+ return (0, 0, _(u"Bad Shapefile"))
if feat.geom.geom_type == 'MultiLineString':
geoms = [geom.wkt for geom in feat.geom]
import_key = feat.get(id_name) if id_name and len(geoms) == 1 else ''
for geom in geoms:
dct = {geom_key:'SRID=%s;%s' % (srid, geom),
- 'name':name
+ 'name':name,
+ 'origin':self.importer_instance.origin,
+ 'license':self.importer_instance.license
}
item, updated, created = self.create_or_update_item(
geom_cls, dct, import_key)
@@ -441,26 +446,91 @@ class CSVManager(ImportManager):
u"""
CSV importer
"""
- _COLS = [("Id", 'pk'), (_(u"Name"), 'name'),
+ @classmethod
+ def set_categories(value):
+ return
+
+ # (label, getter, setter)
+ COLS = [("Id", 'pk', 'pk'), (_(u"Name"), '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)]}
+ [c.name for c in obj.categories.all()]),
+ set_categories),
+ (_(u"State"), 'status', lambda x: x),
+ (_(u"Description"), 'description', 'description'),
+ (_(u"Localisation"), 'geometry', 'geometry')]
+
+ def get(self):
+ u"""
+ Get data from a CSV source
+
+ Return a tuple with:
+ - number of new item ;
+ - number of item updated ;
+ - error detail on error
+ """
+ from models import Marker, Route
+ new_item, updated_item, msg = 0, 0, ''
+ source, msg = self.get_source_file(['.csv'])
+ if msg:
+ return (0, 0, msg)
+ reader = csv.reader(source, delimiter=';', quotechar='"')
+ prop_cols = []
+ for pm in Marker.properties():
+ prop_cols.append((pm.name, pm.getAttrName(),
+ pm.getAttrName()+'_set'))
+ cols = self.COLS + prop_cols
+ datas = []
+ for idx, row in enumerate(reader):
+ if not idx: # first row
+ try:
+ assert(len(row) >= len(cols))
+ except AssertionError:
+ return (0, 0, _(u"Invalid CSV format"))
+ continue
+ if len(row) < len(cols):
+ continue
+ pk, name, cats, state = row[0], row[1], row[2], row[3]
+ description, geom = row[4], row[5]
+ COL_INDEX = 6
+ dct = {'description':description,
+ 'name':name,
+ 'origin':self.importer_instance.origin,
+ 'license':self.importer_instance.license}
+ cls = None
+ if 'POINT' in geom:
+ cls = Marker
+ dct['point'] = geom
+ elif 'LINE' in geom:
+ cls = Route
+ dct['route'] = geom
+ else:
+ continue
+ import_key = pk if pk else name
+ item, updated, created = self.create_or_update_item(cls, dct,
+ import_key, pk=pk)
+ if updated:
+ updated_item += 1
+ if created:
+ new_item += 1
+ for idx, col in enumerate(cols[COL_INDEX:]):
+ name, getter, setter_val = col
+ setter = getattr(item, setter_val)
+ val = row[idx+COL_INDEX]
+ setter(item, val)
+ return (new_item, updated_item, msg)
@classmethod
def export(cls, queryset):
dct = {'description':unicode(datetime.date.today()), 'data':[]}
cls_name = queryset.model.__name__.lower()
- cols = cls.COLS[cls_name][:]
+ cols = cls.COLS
for pm in queryset.model.properties():
- cols.append((pm.name, pm.getAttrName()))
- header = [lbl for lbl, attr in cols]
+ cols.append((pm.name, pm.getAttrName(), pm.getAttrName()+'_set'))
+ header = [col[0] for col in cols]
dct['data'].append(header)
for item in queryset.all():
data = []
- for lbl, attr in cols:
+ for (lbl, attr, setr) in cols:
if callable(attr):
data.append(attr(item))
else:
@@ -468,9 +538,66 @@ class CSVManager(ImportManager):
dct['data'].append(data)
filename = unicode_normalize(settings.PROJECT_NAME + dct['description']\
+ '.csv')
- result = render_to_response('chimere/export_%s.csv' % cls_name, dct)
+ result = render_to_response('chimere/export.csv', dct)
return filename, result
+class GeoRSSManager(ImportManager):
+ u"""
+ RSS importer.
+ This manager only gets and do not produce GeoRSSFeed
+ """
+
+ def get(self):
+ u"""
+ Get data from a GeoRSS simple source
+
+ Return a tuple with:
+ - number of new item ;
+ - number of item updated ;
+ - error detail on error
+ """
+ from models import Marker
+ new_item, updated_item, msg = 0, 0, ''
+ feed = feedparser.parse(self.importer_instance.source)
+ if feed['bozo']:
+ return (0, 0, _(u"RSS feed is not well formed"))
+ for item in feed['items']:
+ if "georss_point" not in item and 'georss_line' not in item:
+ continue
+ cls = None
+ dct = {'origin':self.importer_instance.origin,
+ 'license':self.importer_instance.license}
+ if 'georss_point' in item:
+ cls = Marker
+ try:
+ y, x = item['georss_point'].split(' ')
+ except ValueError:
+ continue
+ dct['point'] = 'SRID=4326;POINT(%s %s)' % (x, y)
+ for k in ['description', 'summary', 'value']:
+ if k in item:
+ dct['description'] = item[k]
+ break
+ else:
+ cls = Route
+ points = item['georss_line'].split(' ')
+ reordered_points = []
+ # lat, lon -> x, y
+ for idx in xrange(len(points)/2):
+ reordered_points.append("%s %s" % (points[idx*2+1],
+ points[idx*2]))
+ dct['route'] = 'SRID=4326;LINESTRING(%s)' % \
+ ",".join(reordered_points)
+
+ dct['name'] = item['title']
+ pl_id = item['id'] if 'id' in item else item['title']
+ it, updated, created = self.create_or_update_item(cls, dct, pl_id)
+ if updated:
+ updated_item += 1
+ if created:
+ new_item += 1
+ return (new_item, updated_item, msg)
+
RE_HOOK = re.compile('\[([^\]]*)\]')
# TODO: manage deleted item from OSM
@@ -483,19 +610,16 @@ class OSMManager(ImportManager):
"""
default_source = settings.CHIMERE_XAPI_URL
- def get(self, source=None):
+ def get(self):
u"""
Get data from the source
- Args:
- - source (None): input file if not provided get it from the distant
- source provided in the importer instance.
Return a tuple with:
- new items;
- updated items;
- error detail on error.
"""
- source, msg = self.get_source_file(source, ['.osm'],
+ source, msg = self.get_source_file(['.osm'],
extra_url=self.importer_instance.filtr)
if not source:
return (0, 0, msg)
@@ -538,6 +662,10 @@ class OSMManager(ImportManager):
for point_id in points if point_id in nodes])
dct = {'route':wkt,
'name':name,
+ 'origin':self.importer_instance.origin \
+ or u'OpenStreetMap.org',
+ 'license':self.importer_instance.license \
+ or u'ODbL',
'import_version':version}
item, updated, created = self.create_or_update_item(
Route, dct, node_id, version)
@@ -567,6 +695,10 @@ class OSMManager(ImportManager):
node.get('lat'))
dct = {'point':point,
'name':name,
+ 'origin':self.importer_instance.origin \
+ or u'OpenStreetMap.org',
+ 'license':self.importer_instance.license \
+ or u'ODbL',
'import_version':version}
item, updated, created = self.create_or_update_item(
Marker, dct, node_id, version)