diff --git a/geotrek/cirkwi/parsers.py b/geotrek/cirkwi/parsers.py index 2285ba0fe4..81b147cb7e 100644 --- a/geotrek/cirkwi/parsers.py +++ b/geotrek/cirkwi/parsers.py @@ -94,6 +94,16 @@ def next_row(self): first += self.rows def get_part(self, dst, src, val): + """ + The CirkwiParser class handles some XML-related specificities for the fields mapping: + + - `src` values should follow an XML path syntax so the separator is '/' instead of '.' + - `src` value "locomotions/locomotion" gets the text value of the first XML element encountered (inside a element at the row's root level) + - "informations/information[@langue='fr']/titre" means we get the text value of the first element having a parent with attribute `langue` equals "fr" + - the "" marker is replaced with the `default_language` of the parser + - a '@@' sequence at the end followed by a attribute name indicates the fetched value should be the value of the attribute (for instance "locomotions/locomotion@@type" will get the value of the 'type' attribute of the element) + - finally the '/*' sequence at the end of a `src` value indicates a list of all XML elements matching the path should be returned (so "locomotions/locomotion/*" has the same meaning than "locomotions.*.locomotion" from the base Parser class) + """ # Recursively extract XML attributes if '@@' in src and src[:2] != '@@': part, attrib = src.split('@@', 1) diff --git a/geotrek/cirkwi/tests/test_parsers.py b/geotrek/cirkwi/tests/test_parsers.py index 179fdfaf8a..e670758d7e 100644 --- a/geotrek/cirkwi/tests/test_parsers.py +++ b/geotrek/cirkwi/tests/test_parsers.py @@ -141,7 +141,7 @@ def test_create_treks(self, mocked_get): # Test update mocked_get.side_effect = self.make_dummy_get('circuits_updated.xml') - call_command('import', 'geotrek.cirkwi.tests.test_parsers.TestCirkwiTrekParserFrUpdateOnly', verbosity=2) + call_command('import', 'geotrek.cirkwi.tests.test_parsers.TestCirkwiTrekParserFrUpdateOnly', verbosity=0) self.assertEqual(Trek.objects.count(), 2) t = Trek.objects.get(name_fr="Le patrimoine de Plancoët à VTT") self.assertEqual(t.eid, '10926') diff --git a/geotrek/common/parsers.py b/geotrek/common/parsers.py index 97cd79b02a..5683d07dad 100644 --- a/geotrek/common/parsers.py +++ b/geotrek/common/parsers.py @@ -43,7 +43,6 @@ from geotrek.common.utils.parsers import add_http_prefix from geotrek.common.utils.translation import get_translated_fields - if 'modeltranslation' in settings.INSTALLED_APPS: from modeltranslation.fields import TranslationField diff --git a/geotrek/common/tests/test_utils.py b/geotrek/common/tests/test_utils.py index e440a04fca..55fdbf4c72 100644 --- a/geotrek/common/tests/test_utils.py +++ b/geotrek/common/tests/test_utils.py @@ -12,7 +12,7 @@ from ..utils.file_infos import get_encoding_file from ..utils.import_celery import create_tmp_destination, subclasses from ..utils.parsers import (add_http_prefix, get_geom_from_gpx, - get_geom_from_kml, maybe_fix_encoding_to_utf8) + get_geom_from_kml, maybe_fix_encoding_to_utf8, GeomValueError) class UtilsTest(TestCase): @@ -142,7 +142,7 @@ def test_gpx_with_route_points_can_be_converted(self): def test_it_raises_an_error_on_not_continuous_segments(self): gpx = self._get_gpx_from('geotrek/trekking/tests/data/apidae_trek_parser/trace_with_not_continuous_segments.gpx') - with self.assertRaises(ValueError): + with self.assertRaises(GeomValueError): get_geom_from_gpx(gpx) def test_it_handles_segment_with_single_point(self): @@ -158,7 +158,7 @@ def test_it_handles_segment_with_single_point(self): def test_it_raises_an_error_when_no_linestring(self): gpx = self._get_gpx_from('geotrek/trekking/tests/data/apidae_trek_parser/trace_with_no_feature.gpx') - with self.assertRaises(ValueError): + with self.assertRaises(GeomValueError): get_geom_from_gpx(gpx) def test_it_handles_multiple_continuous_features(self): @@ -185,7 +185,7 @@ def test_it_handles_multiple_continuous_features_with_one_empty(self): def test_it_raises_error_on_multiple_not_continuous_features(self): gpx = self._get_gpx_from('geotrek/trekking/tests/data/apidae_trek_parser/trace_with_multiple_not_continuous_features.gpx') - with self.assertRaises(ValueError): + with self.assertRaises(GeomValueError): get_geom_from_gpx(gpx) @@ -212,7 +212,7 @@ def test_kml_can_be_converted(self): def test_it_raises_exception_when_no_linear_data(self): kml = self._get_kml_from('geotrek/trekking/tests/data/apidae_trek_parser/trace_with_no_line.kml') - with self.assertRaises(ValueError): + with self.assertRaises(GeomValueError): get_geom_from_kml(kml) diff --git a/geotrek/common/utils/parsers.py b/geotrek/common/utils/parsers.py index 246a22b355..0e2859a17b 100644 --- a/geotrek/common/utils/parsers.py +++ b/geotrek/common/utils/parsers.py @@ -11,6 +11,10 @@ from geotrek.common.utils.file_infos import get_encoding_file +class GeomValueError(Exception): + pass + + def add_http_prefix(url): if url.startswith('http'): return url @@ -64,7 +68,7 @@ def maybe_get_linestring_from_layer(layer): if geos.geom_type == 'MultiLineString': geos = geos.merged # If possible we merge the MultiLineString into a LineString if geos.geom_type == 'MultiLineString': - raise ValueError( + raise GeomValueError( _("Feature geometry cannot be converted to a single continuous LineString feature")) geoms.append(geos) @@ -72,7 +76,7 @@ def maybe_get_linestring_from_layer(layer): full_geom.srid = geoms[0].srid full_geom = full_geom.merged # If possible we merge the MultiLineString into a LineString if full_geom.geom_type == 'MultiLineString': - raise ValueError( + raise GeomValueError( _("Geometries from various features cannot be converted to a single continuous LineString feature")) return full_geom @@ -91,7 +95,7 @@ def maybe_get_linestring_from_layer(layer): if geos: break else: - raise ValueError("No LineString feature found in GPX layers tracks or routes") + raise GeomValueError("No LineString feature found in GPX layers tracks or routes") geos.transform(settings.SRID) return geos @@ -113,7 +117,7 @@ def get_first_geom_with_type_in(types, geoms): for t in types: if g.geom_type.name.startswith(t): return g - raise ValueError('The attached KML geometry does not have any LineString or MultiLineString data') + raise GeomValueError('The attached KML geometry does not have any LineString or MultiLineString data') with NamedTemporaryFile(mode='w+b', dir=settings.TMP_DIR) as ntf: ntf.write(data) diff --git a/geotrek/trekking/parsers.py b/geotrek/trekking/parsers.py index 123e37b76c..827f75c492 100644 --- a/geotrek/trekking/parsers.py +++ b/geotrek/trekking/parsers.py @@ -26,7 +26,7 @@ GeotrekParser, GlobalImportError, Parser, RowImportError, ShapeParser, DownloadImportError, ValueImportError) -from geotrek.common.utils.parsers import get_geom_from_gpx, get_geom_from_kml +from geotrek.common.utils.parsers import get_geom_from_gpx, get_geom_from_kml, GeomValueError from geotrek.core.models import Path, Topology from geotrek.trekking.models import (POI, Accessibility, DifficultyLevel, OrderedTrekChild, Service, Trek, @@ -698,7 +698,7 @@ def filter_geom(self, src, val): elif ext == 'kmz': kml_file = zipfile.ZipFile(io.BytesIO(geom_file)).read('doc.kml') return get_geom_from_kml(kml_file) - except ValueError as e: + except GeomValueError as e: raise RowImportError(str(e)) def filter_labels(self, src, val):