Files
Laurent Chedanne dadb1d4f23 OVea version
2026-06-26 10:39:30 +02:00

217 lines
6.7 KiB
Python

# -*- 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'])