# -*- coding: utf-8 -*- """ Script permettant de générer des fichiers CSV à partir d'agenda Nextcloud contenant un récapitulatif des heures de travail effectué Pour Crealead @author: Jordan @date: 17/11/2022 @TODO: Améliorer la structure du code, pour l'instant c'est un peu moche @TODO: Ajouter des paramètres pour le mois, et le verbose pour avoir des logs @TOOD: README sur l'utilisation du script """ import sys import caldav import json import csv import calendar from datetime import date, datetime, timezone import pytz from urllib.parse import unquote import re class EventRow(): def __init__(self): self.title = '' self.start_date = '' self.start_time = '' self.end_date = '' self.end_time = '' self.categories = '' self.location = '' self.description = '' def set_title(self, title): self.title = title def set_start(self, start): self.start_date = start.date() self.start_time = start.time() def set_end(self, end): self.end_date = end.date() self.end_time = end.time() def set_categories(self, categories): self.categories = categories def set_location(self, location): self.location = location def set_description(self, description): self.description = description def get_csv_row(self): """Return CSV row""" return [self.title,self.start_date,self.start_time,self.end_date,self.end_time,self.categories,self.location,self.description] def load_json_config(filename): """Return the config from JSON file""" with open(filename, 'r') as f: config_json = json.load(f) return config_json def init_connection(config): """Return all the calendars from the server""" dav_client = caldav.DAVClient(url=config['caldav_url'], username=config['username'], password=config['password']) try: dav_connection = dav_client.principal() except Exception as e: print("Une erreur a eu lieu pendant la connexion : ", e) sys.exit() calendars_server = dav_connection.calendars() if not calendars_server: print("Aucun calendrier disponible sur le compte !") sys.exit() return calendars_server def link_calendars(config, calendars_server): """Do the link between calendar we search (from the config) and calendar available on the remote server""" calendars_find = [] for calendar_config in config['calendars']: cpt = 0 for calendar_server in calendars_server: if calendar_config['calendar_name'] in calendar_server.name: cpt += 1 calendar_find = calendar_config calendar_find['calendar_server'] = calendar_server calendars_find.append(calendar_find) if not cpt: print("Le calendrier nommé " + calendar_config['calendar_name'] + " pour le fichier " + calendar_config['csv_name'] + " n'est pas trouvé sur le serveur !") sys.exit() elif cpt >= 2: print("Il y a plusieurs calendriers sur le serveur correspondant au calendrier nommé " + calendar_config['calendar_name'] + " pour le fichier " + calendar_config['csv_name'] + " !") print(calendars_find) sys.exit() return calendars_find def split_data(data): """Return string splitted by ';' separator""" return data.split(':') def datetime_format(date_str): """Return date formated""" try: # Nextcloud Format date_formated = datetime.strptime(date_str, '%Y%m%dT%H%M%SZ') except: # Zimbra format date_formated = datetime.strptime(date_str, '%Y%m%d') paris_tz = pytz.timezone('Europe/Paris') date_formated = date_formated.replace(tzinfo=timezone.utc).astimezone(paris_tz) #date_formated = date_formated.astimezone(paris_tz) return date_formated def get_all_events(config, calendars_find): """Return row with the specific information we need to write it in CSV""" calendars_to_write = [] for calendar_find in calendars_find: print(calendar_find['csv_name'], "...") events_fetched = calendar_find['calendar_server'].date_search(start=datetime(config['year_start'], config['month_start'], config['day_start']), end=datetime(config['year_end'], config['month_end'], config['day_end']), expand=True) event_rows = [] for event in events_fetched: event_fields = list(map(split_data, event.data.splitlines())) # Fix problem #2023022110004936 has = False for sub in event_fields: if 'SUMMARY' in sub: has = True break if not has: continue event_row = EventRow() for field in ['SUMMARY', 'DTSTART', 'DTEND', 'CATEGORIES', 'LOCATION', 'DESCRIPTION']: for event_field in event_fields: if field in event_field[0]: if field == "SUMMARY": event_row.set_title(event_field[1]) elif field == "DTSTART": date = datetime_format(event_field[1]) event_row.set_start(date) elif field == "DTEND": date = datetime_format(event_field[1]) event_row.set_end(date) elif field == "CATEGORIES": event_row.set_categories(event_field[1]) elif field == "LOCATION": event_row.set_location(event_field[1]) elif field == "DESCRIPTION": event_row.set_description(clean_description(event_field[1])) event_rows.append(event_row) sorted_rows = sorted(event_rows, key=lambda l: datetime.strptime(str(l.start_date)+'T'+str(l.start_time), "%Y-%m-%dT%H:%M:%S")) calendars_to_write.append({'csv_name' : calendar_find['csv_name'], 'line' : sorted_rows}) return calendars_to_write def clean_description(value): if value.startswith("text/html,"): value = value[len("text/html,"):] value = unquote(value) value = re.sub(r'<[^>]+>', '', value) return value.strip().strip('"') def write_csv(filename, rows): """Write CSV file""" with open(filename, 'w') as file: writer = csv.writer(file, delimiter=';') csv_title= ['Titre', 'Date debut', 'Heure debut', 'Date fin', 'Heure fin', 'Categories', 'Emplacement', 'Description'] writer.writerow(csv_title) for row in rows: writer.writerow(row.get_csv_row()) if __name__ == "__main__": # Load config config = load_json_config("./config.json") # Init connection with server calendars_server = init_connection(config) # Link calendars in config with calendar from server calendars_find = link_calendars(config, calendars_server) # Get all events calendars_to_write = get_all_events(config, calendars_find) # Write CSV for calendar_to_write in calendars_to_write: write_csv(calendar_to_write['csv_name'], calendar_to_write['line'])