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.
This repo is archived. You can view files and clone it, but cannot push or open issues/pull-requests.

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')