Flux Touristiques Tourinsoft (Tourinfrance)

450 views
Skip to first unread message

Sylvain M.

unread,
May 13, 2016, 4:24:18 AM5/13/16
to Communauté Geotrek
Bonjour à tous,

Notre portail GeoTrek avance sérieusement :) (au niveau technique, tout est OK, mais il reste des détails politiques à régler)
Nous souhaitons avant de l'ouvrir intégrer les flux touristiques de nos CDT, au format Tourinsoft.
Nous avons bien les URL des flux, avec des clés (et de la documentation technique).
Par exemple :
http://wcf.tourinsoft.com/Syndication/cdtXX/ID/
http://wcf.tourinsoft.com/Syndication/3.0/cdtXX/ID/

Cependant, malgré la documentation, nous ne parvenons à intégrer une URL fonctionnelle dans GeoTrek.

Si possible, ceux qui les utilisent pourraient-ils nous envoyer un/des exemple(s) d'URL fonctionnelle(s) dans GeoTrek (sans l'ID de syndication).

(pour ceux qui voudraient jeter un coup d’œil sur le futur portail, on peut envoyer l'URL en message privé ;) )

Merci à vous !

A+

Sylvain Montagner
PNR Normandie-Maine

Pierre LABADIE

unread,
May 13, 2016, 5:23:06 AM5/13/16
to Sylvain M., Communauté Geotrek
Bonjour Sylvain,

Nos données issues du SIT sont également au format Tourinsoft mais je ne peux malheureusement pas t'aider
 directement car c'est Makina qui a géré toute la phase de développement/déploiement (je n'ai pas les compétences pour).
Peut-être en regardant les contenus touristiques affichés dans le portail rando-champagne-ardenne.com pourras-tu trouver une piste ?

Bonne journée,



Pierre LABADIE
Chargé de Développement Véloroutes & Voies Vertes
5 rue de Jéricho
B.P. 50319

51013 Châlons en Champagne Cedex
T (0)3 26 21 85 80
www.tourisme-champagne-ardenne.com
     

   




--
Vous recevez ce message, car vous êtes abonné au groupe Google Groupes "Communauté Geotrek".
Pour vous désabonner de ce groupe et ne plus recevoir d'e-mails le concernant, envoyez un e-mail à l'adresse geotrek-fr+...@googlegroups.com.
Pour envoyer un message à ce groupe, envoyez un e-mail à l'adresse geotr...@googlegroups.com.
Cette discussion peut être lue sur le Web à l'adresse https://groups.google.com/d/msgid/geotrek-fr/7193331a-89f9-40e2-915c-c21da983fb94%40googlegroups.com.
Pour obtenir davantage d'options, consultez la page https://groups.google.com/d/optout.

Camille MONCHICOURT

unread,
May 13, 2016, 5:52:05 AM5/13/16
to Sylvain M., Communauté Geotrek
Salut Sylvain,

Dans la V1 il y avait une notion de Sources de données externes, paramétrables dans l'Adminsite. 
Elle permettait d'importer des données depuis un flux SIT (TourinFrance, SITRA ou GeoJSON) comme indiqué dans la doc http://geotrek.readthedocs.io/en/master/advanced-configuration.html#external-vectorial-layers
Et de les faire afficher sur le portail Geotrek-rando dans une couche annexe activable ou non par le visiteur. 
Voir les refuges et Gites d'étapes activables en bas à gauche sur la carte dans notre portail Rando Ecrins encore en V1 - http://rando.ecrins-parcnational.fr/fr/

Dans la V2, la notion de Contenus Touristiques a été intégrée. Elle permet de publier des offres touristiques au même niveau que les randos (et non plus seulement comme une couche annexe affichable ou non). Du coup la notion de Sources de données externes existe toujours dans Geotrek-admin mais ne sont plus publiés sur Geotrek-rando. 
Les données provenant de SIT peuvent désormais être importées dans les catégories de Contenus touristiques en utilisant le nouveau module d'Import - http://geotrek.readthedocs.io/en/master/import.html
C'est d'ailleurs comme ça que sont alimentés les catégories SE LOGER, SE RESTAURER... sur le portail du PNR Grands Causses - http://rando.parc-grands-causses.fr/

Il faudrait voir avec Makina Corpus comment ils ont configuré l'import depuis le SIT Aveyron (format Tourinfrance) pour le PNR GC car la doc d'import ne précise que pour SITRA (SIT Rhone-Alpes et Esprit parc national). 

Bonne journée.


   
Camille MONCHICOURT
Chef du pôle SI / Géomaticien

Parc national des Ecrins
Tél : +33 (0)4 92 40 20 30
www.ecrins-parcnational.fr


De: "Sylvain M." <s...@parc-normandie-maine.fr>
À: "Communauté Geotrek" <geotr...@googlegroups.com>
Envoyé: Vendredi 13 Mai 2016 10:24:18
Objet: [geotrek-fr] Flux Touristiques Tourinsoft (Tourinfrance)

Sylvain M.

unread,
May 13, 2016, 8:31:00 AM5/13/16
to Communauté Geotrek, s...@parc-normandie-maine.fr
Merci Camille pour ces retours.

Nous travaillons, en effet sur la V2 et nous allons donc voir si nous sommes en mesure de configurer le fichier (bulkimport/parsers.py) avec ce qui est expliqué dans la doc, mais en effet il se peut que nous n'y arrivions pas.
Si quelqu'un à la gentillesse de nous envoyer un exemple de fichier parsers.py adapté aux flux Tourinsoft, ce serait formidable !
Sinon, nous nous rapprocherons de Makina Corpus pour nous aider à configurer l'import et compléter la documentation.

Encore merci et bon week-end.

Sylvain

sebastien.millet

unread,
Jul 15, 2016, 10:10:26 AM7/15/16
to Communauté Geotrek

Bonjour à tous,

Egalement en charge de ce projet avec Sylvain au Parc Normandie-Maine, nous avons donc finalement  fait le choix de solliciter Makina Corpus ( très pro) pour la création du fichier Parsers.py pour l'intégration des flux touristiques Tourinsoft.

Nous aurions souhaité que soit faite en même temps la mise à jour de la documentation  mais cela me parait difficile au regard du fichier qui nous a été fourni et les quelques modification que j'ai effectuées.

Je m'explique...

Sur notre territoire, les cdt61 et 72 nous fournissent des flux,  mais pour une même catégorie à créer (hébergement par exemple), le (ou les champ) champ(s) à identifier et à utiliser sont parfois différents.
Egalement certaines valeurs d'un même champ  peuvent être différente pour une même catégorie :

A titre d'exemple pour la catégorie campings il a fallu pour le  flux du cdt 72  sélectionner la valeur "Hôtellerie de plein air (camping)" dans le champ "ObjectTypeName",  alors que pour pour le cdT 61 il faut utiliser les valeurs : "Terrain de camping", "Aire Naturelle", "Camping rural" et "Camping" dans le Champ "Classification".

L'identification des champs et des valeurs n'a pas été très simple, je me suis appuyé sur la documentation en ligne de Tourinsoft (http://api-doc.tourinsoft.com/#/syndication-fonctionnelle), ainsi que les adresse des flux au format json.
Si vous souhaitez jeter un œil au résultat , c'est à cette adresse : http://149.202.129.97
Les catégories créées par l'import Tourinsoft sont : loisirs et découvertes, Hébergement et restaurant
Il s'agit de notre portail provisoire de travail, merci de ne pas diffuser l'adresse

Donc,souhaitant néanmoins que communauté Geotrek profite du travail réalisé, je vous joins notre fichier parsers.py qui devra être adapté à votre configuration (champs, valeurs,...)


# -*- encoding: utf-8 -*-

import requests

from django.conf import settings
from django.contrib.gis.geos import Point
from django.utils.translation import ugettext as _
from geotrek.common.parsers import TourInSoftParser, RowImportError, GlobalImportError
from geotrek.tourism.models import TouristicContent, TouristicContentType


class NormandieMainParser(TourInSoftParser):
    delete_attachments = True
    url = 'http://wcf.tourinsoft.com/Syndication/cdt61/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx/Objects'
    model = TouristicContent
    eid = 'eid'
    constant_fields = {
        'category': u"Test",
        'published': True,
    }
    natural_keys = {
        'category': 'label',
        'type1': 'label',
    }
    non_fields = {
        'attachments': 'Photos',
    }
    field_options = {
        'name': {
            'required': True,
        },
        'geom': {
            'required': True,
        },
        'category': {
            'create': True,
        },
        'type1': {
            'create': True,
        },
    }

    def filter_attachments(self, src, val):
        if not val:
            return []
        return [subval.split('|') for subval in val.split('#') if subval.split('|')[0]]

    def filter_contact(self, src, val):
        (Adresse1, Adresse1Suite, Adresse2, Adresse3, CodePostal, Commune, Cedex) = val
        lines = [line for line in [
            ' '.join([part for part in [Adresse1, Adresse1Suite] if part]),
            Adresse2,
            Adresse3,
            ' '.join([part for part in [CodePostal, Commune, Cedex] if part]),
        ] if line]
        return '<br>'.join(lines)

    def filter_geom(self, src, val):
        lat, lng = val
        if lng == '' or lat == '':
            raise RowImportError(u"Required value for fields 'GmapLatitude' and 'GmapLongitude'.")
        geom = Point(float(lng), float(lat), srid=4326)  # WGS84
        geom.transform(settings.SRID)
        return geom

class Normandie61MainParser(NormandieMainParser):
    fields = {
        'eid': 'SyndicObjectID',
        'name': 'SyndicObjectName',
        'description_teaser': 'DescriptifSynthetique',
        'description': 'DescriptionCommerciale',
        'contact': ('Adresse1', 'Adresse1Suite', 'Adresse2', 'Adresse3', 'CP', 'Commune', 'Cedex'),
        'email': 'CommMail',
        'website': 'CommWeb',
        'geom': ('GmapLatitude', 'GmapLongitude'),
    }

class NormandieAccomodation61Parser(Normandie61MainParser):
    constant_fields = {
        'category': u"Hébergement",
        'published': True,
    }
    def filter_type1(self, src, val):
        type1 = None
        if val == u"Meublés, locations, gîtes":
            type1 =  u"Gîtes"
        if val == u"Hébergement groupe":
            type1 =  u"Hébergements collectifs"
        if val == u"Chambre d'hôtes":
            type1 =  u"Chambres d'hôtes"
        if val == u"Insolite":
            type1 =  u"Hébergements insolites"
        if val in [u"Terrain de camping", u"Aire Naturelle", u"Camping rural", u"Parc résidentiel de loisir", u"Camping à la ferme", u"Camping"]:
            type1 =  u"Camping"           
        if val in [u"Hôtel", u"Hôtel-Restaurant"]:
            type1 =  u"Hôtels"           
        if val == u"Aire de camping-car":
            type1 =  u"Camping-car"
        if type1:
            type1_val, created = (TouristicContentType.objects.get_or_create(label=type1, in_list=1, category=self.obj.category))
            if created:
                self.add_warning(_(u"type1 '{val}' did not exist in Geotrek-Admin and was automatically created").format(val=type1_val))
            return [type1_val]
        raise RowImportError(u"No type1 {type1}".format(type1=type1))

class Normandie72MainParser(NormandieMainParser):
    base_url = 'http://cdt72.media.tourinsoft.eu/upload/'
    fields = {
        'eid': 'SyndicObjectID',
        'name': 'SyndicObjectName',
        'description_teaser': 'DescriptifSynthetique',
        'description': 'Descriptif',
        'contact': ('Adresse1', 'Adresse1Suite', 'Adresse2', 'Adresse3', 'CodePostal', 'Commune', 'Cedex'),
        'email': 'CommMail',
        'website': 'CommWeb',
        'geom': ('GmapLatitude', 'GmapLongitude'),
    }

    @property
    def items(self):
        return self.root['value']

    def next_row(self):
        skip = 0
        while True:
            params = {
                '$format': 'json',
                '$inlinecount': 'allpages',
                '$top': 1000,
                '$skip': skip,
            }
            response = requests.get(self.url, params=params)
            if response.status_code != 200:
                raise GlobalImportError(_(u"Failed to download {url}. HTTP status code {status_code}").format(url=self.url, status_code=response.status_code))
            self.root = response.json()
            self.nb = int(self.root['odata.count'])
            for row in self.items:
                yield {self.normalize_field_name(src): val for src, val in row.iteritems()}
            skip += 1000
            if skip >= self.nb:
                return

    def filter_attachments(self, src, val):
        if not val:
            return []
        return [subval.split('||') for subval in val.split('##') if subval.split('||')[0]]

class NormandieRestaurants61Parser(Normandie61MainParser):
    label = u"Restaurants - 61"
    url = 'http://wcf.tourinsoft.com/Syndication/cdt61/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx/Objects'
    constant_fields = {
        'category': u"Restaurants",
        'published': True,
    }

class NormandieRestaurants72Parser(Normandie72MainParser):
    label = u"Restaurants - 72"
    url = 'http://wcf.tourinsoft.com/Syndication/3.0/cdt72/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx/Objects'
    constant_fields = {
        'category': u"Restaurants",
        'published': True,
    }

class NormandieAccomodationCamping61Parser(NormandieAccomodation61Parser):
    label = u"Hébergements - Camping - 61"
    url = 'http://wcf.tourinsoft.com/Syndication/cdt61/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx/Objects'
    m2m_fields = {
        'type1': ('Classification'),
    }

class NormandieAccomodationRent61Parser(NormandieAccomodation61Parser):
    label = u"Hébergements - Location - 61"
    url = 'http://wcf.tourinsoft.com/Syndication/cdt61/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx/Objects'
    m2m_fields = {
        'type1': ('Classification'),
    }

class NormandieAccomodationHotel61Parser(NormandieAccomodation61Parser):
    label = u"Hébergements - Hôtel - 61"
    url = 'http://wcf.tourinsoft.com/Syndication/cdt61/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx/Objects'
    m2m_fields = {
         'type1': ('Classification'),
    }

class NormandieAccomodationVillage61Parser(Normandie61MainParser):
    constant_fields = {
        'category': u"Hébergement",
        'published': True,
    }
    label = u"Hébergements - Gîte - 61"
    url = 'http://wcf.tourinsoft.com/Syndication/cdt61/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx/Objects'
    m2m_constant_fields = {
        'type1': ["Gîtes"],
    }

class NormandieAccomodation72Parser(Normandie72MainParser):
    constant_fields = {
        'category': u"Hébergement",
        'published': True,
    }

    def filter_type1(self, src, val):
        object_type, object_subtype = val
        type1 = None
        if object_type == u"Hôtellerie de plein air (camping)":
            type1 = u"Camping"
        if object_type == u"Aire de Camping Car":
            type1 = u"Camping-car"
        if object_type == u"Hébergements locatifs (meublés et chambres d'hôtes)":
            if object_subtype in [u"Meublés", u"Locatif sur camping", u"Village locatif"]:
                type1 = u"Gîtes"
            if object_subtype == u"Chambres d'hôtes":
                type1 = u"Chambres d'hôtes"
            if object_subtype == u"Hébergement insolite":
                type1 = u"Hébergements insolites"
        if object_type == u"Hébergements Collectifs":
            type1 = u"Hébergements collectifs"
        if object_type == u"Hôtellerie":
            if object_subtype in [u"Hôtel", u"Hôtel - Restaurant"]:
                type1 = u"Hôtels"
        if type1:
            type1_val, created = (TouristicContentType.objects.get_or_create(label=type1, in_list=1, category=self.obj.category))
            if created:
                self.add_warning(_(u"type1 '{val}' did not exist in Geotrek-Admin and was automatically created").format(val=type1_val))
            return [type1_val]
        self.add_warning(_(u"No parsing defined for type1 '{val}' ").format(val=type1))
        return []
       
class NormandieAccomodationRent72Parser(NormandieAccomodation72Parser):
    label = u"Hébergements - 72"
    url = 'http://wcf.tourinsoft.com/Syndication/3.0/cdt72/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx/Objects'
    m2m_fields = {
        'type1': ('ObjectTypeName', 'Type'),
    }

class NormandieLeisureSportAndCulture61Parser(Normandie61MainParser):
    constant_fields = {
        'category': u"Loisirs et découvertes",
        'published': True,
    }
    label = u"Loisirs et découvertes - Sport et culture - 61"
    url = 'http://wcf.tourinsoft.com/Syndication/cdt61/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx/Objects'
    m2m_fields = {
        'type1': ('ClassificationActivitesCulturelles', 'ClassificationActivitesSportives'),
    }
    def filter_type1(self, src, val):
        culture_val, sport_val = val
        print(culture_val, sport_val)
        type1 = None
        if culture_val is not None:
            type1 =  u"Activités culturelles"
        if sport_val is not None:
            type1 =  u"Activités sportives"
        if type1:
            type1_val, created = (TouristicContentType.objects.get_or_create(label=type1, in_list=1, category=self.obj.category))
            if created:
                self.add_warning(_(u"type1 '{val}' did not exist in Geotrek-Admin and was automatically created").format(val=type1_val))
            return [type1_val]
        raise RowImportError(u"No type1 {type1}".format(type1=type1))

class NormandieLeisureNature61Parser(Normandie61MainParser):
    constant_fields = {
        'category': u"Loisirs et découvertes",
        'published': True,
    }
    label = u"Loisirs et découvertes - Patrimoine naturel - 61"
    url = 'http://wcf.tourinsoft.com/Syndication/cdt61/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx/Objects'
    m2m_constant_fields = {
        'type1': ["Patrimoine naturel"],
    }

class NormandieLeisureFood61Parser(Normandie61MainParser):
    constant_fields = {
        'category': u"Loisirs et découvertes",
        'published': True,
    }
    label = u"Loisirs et découvertes - Dégustation - 61"
    url = 'http://wcf.tourinsoft.com/Syndication/cdt61/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx/Objects'
    m2m_constant_fields = {
        'type1': ["Dégustation"],
    }

class NormandieLeisureMuseum61Parser(Normandie61MainParser):
    constant_fields = {
        'category': u"Loisirs et découvertes",
        'published': True,
    }
    label = u"Loisirs et découvertes - Musées et patrimoine culturel - 61"
    url = 'http://wcf.tourinsoft.com/Syndication/cdt61/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx/Objects'
    m2m_constant_fields = {
        'type1': ["Musées - Patrimoine culturel"],
    }

class NormandieLeisureCulture72Parser(Normandie72MainParser):
    constant_fields = {
        'category': u"Loisirs et découvertes",
        'published': True,
    }
    label = u"Loisirs et découvertes - Activités culturelles - 72"
    url = 'http://wcf.tourinsoft.com/Syndication/3.0/cdt72/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx/Objects'
    m2m_constant_fields = {
        'type1': ["Activités culturelles"],
    }

class NormandieLeisureSport72Parser(Normandie72MainParser):
    constant_fields = {
        'category': u"Loisirs et découvertes",
        'published': True,
    }
    label = u"Loisirs et découvertes - Activités sport et nature - 72"
    url = 'http://wcf.tourinsoft.com/Syndication/3.0/cdt72/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx/Objects'
    m2m_constant_fields = {
        'type1': ["Activités sportives"],
    }

class NormandieLeisureDiscovery72Parser(Normandie72MainParser):
    constant_fields = {
        'category': u"Loisirs et découvertes",
        'published': True,
    }
    label = u"Loisirs et découvertes - Découverte - 72"
    url = 'http://wcf.tourinsoft.com/Syndication/3.0/cdt72/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx/Objects'
    m2m_fields = {
        'type1': ('ObjectTypeName'),
    }
    def filter_type1(self, src, val):
        type1 = None
        if val == u"Patrimoine naturel":
            type1 =  u"Patrimoine naturel"
        if val == u"D\u00e9gustations (tous produits)":
            type1 =  u"Dégustation"
        if val == u"Patrimoine culturel":
            type1 =  u"Musées - Patrimoine culturel"
        if type1:
            type1_val, created = (TouristicContentType.objects.get_or_create(label=type1, in_list=1, category=self.obj.category))
            if created:
                self.add_warning(_(u"type1 '{val}' did not exist in Geotrek-Admin and was automatically created").format(val=type1_val))
            return [type1_val]
        raise RowImportError(u"No type1 {type1}".format(type1=type1))

Voilà, espérant avoir été utile, passez un bon été malgré les événements malheureux.

Sébastien

camille.m...@ecrins-parcnational.fr

unread,
Jul 15, 2016, 11:42:28 AM7/15/16
to geotr...@googlegroups.com

Merci, très intéressant.



Camille MONCHICOURT
Chef du pôle SI / Géomaticien

Parc national des Écrins
--
Vous recevez ce message, car vous êtes abonné au groupe Google Groupes "Communauté Geotrek".
Pour vous désabonner de ce groupe et ne plus recevoir d'e-mails le concernant, envoyez un e-mail à l'adresse geotrek-fr+...@googlegroups.com.
Pour envoyer un message à ce groupe, envoyez un e-mail à l'adresse geotr...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages