diff --git a/apps/schedule/feeds.py b/apps/schedule/feeds.py index b7a30df47..19a72214e 100644 --- a/apps/schedule/feeds.py +++ b/apps/schedule/feeds.py @@ -1,18 +1,16 @@ import json -from datetime import timedelta from icalendar import Calendar, Event from flask import request, abort, current_app as app, redirect, url_for, Response from flask_cors import cross_origin from flask_login import current_user -from math import ceil -from main import external_url -from models import event_year, event_start, event_end +from models import event_year from models.user import User from models.cfp import Proposal from ..common import feature_flag, feature_enabled, json_response -from .schedule_xml import export_frab, get_day_start_end, get_duration +from .schedule_json import export_frab_json +from .schedule_xml import export_frab from .historic import feed_historic from .data import ( _get_scheduled_proposals, @@ -20,7 +18,7 @@ _convert_time_to_str, _get_upcoming, ) -from . import event_tz, schedule +from . import schedule def _format_event_description(event): @@ -37,9 +35,9 @@ def _format_event_description(event): venue_str = event["venue"] if event["map_link"]: venue_str = f'{venue_str} ({event["map_link"]})' - footer_block.append(f'Venue: {venue_str}') + footer_block.append(f"Venue: {venue_str}") if footer_block: - description += '\n\n' + '\n'.join(footer_block) + description += "\n\n" + "\n".join(footer_block) return description @@ -78,102 +76,10 @@ def schedule_frab_json(year): .all() ) - duration_days = ceil((event_end() - event_end()).total_seconds / 86400) - - rooms = [proposal.scheduled_venue.name for proposal in schedule] - - schedule_json = { - "version": "1.0-public", - "conference": { - "acronym": "emf{}".format(event_year()), - "days": [], - "daysCount": duration_days, - "end": event_end().strftime("%Y-%m-%d"), - "rooms": [ - { - "name": room, - } - for room in rooms - ], - "start": event_start().strftime("%Y-%m-%d"), - "time_zone_name": event_tz, - "timeslot_duration": "00:10", - "title": "Electromagnetic Field {}".format(event_year()), - "url": external_url("main"), - }, - } - - for day in range(0, duration_days): - day_dt = event_start() + timedelta(days=day) - day_start, day_end = get_day_start_end(day_dt) - day_schedule = { - "date": day_dt.strftime("%Y-%m-%d"), - "day_end": day_start.isoformat(), - "day_start": day_end.isoformat(), - "index": day, - "rooms": {}, - } - for room in rooms: - day_schedule["rooms"][room] = [] - for proposal in schedule: - if proposal.scheduled_venue.name != room: - # TODO find a better way to do that - continue - links = { - proposal.c3voc_url, - proposal.youtube_url, - proposal.thumbnail_url, - proposal.map_link, - } - links.discard(None) - links.discard("") - day_schedule["rooms"][room].append( - { - "abstract": None, # The proposal model does not implement abstracts - "attachments": [], - "date": event_tz.localize(proposal.start_date).isoformat(), - "description": proposal.description, - "do_not_record": proposal.video_privacy != "public", - "duration": get_duration(proposal.start_date, proposal.end_date), - "guid": None, - "id": proposal.id, - # This assumes there will never be a non-english talk, - # which is probably fine for a conference in the UK. - "language": "en", - "links": sorted(links), - "persons": [ - { - "name": name.strip(), - "public_name": name.strip(), - } - for name in (proposal.published_names or proposal.user.name).split(",") - ], - "recording_license": "CC BY-SA 3.0", - "room": room, - "slug": "emf{}-{}-{}".format( - event_year(), - proposal.id, - proposal.slug, - ), - "start": event_tz.localize(proposal.start_date).strftime("%H:%M"), - "subtitle": None, - "title": proposal.display_title, - "track": None, - "type": proposal.type, - "url": external_url( - ".item", - year=event_year(), - proposal_id=proposal.id, - slug=proposal.slug, - ), - } - ) - schedule_json["conference"]["days"].append(day_schedule) - return Response( json.dumps( { - "schedule": schedule_json, + "schedule": export_frab_json(schedule), "$schema": "https://c3voc.de/schedule/schema.json", "generator": { "name": "emfcamp-website", @@ -187,7 +93,8 @@ def schedule_frab_json(year): @schedule.route("/schedule/.frab") def schedule_frab(year): - return redirect(url_for('schedule_frab_xml', year=year), code=301) + return redirect(url_for("schedule_frab_xml", year=year), code=301) + @schedule.route("/schedule/frab-.xml") def schedule_frab_xml(year): diff --git a/apps/schedule/schedule_json.py b/apps/schedule/schedule_json.py new file mode 100644 index 000000000..b96dbb36f --- /dev/null +++ b/apps/schedule/schedule_json.py @@ -0,0 +1,128 @@ +from datetime import timedelta +from math import ceil + +from main import external_url +from models import event_end, event_start, event_year + +from . import event_tz +from .schedule_xml import get_day_start_end, get_duration + + +def events_per_day_and_room(schedule): + days = { + current_date.date(): { + "index": index + 1, + "start": get_day_start_end(event_start() + timedelta(days=index))[0], + "end": get_day_start_end(event_start() + timedelta(days=index))[1], + "rooms": {}, + } + for index, current_date in enumerate( + event_start() + timedelta(days=i) for i in range((event_end() - event_start()).days + 1) + ) + } + + for proposal in schedule: + talk_date = proposal.start_date.date() + if talk.start_date.hour < 4 and talk_date != event_start().date(): + talk_date -= timedelta(days=1) + if talk_date not in days: + # Event is outside the scheduled event duration. + continue + if proposal.scheduled_venue.name not in days[talk_date]: + days[talk_date][proposal.scheduled_venue.name] = [proposal] + else: + days[talk_date][proposal.scheduled_venue.name].append(proposal) + + return days.values() + + +def export_frab_json(schedule): + duration_days = ceil((event_end() - event_end()).total_seconds / 86400) + + rooms = set([proposal.scheduled_venue.name for proposal in schedule]) + + schedule_json = { + "version": "1.0-public", + "conference": { + "acronym": "emf{}".format(event_year()), + "days": [], + "daysCount": duration_days, + "end": event_end().strftime("%Y-%m-%d"), + "rooms": [ + { + "name": room, + } + for room in sorted(rooms) + ], + "start": event_start().strftime("%Y-%m-%d"), + "time_zone_name": event_tz, + "timeslot_duration": "00:10", + "title": "Electromagnetic Field {}".format(event_year()), + "url": external_url("main"), + }, + } + + for day in events_per_day_and_room: + day_schedule = { + "date": day["start"].strftime("%Y-%m-%d"), + "day_end": day["start"].isoformat(), + "day_start": day["end"].isoformat(), + "index": day["index"], + "rooms": {}, + } + for room, events in sorted(day["rooms"].items()): + day_schedule["rooms"][room] = [] + for proposal in events: + links = { + proposal.c3voc_url, + proposal.youtube_url, + proposal.thumbnail_url, + proposal.map_link, + } + links.discard(None) + links.discard("") + day_schedule["rooms"][room].append( + { + "abstract": None, # The proposal model does not implement abstracts + "attachments": [], + "date": event_tz.localize(proposal.start_date).isoformat(), + "description": proposal.description, + "do_not_record": proposal.video_privacy != "public", + "duration": get_duration(proposal.start_date, proposal.end_date), + "guid": None, + "id": proposal.id, + # This assumes there will never be a non-english talk, + # which is probably fine for a conference in the UK. + "language": "en", + "links": sorted(links), + "persons": [ + { + "name": name.strip(), + "public_name": name.strip(), + } + for name in (proposal.published_names or proposal.user.name).split(",") + ], + "recording_license": "CC BY-SA 3.0", + "room": room, + "slug": "emf{}-{}-{}".format( + event_year(), + proposal.id, + proposal.slug, + ), + "start": event_tz.localize(proposal.start_date).strftime("%H:%M"), + "subtitle": None, + "title": proposal.display_title, + # Contrary to the infobeamer frab module, the json module does not allow users to set colours + # for tracks themselves. It instead relies on the schedule itself to provide those colours. + "track": None, + "type": proposal.type, + "url": external_url( + ".item", + year=event_year(), + proposal_id=proposal.id, + slug=proposal.slug, + ), + } + ) + schedule_json["conference"]["days"].append(day_schedule) + return schedule_json