Skip to content
Snippets Groups Projects
Select Git revision
  • 48abb55926960882de329f043ee65154496eba7d
  • develop default protected
  • ical-export
  • feature/audit_log
  • fix/index
  • badge-redeem-404
  • 720-schedule_source
  • room-docu
  • chore/event-views
  • 511-schedule-foo-fixed
  • 607-schedule-versions
  • deploy/curl-verbose
  • fix/public-badge-access-rights
  • 445-schedule-redirects
  • 623-wiki-im-baustellenmodus-sollte-mal-als-wiki-admin-trotzdem-seiten-anlegen-bearbeiten-konnen
  • fix/registration_mail_subject
  • feature/conference-query-set
  • feature/568-habitatmanagement
  • feat/unit-integration-tests
  • camp23-prod
  • production
  • prod-2024-12-27_20-15
  • prod-2024-12-27_16-37
  • prod-2024-12-27_16-01
  • prod-2024-12-27_13-29
  • prod-2024-12-27_00-34
  • prod-2024-12-26_21-45
  • prod-2024-12-26_13-12
  • prod-2024-12-26_00-21
  • prod-2024-12-25_21-04
  • prod-2024-12-25_15-54
  • prod-2024-12-25_01-29
  • prod-2024-12-24_14-48
  • prod-2024-12-23_23-39
  • prod-2024-12-22_21-12
  • prod-2024-12-22_17-25
  • prod-2024-12-22_01-34
  • prod-2024-12-22_00-55
  • prod-2024-12-21_13-42
  • prod-2024-12-21_10-44
  • prod-2024-12-20_12-25
41 results

schedule.py

Blame
  • 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)