Refactoring of caldav_recurring_task
This commit is contained in:
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
.idea
|
||||||
|
configuration.yml
|
||||||
|
tasks.yml
|
||||||
45
README.md
45
README.md
@@ -1,29 +1,30 @@
|
|||||||
# Dorfsvald PIM
|
# CalDav Recurring Tasks Generator
|
||||||
|
|
||||||
## Téléphone:
|
This script is made to be ran once a day. It will create CalDav tasks as described in file tasks.yml. The tasks are created 2 days ahead of their tasks
|
||||||
|
|
||||||
### Syncevolution
|
## Configuration
|
||||||
|
|
||||||
'''
|
Update configuration.yml file with caldav server url, calendar name, username and password
|
||||||
sudo -u radicale /usr/bin/syncevo-http-server http://127.0.0.1:9000/syncml --start-dbus-session --debug
|
|
||||||
'''
|
|
||||||
|
|
||||||
### Filtres
|
Describe recurring tasks in tasks.yml file
|
||||||
|
|
||||||
Event:
|
task_name:
|
||||||
- Enlever event passés d'un mois
|
start:
|
||||||
|
freq: MONTHLY
|
||||||
|
bymonthday: -5
|
||||||
|
duration:
|
||||||
|
days: 5
|
||||||
|
title: A title for the task
|
||||||
|
body: A body for the task
|
||||||
|
priority: 1
|
||||||
|
|
||||||
|
See https://dateutil.readthedocs.io/en/stable/rrule.html for more information on the start section syntax
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
./main.py
|
||||||
|
|
||||||
|
## TODO
|
||||||
|
|
||||||
Task:
|
Task:
|
||||||
- Enlever tasks terminée
|
- Remove completed tasks on new occurence creation
|
||||||
- ?
|
- Allow the script to run several times on the same day
|
||||||
|
|
||||||
Journal:
|
|
||||||
- Ajouter une ligne 5871-DTSTART;VALUE=DATE:20161205
|
|
||||||
|
|
||||||
Contacts:
|
|
||||||
- Enelver contacts sans tel / email
|
|
||||||
- Simplifier les contacts
|
|
||||||
|
|
||||||
|
|
||||||
### Requirements
|
|
||||||
caldav pyyaml ipdb icalendar
|
|
||||||
|
|||||||
5
configuration.example.yml
Normal file
5
configuration.example.yml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
dav_server:
|
||||||
|
url: https://caldav.server.url/
|
||||||
|
user: username_example
|
||||||
|
pass: password_example
|
||||||
|
calendar: calendar_name
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
dav_server:
|
|
||||||
url: http://127.0.0.1:5232/webdav/
|
|
||||||
user: ggentile
|
|
||||||
pass: ID0T'8h6
|
|
||||||
@@ -1,90 +0,0 @@
|
|||||||
#! /home/ggentile/perso/projects/python/Envs/dorfsvald-pim/bin/python3
|
|
||||||
|
|
||||||
import os
|
|
||||||
import datetime
|
|
||||||
import calendar
|
|
||||||
|
|
||||||
from yaml import load
|
|
||||||
from icalendar.parser import Contentlines, Contentline
|
|
||||||
from dateutil.rrule import rrule, FREQNAMES
|
|
||||||
import caldav
|
|
||||||
|
|
||||||
|
|
||||||
class Task:
|
|
||||||
def __init__(self, ref_date, name, data):
|
|
||||||
self.ref_date = ref_date
|
|
||||||
self.name = name
|
|
||||||
data['start']['freq'] = FREQNAMES.index(data['start']['freq'])
|
|
||||||
self.date_begin = rrule(count=1, dtstart=ref_date, **data['start'])[0]
|
|
||||||
self.date_end = self.date_begin + datetime.timedelta(microseconds=-1, **data['duration'])
|
|
||||||
|
|
||||||
self.title = data['title']
|
|
||||||
self.body = data['body']
|
|
||||||
self.priority = data['priority']
|
|
||||||
|
|
||||||
def to_ical(self):
|
|
||||||
current_dtstamp = datetime.datetime.now().strftime('%Y%m%dT%H%M%SZ')
|
|
||||||
uid = "DV-Taskmanager-{}-{}".format(self.name, self.date_end.strftime('%Y%m%d'))
|
|
||||||
c = Contentlines([
|
|
||||||
Contentline('BEGIN:VCALENDAR'),
|
|
||||||
Contentline('VERSION:2.0'),
|
|
||||||
Contentline('PRODID:-//DorfsvaldNet//Pim Atutoask Client//FR'),
|
|
||||||
Contentline('BEGIN:VTODO'),
|
|
||||||
Contentline('DTSTAMP:%s' % current_dtstamp),
|
|
||||||
Contentline('UID:%s' % uid),
|
|
||||||
Contentline('CREATED:%s' % current_dtstamp),
|
|
||||||
Contentline('LAST-MODIFIED:%s' % current_dtstamp),
|
|
||||||
Contentline('SEQUENCE:%s' % '4'),
|
|
||||||
Contentline('DESCRIPTION:%s' % self.body),
|
|
||||||
Contentline('SUMMARY:%s' % self.title),
|
|
||||||
Contentline('PRIORITY:%s' % self.priority),
|
|
||||||
Contentline('DUE;VALUE=DATE:%s' % self.date_end.strftime('%Y%m%d')),
|
|
||||||
Contentline('DTSTART;VALUE=DATE:%s' % self.date_begin.strftime('%Y%m%d')),
|
|
||||||
Contentline('PERCENT-COMPLETE:0'),
|
|
||||||
Contentline('STATUS:NEEDS-ACTION'),
|
|
||||||
Contentline('END:VTODO'),
|
|
||||||
Contentline('END:VCALENDAR')
|
|
||||||
])
|
|
||||||
|
|
||||||
return c.to_ical()
|
|
||||||
|
|
||||||
|
|
||||||
class Client:
|
|
||||||
def __init__(self, url, username, password, calendar_name):
|
|
||||||
self.client = caldav.DAVClient(url, username=username, password=password)
|
|
||||||
self.principal = self.client.principal()
|
|
||||||
self.calendar = None
|
|
||||||
for c in self.principal.calendars():
|
|
||||||
if c.name == calendar_name:
|
|
||||||
self.calendar = c
|
|
||||||
|
|
||||||
if self.calendar is None:
|
|
||||||
raise LookupError('No calendar named "{}"'.format(calendar_name))
|
|
||||||
|
|
||||||
def add_event(self, event):
|
|
||||||
self.calendar.add_event(task.to_ical())
|
|
||||||
|
|
||||||
|
|
||||||
# conf = load('./configuration.yml')
|
|
||||||
|
|
||||||
ref_date = datetime.date.today() + datetime.timedelta(days=2)
|
|
||||||
with open('./tasks.yml', 'r') as content_file:
|
|
||||||
tasks_conf = load(content_file.read())
|
|
||||||
|
|
||||||
task_list = []
|
|
||||||
for task_name, task_data in tasks_conf.items():
|
|
||||||
t = Task(ref_date, task_name, task_data)
|
|
||||||
if t.date_begin.date() == ref_date:
|
|
||||||
task_list.append(t)
|
|
||||||
|
|
||||||
username="ggentile"
|
|
||||||
password="ID0t'8h6"
|
|
||||||
url = "https://cloud.dorfsvald.net/webdav/ggentile/"
|
|
||||||
calendar = "calendar"
|
|
||||||
|
|
||||||
|
|
||||||
client = Client(url, username, password, calendar)
|
|
||||||
for task in task_list:
|
|
||||||
client.add_event(task)
|
|
||||||
|
|
||||||
print('process finished: {} tasks created'.format(len(task_list)))
|
|
||||||
85
main.py
Executable file
85
main.py
Executable file
@@ -0,0 +1,85 @@
|
|||||||
|
import datetime
|
||||||
|
|
||||||
|
from yaml import load, FullLoader
|
||||||
|
from icalendar.parser import Contentlines, Contentline
|
||||||
|
from dateutil.rrule import rrule, FREQNAMES
|
||||||
|
import caldav
|
||||||
|
|
||||||
|
|
||||||
|
class Task:
|
||||||
|
def __init__(self, reference_date, name, data):
|
||||||
|
self.ref_date = reference_date
|
||||||
|
self.name = name
|
||||||
|
data['start']['freq'] = FREQNAMES.index(data['start']['freq'])
|
||||||
|
self.date_begin = rrule(count=1, dtstart=reference_date, **data['start'])[0]
|
||||||
|
self.date_end = self.date_begin + datetime.timedelta(microseconds=-1, **data['duration'])
|
||||||
|
|
||||||
|
self.title = data['title']
|
||||||
|
self.body = data['body']
|
||||||
|
self.priority = data['priority']
|
||||||
|
|
||||||
|
def to_ical(self):
|
||||||
|
current_dtstamp = datetime.datetime.now().strftime('%Y%m%dT%H%M%SZ')
|
||||||
|
uid = f"CaldavRecurringTask-{self.name}-{self.date_end.strftime('%Y%m%d')}"
|
||||||
|
c = Contentlines([
|
||||||
|
Contentline('BEGIN:VCALENDAR'),
|
||||||
|
Contentline('VERSION:2.0'),
|
||||||
|
Contentline('PRODID:-//DorfsvaldNet//CaldavRecuringTaskClient//FR'),
|
||||||
|
Contentline('BEGIN:VTODO'),
|
||||||
|
Contentline(f'DTSTAMP:{current_dtstamp}'),
|
||||||
|
Contentline(f'UID:{uid}'),
|
||||||
|
Contentline(f'CREATED:{current_dtstamp}'),
|
||||||
|
Contentline(f'LAST-MODIFIED:{current_dtstamp}'),
|
||||||
|
Contentline(f'SEQUENCE:{"4"}'),
|
||||||
|
Contentline(f'DESCRIPTION:{self.body}'),
|
||||||
|
Contentline(f'SUMMARY:{self.title}'),
|
||||||
|
Contentline(f'PRIORITY:{self.priority}'),
|
||||||
|
Contentline(f'DUE;VALUE=DATE:{self.date_end.strftime("%Y%m%d")}'),
|
||||||
|
Contentline(f'DTSTART;VALUE=DATE:{self.date_begin.strftime("%Y%m%d")}'),
|
||||||
|
Contentline('PERCENT-COMPLETE:0'),
|
||||||
|
Contentline('STATUS:NEEDS-ACTION'),
|
||||||
|
Contentline('END:VTODO'),
|
||||||
|
Contentline('END:VCALENDAR')
|
||||||
|
])
|
||||||
|
|
||||||
|
return c.to_ical()
|
||||||
|
|
||||||
|
|
||||||
|
class Client:
|
||||||
|
def __init__(self, url, username, password, calendar_name):
|
||||||
|
self.client = caldav.DAVClient(url, username=username, password=password)
|
||||||
|
self.principal = self.client.principal()
|
||||||
|
self.calendar = None
|
||||||
|
for c in self.principal.calendars():
|
||||||
|
if c.name == calendar_name:
|
||||||
|
self.calendar = c
|
||||||
|
break
|
||||||
|
|
||||||
|
if self.calendar is None:
|
||||||
|
raise LookupError('No calendar named "{}" found'.format(calendar_name))
|
||||||
|
|
||||||
|
def add_event(self, new_task):
|
||||||
|
self.calendar.add_event(new_task.to_ical())
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
with open('./configuration.yml', 'r') as configuration_file:
|
||||||
|
conf = load(configuration_file.read(), Loader=FullLoader)
|
||||||
|
|
||||||
|
ref_date = datetime.date.today() + datetime.timedelta(days=2)
|
||||||
|
with open('tasks.example.yml', 'r') as content_file:
|
||||||
|
tasks_conf = load(content_file.read(), Loader=FullLoader)
|
||||||
|
|
||||||
|
task_list = []
|
||||||
|
for task_name, task_data in tasks_conf.items():
|
||||||
|
t = Task(ref_date, task_name, task_data)
|
||||||
|
if t.date_begin.date() == ref_date:
|
||||||
|
task_list.append(t)
|
||||||
|
|
||||||
|
if task_list:
|
||||||
|
server = conf['dav_server']
|
||||||
|
client = Client(server['url'], server['user'], server['pass'], server['calendar'])
|
||||||
|
for task in task_list:
|
||||||
|
client.add_event(task)
|
||||||
|
|
||||||
|
print('process finished: {} tasks created'.format(len(task_list)))
|
||||||
4
requirements.txt
Normal file
4
requirements.txt
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
pyyaml
|
||||||
|
icalendar
|
||||||
|
caldav
|
||||||
|
python-dateutil
|
||||||
54
tasks.example.yml
Normal file
54
tasks.example.yml
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
# see http://dateutil.readthedocs.io/en/stable/rrule.html
|
||||||
|
# freq, dtstart=None, interval=1, wkst=None, count=None, until=None, bysetpos=None,
|
||||||
|
# bymonth=None, bymonthday=None, byyearday=None, byeaster=None, byweekno=None, byweekday=None,
|
||||||
|
# byhour=None, byminute=None, bysecond=None, cache=False
|
||||||
|
# Where freq must be one of YEARLY, MONTHLY, WEEKLY, DAILY, HOURLY, MINUTELY, or SECONDLY.
|
||||||
|
|
||||||
|
monthly_task:
|
||||||
|
start:
|
||||||
|
freq: MONTHLY
|
||||||
|
bymonthday: 1
|
||||||
|
duration:
|
||||||
|
days: 10
|
||||||
|
title: Monthly task
|
||||||
|
body: A task that is created once a month and starts on the first day of the month
|
||||||
|
priority: 1
|
||||||
|
|
||||||
|
other_monthly_task:
|
||||||
|
start:
|
||||||
|
freq: MONTHLY
|
||||||
|
bymonthday: -5
|
||||||
|
duration:
|
||||||
|
days: 5
|
||||||
|
title: Another Monthly task
|
||||||
|
body: A task that is created once a month and starts five days before the end of the month
|
||||||
|
priority: 1
|
||||||
|
|
||||||
|
daily_task:
|
||||||
|
start:
|
||||||
|
freq: DAILY
|
||||||
|
duration:
|
||||||
|
days: 1
|
||||||
|
title: A Daily task
|
||||||
|
body: This a task that is created everyday
|
||||||
|
priority: 1
|
||||||
|
|
||||||
|
yearly_task:
|
||||||
|
start:
|
||||||
|
freq: YEARLY
|
||||||
|
byyearday: 10
|
||||||
|
duration:
|
||||||
|
days: 60
|
||||||
|
title: A Yearly task
|
||||||
|
body: This a task that is created once a year, on the tenth day of the year
|
||||||
|
priority: 1
|
||||||
|
|
||||||
|
another_yearly_task:
|
||||||
|
start:
|
||||||
|
freq: YEARLY
|
||||||
|
byweekno: 4
|
||||||
|
duration:
|
||||||
|
days: 60
|
||||||
|
title: A Yearly task
|
||||||
|
body: This a task that is created once a year, on the fourth week of the year
|
||||||
|
priority: 1
|
||||||
69
tasks.yml
69
tasks.yml
@@ -1,69 +0,0 @@
|
|||||||
# see http://dateutil.readthedocs.io/en/stable/rrule.html
|
|
||||||
# freq, dtstart=None, interval=1, wkst=None, count=None, until=None, bysetpos=None,
|
|
||||||
# bymonth=None, bymonthday=None, byyearday=None, byeaster=None, byweekno=None, byweekday=None,
|
|
||||||
# byhour=None, byminute=None, bysecond=None, cache=False
|
|
||||||
# Where freq must be one of YEARLY, MONTHLY, WEEKLY, DAILY, HOURLY, MINUTELY, or SECONDLY.
|
|
||||||
|
|
||||||
reglement_piano:
|
|
||||||
start:
|
|
||||||
freq: MONTHLY
|
|
||||||
bymonthday: -5
|
|
||||||
duration:
|
|
||||||
days: 5
|
|
||||||
title: Règlement Piano
|
|
||||||
body: 460713
|
|
||||||
priority: 1
|
|
||||||
loyer:
|
|
||||||
start:
|
|
||||||
freq: MONTHLY
|
|
||||||
bymonthday: 1
|
|
||||||
duration:
|
|
||||||
days: 10
|
|
||||||
title: Loyer
|
|
||||||
body: https://fr.foncia.com/login\nA797GEA4EP23F
|
|
||||||
priority: 1
|
|
||||||
declaration_polemploi:
|
|
||||||
start:
|
|
||||||
freq: MONTHLY
|
|
||||||
bymonthday: 1
|
|
||||||
duration:
|
|
||||||
days: 10
|
|
||||||
title: Déclaration Pôle Emploi
|
|
||||||
body: 3851096Z\n847323
|
|
||||||
priority: 1
|
|
||||||
declaration_ae:
|
|
||||||
start:
|
|
||||||
freq: MONTHLY
|
|
||||||
bymonthday: 1
|
|
||||||
bymonth: [1,4,7,10]
|
|
||||||
duration:
|
|
||||||
days: 30
|
|
||||||
title: Déclaration AE
|
|
||||||
body: 51450594000031
|
|
||||||
priority: 1
|
|
||||||
facturation_x2i:
|
|
||||||
start:
|
|
||||||
freq: MONTHLY
|
|
||||||
bymonthday: -10
|
|
||||||
duration:
|
|
||||||
days: 10
|
|
||||||
title: Facture X2I
|
|
||||||
body: N/A
|
|
||||||
priority: 1
|
|
||||||
declaration_impots:
|
|
||||||
start:
|
|
||||||
freq: YEARLY
|
|
||||||
byyearday: 90
|
|
||||||
duration:
|
|
||||||
days: 60
|
|
||||||
title: Déclaration impôts
|
|
||||||
body: 1480545752413
|
|
||||||
priority: 1
|
|
||||||
#test:
|
|
||||||
#start:
|
|
||||||
#freq: DAILY
|
|
||||||
#duration:
|
|
||||||
#days: 60
|
|
||||||
#title: test_test_new_test
|
|
||||||
#body: 1480545752413
|
|
||||||
#priority: 1
|
|
||||||
Reference in New Issue
Block a user