Select Git revision
schedule.py
Forked from
hub / hub
Source project has a limited visibility.
schedulejson.py 5.89 KiB
import json
from collections import OrderedDict
from urllib.parse import urlparse
import requests
from requests_file import FileAdapter
from .base import BaseScheduleSupport, filter_additional_data, schedule_time_to_timedelta
s = requests.Session()
s.mount('file://', FileAdapter())
class ScheduleJSONSupport(BaseScheduleSupport):
identifier = 'schedule-json'
readonly = True
configuration_fields = {
# 'key': (type, default value, mandatory, translation text)
'feedback': ('bool', True, False, 'Enable/Disable feedback url generation'),
'kind': ('string', 'assembly', False, 'kind of events, either "assembly" or "official" or "sos" or "lightning"'),
'headers': ('dict', {}, False, 'HTTP headers to send with the request e.g. Authorization'),
}
def ready(self):
r = s.head(self.remote_url)
return r.ok
def fetch(self):
"""
This method is the workhorse of the schedule support module:
its job is to query upstream for the current set of data.
It shall return a dictionary with keys 'rooms' and 'events',
each containing a dictionary with entries mapping a source id
to a dictionary which can be understood by Room.from_dict()
and Event.from_dict() respectively.
The hub will update events it already knows by the source id:
all events need to have an unique but stable identifier, i.e.
if the name of the event changes the identifier must not change!
"""
headers = {}
if self.conf_value('headers'):
headers = self.conf_value('headers')
schedule = ScheduleJSON.from_url(self.remote_url, headers={**headers, 'Accept-Language': 'en'})
schedule_de = ScheduleJSON.from_url(self.remote_url, headers={**headers, 'Accept-Language': 'de'}, event_map=True)
instance = urlparse(schedule.get('base_url', self.remote_url))
host = f'//{instance.netloc}'
kind = self.conf_value('kind')
def ensure_full_url(uri):
if not uri:
return None
if not uri.startswith('http') and not uri.startswith('//'):
return f'{host}{uri}'
return uri
return {
'version': schedule.version(),
'rooms': {r['name']: r for r in schedule.rooms()},
'events': {
e.get('id'): {
'guid': e.get('guid'),
'slug': e.get('slug').split(f"{e.get('id')}-")[1][0:150].strip('-') or e.get('slug')[0:150].strip('-'),
'name': e.get('title'),
'language': e.get('language'),
'abstract': e.get('abstract') or '',
'description': e.get('description') or '',
'description_en': e.get('description') or '',
'description_de': schedule_de.event(e.get('guid')).get('description') or '',
'track': e.get('track'),
'room': e.get('room'),
'schedule_start': e.get('date'),
'schedule_duration': str(schedule_time_to_timedelta(e.get('duration'))),
'is_public': True,
'kind': kind,
'speakers': e.get('persons', []),
'banner_image_url': ensure_full_url(e.get('logo')),
'additional_data': filter_additional_data(e, self.computed_data(e)),
}
for e in schedule.events()
},
}
def computed_data(self, event: dict):
# TODO only add feedback_url if feedback is enabled via configuraiton_fields in ScheduleSource config
if self.conf_value('feedback'):
return {'feedback_url': f"{event['url']}feedback/"}
return {}
class ScheduleJSON:
"""
Schedule from JSON document
"""
_schedule = None
_events = None
def __init__(self, json, event_map=False):
self._schedule = json
if event_map:
self._events = {e.get('guid'): e for e in self.events()}
@classmethod
def from_url(cls, url, client=None, headers=None, event_map=False):
r = (client if client else s).get(url, headers=headers)
if r.ok is False:
raise Exception(f'Request failed, HTTP {r.status_code}.')
# maintain order from input file
schedule = json.JSONDecoder(object_pairs_hook=OrderedDict).decode(r.text)
# Close the raw file handle if it's still open
if hasattr(r, 'raw') and r.raw.closed is False:
r.raw.close()
return ScheduleJSON(json=schedule['schedule'], event_map=event_map)
def __getitem__(self, key):
return self._schedule[key]
def get(self, key, default=None):
return self._schedule.get(key, default)
def schedule(self):
return self._schedule
def version(self):
return self._schedule.get('version')
def days(self):
return self._schedule.get('conference').get('days')
def rooms(self):
# try to access the room dict from schedule.json gen 2021
rooms = self._schedule.get('conference', {}).get('rooms', [])
if rooms:
return list(rooms)
# looks like we have an older schudule.json (gen 2020), without a dedicated room list
# so we have use a fallback and iterate all days adding the rooms to a set, creating uniqueness
rooms = set()
for day in self.days():
for roomname in day.get('rooms'):
rooms.add(roomname)
return [{'name': name} for name in rooms]
def events(self):
for day in self.days():
for room in day.get('rooms'):
yield from day.get('rooms')[room]
def event(self, guid):
if guid in self._events:
return self._events[guid]
return None
def __str__(self):
return json.dumps(self._schedule, indent=2)