import pandas import datetime import icalendar import uuid from pathlib import Path #pd_eleves = pandas.read_csv('Resources/eleves-v2.csv', delimiter=";", header=None) #np_eleves = pd_eleves.to_numpy() pd_colles = pandas.read_csv('static/colloscope-v3.csv', delimiter=";", header=None) np_colles = pd_colles.to_numpy() local_tz = "Europe/Paris" jour_to_delta = { "lundi": 0, "mardi": 1, "mercredi": 2, "jeudi": 3, "vendredi": 4, } jour_to_byday = { "lundi": "MO", "mardi": "TU", "mercredi": "WE", "jeudi": "TH", "vendredi": "FR", } langues = ["Anglais", "Allemand", "Espagnol"] option_langues = { "EN": ["Anglais LV1"], "EN-DE": ["Anglais LV1", "Allemand LV2"], "EN-ES": ["Anglais LV1", "Espagnol LV2"], "DE-EN": ["Allemand LV1", "Anglais LV2"], "ES-EN": ["Espangol LV1", "Anglais LV2"] } def display(cal): return cal.to_ical().decode("utf-8").replace('\r\n', '\n').strip() def open_ics(filename: str) -> icalendar.Calendar: """ Ouvre un fichier .ics et renvoie un objet Calendar """ p = Path(__file__).with_name('Resources') p = Path(p, filename) with p.open('r') as f: opened_cal = icalendar.Calendar.from_ical(f.read()) return opened_cal def add_events_from_ics(filename: str, new_cal: icalendar.Calendar): """ Ajoute des évènements depuis un fichier .ics vers un objet icalendar.Calendar """ cal_to_import = open_ics(filename=filename) for event in cal_to_import.walk("vevent"): new_cal.add_component(event) return new_cal def get_td_groupe(groupe: str) -> str: """ Renvoie le groupe de TD d'un groupe de colle (A, B ou SI) """ for row in np_eleves[1:]: if row[2] == groupe: return row[3] def debut_semaine_to_datetime(date_string: str) -> datetime.time: """ Transforme un string de debut de semaine au format dd/mm/yyyy en datetime.time """ return datetime.datetime.strptime(date_string, "%d/%m/%y") def heure_to_timedelta(heure: str) -> datetime.timedelta: """ Transforme une heure au format "8h30" ou "1h30" ou "2h" en timedelta. """ heure = heure.split("h") delta = datetime.timedelta(hours=int(heure[0])) if heure[1] != '': delta += datetime.timedelta(minutes=int(heure[1])) return delta def creer_evenement(titre: str, debut: datetime.datetime, duree: datetime.timedelta, description: str = None, localisation: str = None, rrule: str = None) -> icalendar.Event: """ Renvoie un évènement icalendar.Event() titre str: le titre de l'évènement debut datetime.datetime: la date de début duree datetime.timedelta: la durée description str: la description (Optionelle) localisation str: localisation (Optionelle) rrule str: règle de récurrence https://icalendar.org/iCalendar-RFC-5545/3-8-5-3-recurrence-rule.html (Optionelle) """ new_event = icalendar.Event() new_event.add("summary", titre) new_event.add("dtstart", debut, parameters={"tzid": local_tz}) debut += duree new_event.add("dtend", debut, parameters={"tzid": local_tz}) if localisation is not None: new_event.add("location", localisation) if description is not None: new_event.add("description", description) if rrule is not None: new_event.add("rrule", rrule) new_event.add("uid", str(uuid.uuid4())) new_event.add("dtstamp", datetime.datetime.now()) # nécessaire pour se conformer à la norme RFC 5545 return new_event def get_colles_groupe(np_colles, groupe, option_langue): """ Renvoie les colles pour un groupe sous la forme d'une liste d'évènements ICS np_colles: l'array 2D numpy contenant le colloscope groupe: str, le numéro du groupe option_langue: str, les options de langue """ liste_colles = [] for row in np_colles[4:40]: if pandas.isnull(row[1]) or row[1] == "pas de colle": # il n'y a pas de colle, on skip ! continue # Si la colle n'est pas la LV1 de l'élève, on skip if row[1] == "Anglais" and not option_langue.startswith("EN"): continue elif row[1] == "Allemand" and not option_langue.startswith("DE"): continue elif row[1] == "Espagnol" and not option_langue.startswith("ES"): continue for index, colle in enumerate(row[8:]): if pandas.isnull(colle): continue # les huit premières cases ne sont pas concernées if "," in list(colle): if groupe not in colle.split(","): continue elif colle != groupe: continue # dans le cas où plusieurs groupes de colles soient concernés duree = heure_to_timedelta(row[4]) # transforme la durée en timedelta date = debut_semaine_to_datetime(np_colles[1][index+8]) date += datetime.timedelta(days=jour_to_delta[row[2]]) date += heure_to_timedelta(row[3]) # génère la date à partir de la semaine, du jour et de l'heure localisation = row[7] if not pandas.isnull(row[7]) else None new_colle = creer_evenement( titre=f"Colle {row[1]}", description=f"{row[5]}" if not pandas.isnull(row[5]) else None, localisation=localisation, debut=date, duree=duree ) liste_colles.append(new_colle) return liste_colles def get_cours(np_colles, groupe): """ Renvoie les colles pour un groupe sous la forme d'une liste d'évènements ICS, cad icalendar.Event np_colles: l'array 2D numpy contenant le colloscope groupe: str, le numéro du groupe """ listes_cours = [] groupe_td = get_td_groupe(groupe) for row in np_colles[41:66]: if pandas.isnull(row[1]): continue first_date = np_colles[1][8] eleves = row[8] if "à" in list(eleves): eleves = eleves.split(" ") debut, fin = [grp for grp in eleves if grp.isdigit()] if not int(debut) <= int(groupe) <= int(fin): continue elif "+" in list(eleves): eleves = eleves.split("+") if groupe not in [grp for grp in eleves if grp.isdigit()]: if groupe_td not in [grp for grp in eleves]: if int(row[6]) > 1: if groupe in [grp for grp in row[9].split("+") if grp.isdigit()]: first_date = np_colles[1][9] elif groupe_td in [grp for grp in row[9].split("+")]: first_date = np_colles[1][9] else: continue else: continue # si la 1ère occurence du cours est la 1ère ou la 2e semaine elif groupe != eleves and groupe_td != eleves: if pandas.isnull(row[9]) or (groupe != row[9] and groupe_td != row[9]): continue else: first_date = np_colles[1][9] # vérification que le groupe est concerné par le cours first_date = debut_semaine_to_datetime(first_date) first_date += heure_to_timedelta(row[3]) first_date += datetime.timedelta(days=jour_to_delta[row[2]]) duree = heure_to_timedelta(row[4]) localisation = row[7] if not pandas.isnull(row[7]) else None last_date = debut_semaine_to_datetime(np_colles[1][-1]) + datetime.timedelta(days=5) rrule = {"FREQ": "WEEKLY", "UNTIL": last_date, "INTERVAL": row[6], "BYDAY": jour_to_byday[row[2]], "WKST": "MO"} new_cours = creer_evenement( titre=row[1], description=row[5], localisation=localisation, debut=first_date, duree=duree, rrule=rrule ) listes_cours.append(new_cours) return listes_cours def get_langues(langues): """ Renvoie la liste de cours de langue associés sous forme de list[icalendar.Event] """ liste_cours = [] langues = option_langues[langues] for row in np_colles[67:]: if row[1] in langues: debut = debut_semaine_to_datetime(np_colles[1][8]) debut += heure_to_timedelta(row[3]) debut += datetime.timedelta(days=jour_to_delta[row[2]]) duree = heure_to_timedelta(row[4]) last_date = debut_semaine_to_datetime(np_colles[1][-1]) + datetime.timedelta(days=5) rrule = {"FREQ": "WEEKLY", "UNTIL": last_date, "INTERVAL": row[6], "BYDAY": jour_to_byday[row[2]], "WKST": "MO"} new_cours = creer_evenement( titre=row[1], debut=debut, duree=duree, rrule=rrule ) liste_cours.append(new_cours) return liste_cours def get_calendar(groupe: str, langues: str): """" Renvoie un calendrier (icalendar.Calendar) pour un groupe avec des langues donné """ new_cal_edt = open_ics("Base_Calendar.ics") # if split: # new_cal_colles = open_ics("Base_Calendar.ics") for event in get_langues(langues): new_cal_edt.add_component(event) for event in get_cours(np_colles, groupe): new_cal_edt.add_component(event) # if split: # for event in get_colles_groupe(np_colles, groupe, langues): # new_cal_colles.add_component(event) # return new_cal_edt.to_ical(), new_cal_colles.to_ical() # else: # for event in get_colles_groupe(np_colles, groupe, langues): # new_cal_edt.add_component(event) return new_cal_edt.to_ical()