Skip to content

Commit

Permalink
✨ [FEAT] Add CirkwiParser to retrieve Treks and Touristic Contents (r…
Browse files Browse the repository at this point in the history
…efs #3947)
  • Loading branch information
Chatewgne committed Mar 1, 2024
1 parent 09e45fd commit f7979b8
Show file tree
Hide file tree
Showing 2 changed files with 162 additions and 0 deletions.
1 change: 1 addition & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ CHANGELOG

- Add `include_externals` filter to Cirkwi trek exports, to allow excluding treks with an external id (eid) (#3947)
- Tourism : add price to TouristicEvent model - ref #3587
- Add `CirkwiParser` to retrieve Treks and Touristic Contents from Cirkwi (refs #3947)

**Improvments**

Expand Down
161 changes: 161 additions & 0 deletions geotrek/cirkwi/parsers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
from django.conf import settings
from django.contrib.gis.geos import Point
from django.utils.translation import gettext as _

from geotrek.common.parsers import AttachmentParserMixin, XmlParser
from geotrek.tourism.models import TouristicContent, TouristicContentType1
from geotrek.trekking.models import Trek
from geotrek.trekking.parsers import ApidaeTrekParser


class CirkwiParser(AttachmentParserMixin, XmlParser):
eid = 'eid'
field_options = {
"geom": {"required": True},
"name": {"required": True},
}
constant_fields = {
'published': True,
}

def get_part(self, dst, src, val):
if val is None:
return None
# Recursively extract XML attributes
if '@@' in src and src[:2] != '@@':
part, attrib = src.split('@@', 1)
return self.get_part(dst, f"@@{attrib}", val.find(part))
# Extract XML attributes
elif src[:2] == '@@':
return val.attrib[src[2:]]
else:
# Return a list of XML elements
if src[-2:] == '/*':
return val.findall(src[:-2])
# Return inner text if XML element exists
if val.find(src) is None:
return None
return val.find(src).text

def filter_description(self, src, val):
descr, compl_title, compl_descr = val
if compl_title and compl_descr:
return f"{descr}\n\n\n{compl_title}: {compl_descr}"
return descr

def filter_attachments(self, src, val):
attachments = []
for attachment in val:
legend = attachment.find('legende')
if legend is not None:
legend = legend.text
url = attachment.find('url').text
author = attachment.find('credit')
if author is not None:
author = author.text
attachments.append([url, legend, author])
return attachments


class CirkwiTrekParserFr(CirkwiParser):
default_language = 'fr'
model = Trek
url = "https://demo-admin.geotrek.fr/static/circkwi.xml"
results_path = 'circuit'
fields = {
"eid": "@@id_circuit",
"name": f"informations/information[@langue='{default_language}']/titre",
"description": (f"informations/information[@langue='{default_language}']/description",
f"informations/information[@langue='{default_language}']/informations_complementaires/information_complementaire/titre",
f"informations/information[@langue='{default_language}']/informations_complementaires/information_complementaire/description"),
"geom": "sens_circuit/fichier_trace@@url",
}
constant_fields = {
'practice': "Pédestre"
}
non_fields = {
'attachments': f"informations/information[@langue='{default_language}']/medias/images/image/*"
}
natural_keys = {
'practice': 'name'
}

def filter_geom(self, src, val):
response = self.request_or_retry(url=val)
return ApidaeTrekParser._get_geom_from_gpx(response.content)


class CirkwiTouristicContentParserFr(CirkwiParser):
default_language = 'fr'
model = TouristicContent
url = "https://demo-admin.geotrek.fr/static/poicirkwi.xml"
results_path = 'poi'
fields = {
"eid": "@@id_poi",
"name": f"informations/information[@langue='{default_language}']/titre",
"description": (f"informations/information[@langue='{default_language}']/description",
f"informations/information[@langue='{default_language}']/informations_complementaires/information_complementaire/titre",
f"informations/information[@langue='{default_language}']/informations_complementaires/information_complementaire/description"),
"geom": ("adresse/position/lng", "adresse/position/lat"),
"practical_info": ("adresse/num", "adresse/rue", "adresse/cp", "adresse/ville", f"informations/information[@langue='{default_language}']/informations_complementaires/information_complementaire/*"),
"category": "categories/categorie/*",
}
m2m_fields = {
"type1": "categories/categorie/*",
}
non_fields = {
'attachments': f"informations/information[@langue='{default_language}']/medias/images/image/*"
}
field_options = {
"geom": {"required": True},
"name": {"required": True},
'category': {'create': True},
'type1': {'create': True},
}
natural_keys = {
'category': 'label',
'type1': 'label',
}

def filter_practical_info(self, src, val):
num, street, code, city, other_infos = val
infos = ''
if (num and street) or (code and city):
infos += '<strong>Adresse : </strong><br>'
if num and street:
infos += f"{num} {street}<br>"
if code and city:
infos += f"{code} {city}<br>"
for other_info in other_infos:
infos += f"<br><strong>{other_info.find('titre').text} : </strong><br>"
infos += f"{other_info.find('description').text}<br>"
if not infos:
return None
return infos

def filter_category(self, src, val):
if val is None:
return None
name = val[0].attrib["nom"]
return self.apply_filter('category', src, name)

def filter_type1(self, src, val):
if val is None or len(val) < 2:
return None
label = val[1].attrib["nom"]
if self.field_options["type1"]["create"]:
type1, __ = TouristicContentType1.objects.get_or_create(category=self.obj.category, label=label)
else:
try:
type1 = TouristicContentType1.objects.get(category=self.obj.category, label=label)
except TouristicContentType1.DoesNotExist:
self.add_warning(
_("Type 1 '{type}' does not exist for category '{cat}'. Please add it").format(
type=label, cat=self.obj.category.label))
return [type1]

def filter_geom(self, src, val):
lng, lat = val
geom = Point(float(lng), float(lat), srid=4326) # WGS84
geom.transform(settings.SRID)
return geom

0 comments on commit f7979b8

Please sign in to comment.