diff --git a/fahrplan/datetime.py b/fahrplan/datetime.py index 4e6e1bb..acdfcc4 100644 --- a/fahrplan/datetime.py +++ b/fahrplan/datetime.py @@ -11,7 +11,7 @@ def format_datetime(datetime: dt.datetime): def parse_datetime(date_string: str): - return dt.datetime.strptime(date_string, DATETIME_FORMAT) + return dt.datetime.strptime(date_string.split('+')[0], DATETIME_FORMAT) def format_time(time: dt.time): diff --git a/fahrplan/model/event.py b/fahrplan/model/event.py index 573e3d7..85f0f1f 100644 --- a/fahrplan/model/event.py +++ b/fahrplan/model/event.py @@ -1,3 +1,5 @@ +import logging + import datetime as dt from typing import Dict, Union, Callable @@ -32,7 +34,7 @@ def __init__(self, uid: int, date: dt.datetime, start: dt.time, duration: dt.tim self.description = description self.logo = logo if not persons: - raise FahrplanError("Excepted at least one person for event creation, none are given.") + logging.warning(f"Event {uid} has no persons!") self.persons = persons self.links = links or {} self.attachments = attachments or {} diff --git a/fahrplan/xml/writer.py b/fahrplan/xml/writer.py index 87ce69c..21b1c35 100644 --- a/fahrplan/xml/writer.py +++ b/fahrplan/xml/writer.py @@ -29,7 +29,12 @@ def tag(self, tag: str, inner: Any, **properties): self.buffer += f"<{self.format_properties(tag, properties)} />\n" return - inner = str(inner).replace('&', '&') + inner = str(inner) + inner = inner.replace('&', '&') + inner = inner.replace('<', '<') + inner = inner.replace('>', '>') + inner = inner.replace('"', '"') + inner = inner.replace("'", ''') if "\n" not in str(inner): self.buffer += f"<{self.format_properties(tag, properties)}>{inner}\n" diff --git a/graphql/__init__.py b/graphql/__init__.py new file mode 100644 index 0000000..df82f46 --- /dev/null +++ b/graphql/__init__.py @@ -0,0 +1 @@ +from .query import run_query \ No newline at end of file diff --git a/graphql/query.py b/graphql/query.py new file mode 100644 index 0000000..b876864 --- /dev/null +++ b/graphql/query.py @@ -0,0 +1,10 @@ +import requests + +# Adapted from https://gist.github.com/gbaman/b3137e18c739e0cf98539bf4ec4366ad + +def run_query(apiurl, query): + request = requests.post(apiurl, json={'query': query}) + if request.status_code == 200: + return request.json() + else: + raise Exception("Query failed to run by returning code of {}. {}".format(request.status_code, query)) \ No newline at end of file diff --git a/handlers/directory.py b/handlers/directory.py index aedb264..8111c74 100644 --- a/handlers/directory.py +++ b/handlers/directory.py @@ -1,13 +1,14 @@ from typing import Type from .base import ImportHandler, ExportHandler -from .import_handlers import FakeImportHandler, CSVImportHandler, JSONImportHandler +from .import_handlers import FakeImportHandler, CSVImportHandler, JSONImportHandler, C3DataImportHandler from .export_handlers import BasicXMLExportHandler, ExtendedXMLExportHandler import_handlers = { "csv": CSVImportHandler, "fake": FakeImportHandler, "json": JSONImportHandler, + "c3data": C3DataImportHandler, } export_handlers = { diff --git a/handlers/import_handlers/__init__.py b/handlers/import_handlers/__init__.py index 3a0cdb6..76efbd4 100644 --- a/handlers/import_handlers/__init__.py +++ b/handlers/import_handlers/__init__.py @@ -1,3 +1,4 @@ from .csv import CSVImportHandler from .fake import FakeImportHandler -from .json import JSONImportHandler \ No newline at end of file +from .json import JSONImportHandler +from .c3data import C3DataImportHandler \ No newline at end of file diff --git a/handlers/import_handlers/c3data.py b/handlers/import_handlers/c3data.py new file mode 100644 index 0000000..bedfd68 --- /dev/null +++ b/handlers/import_handlers/c3data.py @@ -0,0 +1,145 @@ +import logging + +from hacks import noexcept +from ..base import ImportHandler +from graphql import run_query +from pprint import pprint +import json + +from fahrplan.datetime import parse_date, parse_datetime, parse_duration, parse_time + +from fahrplan.model.conference import Conference +from fahrplan.model.schedule import Schedule +from fahrplan.model.day import Day +from fahrplan.model.event import Event + +query_template = """ +query { + conference: conferenceByAcronym(acronym: "__ACRONYM__") { + acronym + title + start: startDate + end: endDate + days: daysByConferenceId { + nodes { + index + date + startDate + endDate + } + } + rooms: roomsByConferenceId { + nodes { + name + } + } + events: eventsByConferenceId { + nodes { + id: localId + guid + dayIndex + roomName + logo + startDate + startTime + duration: durationTime + slug + title + subtitle + track: trackByTrackId { + name + } + eventType + language + abstract + description + recordingLicense + doNotRecord + persons(roles: [SPEAKER, MODERATOR]) { + nodes { + id + publicName + } + } + links { + title + url + } + attachments { + title + url + } + } + } + } +} +""" + + +log = logging.getLogger(__name__) + + +class C3DataImportHandler(ImportHandler): + @noexcept(log) + def run(self): + query = query_template.replace('__ACRONYM__', self.config.get('acronym')) + resp = run_query(self.config.get('url'), query) + conf_tree = resp['data']['conference'] + + conference = Conference( + title=conf_tree['title'], + acronym=conf_tree['acronym'], + start=conf_tree['start'], + end=conf_tree['end'], + day_count=0, + time_slot_duration=parse_duration("0:05") + ) + schedule = Schedule(conference=conference, version="ASDF") + + for day_tree in conf_tree['days']['nodes']: + schedule.add_day(Day( + index=day_tree['index'], + date=parse_date(day_tree['date']), + start=parse_datetime(day_tree['startDate']), + end=parse_datetime(day_tree['endDate']) + )) + + for room_tree in conf_tree['rooms']['nodes']: + schedule.add_room(room_tree['name']) + + for event_tree in conf_tree['events']['nodes']: + persons = dict() + for person_tree in event_tree['persons']['nodes'] or (): + persons[int(person_tree['id'])] = person_tree['publicName'] + + links = dict() + for link_tree in event_tree['links'] or (): + links[link_tree['title']] = link_tree['url'] + + attachments = dict() + for attachment_tree in event_tree['links'] or (): + attachments[attachment_tree['title']] = attachment_tree['url'] + + schedule.add_event(int(event_tree['dayIndex']), event_tree['roomName'], Event( + uid=event_tree['id'], + guid=event_tree['guid'], + logo=event_tree['logo'], + date=parse_datetime(event_tree['startDate']), + start=parse_time(event_tree['startTime']), + duration=parse_duration(event_tree['duration']), + slug=event_tree['slug'], + title=event_tree['title'], + subtitle=event_tree['subtitle'], + track=event_tree['track'], + event_type=event_tree['eventType'], + language=event_tree['language'], + abstract=event_tree['abstract'], + description=event_tree['description'], + recording_license=event_tree['recordingLicense'], + recording_optout=event_tree['doNotRecord'], + persons=persons, + links=links, + attachments=attachments, + )) + + return schedule diff --git a/handlers/import_handlers/json.py b/handlers/import_handlers/json.py index 179df25..b492193 100644 --- a/handlers/import_handlers/json.py +++ b/handlers/import_handlers/json.py @@ -41,8 +41,8 @@ def run(self): day = Day( index=day_tree['index'], date=parse_date(day_tree['date']), - start=parse_datetime(day_tree['start']), - end=parse_datetime(day_tree['end']) + start=parse_datetime(day_tree['day_start'].split('+')[0]), + end=parse_datetime(day_tree['day_end'].split('+')[0]) ) schedule.add_day(day) @@ -52,7 +52,7 @@ def run(self): for talk in room_talks: persons = {} for person_info in talk['persons']: - name = person_info['full_public_name'] + name = person_info['public_name'] # generate some hopefully unique ids if they are 0 uid = person_info['id'] or (crc32(name.encode()) & 0xffffffff) persons[uid] = name @@ -71,9 +71,11 @@ def run(self): url = attachment_info['url'] attachments[url] = title + log.debug(f'Event id {talk["id"]}') + day.add_event(room_name, Event( uid=talk['id'], - date=parse_datetime(talk['date']), + date=parse_datetime(talk['date'].split('+')[0]), start=parse_time(talk['start']), duration=parse_duration(talk['duration']), slug=talk['slug'],