summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorÉtienne Loks <etienne.loks@proxience.com>2015-02-14 14:33:53 +0100
committerÉtienne Loks <etienne.loks@proxience.com>2015-02-14 14:33:53 +0100
commit06008ace42c6f972b96532e76ef69cf35b0b9eea (patch)
treed7860b8022fe36d85ab1fe2fcf8b69cc1d1a074c
parent72f4ae70dee56b5e532a579aeae7f5cc22f49813 (diff)
parentafbcd9cf0578f70ac25afac0199446a43d317b52 (diff)
downloadChimère-06008ace42c6f972b96532e76ef69cf35b0b9eea.tar.bz2
Chimère-06008ace42c6f972b96532e76ef69cf35b0b9eea.zip
Merge branch 'v2.1'
-rw-r--r--chimere/tests.py18
-rw-r--r--chimere/tests/magny-xml.xslt21
-rw-r--r--chimere/tests/magny.xml123
-rw-r--r--chimere/utils.py207
4 files changed, 293 insertions, 76 deletions
diff --git a/chimere/tests.py b/chimere/tests.py
index ef54d0b..3a3144e 100644
--- a/chimere/tests.py
+++ b/chimere/tests.py
@@ -137,8 +137,9 @@ class ImporterTest:
nb, nb_updated, res = importer.manager.get()
if awaited_nb == None:
continue
- self.assertEqual(nb, awaited_nb, msg=u"%s: get test failed" %
- unicode(self.__class__))
+ self.assertEqual(nb, awaited_nb,
+ msg=u"%s: get test failed - got %d when %d was awaited" %
+ (unicode(self.__class__), nb, awaited_nb))
self.assertEqual(nb_updated, 0)
for cat in importer.categories.all():
if cat not in nb_by_cat:
@@ -311,6 +312,17 @@ class HtmlXsltImporterTest(TestCase, ImporterTest):
importer1.categories.add(subcategories[0])
self.marker_importers = [(importer1, 7),]
+class XmlXsltImporterTest(TestCase, ImporterTest):
+ def setUp(self):
+ subcategories = subcategory_setup()
+ xslt1 = File(open(test_dir_path + 'tests/magny-xml.xslt'))
+ importer1 = Importer.objects.create(importer_type='XXLT',
+ source='http://www.chymeres.net/test/magny.xml',
+ source_file=xslt1,
+ default_localisation='SRID=4326;POINT(-4.5 48.4)',)
+ importer1.categories.add(subcategories[0])
+ self.marker_importers = [(importer1, 10),]
+
class FeedsTest(TestCase):
def setUp(self):
self.areas = areas_setup()
@@ -442,7 +454,7 @@ class DynamicCategoryTest(TestCase):
response = self.client.get(url)
self.assertEqual(200, response.status_code)
cats = json.loads(response.content)['categories']
- self.assertEqual(len(cats), 2)
+ self.assertEqual(len(cats), 5)
class NewsTest(TestCase):
def setUp(self):
diff --git a/chimere/tests/magny-xml.xslt b/chimere/tests/magny-xml.xslt
new file mode 100644
index 0000000..1da9212
--- /dev/null
+++ b/chimere/tests/magny-xml.xslt
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+<xsl:output method="xml" indent="yes"/>
+ <xsl:template match="/">
+ <items>
+ <xsl:for-each select="/nodes/node">
+ <item>
+ <date><xsl:value-of select="date"/></date>
+ <name>Magny-les-hameaux : <xsl:value-of select="titre"/></name>
+ <category><xsl:value-of select="tiquette"/></category>
+ <link><xsl:value-of select="lien"/></link>
+ <description><xsl:value-of select="corps"/></description>
+ <key>magny-les-hameaux-<xsl:value-of select="titre"/></key>
+ <lat><xsl:value-of select="latitude"/></lat>
+ <lon><xsl:value-of select="longitude"/></lon>
+ </item>
+ </xsl:for-each>
+ </items>
+ </xsl:template>
+</xsl:stylesheet>
+
diff --git a/chimere/tests/magny.xml b/chimere/tests/magny.xml
new file mode 100644
index 0000000..47304f9
--- /dev/null
+++ b/chimere/tests/magny.xml
@@ -0,0 +1,123 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<nodes>
+ <node>
+ <tiquette>Cirque - Mime</tiquette>
+ <Titre>Le cirque Plume pour les seniors</Titre>
+ <Date>2015-02-01T15:00:00</Date>
+ <Corps>
+ Le service seniors organise une sortie pour découvrir ou re-découvrir le cirque Plume, au Théâtre de St-Quentin-en-Yvelines. Il souffle, cette année, ses 30 bougies. Trente ans d’envolées oniriques, acrobatiques et musicales. Trente ans de triomphes. Un réjouissant spectacle de grâce, poésie et magie.</Corps>
+ <Lien>http://www.magny-les-hameaux.fr/node/3187</Lien>
+ <Latitude></Latitude>
+ <Longitude></Longitude>
+ </node>
+ <node>
+ <tiquette>Environnement</tiquette>
+ <Titre>Journée de chasse</Titre>
+ <Date>2015-02-03T08:00:00</Date>
+ <Corps>
+ L’ONF nous informe que des journées de chasse sont organisées de novembre à février dans les Forêts domaniales de Port-Royal/Trappes et Port-Royal/Mérantais.&lt;br /&gt;La chasse est pratiquée chaque année en forêt domaniale. Elle permet de réguler les populations de sangliers et de chevreuils et contribue ainsi à la conservation des écosystèmes forestiers.&lt;br /&gt;</Corps>
+ <Lien>http://www.magny-les-hameaux.fr/node/3038</Lien>
+ <Latitude></Latitude>
+ <Longitude></Longitude>
+ </node>
+ <node>
+ <tiquette>Exposition</tiquette>
+ <Titre>Exposition « Agir pour l’Avenir »</Titre>
+ <Date>2015-02-04T09:00:00 » 2015-02-28T17:00:00</Date>
+ <Corps>
+ Souvenez-vous en classe, derrière vous… il y avait sûrement des planches pédagogiques de la maison Deyrolle…
+
+ Depuis 2007, la Maison Deyrolle relance l’édition de nouvelles planches pédagogiques sur le développement durable, sous le nom de « Agir pour l’Avenir », qui abordent les enjeux environnementaux et sociétaux contemporains.</Corps>
+ <Lien>http://www.magny-les-hameaux.fr/node/3240</Lien>
+ <Latitude></Latitude>
+ <Longitude></Longitude>
+ </node>
+ <node>
+ <tiquette>Jeune public</tiquette>
+ <Titre>Les après-midis « contes » à la médiathèque Jacques Brel</Titre>
+ <Date>2015-02-04T14:30:00</Date>
+ <Corps>
+ Le centre social Albert Schweitzer vous invite à venir participer à des après-midis au cours desquels papa ou maman pourra passer un agréable moment avec son enfant à la Médiathèque Jacques Brel.
+
+ Mercredis 7 janvier, 4 et 18 février, 11 et 25 mars 2015
+
+ Rendez-vous à l’accueil du Centre Social</Corps>
+ <Lien>http://www.magny-les-hameaux.fr/node/3168</Lien>
+ <Latitude></Latitude>
+ <Longitude></Longitude>
+ </node>
+ <node>
+ <tiquette>Événement</tiquette>
+ <Titre>Café du jeudi 2015</Titre>
+ <Date>2015-02-05T08:00:00</Date>
+ <Corps>
+ Les Cafés du Jeudi vous accueillent à Magny les Hameaux, deux fois par mois, le jeudi matin de 9 heures à 11h30, au pôle associatif Blaise Pascal, salle Coluche.
+
+ Il s’agit d’un moment échanges autour des passions et centres d'intérêt des participants : collections, histoire de Magny, lecture, cuisine, jardin, … mais aussi de vos préoccupations du moment.</Corps>
+ <Lien>http://www.magny-les-hameaux.fr/node/3139</Lien>
+ <Latitude></Latitude>
+ <Longitude></Longitude>
+ </node>
+ <node>
+ <tiquette>Atelier</tiquette>
+ <Titre>Atelier informatique : coup de pouce pour les demandeurs d’emploi</Titre>
+ <Date>2015-02-05T08:30:00</Date>
+ <Corps>
+ Vous êtes à la recherche d’un emploi, d’un stage ou d’une formation et vous ne maîtrisez pas les outils informatiques ? Sachez que le service Emploi de la Ville et le CIDFF (Centre d’information du droit des femmes) organisent, tous les jeudis, des ateliers d’initiation pour vous apprendre le B.A.BA de l’informatique : WORD, EXCEL, la navigation Internet, l’envoi de mails…
+</Corps>
+ <Lien>http://www.magny-les-hameaux.fr/node/3179</Lien>
+ <Latitude></Latitude>
+ <Longitude></Longitude>
+ </node>
+ <node>
+ <tiquette>Atelier</tiquette>
+ <Titre>SOS informatique : Internet ! C’est facile !</Titre>
+ <Date>2015-02-05T15:00:00</Date>
+ <Corps>
+ Les médiathèques étendent leurs propositions d’accompagnement dans les domaines du numérique pour vous aider dans votre pratique quotidienne, l’emploi, l’aide aux devoirs et les réseaux sociaux.
+</Corps>
+ <Lien>http://www.magny-les-hameaux.fr/node/3175</Lien>
+ <Latitude>48.722817</Latitude>
+ <Longitude>2.094394</Longitude>
+ </node>
+ <node>
+ <tiquette>Conférence</tiquette>
+ <Titre>Semaine de l&#039;emploi</Titre>
+ <Date>2015-02-06T09:00:00 » 2015-02-14T16:00:00</Date>
+ <Corps>
+ La Communauté d’agglomération de Saint-Quentin-en-Yvelines et ses partenaires se mobilisent et organisent du 6 au 14 février la 19èmeSemaine de l’emploi.
+
+ Objectif : créer localement des lieux de rencontre entre les entreprises et un public en découverte d’un métier ou en recherche d’emploi.
+
+ Vous êtes à la recherche d’un emploi ?&lt;br /&gt;
+ Vous aimeriez être accompagné dans votre parcours professionnel ?&lt;br /&gt;
+ Vous souhaitez découvrir les métiers de demain ?&lt;br /&gt;
+ De nombreux événements sont organisés pour vous : découvrez le programme, munissez-vous de vos CV et rendez-vous sur l’une des manifestations organisées pour vous !
+</Corps>
+ <Lien>http://www.magny-les-hameaux.fr/node/3258</Lien>
+ <Latitude></Latitude>
+ <Longitude></Longitude>
+ </node>
+ <node>
+ <tiquette>Événement</tiquette>
+ <Titre>Collecte de denrées alimentaires</Titre>
+ <Date>2015-02-07T08:00:00</Date>
+ <Corps>
+ Le secours populaire organise une collecte alimentaire dans la galerie marchande de votre Intermarché, les samedis suivants : 
+</Corps>
+ <Lien>http://www.magny-les-hameaux.fr/node/3241</Lien>
+ <Latitude></Latitude>
+ <Longitude></Longitude>
+ </node>
+ <node>
+ <tiquette>Événement</tiquette>
+ <Titre>Commerce équitable</Titre>
+ <Date>2015-02-07T09:00:00 » 2015-02-07T11:30:00</Date>
+ <Corps>
+ L'association Les Amis de l'Estaminet et de la Culture vous propose de venir acheter des produits d'artisans du monde (bijoux, alimentation, produits de beauté, vêtements...), une fois par mois. Cette action est liée à une action humanitaire.
+</Corps>
+ <Lien>http://www.magny-les-hameaux.fr/node/3004</Lien>
+ <Latitude></Latitude>
+ <Longitude></Longitude>
+ </node>
+</nodes>
diff --git a/chimere/utils.py b/chimere/utils.py
index 73e38ba..55fc45c 100644
--- a/chimere/utils.py
+++ b/chimere/utils.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-# Copyright (C) 2012-2013 Étienne Loks <etienne.loks_AT_peacefrogsDOTnet>
+# Copyright (C) 2012-2015 Étienne Loks <etienne.loks_AT_peacefrogsDOTnet>
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as
@@ -870,17 +870,45 @@ except UnicodeEncodeError:
for locale in MONTH_NAMES}
DATE_PARSINGS = {'fr_FR':[
- re.compile(r'(?P<day1>\d{1,2}) '\
- r'(?P<month1>'+ '|'.join(UNI_MONTH_NAMES['fr_FR']) +') '\
- r'(?P<year1>\d{4})?[^\d]*'\
- r'(?P<day2>\d{1,2}) '\
- r'(?P<month2>'+ '|'.join(UNI_MONTH_NAMES['fr_FR']) +') *'\
- r'(?P<year2>\d{4})?.*'),
- re.compile(r'(?P<day1>\d{1,2}) '\
- r'(?P<month1>'+ '|'.join(UNI_MONTH_NAMES['fr_FR']) +') *'\
- r'(?P<year1>\d{4})?')
- ]
- }
+ re.compile(r'(?P<day1>\d{1,2}) '\
+ r'(?P<month1>'+ '|'.join(UNI_MONTH_NAMES['fr_FR']) +') '\
+ r'(?P<year1>\d{4})?[^\d]*'\
+ r'(?P<day2>\d{1,2}) '\
+ r'(?P<month2>'+ '|'.join(UNI_MONTH_NAMES['fr_FR']) +') *'\
+ r'(?P<year2>\d{4})?.*'),
+ re.compile(r'(?P<day1>\d{1,2}) '\
+ r'(?P<month1>'+ '|'.join(UNI_MONTH_NAMES['fr_FR']) +') *'\
+ r'(?P<year1>\d{4})?')
+ ],
+ 'en':[
+ re.compile(r'(?P<year1>\d{4})-'\
+ r'(?P<month1>\d{2})-'\
+ r'(?P<day1>\d{2})'\
+ r'(?:T'\
+ r'(?P<hour1>\d{2})?:'\
+ r'(?P<minut1>\d{2})?:'\
+ r'(?P<second1>\d{2})'\
+ r')?.*'\
+ r'(?P<year2>\d{4})-'\
+ r'(?P<month2>\d{2})-'\
+ r'(?P<day2>\d{2})'\
+ r'(?:T'\
+ r'(?P<hour2>\d{2})?:'\
+ r'(?P<minut2>\d{2})?:'\
+ r'(?P<second2>\d{2})'\
+ r')?.*'
+ ),
+ re.compile(r'(?P<year1>\d{4})-'\
+ r'(?P<month1>\d{2})-'\
+ r'(?P<day1>\d{2})'\
+ r'(?:T'\
+ r'(?P<hour1>\d{2})?:'\
+ r'(?P<minut1>\d{2})?:'\
+ r'(?P<second1>\d{2})'\
+ r')?'
+ )
+ ],
+ }
def clean_field(value):
return value.strip()
@@ -897,6 +925,7 @@ class HtmlXsltManager(ImportManager):
- error detail on error.
"""
from models import Marker
+ self.marker_cls = Marker
try:
main_page = urllib2.urlopen(self.importer_instance.source)
assert main_page.getcode() == 200
@@ -966,71 +995,103 @@ class HtmlXsltManager(ImportManager):
for r, replaced in RE_CLEANS:
val = re.sub(r, replaced % {'base_url':base_url}, val)
item[k] = html_unescape(val)
- updated_item, new_item = 0, 0
- key_categories = self.importer_instance.get_key_category_dict()
- missing_cats = set()
+ self.key_categories = self.importer_instance.get_key_category_dict()
+ self.missing_cats = set()
+ self.updated_item, self.new_item = 0, 0
for item in items:
- if not self.importer_instance.default_localisation and \
- not "point" in item and not ("lat" in item and item['lat']):
- continue
- cls = None
- dct = {'origin':"<a href='%s'>%s</a>" % (item['link'],
- self.importer_instance.origin),
- 'license':self.importer_instance.license,
- 'name':item['name']}
- category = None
- if 'category' in item and item['category']:
- if item['category'] in key_categories:
- category = key_categories[item['category']]
- else:
- missing_cats.add(item['category'])
- cls = Marker
- if 'point' in item:
- x, y = item['point'].split(",")
- dct['point'] = 'SRID=4326;POINT(%s %s)' % (x, y)
- elif 'lat' in item and item['lat']:
- dct['point'] = 'SRID=4326;POINT(%s %s)' % (item['lon'],
- item['lat'])
- else:
- dct['point'] = self.importer_instance.default_localisation
- dct['description'] = item['description']
- if 'date' in item:
- has_dates = False
- for locale in DATE_PARSINGS:
- if has_dates:
- break
- for r in DATE_PARSINGS[locale]:
- m = r.search(item['date'])
- if not m:
- continue
- has_dates = True
- values = m.groupdict()
- year1 = datetime.date.today().year if 'year1' not in values \
- else int(values['year1'])
- dct['start_date'] = datetime.date(year1,
- MONTH_NAMES[locale].index(values['month1'].encode('utf-8')) + 1,
- int(values['day1']))
- if 'day2' not in values:
- break
- year2 = datetime.date.today().year if 'year2' not in values \
- else int(values['year2'])
- dct['end_date'] = datetime.date(year2,
- MONTH_NAMES[locale].index(values['month2'].encode('utf-8')) + 1,
- int(values['day2']))
- break
- key = item['key']
- it, updated, created = self.create_or_update_item(cls, dct, key,
- category=category)
- if updated:
- updated_item += 1
- if created:
- new_item += 1
+ self.add_dct_item(item)
msg = ''
- if missing_cats:
+ if self.missing_cats:
msg = _(u"Names \"%s\" doesn't match existing categories. "
u"Modify the import to match theses names with categories.") % (
- u'", "'.join(missing_cats))
- return (new_item, updated_item, msg)
+ u'", "'.join(self.missing_cats))
+ return (self.new_item, self.updated_item, msg)
+
+ @classmethod
+ def _internal_parse_date(cls, locale, year, month, day):
+ try:
+ year = datetime.date.today().year if not year else int(year)
+ except ValueError:
+ return
+ month = month.encode('utf-8')
+ if locale in MONTH_NAMES and month in MONTH_NAMES[locale]:
+ month = MONTH_NAMES[locale].index(month) + 1
+ else:
+ try:
+ month = int(month)
+ except ValueError:
+ return
+ try:
+ day = int(day)
+ except ValueError:
+ return
+ try:
+ return datetime.date(year, month, day)
+ except ValueError:
+ return
+
+ def parse_date(self, date):
+ dct = {}
+ has_dates = False
+ for locale in DATE_PARSINGS:
+ if has_dates:
+ break
+ for r in DATE_PARSINGS[locale]:
+ m = r.search(date)
+ if not m:
+ continue
+ values = m.groupdict()
+ date = self._internal_parse_date(locale,
+ 'year1' in values and values['year1'],
+ values['month1'], values['day1'])
+ if not date:
+ continue
+ dct['start_date'] = date
+ has_dates = True
+ if 'day2' not in values:
+ break
+ date = self._internal_parse_date(locale,
+ 'year2' in values and values['year2'],
+ values['month2'], values['day2'])
+ if date:
+ dct['end_date'] = date
+ break
+ return dct
+
+ def add_dct_item(self, item):
+ if not self.importer_instance.default_localisation and \
+ not "point" in item and not ("lat" in item and item['lat']):
+ return
+ cls = None
+ dct = {'origin':"<a href='%s'>%s</a>" % (item['link'],
+ self.importer_instance.origin),
+ 'license':self.importer_instance.license,
+ 'name':item['name']}
+ category = None
+ if 'category' in item and item['category']:
+ if item['category'] in self.key_categories:
+ category = self.key_categories[item['category']]
+ else:
+ self.missing_cats.add(item['category'])
+ cls = self.marker_cls
+ if 'point' in item:
+ x, y = item['point'].split(",")
+ dct['point'] = 'SRID=4326;POINT(%s %s)' % (x, y)
+ elif 'lat' in item and item['lat']:
+ dct['point'] = 'SRID=4326;POINT(%s %s)' % (item['lon'],
+ item['lat'])
+ else:
+ dct['point'] = self.importer_instance.default_localisation
+ dct['description'] = item['description']
+ if 'date' in item:
+ dct.update(self.parse_date(item['date']))
+ key = item['key']
+ it, updated, created = self.create_or_update_item(cls, dct, key,
+ category=category)
+ if updated:
+ self.updated_item += 1
+ if created:
+ self.new_item += 1
class XMLXsltManager(HtmlXsltManager):
PARSER = 'XMLParser'