Skip to content
Extraits de code Groupes Projets
adl-submit.py 15,2 ko
Newer Older
  • Learn to ignore specific revisions
  • Nicolas G's avatar
    Nicolas G a validé
    #!/usr/bin/env python3
    
    # -*- coding: utf-8 -*-
    
    # Copyright (C) 2005 Thomas Petazzoni <thomas.petazzoni@enix.org>
    #
    # This program is free software; you can redistribute it and/or modify
    # it under the terms of the GNU General Public License as published by
    # the Free Software Foundation; version 2 of the License.
    #
    # This program is distributed in the hope that it will be useful,
    # but WITHOUT ANY WARRANTY; without even the implied warranty of
    # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    # GNU General Public License for more details.
    #
    # You should have received a copy of the GNU General Public License
    # along with this program; if not, write to the Free Software
    # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    
    import xml.dom.minidom
    import getopt
    import sys
    import pycurl
    
    Nicolas G's avatar
    Nicolas G a validé
    import io
    
    import re
    import time
    import locale
    
    Nicolas G's avatar
    Nicolas G a validé
    from urllib.parse import urlencode
    
    Nicolas G's avatar
    Nicolas G a validé
    baseUrl = "https://www.agendadulibre.org/"
    
    #baseUrl = "http://localhost:3000/"
    
    
    locale.setlocale(locale.LC_ALL, ('fr_FR', 'utf-8'))
    
    eventFields = [ "title", "start-date", "end-date", "start-hour",
    
                    "end-hour", "description", "place_name", "address", "city",
    
                    "region", "url", "contact", "submitter", "tags" ]
    
    Nicolas G's avatar
    Nicolas G a validé
       'Alsace-Champagne-Ardenne-Lorraine'  : 1,
       'Aquitaine-Limousin-Poitou-Charentes': 2,
       'Auvergne-Rhône-Alpes'               : 3,
       'Normandie'                          : 4,
       'Bourgogne-Franche-Comté'            : 5,
       'Bretagne'                           : 6,
       'Centre-Val de Loire'                : 7,
       'Corse'                              : 9,
       'Île-de-France'                      : 12,
       'Languedoc-Roussillon-Midi-Pyrénées' : 13,
       'Hauts-de-France'                    : 17,
       'Provence-Alpes-Côte-d\'Azur'        : 21,
       'Pays de la Loire'                   : 18,
       'Guadeloupe'                         : 23,
       'Guyane'                             : 24,
       'Martinique'                         : 25,
       'La Réunion'                         : 26,
       'Autre pays'                         : 27,
       'Mayotte'                            : 28,
       'Collectivités d\'outre-mer'         : 29,
       'Collectivité sui generis'           : 30
    
    Nicolas G's avatar
    Nicolas G a validé
        print("""Soumettre un évènement dans l'Agenda du Libre
    
    
    Options:
        --file        event.xml   Fichier XML décrivant l'évènement.
        --test-output test.html   Fichier de sortie HTML de test
        --start-date  YYYY-MM-DD  Date de début de l'évènement.
        --end-date    YYYY-MM-DD  Date de fin de l'évènement.
        --start-hour  HH:MM       Heure de début de l'évènement.
        --end-hour    HH:MM       Heure de fin de l'évènement.
        --title       chaine      Titre de l'évènement.
        --description chaine-html Description de l'évènement.
    
        --place_name  chaine      Nom du lieu (pas utilisé par OSM)
    
        --address     chaine      Position de l'événement (utile pour une carte OSM).
    
        --city        chaine      Ville de l'évènement.
    
        --region      chaine      Région de l'évènement.
    
        --url         chaine      URL décrivant l'évènement.
        --contact     chaine      E-mail de contact.
        --tags        chaine      Liste des tags.
    
    Exemple de fichier XML:
        <?xml version="1.0" encoding="UTF-8"?>
        <event>
            <title>Permanence Logiciels Libres</title>
            <start-hour>18:00</start-hour>
            <end-hour>21:00</end-hour>
            <description><![CDATA[
    
             <p><a href="http://www.gulliver.eu.org">Gulliver</a> organise chaque
             semaine une permanence <i>Logiciels Libres</i> ouverte à tous,
             membre de l'association ou non.</p>
    
                <p>Durant cette permanence, vous pourrez trouver des réponses aux
                questions que vous vous posez au sujet du Logiciel Libre, ainsi que
                de l'aide pour résoudre vos problèmes d'installation, de
                configuration et d'utilisation de Logiciels Libres. N'hésitez pas
                à apporter votre ordinateur, afin que les autres participants
                puissent vous aider.</p>
    
                <p>Une connexion Internet est disponible sur place, ainsi que les
                mises à jour pour les distributions GNU/Linux les plus
                courantes.</p>
    
                <p>Cette permanence a lieu à la <a
                href=\"http://www.grand-cordel.com/\">MJC du Grand Cordel</a>, 18
                rue des Plantes à Rennes.</p>
    
            ]]></description>
    
            <place_name>Cabane du pêcheur</place_name>
    
            <address>Place de la république</address>
    
            <city>Rennes</city>
            <region>Bretagne</region>
            <url>http://www.gulliver.eu.org</url>
            <contact>contact@gulliver.eu.org</contact>
            <tags>gulliver permanence</tags>
        </event>
    
    Valeurs des champs:
        Le fichier XML peut contenir des champs dont le nom est semblable
        à celui des options, à savoir start-date, end-date,
    
        start-hour, end-hour, title, description, place_name, address, city,
        region, url et contact. Si un champ est défini à la fois dans le fichier
        XML et sur la ligne de commande, alors c'est la valeur donnée sur la
    
        ligne de commande qui l'emporte. Entre le fichier XML et la ligne de
        commande, tous les champs doivent être définis, sinon l'ajout
        de l'évènement sera refusé. Le seul champ qui peut être
        vide est end-date, auquel cas il sera positionné à la même
        valeur que start-date.
    
    Remplacements:
        Si la chaîne $month est trouvée dans la description, elle sera
        automatiquement remplacée par le nom du mois de la date de début
        de l'évènement.
        Si la chaîne $date est trouvée dans la description, elle sera
        automatiquement remplacée par la date de début de l'évènement
        (numéro du jour dans le mois puis nom du mois).
    
    Exemple d'utilisation:
        ./adl-submit.py --file event.xml --start-date 2005-12-10
    
        Ajoutera l'évènement décrit dans le fichier event.xml
        (donné ci-dessous) pour la date du 10 décembre 2005. Puisque
        le champ end-date n'est pas spécifié, alors il vaudra la
        même chose que start-date, c'est à dire le 10 décembre
        2005.
    
        Pour vérifier que le formatage est correct avant l'envoi,
        on pourra utiliser:
    
        ./adl-submit.py --file event.xml --start-date 2005-12-10
            --test-output test.html
    
        et regarder le fichier test.html avec un navigateur Web.
    
    Nicolas G's avatar
    Nicolas G a validé
    """)
    
        sys.exit (1)
    
    def HandleXmlFile(file, values):
        dom = xml.dom.minidom.parse(file)
        for node in dom.getElementsByTagName("event")[0].childNodes:
            if node.nodeType == node.ELEMENT_NODE:
                val = node.childNodes[0]
                for field in eventFields:
                    if node.nodeName == field:
                        values[field] = val.data
    
    def HandleParamValue(param, val, values):
        for field in eventFields:
            if param == "--" + field:
                values[field] = val
    
    def ParseOptions(options):
    
    Nicolas G's avatar
    Nicolas G a validé
        getoptOptions = [elt + "=" for elt in eventFields]
    
        getoptOptions.append ("file=")
        getoptOptions.append ("help")
        getoptOptions.append("test-output=")
        eventFieldValues = {}
    
        testOutputFile = ""
    
        try:
            opts, args = getopt.getopt(options, "", getoptOptions)
        except getopt.GetoptError:
    
    Nicolas G's avatar
    Nicolas G a validé
            print("Option inconnue.")
    
            Usage()
    
        if opts == []:
            Usage()
    
        for param, val in opts:
            if param == "--help":
                Usage()
    
            if param == "--file":
                HandleXmlFile(val, eventFieldValues)
    
            if param == "--test-output":
                testOutputFile = val
    
        for param, val in opts:
            HandleParamValue (param, val, eventFieldValues)
    
        return (eventFieldValues, testOutputFile)
    
    def getAuthToken(baseUrl):
    
    Nicolas G's avatar
    Nicolas G a validé
        contents = io.BytesIO()
    
        curl = pycurl.Curl()
    
    Nicolas G's avatar
    Nicolas G a validé
        try:
            curl.setopt(curl.URL, baseUrl)
            curl.setopt(curl.WRITEDATA, contents)
            curl.setopt(pycurl.COOKIEJAR, '/tmp/cookie.txt')
            curl.setopt(pycurl.COOKIEFILE, '/tmp/cookie.txt')
            curl.perform()
        finally:
            curl.close()
    
        contents = contents.getvalue().decode()
        m = re.findall(r'(<meta name="csrf-token" content="(.*?)" />)', contents)
    
        return m[0][1]
    
    
    def SubmitEvent(event, testOutputFile):
    
    Nicolas G's avatar
    Nicolas G a validé
        if "end-date" not in event and 'start-date' in event:
    
            event ["end-date"] = event ["start-date"]
    
    
    Nicolas G's avatar
    Nicolas G a validé
        if "submitter" not in event and 'contact' in event:
    
            event ['submitter'] = event['contact']
    
        for field in eventFields:
    
    Nicolas G's avatar
    Nicolas G a validé
            if field not in event:
                print(("Le champ '%s' n'est pas renseigné") % field)
    
                return
    
        if re.compile(r'^[^\<\>]*$').search (event['title']) is None:
    
    Nicolas G's avatar
    Nicolas G a validé
            print(("Problème de formatage dans le titre: '%s'. Les tags HTML ne sont pas autorisés.") % event['title'])
    
            return
    
        try:
            startDate = time.strptime(event['start-date'], "%Y-%m-%d")
        except ValueError:
    
    Nicolas G's avatar
    Nicolas G a validé
            print(("Problème de formatage dans la date de début: '%s'. Elle doit être de la forme AAAA-MM-JJ") % event['start-date'])
    
            return
    
        try:
            endDate = time.strptime(event['end-date'], "%Y-%m-%d")
        except ValueError:
    
    Nicolas G's avatar
    Nicolas G a validé
            print(("Problème de formatage dans la date de fin: '%s'. Elle doit être de la forme AAAA-MM-JJ") % event['end-date'])
    
            return
    
        try:
            startHour = time.strptime(event['start-hour'], "%H:%M")
        except ValueError:
    
    Nicolas G's avatar
    Nicolas G a validé
            print(("Problème de formatage dans l'heure de début: '%s'. Elle doit être de la forme: HH:MM") % event['start-hour'])
    
            return
    
        try:
            endHour = time.strptime(event['end-hour'], "%H:%M")
        except ValueError:
    
    Nicolas G's avatar
    Nicolas G a validé
            print(("Problème de formatage dans l'heure de fin: '%s'. Elle doit être de la forme HH:MM") % event['start-hour'])
    
            return
    
        for tag in event['tags'].split(' '):
            if len(tag) < 3:
    
    Nicolas G's avatar
    Nicolas G a validé
                print(("Le tag '%s' est trop petit, minimum de 3 caractères") % tag)
    
                return
    
        startDate = (startDate[0], startDate[1], startDate[2], startHour[3],
                     startHour[4], startDate[5], startDate[6], startDate[7], startDate[8])
        endDate   = (endDate[0],   endDate[1],   endDate[2],   endHour[3],
                     endHour[4],   endDate[5],   endDate[6],   endDate[7],   endDate[8])
    
        if time.mktime(startDate) <= time.time():
    
    Nicolas G's avatar
    Nicolas G a validé
            print("ERREUR: La date de début de l'évènement est dans le passé.")
    
            return
    
        if time.mktime(endDate) <= time.time():
    
    Nicolas G's avatar
    Nicolas G a validé
            print("ERREUR: La date de fin de l'évènement est dans le passé.")
    
            return
    
        if time.mktime(endDate) < time.mktime(startDate):
    
    Nicolas G's avatar
    Nicolas G a validé
            print("ERREUR: La date de fin de l'évènement est avant la date de début.")
    
            return
    
        if re.compile(r'^[^\<\>]*$').search (event['city']) is None:
    
    Nicolas G's avatar
    Nicolas G a validé
            print(("ERREUR: Problème de formatage dans le nom de la ville: '%s'. Les tags HTML sont interdits.") % event['city'])
    
    Nicolas G's avatar
    Nicolas G a validé
        if (event['region'] in regions) is False:
            print(("ERREUR: La région '%s' n'existe pas.") % event['region'])
            print("Les régions existantes sont:")
    
            for name in regions:
    
    Nicolas G's avatar
    Nicolas G a validé
                print((" - ") + name)
    
            return
    
        if re.compile(r'^http://.*$').search (event['url']) is None and re.compile(r'^https://.*$').search (event['url']) is None:
    
    Nicolas G's avatar
    Nicolas G a validé
            print(("ERREUR: Problème de formatage dans l'URL: '%s'. Elle doit commencer par http:// ou https://.") % event['url'])
    
            return
    
        if re.compile(r'^([A-Za-z0-9_\.\-]*)@([A-Za-z0-9_\-]*)\.([A-Za-z0-9_\.\-]*)$').search (event['contact']) is None:
    
    Nicolas G's avatar
    Nicolas G a validé
            print(("ERREUR: Problème de formatage dans l'adresse e-mail.") % event ['contact'])
    
            return
    
        if re.compile(r'^([A-Za-z0-9_\.\-]*)@([A-Za-z0-9_\-]*)\.([A-Za-z0-9_\.\-]*)$').search (event['submitter']) is None:
    
    Nicolas G's avatar
    Nicolas G a validé
            print(("ERREUR: Problème de formatage dans l'adresse e-mail.") % event ['submitter'])
    
    Nicolas G's avatar
    Nicolas G a validé
        monthstr = time.strftime("%B", startDate)
        datestr  = time.strftime("%d %B", startDate)
    
    
        event['description'] = event['description'].replace("$month", monthstr)
        event['description'] = event['description'].replace("$date", datestr)
    
        curl = pycurl.Curl()
    
    Nicolas G's avatar
    Nicolas G a validé
        try:
            contents = io.BytesIO()
            curl.setopt(curl.WRITEFUNCTION, contents.write)
    
    Nicolas G's avatar
    Nicolas G a validé
            if testOutputFile:
    
                curl.setopt (curl.URL, baseUrl + 'events/preview?format=html')
    
    Nicolas G's avatar
    Nicolas G a validé
            else:
                curl.setopt (curl.URL, baseUrl + 'events')
    
            fields = {
                'authenticity_token':    str(getAuthToken(baseUrl+'events/new')),
                'event[title]':          event['title'],
                'event[start_time(3i)]': str(startDate[2]),
                'event[start_time(2i)]': str(startDate[1]),
                'event[start_time(1i)]': str(startDate[0]),
                'event[start_time(4i)]': str(startDate[3]),
                'event[start_time(5i)]': str(startDate[4]),
                'event[end_time(3i)]':   str(endDate[2]),
                'event[end_time(2i)]':   str(endDate[1]),
                'event[end_time(1i)]':   str(endDate[0]),
                'event[end_time(4i)]':   str(endHour[3]),
                'event[end_time(5i)]':   str(endHour[4]),
                'event[description]':    event['description'],
                'event[place_name]':     event['place_name'],
                'event[address]':        event['address'],
                'event[city]':           event['city'],
                'event[region_id]':      str(regions[event['region']]),
                'event[locality]':       str(0),
                'event[url]':            event['url'],
                'event[contact]':        event['contact'],
                'event[submitter]':      event['submitter'],
                'event[tag_list]':       event['tags']
            }
            fields = urlencode(fields)
    
            curl.setopt(curl.POSTFIELDS,  fields)
            curl.setopt(pycurl.COOKIEJAR, '/tmp/cookie.txt')
            curl.setopt(pycurl.COOKIEFILE, '/tmp/cookie.txt')
            curl.perform()
    
            if testOutputFile:
                if curl.getinfo(curl.HTTP_CODE) != 200:
                    print("Erreur lors de la récupération de la sortie HTML")
                    sys.exit(0)
                fp = open(testOutputFile, "w")
                s = contents.getvalue().decode()
    
                s = re.sub(r'<head.*>', r'\g<0><base href="%s">' % baseUrl, s, flags=re.IGNORECASE)
    
    Nicolas G's avatar
    Nicolas G a validé
                fp.write(s)
                fp.close()
    
    Nicolas G's avatar
    Nicolas G a validé
                if curl.getinfo(curl.HTTP_CODE) != 302:
                    print("Erreur lors de la soumission de l'évènement")
                    sys.exit(0)
                else:
                    print("Évènement soumis avec succès. Il sera prochainement validé par un modérateur.")
        finally:
            curl.close()
    
    def ensure_latest_version_is_used():
        # Check that we are running the latest version of the adl-submit
        # script
        contents = io.BytesIO()
        curl = pycurl.Curl()
        try:
            curl.setopt(curl.WRITEDATA, contents)
            curl.setopt (curl.URL, baseUrl + './adl-submit-latest-version')
            curl.perform()
            if curl.getinfo(curl.HTTP_CODE) == 200:
                contents = contents.getvalue().decode()
    
    echarp's avatar
    echarp a validé
                if float(contents) != float('3.6'):
    
                    print("Votre script n'est plus à jour, merci de télécharger la nouvelle version à l'adresse")
                    print(("%sadl-submit.py") % baseUrl)
                    sys.exit(1)
        finally:
            curl.close()
    
    
    
    if __name__ == "__main__":
        if (len(sys.argv) <= 1) or sys.argv[1] == "--help":
            Usage()
    
        (event, testOutputFile) = ParseOptions(sys.argv[1:])
    
        ensure_latest_version_is_used()
    
        SubmitEvent(event, testOutputFile)
    
    Nicolas G's avatar
    Nicolas G a validé