You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
149 lines
4.9 KiB
Python
149 lines
4.9 KiB
Python
# schichtleiter
|
|
# Copyright (C) 2022 Luca Schmid
|
|
#
|
|
# This program is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
from collections import namedtuple
|
|
from datetime import *
|
|
from icalendar import Calendar
|
|
from itertools import count
|
|
from xml.dom import Node
|
|
from xml.dom.minidom import getDOMImplementation
|
|
|
|
Event = namedtuple('Event', ['start', 'duration', 'guid'])
|
|
|
|
dom = getDOMImplementation()
|
|
|
|
# https://github.com/voc/voctosched/blob/master/schema/basic.xsd
|
|
def calendar_to_xml(cal):
|
|
if not isinstance(cal, Calendar):
|
|
raise TypeError('argument must be an instance of icalendar.Calendar')
|
|
|
|
days = {}
|
|
version = None
|
|
|
|
for event in cal.walk('vevent'):
|
|
uid = event.decoded('uid').decode()
|
|
room = event.decoded('summary').decode() # we expect the event summary (i.e. title) to be the room name
|
|
start = event.decoded('dtstart').astimezone(timezone.utc)
|
|
end = event.decoded('dtend').astimezone(timezone.utc)
|
|
|
|
start_date = start.date()
|
|
end_date = end.date()
|
|
|
|
day = start_date
|
|
while day <= end_date:
|
|
day_start = time(tzinfo=timezone.utc) if day > start_date else start.timetz()
|
|
day_end = time(23, 59, 59, tzinfo=timezone.utc) if day < end_date else end.timetz()
|
|
rooms = {}
|
|
|
|
if day in days:
|
|
day_start = days[day][0] if days[day][0] < day_start else day_start
|
|
day_end = days[day][1] if days[day][1] > day_end else day_end
|
|
rooms = days[day][2]
|
|
|
|
if room not in rooms:
|
|
rooms[room] = []
|
|
|
|
days[day] = (day_start, day_end, rooms)
|
|
day += timedelta(days=1)
|
|
|
|
delta = int((end-start).total_seconds())
|
|
duration = []
|
|
duration.append(delta//3600)
|
|
delta -= duration[-1] * 3600
|
|
duration.append(delta//60)
|
|
delta -= duration[-1] * 60
|
|
duration.append(delta)
|
|
duration = ':'.join(map(lambda n: str(n).rjust(2, '0'), duration))
|
|
|
|
days[start_date][2][room].append(Event(start, duration, uid))
|
|
|
|
if 'last-modified' in event:
|
|
last_modified = event.decoded('last-modified')
|
|
if version == None or version < last_modified:
|
|
version = last_modified.astimezone(timezone.utc)
|
|
|
|
if version == None:
|
|
version = datetime.now(timezone.utc)
|
|
|
|
document = dom.createDocument(None, 'schedule', None)
|
|
schedule = document.documentElement
|
|
|
|
def root(child):
|
|
schedule.appendChild(child)
|
|
|
|
def elem(name, *args, **kwargs):
|
|
e = document.createElement(name)
|
|
|
|
for child in args:
|
|
if isinstance(child, str):
|
|
child = document.createTextNode(child)
|
|
if not isinstance(child, Node):
|
|
child = document.createTextNode(str(child))
|
|
e.appendChild(child)
|
|
|
|
for k, v in kwargs.items():
|
|
if k.startswith('_'):
|
|
k = k[1:]
|
|
attr = document.createAttribute(k)
|
|
attr.value = str(v)
|
|
e.setAttributeNode(attr)
|
|
|
|
return e
|
|
|
|
root(elem(
|
|
'version',
|
|
str(version),
|
|
))
|
|
|
|
root(elem(
|
|
'conference',
|
|
elem('title', ''),
|
|
elem('acronym', ''),
|
|
))
|
|
|
|
fmt = lambda dt: dt.isoformat(timespec='seconds')
|
|
id = count(1)
|
|
for index, day in enumerate(sorted(days.keys())):
|
|
start, end, rooms = days[day]
|
|
root(elem(
|
|
'day',
|
|
*map(lambda room, events: elem(
|
|
'room',
|
|
*map(lambda event: elem(
|
|
'event',
|
|
elem('room', room),
|
|
elem('title', ''),
|
|
elem('subtitle', ''),
|
|
elem('type', ''),
|
|
elem('date', fmt(event.start)),
|
|
elem('start', event.start.time().isoformat(timespec='seconds')),
|
|
elem('duration', event.duration),
|
|
elem('abstract', ''),
|
|
elem('slug', event.guid),
|
|
elem('track', ''),
|
|
id=next(id),
|
|
guid=event.guid,
|
|
), events),
|
|
_name=room,
|
|
), rooms.keys(), rooms.values()),
|
|
date=day.isoformat(),
|
|
start=fmt(datetime.combine(day, start)),
|
|
end=fmt(datetime.combine(day, end)),
|
|
index=index+1,
|
|
))
|
|
|
|
return document.toxml(encoding='utf-8')
|