Migrate to central appointment database #45
@ -1,5 +1,6 @@
|
|||||||
import datetime as dt
|
import datetime as dt
|
||||||
import re, logging
|
import re, logging
|
||||||
|
import zoneinfo
|
||||||
|
|
||||||
from .sync import Event
|
from .sync import Event
|
||||||
|
|
||||||
@ -15,21 +16,26 @@ class IcalHelper:
|
|||||||
'Sun': 6
|
'Sun': 6
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tzDE = zoneinfo.ZoneInfo('Europe/Berlin')
|
||||||
|
deltaWeek = dt.timedelta(weeks=1)
|
||||||
|
deltaDay = dt.timedelta(days=1)
|
||||||
|
firstDay = dt.date(year=dt.datetime.now().year, month=1, day=1)
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self._l = logging.getLogger(__name__)
|
self._l = logging.getLogger(__name__)
|
||||||
self.dDay = dt.timedelta(days=1)
|
|
||||||
self.firstDay = dt.datetime(year=dt.datetime.now().year, month=1, day=1)
|
|
||||||
self.reTime = re.compile(r'(\d{1,2}):(\d{2})')
|
self.reTime = re.compile(r'(\d{1,2}):(\d{2})')
|
||||||
|
|
||||||
def getStart(self, event: Event, holidays):
|
def getStart(self, event: Event, holidays):
|
||||||
self._getFirstWeekdayInYear(event.day)
|
firstDay = self._getFirstOccurence(event, holidays)
|
||||||
self._getFirstOccurence(event, holidays)
|
|
||||||
pass
|
if firstDay is None:
|
||||||
|
return None, None
|
||||||
|
return firstDay, dt.datetime.combine(firstDay, self._getTime(event.start), tzinfo=self.tzDE)
|
||||||
|
|
||||||
def _getFirstWeekdayInYear(self, weekday):
|
def _getFirstWeekdayInYear(self, weekday):
|
||||||
candidate = self.firstDay
|
candidate = self.firstDay
|
||||||
while candidate.weekday() != self._WEEKDAY_MAP[weekday]:
|
while candidate.weekday() != self._WEEKDAY_MAP[weekday]:
|
||||||
candidate += self.dDay
|
candidate += self.deltaDay
|
||||||
|
|
||||||
self._l.log(5, 'First %s in year is %s', weekday, candidate)
|
self._l.log(5, 'First %s in year is %s', weekday, candidate)
|
||||||
|
|
||||||
@ -41,6 +47,72 @@ class IcalHelper:
|
|||||||
def _getSortedFeasts(self, holidays):
|
def _getSortedFeasts(self, holidays):
|
||||||
return sorted(holidays['feasts'])
|
return sorted(holidays['feasts'])
|
||||||
|
|
||||||
|
def _isFeast(self, candidate, feasts):
|
||||||
|
return candidate in feasts
|
||||||
|
|
||||||
|
def _isHoliday(self, candidate, holidays):
|
||||||
|
for h in holidays:
|
||||||
|
if h['from'] <= candidate <= h['to']:
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _getLastHolidayException(self, holidays):
|
||||||
|
holidayList = list(self._getSortedHolidays(holidays))
|
||||||
|
holidayList.reverse()
|
||||||
|
lastHoliday = holidayList[0]
|
||||||
|
lastHolidayDay = lastHoliday['to']
|
||||||
|
|
||||||
|
feastList = list(self._getSortedFeasts(holidays))
|
||||||
|
feastList.reverse()
|
||||||
|
lastFeast = feastList[0]
|
||||||
|
lastFeastDay = lastFeast
|
||||||
|
|
||||||
|
return max(lastHolidayDay, lastFeastDay)
|
||||||
|
|
||||||
def _getFirstOccurence(self, event: Event, holidays):
|
def _getFirstOccurence(self, event: Event, holidays):
|
||||||
firstWeekday = self._getFirstWeekdayInYear(event.day)
|
firstWeekday = self._getFirstWeekdayInYear(event.day)
|
||||||
firstWeekday.tzinfo = dt.timezone.tzname('Europe/Berlin')
|
|
||||||
|
|
||||||
|
candidate = firstWeekday
|
||||||
|
while candidate.year == firstWeekday.year:
|
||||||
|
if self._isFeast(candidate, holidays['feasts']):
|
||||||
|
self._l.log(5, 'Found feast %s', candidate)
|
||||||
|
candidate += self.deltaWeek
|
||||||
|
continue
|
||||||
|
|
||||||
|
if self._isHoliday(candidate, holidays['holidays']):
|
||||||
|
self._l.log(5, 'Found holiday %s', candidate)
|
||||||
|
candidate += self.deltaWeek
|
||||||
|
continue
|
||||||
|
|
||||||
|
self._l.log(5, 'Found first occurence %s', candidate)
|
||||||
|
return candidate
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
# firstWeekday.tzinfo = dt.timezone.tzname('Europe/Berlin')
|
||||||
|
|
||||||
|
def _getTime(self, startStr):
|
||||||
|
match = self.reTime.match(startStr)
|
||||||
|
return dt.time(int(match.group(1)), int(match.group(2)))
|
||||||
|
|
||||||
|
def getHolidayExceptions(self, event: Event, startDay, holidays):
|
||||||
|
lastHolidayDay = self._getLastHolidayException(holidays)
|
||||||
|
|
||||||
|
pointer = startDay
|
||||||
|
exeptions = []
|
||||||
|
while pointer <= lastHolidayDay:
|
||||||
|
ex = dt.datetime.combine(pointer, self._getTime(event.start), tzinfo=self.tzDE)
|
||||||
|
|
||||||
|
if self._isFeast(pointer, holidays['feasts']):
|
||||||
|
exeptions.append(ex)
|
||||||
|
|
||||||
|
if self._isHoliday(pointer, holidays['holidays']) and not event.external:
|
||||||
|
exeptions.append(ex)
|
||||||
|
|
||||||
|
pointer += self.deltaWeek
|
||||||
|
|
||||||
|
if len(exeptions) > 0:
|
||||||
|
return exeptions
|
||||||
|
return None
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import logging, yaml, pprint, json, re
|
import logging, yaml, pprint, json, re
|
||||||
import hashlib
|
import hashlib, uuid
|
||||||
import caldav
|
import caldav
|
||||||
import datetime as dt
|
import datetime as dt
|
||||||
|
|
||||||
from . import login
|
from . import login, debug
|
||||||
|
|
||||||
_l = logging.getLogger(__name__)
|
_l = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -50,7 +50,8 @@ class Event:
|
|||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
wAge = f' ({self.age})' if self.age is not None else ''
|
wAge = f' ({self.age})' if self.age is not None else ''
|
||||||
return f'Ev({self.title}{wAge}) [{self.day}, {self.start}, {self.duration}]'
|
ext = 'Ext' if self.external else ''
|
||||||
|
return f'{ext}Ev({self.title}{wAge}) [{self.day}, {self.start}, {self.duration}]'
|
||||||
|
|
||||||
def getHash(self, holidays):
|
def getHash(self, holidays):
|
||||||
def fixHolidays(holidays):
|
def fixHolidays(holidays):
|
||||||
@ -86,10 +87,22 @@ class Event:
|
|||||||
# pprint.pprint(data, indent=4, width=100)
|
# pprint.pprint(data, indent=4, width=100)
|
||||||
hasher = hashlib.sha1()
|
hasher = hashlib.sha1()
|
||||||
hasher.update(json.dumps(data, sort_keys=True).encode('utf-8'))
|
hasher.update(json.dumps(data, sort_keys=True).encode('utf-8'))
|
||||||
return hasher.hexdigest()
|
first = hasher.hexdigest()
|
||||||
|
second = uuid.uuid4().hex
|
||||||
|
return f'{first}___{second}', first
|
||||||
|
|
||||||
dDay = dt.timedelta(days=1)
|
dDay = dt.timedelta(days=1)
|
||||||
|
|
||||||
|
_MAP_WEEKDAY_ICAL = {
|
||||||
|
'Mon': 'MO',
|
||||||
|
'Tue': 'TU',
|
||||||
|
'Wed': 'WE',
|
||||||
|
'Thu': 'TH',
|
||||||
|
'Fri': 'FR',
|
||||||
|
'Sat': 'SA',
|
||||||
|
'Sun': 'SU',
|
||||||
|
}
|
||||||
|
|
||||||
def addToCalendar(self, calendar: caldav.Calendar, holidays):
|
def addToCalendar(self, calendar: caldav.Calendar, holidays):
|
||||||
from . import cal_helper
|
from . import cal_helper
|
||||||
|
|
||||||
@ -99,15 +112,44 @@ class Event:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
helper = cal_helper.IcalHelper()
|
helper = cal_helper.IcalHelper()
|
||||||
helper.getStart(self, holidays)
|
startDay, start = helper.getStart(self, holidays)
|
||||||
|
|
||||||
|
if start is None:
|
||||||
|
_l.warning('Could not get start time for event %s in the current year', self)
|
||||||
|
return
|
||||||
|
|
||||||
|
uid, uidFirst = self.getHash(holidays)
|
||||||
icalData = {
|
icalData = {
|
||||||
'uid': self.getHash(holidays),
|
'uid': uid,
|
||||||
'DTSTART': dt.datetime.now(),
|
'DTSTART': start,
|
||||||
'DTEND': dt.datetime.now() + dt.timedelta(minutes=self.duration),
|
'DTEND': start + dt.timedelta(minutes=self.duration),
|
||||||
'SUMMARY': self.title
|
'SUMMARY': self.title,
|
||||||
|
'RRULE': {
|
||||||
|
'FREQ': 'DAILY',
|
||||||
|
'BYDAY': self._MAP_WEEKDAY_ICAL[self.day]
|
||||||
}
|
}
|
||||||
# calendar.add_event(**icalData)
|
}
|
||||||
|
# debug.debugger()
|
||||||
|
|
||||||
|
holidayExceptions = helper.getHolidayExceptions(self, startDay, holidays)
|
||||||
|
if holidayExceptions is not None:
|
||||||
|
icalData['EXDATE'] = holidayExceptions
|
||||||
|
|
||||||
|
desc = ''
|
||||||
|
if self.age is not None:
|
||||||
|
desc += f'Jahrgänge: {self.age}'
|
||||||
|
if len(self.description) > 0:
|
||||||
|
desc += '\n\n'
|
||||||
|
desc += self.description
|
||||||
|
if len(desc) > 0:
|
||||||
|
icalData['DESCRIPTION'] = desc
|
||||||
|
|
||||||
|
_l.log(5, 'Adding event\n%s', pprint.pformat(icalData, indent=4, width=100))
|
||||||
|
|
||||||
|
ical = caldav.lib.vcal.create_ical(**icalData)
|
||||||
|
_l.log(5, 'Created event\n%s', pprint.pformat(ical, indent=4, width=100))
|
||||||
|
_l.log(5, 'Created event\n%s', ical)
|
||||||
|
calendar.add_event(**icalData)
|
||||||
|
|
||||||
def _unpackSchedules(schedule):
|
def _unpackSchedules(schedule):
|
||||||
def packSingleCalendar(cal):
|
def packSingleCalendar(cal):
|
||||||
@ -121,7 +163,7 @@ def _unpackSchedules(schedule):
|
|||||||
|
|
||||||
if 'age' in ev:
|
if 'age' in ev:
|
||||||
e.age = ev['age']
|
e.age = ev['age']
|
||||||
if ev.get('extern', False):
|
if ev.get('extern', False) or ev.get('external', False):
|
||||||
e.external = True
|
e.external = True
|
||||||
if 'desc' in ev:
|
if 'desc' in ev:
|
||||||
e.description = ev['desc']
|
e.description = ev['desc']
|
||||||
@ -216,7 +258,8 @@ class CalendarSynchonizer:
|
|||||||
ret = {}
|
ret = {}
|
||||||
|
|
||||||
for e in events:
|
for e in events:
|
||||||
uid = str(e.icalendar_component['uid'])
|
uidRaw = str(e.icalendar_component['uid'])
|
||||||
|
uid = uidRaw.split('___', 1)[0]
|
||||||
_l.log(5, 'Event with uid %s was found.', uid)
|
_l.log(5, 'Event with uid %s was found.', uid)
|
||||||
ret[uid] = e
|
ret[uid] = e
|
||||||
|
|
||||||
@ -228,9 +271,9 @@ class CalendarSynchonizer:
|
|||||||
ret = {}
|
ret = {}
|
||||||
|
|
||||||
for e in schedule:
|
for e in schedule:
|
||||||
uid = e.getHash(holidays)
|
uid, uidFirst = e.getHash(holidays)
|
||||||
_l.log(5, 'Event with uid %s was found.', uid)
|
_l.log(5, 'Event with uid part %s was found.', uidFirst)
|
||||||
ret[uid] = e
|
ret[uidFirst] = e
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user