From e7f2ccdd870f998b9199b85bf2c486f8d1d0cceb Mon Sep 17 00:00:00 2001
From: Alban Gruin
Date: Sat, 25 Aug 2018 22:54:33 +0200
Subject: management: création d’un sous-module parser

Signed-off-by: Alban Gruin <alban@pa1ch.fr>
---
 management/commands/_private.py   | 164 --------------------------------------
 management/commands/timetables.py |   4 +-
 2 files changed, 2 insertions(+), 166 deletions(-)
 delete mode 100644 management/commands/_private.py

(limited to 'management/commands')

diff --git a/management/commands/_private.py b/management/commands/_private.py
deleted file mode 100644
index 94c1918..0000000
--- a/management/commands/_private.py
+++ /dev/null
@@ -1,164 +0,0 @@
-#    Copyright (C) 2017-2018  Alban Gruin
-#
-#    celcatsanitizer is free software: you can redistribute it and/or modify
-#    it under the terms of the GNU Affero General Public License as published
-#    by the Free Software Foundation, either version 3 of the License, or
-#    (at your option) any later version.
-#
-#    celcatsanitizer 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 Affero General Public License for more details.
-#
-#    You should have received a copy of the GNU Affero General Public License
-#    along with celcatsanitizer.  If not, see <http://www.gnu.org/licenses/>.
-
-import datetime
-import re
-
-from bs4 import BeautifulSoup
-from django.utils import timezone
-
-from ...models import Course, Group, Room
-from ...utils import get_week
-
-import requests
-import edt
-
-
-def add_time(date, time):
-    ptime = datetime.datetime.strptime(time, "%H:%M")
-    delta = datetime.timedelta(hours=ptime.hour, minutes=ptime.minute)
-    return date + delta
-
-
-def delete_courses_in_week(source, year, week, today):
-    start, end = get_week(year, week)
-    Course.objects.filter(begin__gte=max(start, today), begin__lt=end,
-                          source=source).delete()
-
-
-def get_event(source, event, event_week, today):
-    """Renvoie une classe Course à partir d’un événement récupéré par BS4"""
-    # On récupère la date de l’évènement à partir de la semaine
-    # et de la semaine référencée, puis l’heure de début et de fin
-    date = event_week + datetime.timedelta(int(event.day.text))
-    begin = add_time(date, event.starttime.text)
-    end = add_time(date, event.endtime.text)
-
-    # On ne traite pas le cours si il commence après le moment du traitement
-    if today is not None and begin < today:
-        return
-
-    # Création de l’objet cours
-    course = Course.objects.create(source=source, begin=begin, end=end)
-
-    # On récupère les groupes concernés par les cours
-    groups = [Group.objects.get_or_create(source=source,
-                                          celcat_name=item.text)[0]
-              for item in event.resources.group.find_all("item")]
-    course.groups.add(*groups)
-
-    # On récupère le champ « remarque »
-    if event.notes is not None:
-        course.notes = "\n".join(event.notes.find_all(text=True))
-
-    # On récupère le champ « nom »
-    if event.resources.module is not None:
-        course.name = event.resources.module.item.text
-    elif event.category is not None:
-        # Il est possible qu’un cours n’ait pas de nom. Oui oui.
-        # Qui sont les concepteurs de ce système ? Quels sont leurs
-        # réseaux ?
-        # Bref, dans ce cas, si le cours a un type, il devient son nom.
-        course.type = event.category.text
-        # Si il n’a pas de type (mais je ne pense pas que ça soit possible…),
-        # il obtiendra une valeur par défaut définie à l’avance.
-
-    # Récupération du type de cours
-    if event.category is not None:
-        course.type = event.category.text
-
-    # Si un cours a une salle attribuée (oui, il est possible qu’il n’y
-    # en ait pas… qui sont ils, leurs réseaux, tout ça…), on les insère
-    # dans la base de données, et on les ajoute dans l’objet cours
-    if event.resources.room is not None:
-        rooms = [Room.objects.get_or_create(name=item.text)[0]
-                 for item in event.resources.room.find_all("item")]
-        course.rooms.add(*rooms)
-
-    return course
-
-
-def get_events(source, soup, weeks_in_soup, today, year=None, week=None):
-    """Récupère tous les cours disponibles dans l’emploi du temps Celcat.
-    Le traîtement se limitera à la semaine indiquée si il y en a une."""
-    for event in soup.find_all("event"):
-        event_week = weeks_in_soup[event.rawweeks.text]
-        event_week_num = event_week.isocalendar()[1]  # Numéro de semaine
-
-        # On passe le traitement si la semaine de l’événement ne correspond pas
-        # à la semaine passée, ou qu’il ne contient pas de groupe ou n’a pas de
-        # date de début ou de fin.
-        if (event_week_num == week and event_week.year == year or
-            year is None or week is None) and \
-           event.resources.group is not None and \
-           event.starttime is not None and event.endtime is not None:
-            course = get_event(source, event, event_week, today)
-
-            # On renvoie le cours si il n’est pas nul
-            if course is not None:
-                yield course
-
-
-def get_update_date(soup):
-    # Explication de la regex
-    #
-    # (\d+)/(\d+)/(\d+)\s+(\d+):(\d+):(\d+)
-    # (\d+)                                 au moins un nombre
-    #      /                                un slash
-    #       (\d+)                           au moins un nombre
-    #            /                          un slash
-    #             (\d+)                     au moins un nombre
-    #                  \s+                  au moins un espace
-    #                     (\d+)             au moins un nombre
-    #                          :            un deux-points
-    #                           (\d+)       au moins un nombre
-    #                                :      un deux-points
-    #                                 (\d+) au moins un nombre
-    datetime_regex = re.compile(r"(\d+)/(\d+)/(\d+)\s+(\d+):(\d+):(\d+)")
-    search = datetime_regex.search(soup.footer.text)
-    if search is None:
-        return None
-
-    day, month, year, hour, minute, second = [int(v) for v in search.groups()]
-    date = datetime.datetime(year, month, day, hour, minute, second)
-    return timezone.make_aware(date)
-
-
-def get_weeks(soup):
-    # Les semaines sont référencées de manière assez… exotique
-    # En gros, il y a une liste d’éléments span qui contiennent une sorte d’ID
-    # de la semaine, formaté de la manière suivante :
-    # NNNNNNNNNNNNNNNNNNNYNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN
-    # Tous sont de la même longueur, contiennent 51 N et un seul Y.
-    # Allez savoir pourquoi. Il se trouve dans la balise « alleventweeks ».
-    # Un paramètre du span (« date ») représente la date de début.
-    # Un cours contient donc un ID de semaine, puis le nombre de jours après le
-    # début de cette semaine.
-    weeks = {}
-    for span in soup.find_all("span"):  # Liste de toutes les semaines définies
-        # On parse la date et on la fait correspondre à l’ID
-        weeks[span.alleventweeks.text] = timezone.make_aware(
-            datetime.datetime.strptime(span["date"], "%d/%m/%Y"))
-
-    return weeks
-
-
-def get_xml(url):
-    user_agent = "celcatsanitizer/" + edt.VERSION
-    req = requests.get(url, headers={"User-Agent": user_agent})
-    req.encoding = "utf8"
-
-    soup = BeautifulSoup(req.content, "html.parser")
-    return soup
diff --git a/management/commands/timetables.py b/management/commands/timetables.py
index f92ad4e..8fc8ed6 100644
--- a/management/commands/timetables.py
+++ b/management/commands/timetables.py
@@ -23,8 +23,8 @@ from django.db.models import Min
 from ...models import Course, Source
 from ...utils import get_week, tz_now
 
-from ._private import delete_courses_in_week, get_events, get_update_date, \
-    get_weeks, get_xml
+from ..parsers.ups2017 import delete_courses_in_week, get_events, \
+    get_update_date, get_weeks, get_xml
 
 
 @transaction.atomic
-- 
cgit v1.2.1