diff options
Diffstat (limited to 'management/commands/_private.py')
-rw-r--r-- | management/commands/_private.py | 150 |
1 files changed, 83 insertions, 67 deletions
diff --git a/management/commands/_private.py b/management/commands/_private.py index c140f51..8f195a1 100644 --- a/management/commands/_private.py +++ b/management/commands/_private.py @@ -13,33 +13,20 @@ # 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 edt.models import Group, Room, Course from edt.utils import get_week -import datetime -import re - import requests - -class Week: - def __init__(self, number, start): - self.number = number - self.start = timezone.make_aware( - datetime.datetime.strptime(start, "%d/%m/%Y")) - - def get_day(self, id): - return self.start + datetime.timedelta(id) - - @property - def year(self): - return self.start.year - def add_time(date, time): - delta = datetime.timedelta(hours=time.hour, minutes=time.minute) + ptime = datetime.datetime.strptime(time, "%H:%M") + delta = datetime.timedelta(hours=ptime.hour, minutes=ptime.minute) return date + delta def consolidate_group(group): @@ -75,7 +62,7 @@ def consolidate_group(group): def consolidate_groups(groups): for group in groups: - if group.parent == None: + if group.parent is None: consolidate_group(group) def delete_courses_in_week(timetable, year, week): @@ -93,50 +80,69 @@ def get_from_db_or_create(cls, **kwargs): return obj -def get_events(timetable, year, week, soup, weeks_in_soup): +def get_event(timetable, event, event_week): + """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) + + # Création de l’objet cours + course = Course.objects.create(timetable=timetable, begin=begin, end=end) + + # On récupère les groupes concernés par les cours, on les + # « consolide », puis on les insère dans l’objet cours. + groups = [get_from_db_or_create(Group, timetable=timetable, + celcat_name=item.text) + for item in event.resources.group.find_all("item")] + consolidate_groups(groups) + course.groups.add(*groups) + + # On récupère le champ « remarque » + if event.notes is not None: + course.notes = event.notes.text + + # On récupère le nom du cours + if event.resources.module is not None: + course.name = event.resources.module.item.text + else: + # 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, on déplace le champ « remarque » de + # l’objet dans le champ « nom ». + course.name, course.notes = course.notes, None + + # 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 = [get_from_db_or_create(Room, name=item.text) + for item in event.resources.room.find_all("item")] + course.rooms.add(*rooms) + + return course + +def get_events(timetable, soup, weeks_in_soup, 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"): - title = None - type_ = None - groups = None - rooms = None - notes = None - - if weeks_in_soup[event.rawweeks.text].number == week and \ - weeks_in_soup[event.rawweeks.text].year == year and \ + 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: - date = weeks_in_soup[event.rawweeks.text].get_day(int( - event.day.text)) - - begin = add_time(date, datetime.datetime.strptime( - event.starttime.text, "%H:%M")) - end = add_time(date, datetime.datetime.strptime( - event.endtime.text, "%H:%M")) - - groups = [get_from_db_or_create(Group, timetable=timetable, - celcat_name=item.text) - for item in event.resources.group.find_all("item")] - consolidate_groups(groups) - - if event.notes is not None: - notes = event.notes.text - - if event.resources.module is not None: - title = event.resources.module.item.text - elif notes is not None: - title = notes - notes = None - else: - title = "Aucune information" - - if event.category is not None: - type_ = event.category.text - - if event.resources.room is not None: - rooms = [get_from_db_or_create(Room, name=item.text) - for item in event.resources.room.find_all("item")] - - yield title, type_, groups, rooms, notes, begin, end + yield get_event(timetable, event, event_week) def get_update_date(soup): # Explication de la regex @@ -153,7 +159,7 @@ def get_update_date(soup): # (\d+) au moins un nombre # : un deux-points # (\d+) au moins un nombre - datetime_regex = re.compile("(\d+)/(\d+)/(\d+)\s+(\d+):(\d+):(\d+)") + 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 @@ -163,16 +169,26 @@ def get_update_date(soup): 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"): - weeks[span.alleventweeks.text] = Week(int(span.title.text), - span["date"]) + 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): - r = requests.get(url) - r.encoding = "utf8" + req = requests.get(url) + req.encoding = "utf8" - soup = BeautifulSoup(r.content, "html.parser") + soup = BeautifulSoup(req.content, "html.parser") return soup |