diff --git a/poetry.lock b/poetry.lock
index c8cfdcd94..9a17db4b9 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -1651,17 +1651,6 @@ files = [
{file = "sorl_thumbnail-12.10.0-py3-none-any.whl", hash = "sha256:733eb2eee392d4a874f88fb3ed6f0572fa9c361b06e0411b83e435ba69c51f52"},
]
-[[package]]
-name = "sortedcontainers"
-version = "2.4.0"
-description = "Sorted Containers -- Sorted List, Sorted Dict, Sorted Set"
-optional = false
-python-versions = "*"
-files = [
- {file = "sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0"},
- {file = "sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88"},
-]
-
[[package]]
name = "sqlparse"
version = "0.5.0"
@@ -1861,4 +1850,4 @@ files = [
[metadata]
lock-version = "2.0"
python-versions = "~3.10.0"
-content-hash = "9cc3edd81f19cae7b6b3111fad25d06943108f5483fd9b81734d9f4bced8251a"
+content-hash = "da9e9f78cdefddcc430a60795a9be354aec21c9204b6e89834f4d98879740cf8"
diff --git a/pyproject.toml b/pyproject.toml
index 8ea6e44fa..94bea5ce8 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -29,7 +29,6 @@ psycopg2 = "2.9.9"
pytz = "2024.1"
requests = "2.31.0"
sorl-thumbnail = "12.10.0"
-sortedcontainers = "2.4.0"
tabulate = "0.9.0"
uwsgi = "2.0.24"
Pillow = "10.3.0"
diff --git a/src/core/context_processors.py b/src/core/context_processors.py
index 8c69cf1ff..5ceb6e392 100644
--- a/src/core/context_processors.py
+++ b/src/core/context_processors.py
@@ -54,9 +54,5 @@ def sponsors(request):
}
-def events(request):
- return {'schedule_redirect_url': settings.SCHEDULE_REDIRECT_URL}
-
-
def frontend_host(request):
return {'FRONTEND_HOST': settings.FRONTEND_HOST}
diff --git a/src/core/tests/test_views.py b/src/core/tests/test_views.py
index 16f148970..5e0be86db 100644
--- a/src/core/tests/test_views.py
+++ b/src/core/tests/test_views.py
@@ -2,11 +2,7 @@
import os
import pytest
-from django.conf import settings
from django.utils.translation import activate
-from pytest_django.asserts import assertRedirects
-
-from events.models import Schedule
@pytest.mark.django_db
@@ -97,14 +93,8 @@ def test_content_pages(client, parser, content_page_full_path):
assert response.status_code == 200, content_page_full_path
-@pytest.fixture
-def schedule(db):
- """Generate a schedule to prevent the schedule page from returning 404.
- """
- return Schedule.objects.create(html='
')
-
-
-def test_content_pages_links(client, parser, schedule, content_page_full_path):
+@pytest.mark.django_db
+def test_content_pages_links(client, parser, content_page_full_path):
"""Test to make sure all in-site links in a content page work.
"""
if '/surveys/conference/' in content_page_full_path:
@@ -116,14 +106,6 @@ def test_content_pages_links(client, parser, schedule, content_page_full_path):
link_status_codes = []
for tag in link_tags:
link = tag.get('href')
- if "/events/schedule/" in link \
- and settings.SCHEDULE_REDIRECT_URL:
- assertRedirects(
- client.get(link),
- settings.SCHEDULE_REDIRECT_URL,
- fetch_redirect_response=False
- )
- continue
try:
status = client.get(link, follow=True).status_code
except Exception: # Catch internal server error for better reporting.
diff --git a/src/events/admin.py b/src/events/admin.py
index f5ba89006..79e283794 100644
--- a/src/events/admin.py
+++ b/src/events/admin.py
@@ -23,7 +23,6 @@
KeynoteEvent,
ProposedTalkEvent,
ProposedTutorialEvent,
- Schedule,
SponsoredEvent,
Time,
)
@@ -208,10 +207,3 @@ class SponsoredEventAdmin(admin.ModelAdmin):
search_fields = ['title', 'abstract']
prepopulated_fields = {'slug': ['title']}
raw_id_fields = ['host']
-
-
-@admin.register(Schedule)
-class ScheduleAdmin(admin.ModelAdmin):
- fields = ['html', 'created_at']
- readonly_fields = ['created_at']
- list_display = ['created_at']
diff --git a/src/events/forms.py b/src/events/forms.py
index 77e08f395..ab564e4ab 100644
--- a/src/events/forms.py
+++ b/src/events/forms.py
@@ -1,12 +1,6 @@
from django import forms
-from .models import CustomEvent, Schedule
-
-
-class ScheduleCreationForm(forms.ModelForm):
- class Meta:
- model = Schedule
- fields = ['html']
+from .models import CustomEvent
class CustomEventForm(forms.ModelForm):
diff --git a/src/events/management/commands/import_events.py b/src/events/management/commands/import_events.py
index 527d6288a..64dc26f24 100644
--- a/src/events/management/commands/import_events.py
+++ b/src/events/management/commands/import_events.py
@@ -7,11 +7,11 @@
from django.core.management.base import BaseCommand
from django.utils.dateparse import parse_time
-from events.models import Location, Time
-from events.renderers import EVENT_CLASSES
+from events.models import CustomEvent, KeynoteEvent, Location, ProposedTalkEvent, SponsoredEvent, Time
cst = pytz.timezone('Asia/Taipei')
+EVENT_CLASSES = (CustomEvent, KeynoteEvent, ProposedTalkEvent, SponsoredEvent)
DAYS = list(settings.EVENTS_DAY_NAMES.keys())
diff --git a/src/events/migrations/0053_delete_schedule.py b/src/events/migrations/0053_delete_schedule.py
new file mode 100644
index 000000000..9daf95bc2
--- /dev/null
+++ b/src/events/migrations/0053_delete_schedule.py
@@ -0,0 +1,16 @@
+# Generated by Django 3.2.25 on 2024-04-15 09:07
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('events', '0052_remove_sponsoredevent_prefer_time'),
+ ]
+
+ operations = [
+ migrations.DeleteModel(
+ name='Schedule',
+ ),
+ ]
diff --git a/src/events/models.py b/src/events/models.py
index d5b0229d4..01caaf93a 100644
--- a/src/events/models.py
+++ b/src/events/models.py
@@ -117,22 +117,6 @@ class Location:
R4 = '7-r4'
OTHER = '8-oth'
- @classmethod
- def get_md_width(cls, value):
- return {
- '2-all': 4,
- '3-r012': 3,
- '4-r0': 1,
- '5-r1': 1,
- '6-r2': 1,
- '1-r3': 1,
- '7-r4': 1,
- '8-oth': 1,
- }[value]
-
-
-EVENT_ROOMS = {Location.R0, Location.R1, Location.R2, Location.R3, Location.R4}
-
class BaseEvent(ConferenceRelated):
"""Base interface for all events in the schedule.
@@ -455,23 +439,3 @@ def get_absolute_url(self):
return reverse('events_tutorial_detail', kwargs={
'pk': self.proposal.pk,
})
-
-
-class Schedule(ConferenceRelated):
-
- html = models.TextField(
- verbose_name=_('HTML'),
- )
- created_at = models.DateTimeField(
- verbose_name=_('created at'),
- auto_now_add=True,
- )
-
- class Meta:
- verbose_name = _('Schedule')
- verbose_name_plural = _('Schedules')
- ordering = ['-created_at']
- get_latest_by = 'created_at'
-
- def __str__(self):
- return gettext('Schedule created at {}').format(self.created_at)
diff --git a/src/events/renderers.py b/src/events/renderers.py
deleted file mode 100644
index f72cce981..000000000
--- a/src/events/renderers.py
+++ /dev/null
@@ -1,405 +0,0 @@
-import itertools
-
-import sortedcontainers
-from django.conf import settings
-from django.utils.html import format_html, format_html_join
-from django.utils.safestring import mark_safe
-from django.utils.timezone import make_naive
-from django.utils.translation import gettext
-from django.utils.translation import gettext_lazy as _
-
-from core.utils import html_join
-from events.models import (
- CustomEvent,
- KeynoteEvent,
- Location,
- ProposedTalkEvent,
- SponsoredEvent,
-)
-from proposals.utils import format_names
-
-
-def render_customevent(e):
- return format_html(
- '',
- title=e.title,
- )
-
-
-def render_keynoteevent(e):
- return format_html(
- ''
- '
{title}
'
- '
{speaker}'
- '
',
- title=gettext('Keynote'),
- link=e.get_absolute_url(),
- speaker=e.speaker_name,
- )
-
-
-LANG_DISPLAY_MAP = {
- 'ENEN': 'EN',
- 'ZHZH': 'ZH',
- 'ZHEN': _('EN Slides'),
- 'TAI': 'TAI',
-}
-
-
-def render_proposedtalkevent(e):
- proposal = e.proposal
- speaker_names = [info.user.speaker_name for info in proposal.speakers]
- return format_html(
- ''
- '
{title}'
- '
{speakers}'
- '
{lang}
'
- '
',
- link=e.get_absolute_url(),
- title=proposal.title,
- speakers=format_names(speaker_names),
- lang=LANG_DISPLAY_MAP[proposal.language],
- )
-
-
-def render_sponsoredevent(e):
- return format_html(
- '',
- link=e.get_absolute_url(),
- title=e.title,
- speaker=e.host.speaker_name,
- lang=LANG_DISPLAY_MAP[e.language],
- )
-
-
-def render_event(e):
- func_name = f'render_{type(e).__name__.lower()}'
- try:
- func = globals()[func_name]
- except KeyError as err:
- raise ValueError(
- f'No suitable renderer for {e!r} of {type(e)!r}',
- ) from err
- return func(e)
-
-
-def render_block_location(location):
- return format_html(
- ''
- '{display}
',
- key=location.split('-', 1)[-1],
- display={
- Location.ALL: '',
- Location.R012: 'R0 R1 R2',
- Location.R0: 'R0',
- Location.R1: 'R1',
- Location.R2: 'R2',
- Location.R3: 'R3',
- Location.R4: 'R4',
- }.get(location),
- )
-
-
-def _has_tall_event(events):
- return any(
- isinstance(e, ProposedTalkEvent) or isinstance(e, SponsoredEvent)
- for e in events
- )
-
-
-def render_block(
- event, time_map, events, extra_classes=None, *,
- min_height=0, max_height=None):
- location = event.location
- height = time_map[event.end_time] - time_map[event.begin_time]
- if max_height is not None:
- height = min(height, max_height)
- if height == 1 and min_height < 1 and not _has_tall_event(events):
- height = 'small'
- return format_html(
- ''
- '{location}{event}
',
- w=Location.get_md_width(location),
- h=height,
- location=render_block_location(location),
- event=render_event(event),
- classes=(' ' + ' '.join(extra_classes) if extra_classes else ''),
- )
-
-
-def render_attached_period(begin, end):
- begin = make_naive(begin.value)
- end = make_naive(end.value)
- return format_html(
- ''
- '{begin_h}:{begin_m} – {end_h}:{end_m}
',
- begin_h=begin.hour, begin_m=begin.strftime(r'%M'),
- end_h=end.hour, end_m=end.strftime(r'%M'),
- )
-
-
-def _render_blocks(events, time_map):
- """Render a R0-3 belt, a R0-2 partial belt (plus optionally a R3 event),
- or an all-block (with or without R3) format.
- """
- if events[0].location == Location.R3:
- # If this contains R3, shuffle it to the back.
- r3_event, *events = events
-
- # If this is not a smooth row, we need to prevent any block from
- # being rendered as short because we can't handle it with multi-height.
- if any(r3_event.end_time != e.end_time for e in events):
- min_height = 1
- else:
- min_height = 0
- r3_block = render_block(
- r3_event, time_map, events, min_height=min_height,
- )
- else:
- r3_event = None
- r3_block = ''
-
- # Render period block for R3.
- # We cheat a little here: Times in R0-2 are supposed to be identical;
- # only R3 can be different. We just compare the first event's times
- # and R3's. If they are identical, R3 does not need its own period block,
- # otherwise it does.
- r3_period = ''
- rx_begin, rx_end = events[0].begin_time, events[0].end_time
- if r3_event:
- r3_begin = r3_event.begin_time
- r3_end = r3_event.end_time
- if r3_begin != rx_begin or r3_end != rx_end:
- r3_period = render_attached_period(r3_begin, r3_end)
-
- return format_html(
- '{r012_period}{r012_blocks}{r3_period}{r3_block}',
- r012_period=render_attached_period(rx_begin, rx_end),
- r012_blocks=html_join(
- '',
- (render_block(e, time_map, events) for e in events),
- ),
- r3_period=r3_period,
- r3_block=r3_block,
- )
-
-
-def _render_multirow_subrow(event_iter, time_map, events):
- # Render period block for this row, and the first event.
- e0 = next(event_iter)
- cells = [
- render_attached_period(e0.begin_time, e0.end_time),
- render_block(e0, time_map, events)
- ]
- cols = Location.get_md_width(e0.location)
-
- # Keep rendering until we fill a row.
- while cols < 3:
- try:
- e = next(event_iter)
- except StopIteration:
- break
- cells.append(render_block(e, time_map, events))
- cols += Location.get_md_width(e.location)
- return cells
-
-
-def _render_multirow(events, time_map, max_height):
- """Render a complex format that contains a multi-row R3 event.
- """
- sp_event, *events = events
- # Sort events by time, and then location. This is done here because
- # we need to exclude the R3 event (by putting it first).
- events = sorted(events, key=lambda e: (e.begin_time.value, e.location))
- event_iter = iter(events)
-
- # Render R0-R2 events in the first time period.
- cells = _render_multirow_subrow(event_iter, time_map, events)
-
- # Render the multi-row R3 event.
- cells.extend([
- render_attached_period(sp_event.begin_time, sp_event.end_time),
- render_block(
- sp_event, time_map, events, ['pull-right'],
- max_height=max_height,
- ),
- ])
-
- # Render the rest of the events.
- try:
- while True:
- cells += _render_multirow_subrow(event_iter, time_map, events)
- except StopIteration:
- pass
-
- # Join the results.
- return html_join('', cells)
-
-
-def render_columned_period(times, events):
- cell_args = [
- _get_column_period_args(begin, end)
- for begin, end in zip(times[:-1], times[1:])
- if any(e.end_time == end for e in events)
- ]
- cells = format_html_join(
- '',
- '{}:{}
|
{}:{}
',
- iter(cell_args),
- )
- height = len(cell_args)
- if height == 1 and not _has_tall_event(events):
- height = 'small'
- html = format_html(
- '{cells}
',
- height=height,
- cells=cells,
- )
- return html, height
-
-
-def _get_column_period_args(begin_time, end_time):
- begin_naive = make_naive(begin_time.value)
- end_naive = make_naive(end_time.value)
- return (
- begin_naive.hour,
- begin_naive.strftime('%M'),
- end_naive.hour,
- end_naive.strftime('%M'),
- )
-
-
-def render_row(times, events):
- events = sorted(events, key=lambda e: e.location)
- times = sorted(times)
- time_map = {t: i for i, t in enumerate(times)}
-
- period_block, max_height = render_columned_period(times, events)
- try:
- sp_event = events[0]
- except IndexError: # No events in this time period.
- content = ''
- else:
- if (sp_event.location != Location.R3 or sp_event.end_time <= times[1]):
- # If there's no R3 event, or if R3 event is not multi-row.
- content = _render_blocks(events, time_map)
- else:
- # If there is a multi-row R3 event.
- content = _render_multirow(events, time_map, max_height)
- return format_html(
- '{period}'
- '',
- period=period_block,
- content=content,
- )
-
-
-# This is made global so that we can easily test for equality.
-def group_key(e):
- return e.location
-
-
-def make_group(*events):
- return sortedcontainers.SortedListWithKey(events, key=group_key)
-
-
-def collect_event_groups(events):
- # Map of (sorted-tuple-of-times): [list-of-events].
- # This automatically sorts the keys by ``begin_time``.
- groups = sortedcontainers.SortedDict()
-
- for e in events:
-
- e_begin_time = e.begin_time
- e_end_time = e.end_time
-
- # Short circuit: If there's an identical group, just join it.
- if (e_begin_time, e_end_time) in groups:
- groups[(e_begin_time, e_end_time)].add(e)
- continue
-
- # Look for a suitable group.
- for key in groups.keys():
- begin_time, *_, end_time = key
- if ((begin_time <= e_begin_time and end_time >= e_end_time) or
- (e_begin_time <= begin_time and e_end_time >= end_time)):
- # Either key can contain event, or event can contain key.
- events = groups.pop(key)
- events.add(e)
- times = sorted(set(key) | {e_begin_time, e_end_time})
- groups[tuple(times)] = events
- break
- else:
- # No suitable group. Let's make a new one.
- # This automatically sorts the items by location.
- groups[(e_begin_time, e_end_time)] = make_group(e)
-
- return groups
-
-
-EVENT_CLASSES = (CustomEvent, KeynoteEvent, ProposedTalkEvent, SponsoredEvent)
-
-
-def _filter_events(kls, day):
- return (
- kls.objects
- .filter(begin_time__value__date=day, end_time__value__date=day)
- .select_related('begin_time', 'end_time')
- )
-
-
-def render_table(day):
- events = itertools.chain.from_iterable(
- _filter_events(Cls, day)
- for Cls in EVENT_CLASSES
- )
- groups = collect_event_groups(events)
-
- head = mark_safe("""
-
- """)
- body = html_join('', (
- render_row(times, group) for times, group in groups.items()
- ))
- return format_html(
- '{head}{body}
',
- head=head, body=body,
- )
-
-
-def render_day(day, display):
- events = list(itertools.chain.from_iterable(
- _filter_events(Cls, day)
- for Cls in EVENT_CLASSES
- ))
- if not events:
- return ''
- return format_html(
- '{table}',
- slug=day.strftime(r'%Y-%m-%d'),
- display=display, table=render_table(day),
- )
-
-
-def render_all():
- return html_join('', (
- render_day(day, display)
- for day, display in settings.EVENTS_DAY_NAMES.items()
- ))
diff --git a/src/events/templatetags/events.py b/src/events/templatetags/events.py
index f8d2919a5..3ba31e503 100644
--- a/src/events/templatetags/events.py
+++ b/src/events/templatetags/events.py
@@ -1,161 +1,12 @@
-from datetime import datetime, timedelta
-from math import floor
-
from django.conf import settings
from django.template import Library
-from django.template.loader import get_template
-from django.utils import translation
-from django.utils.translation import gettext_lazy as _
-from events.models import KeynoteEvent, Location
-from proposals.utils import format_names
+from events.models import KeynoteEvent
from sponsors.models import OpenRole, Sponsor
-LOCATION_DISPLAY_DICT = {
- Location.R0: _('R1'),
- Location.R1: _('R2'),
- Location.R2: _('R3'),
- Location.R3: _('Multifunction room'),
- Location.R4: _('Goodideas Studio'),
- Location.R012: _('R1, R2, R3'),
- Location.OTHER: _('Other'),
-}
-ROW_UNIT = 5 * 60 # in seconds
-MARKER_UNIT = 30 * 60 # in seconds
-
register = Library()
-@register.simple_tag
-def calc_grid_row(begin, end, timeline_start='08:30'):
- d_format = '%H:%M'
- start_point = datetime.strptime(timeline_start, d_format)
-
- def get_row(t):
- diff = datetime.strptime(t, d_format) - start_point
- return int(diff.seconds / ROW_UNIT) + 1
-
- start_row = get_row(begin)
- end_row = get_row(end)
- return {'start_row': start_row, 'end_row': end_row}
-
-
-@register.filter
-def gen_timeline(start, end):
- timeline = [{'time': start, 'row_start': 1, 'row_end': 6}]
- d_format = '%H:%M'
-
- time_start = datetime.strptime(start, d_format)
- time_end = datetime.strptime(end, d_format)
- ticks_count = floor((time_end - time_start).seconds / MARKER_UNIT)
-
- scale_unit_multiplier = int(MARKER_UNIT / ROW_UNIT) # 6 (magic number)
- time_next = time_start
- row_start_next = 1
- row_end_next = 6
- for __ in range(ticks_count):
- time_next += timedelta(seconds=MARKER_UNIT)
- row_start_next += scale_unit_multiplier
- row_end_next += scale_unit_multiplier
- t = datetime.strftime(time_next, d_format)
- c = '--hour' if t.endswith('00') else '--half-an-hour'
- timeline.append({
- 'time': t,
- 'row_start': row_start_next,
- 'row_end': row_end_next,
- 'class': c
- })
-
- return timeline
-
-
-@register.filter
-def event_cell_class(event):
- event_class = {
- 'events.customevent': 'custom',
- 'events.keynoteevent': 'keynote',
- 'events.proposedtalkevent': 'talk',
- 'events.proposedtutorialevent': 'tutorial',
- 'events.sponsoredevent': 'talk',
- }[event._meta.label_lower]
- classes = [event_class]
- if getattr(event, 'break_event', False):
- classes.append('is-break')
- return ' '.join(classes)
-
-
-@register.filter
-def event_tr_class(slot_info):
- for __, event in slot_info:
- if event._meta.label_lower == 'events.customevent':
- return 'custom'
- return ''
-
-
-@register.filter
-def room_display(value):
- return LOCATION_DISPLAY_DICT.get(value, '')
-
-
-def get_custom_event_display(event):
- template = get_template('events/_includes/schedule_custom_event.html')
- return template.render({'event': event})
-
-
-def get_keynote_event_display(event):
- template = get_template('events/_includes/schedule_keynote_event.html')
- return template.render({'event': event, 'is_remote': event.is_remote})
-
-
-def _render_talk_event_template(event, info, speaker_names, sponsored, is_remote=False):
- template = get_template('events/_includes/schedule_talk_event.html')
- return template.render({
- 'event': event, 'info': info, 'sponsored': sponsored,
- 'speakers': format_names(speaker_names), 'is_remote': is_remote,
- })
-
-
-def get_talk_event_display(event):
- proposal = event.proposal
- speaker_names = [proposal.submitter.speaker_name]
- if getattr(event, '_additional_speaker_count', 1):
- speaker_names.extend(
- proposal.additionalspeaker_set
- .values_list('user__speaker_name', flat=True),
- )
- return _render_talk_event_template(event, proposal, speaker_names, False, event.is_remote)
-
-
-def get_tutorial_event_display(event):
- proposal = event.proposal
- speaker_names = [proposal.submitter.speaker_name]
- if getattr(event, '_additional_speaker_count', 1):
- speaker_names.extend(
- proposal.additionalspeaker_set
- .values_list('user__speaker_name', flat=True),
- )
- return _render_talk_event_template(event, proposal, speaker_names, False, event.is_remote)
-
-
-def get_sponsored_event_display(event):
- return _render_talk_event_template(
- event, event, [event.host.speaker_name], True,
- )
-
-
-@register.filter
-def event_display(event):
- with translation.override('en-us'):
- f = {
- 'events.customevent': get_custom_event_display,
- 'events.keynoteevent': get_keynote_event_display,
- 'events.proposedtalkevent': get_talk_event_display,
- 'events.proposedtutorialevent': get_tutorial_event_display,
- 'events.sponsoredevent': get_sponsored_event_display,
- }[event._meta.label_lower]
- return f(event)
-
-
@register.filter
def event_date_display(event):
try:
diff --git a/src/events/tests/renderers/__init__.py b/src/events/tests/renderers/__init__.py
deleted file mode 100644
index e69de29bb..000000000
diff --git a/src/events/tests/renderers/conftest.py b/src/events/tests/renderers/conftest.py
deleted file mode 100644
index c097f6fe6..000000000
--- a/src/events/tests/renderers/conftest.py
+++ /dev/null
@@ -1,329 +0,0 @@
-import datetime
-
-import pytest
-import pytz
-
-from events.models import (
- CustomEvent,
- KeynoteEvent,
- Location,
- ProposedTalkEvent,
- Time,
-)
-from proposals.models import AdditionalSpeaker
-
-cst = pytz.timezone('Asia/Taipei')
-
-
-class RendererTestUtils:
- @staticmethod
- def is_safe(s):
- """Check whether a string is safe.
-
- This is Django's internal API, but we exploit it for easy testing.
- """
- return not s or hasattr(s, '__html__')
-
-
-@pytest.fixture
-def utils():
- return RendererTestUtils
-
-
-@pytest.fixture
-def keynote_belt_event(db, get_time):
- return KeynoteEvent.objects.create(
- speaker_name='Amber Brown',
- slug='amber-brown',
- begin_time=get_time('2016-06-05 9:00'),
- end_time=get_time('2016-06-05 10:00'),
- location=Location.ALL,
- )
-
-
-@pytest.fixture
-def custom_partial_belt_event(db, get_time):
- return CustomEvent.objects.create(
- title='Job Fair',
- begin_time=get_time('2016-06-04 14:45'),
- end_time=get_time('2016-06-04 15:15'),
- location=Location.R012,
- )
-
-
-@pytest.fixture
-def proposed_talk_block_event(accepted_talk_proposal, another_user, get_time):
- e = ProposedTalkEvent.objects.create(
- proposal=accepted_talk_proposal,
- begin_time=get_time('2016-06-03 16:00'),
- end_time=get_time('2016-06-03 16:45'),
- location=Location.R0,
- )
- AdditionalSpeaker.objects.create(
- user=another_user, proposal=accepted_talk_proposal,
- )
- return e
-
-
-@pytest.fixture
-def events(
- custom_partial_belt_event, keynote_belt_event,
- proposed_talk_block_event, sponsored_block_event):
- return {
- 'custom_event': custom_partial_belt_event,
- 'keynote_event': keynote_belt_event,
- 'proposed_talk_event': proposed_talk_block_event,
- 'sponsored_event': sponsored_block_event,
- }
-
-
-@pytest.fixture
-def day():
- return datetime.date(2016, 8, 19)
-
-
-@pytest.fixture
-def make_time(day):
-
- def _make_time(h, m=0):
- dt = datetime.datetime.combine(day, datetime.time(h, m))
- return Time(value=cst.localize(dt))
-
- return _make_time
-
-
-@pytest.fixture
-def belt_begin_time(make_time):
- return make_time(15)
-
-
-@pytest.fixture
-def belt_end_time(make_time):
- return make_time(16)
-
-
-@pytest.fixture
-def belt_event(belt_begin_time, belt_end_time):
- return KeynoteEvent(
- speaker_name='Amber Brown',
- slug='amber-brown',
- begin_time=belt_begin_time,
- end_time=belt_end_time,
- )
-
-
-@pytest.fixture
-def partial_belt_begin_time(make_time):
- return make_time(1)
-
-
-@pytest.fixture
-def partial_belt_end_time(make_time):
- return make_time(2)
-
-
-@pytest.fixture
-def partial_belt_events(partial_belt_begin_time, partial_belt_end_time):
- event = CustomEvent(
- title='Refreshment',
- location=Location.R012,
- begin_time=partial_belt_begin_time,
- end_time=partial_belt_end_time,
- )
- return [event]
-
-
-@pytest.fixture
-def partial_belt_block_begin_time(make_time):
- return make_time(3)
-
-
-@pytest.fixture
-def partial_belt_block_end_time(make_time):
- return make_time(4)
-
-
-@pytest.fixture
-def partial_belt_block_events(
- partial_belt_block_begin_time, partial_belt_block_end_time):
- events = [
- CustomEvent(
- title='Refreshment',
- location=Location.R012,
- begin_time=partial_belt_block_begin_time,
- end_time=partial_belt_block_end_time,
- ),
- CustomEvent(
- title='Free-market sub-orbital tattoo',
- location=Location.R3,
- begin_time=partial_belt_block_begin_time,
- end_time=partial_belt_block_end_time,
- ),
- ]
- return events
-
-
-@pytest.fixture
-def partial_block_begin_time(make_time):
- return make_time(5)
-
-
-@pytest.fixture
-def partial_block_end_time(make_time):
- return make_time(6)
-
-
-@pytest.fixture
-def partial_block_events(partial_block_begin_time, partial_block_end_time):
- events = [
- CustomEvent(
- title='Boost Maintainability',
- location=Location.R0,
- begin_time=partial_block_begin_time,
- end_time=partial_block_end_time,
- ),
- CustomEvent(
- title='We Made the PyCon TW 2016 Website',
- location=Location.R1,
- begin_time=partial_block_begin_time,
- end_time=partial_block_end_time,
- ),
- CustomEvent(
- title='Deep Learning and Application in Python',
- location=Location.R2,
- begin_time=partial_block_begin_time,
- end_time=partial_block_end_time,
- ),
- ]
- return events
-
-
-@pytest.fixture
-def block_begin_time(make_time):
- return make_time(7)
-
-
-@pytest.fixture
-def block_end_time(make_time):
- return make_time(8)
-
-
-@pytest.fixture
-def block_events(block_begin_time, block_end_time):
- events = [
- CustomEvent(
- title='Boost Maintainability',
- location=Location.R0,
- begin_time=block_begin_time,
- end_time=block_end_time,
- ),
- CustomEvent(
- title='We Made the PyCon TW 2016 Website',
- location=Location.R1,
- begin_time=block_begin_time,
- end_time=block_end_time,
- ),
- CustomEvent(
- title='Deep Learning and Application in Python',
- location=Location.R2,
- begin_time=block_begin_time,
- end_time=block_end_time,
- ),
- CustomEvent(
- title='Free-market sub-orbital tattoo',
- location=Location.R3,
- begin_time=block_begin_time,
- end_time=block_end_time,
- ),
- ]
- return events
-
-
-@pytest.fixture
-def mismatch_block_begin_time(make_time):
- return make_time(9)
-
-
-@pytest.fixture
-def mismatch_block_mid_time(make_time):
- return make_time(10)
-
-
-@pytest.fixture
-def mismatch_block_end_time(make_time):
- return make_time(11)
-
-
-@pytest.fixture
-def mismatch_block_events(
- mismatch_block_begin_time, mismatch_block_mid_time,
- mismatch_block_end_time):
- events = [
- CustomEvent(
- title='Refreshment',
- location=Location.R012,
- begin_time=mismatch_block_begin_time,
- end_time=mismatch_block_end_time,
- ),
- CustomEvent(
- title='Free-market sub-orbital tattoo',
- location=Location.R3,
- begin_time=mismatch_block_begin_time,
- end_time=mismatch_block_mid_time,
- ),
- ]
- return events
-
-
-@pytest.fixture
-def multirow_block_begin_time(make_time):
- return make_time(12)
-
-
-@pytest.fixture
-def multirow_block_mid_time(make_time):
- return make_time(13)
-
-
-@pytest.fixture
-def multirow_block_end_time(make_time):
- return make_time(14)
-
-
-@pytest.fixture
-def multirow_block_events(
- multirow_block_begin_time, multirow_block_mid_time,
- multirow_block_end_time):
- events = [
- CustomEvent(
- title='Boost Maintainability',
- location=Location.R0,
- begin_time=multirow_block_begin_time,
- end_time=multirow_block_mid_time,
- ),
- CustomEvent(
- title='We Made the PyCon TW 2016 Website',
- location=Location.R1,
- begin_time=multirow_block_begin_time,
- end_time=multirow_block_mid_time,
- ),
- CustomEvent(
- title='Deep Learning and Application in Python',
- location=Location.R2,
- begin_time=multirow_block_begin_time,
- end_time=multirow_block_mid_time,
- ),
- CustomEvent(
- title='Free-market sub-orbital tattoo',
- location=Location.R3,
- begin_time=multirow_block_begin_time,
- end_time=multirow_block_end_time,
- ),
- CustomEvent(
- title='Refreshment',
- location=Location.R012,
- begin_time=multirow_block_mid_time,
- end_time=multirow_block_end_time,
- ),
- ]
- return events
diff --git a/src/events/tests/renderers/test_collect.py b/src/events/tests/renderers/test_collect.py
deleted file mode 100644
index cda922946..000000000
--- a/src/events/tests/renderers/test_collect.py
+++ /dev/null
@@ -1,37 +0,0 @@
-import itertools
-
-import pytest
-import sortedcontainers
-
-from events import renderers
-from events.renderers import make_group
-
-
-@pytest.fixture
-def events(
- belt_event, partial_belt_events, partial_belt_block_events,
- partial_block_events, block_events, mismatch_block_events,
- multirow_block_events):
- return itertools.chain(
- [belt_event], partial_belt_events, partial_belt_block_events,
- partial_block_events, block_events, mismatch_block_events,
- multirow_block_events,
- )
-
-
-def test_collect_event_groups(
- belt_event, partial_belt_events, partial_belt_block_events,
- partial_block_events, block_events, mismatch_block_events,
- multirow_block_events, make_time, events):
- groups = renderers.collect_event_groups(events)
- assert groups == sortedcontainers.SortedDict({
- (make_time(1), make_time(2)): make_group(*partial_belt_events),
- (make_time(3), make_time(4)): make_group(*partial_belt_block_events),
- (make_time(5), make_time(6)): make_group(*partial_block_events),
- (make_time(7), make_time(8)): make_group(*block_events),
- (make_time(9), make_time(10), make_time(11)): make_group(
- *mismatch_block_events),
- (make_time(12), make_time(13), make_time(14)): make_group(
- *multirow_block_events),
- (make_time(15), make_time(16)): make_group(belt_event),
- })
diff --git a/src/events/tests/renderers/test_render_block.py b/src/events/tests/renderers/test_render_block.py
deleted file mode 100644
index cfbacccdd..000000000
--- a/src/events/tests/renderers/test_render_block.py
+++ /dev/null
@@ -1,150 +0,0 @@
-import pytest
-
-from events import renderers
-from events.models import CustomEvent, Location
-
-
-@pytest.fixture
-def simple_component_renderer(mocker):
- mocker.patch.multiple(
- 'events.renderers',
- render_event=str,
- render_block_location=lambda v: f'{v} ',
- )
-
-
-@pytest.fixture
-def time_map(events):
- times = set()
- for e in events.values():
- times.add(e.begin_time)
- times.add(e.end_time)
- return {time: i for i, time in enumerate(sorted(times))}
-
-
-# Hack to get all possible location values by introspection.
-POSSIBLE_LOCATIONS = sorted(
- getattr(Location, k)
- for k in Location.__dict__ if k.isupper() and k != 'OTHER'
-)
-
-
-@pytest.mark.parametrize('location', POSSIBLE_LOCATIONS)
-def test_render_block_location(parser, utils, location):
- rendered = renderers.render_block_location(location)
- assert utils.is_safe(rendered)
-
- expected = {
- Location.ALL: (
- ''
- ),
- Location.R012: (
- ''
- 'R0 R1 R2
'
- ),
- Location.R0: (
- ''
- 'R0
'
- ),
- Location.R1: (
- ''
- 'R1
'
- ),
- Location.R2: (
- ''
- 'R2
'
- ),
- Location.R3: (
- ''
- 'R3
'
- ),
- Location.R4: (
- ''
- 'R4
'
- ),
- }.get(location)
- if expected:
- assert parser.arrange(rendered) == parser.arrange(expected)
-
-
-@pytest.mark.parametrize('event_key', [
- 'custom_event', 'keynote_event', 'proposed_talk_event', 'sponsored_event',
-])
-@pytest.mark.usefixtures('simple_component_renderer')
-def test_render_block(parser, utils, time_map, events, event_key):
- e = events[event_key]
- rendered = renderers.render_block(e, time_map, [e])
- assert utils.is_safe(rendered)
-
- expected = {
- 'custom_event': """
-
- 3-r012 Job Fair
-
""",
- 'keynote_event': """
-
- 2-all Keynote: Amber Brown
-
""",
- 'proposed_talk_event': """
-
- 4-r0 Beyond the Style Guides<br>
-
""",
- 'sponsored_event': """
-
- 6-r2 Camera engine office woman lights
-
""",
- }[event_key]
- assert parser.arrange(rendered) == parser.arrange(expected)
-
-
-@pytest.mark.parametrize('event_key,begin,end', [
- ('custom_event', '14:45', '15:15'),
- ('keynote_event', '9:00', '10:00'),
- ('proposed_talk_event', '16:00', '16:45'),
- ('sponsored_event', '11:00', '11:25'),
-])
-def test_render_attached_period(utils, events, event_key, begin, end):
- e = events[event_key]
- rendered = renderers.render_attached_period(e.begin_time, e.end_time)
- assert utils.is_safe(rendered)
- assert rendered == (
- f'{begin} – {end}
'
- )
-
-
-@pytest.mark.parametrize('time_count', [2, 3, 4])
-def test_render_columned_period(parser, utils, make_time, time_count):
- times = [make_time(h) for h in range(time_count)]
- rendered, _ = renderers.render_columned_period(times, [
- CustomEvent(
- title='M<3', location=Location.ALL,
- begin_time=begin_time, end_time=end_time,
- )
- for begin_time, end_time in zip(times[:-1], times[1:])
- ])
- assert utils.is_safe(rendered)
-
- expected = {
- 2: (
- ''
- ),
- 3: (
- ''
- '
0:00
|
1:00
'
- '
1:00
|
2:00
'
- '
'
- ),
- 4: (
- ''
- '
0:00
|
1:00
'
- '
1:00
|
2:00
'
- '
2:00
|
3:00
'
- '
'
- ),
- }[time_count]
- assert parser.arrange(rendered) == parser.arrange(expected)
diff --git a/src/events/tests/renderers/test_render_row.py b/src/events/tests/renderers/test_render_row.py
deleted file mode 100644
index d3fcc5ea1..000000000
--- a/src/events/tests/renderers/test_render_row.py
+++ /dev/null
@@ -1,183 +0,0 @@
-import pytest
-from django.utils.timezone import make_naive
-
-from events import renderers
-
-
-@pytest.fixture
-def simple_renderer(mocker):
-
- def _simple_block_renderer(
- event, time_map, events, extra_classes=None, *,
- min_height=0, max_height=None):
- if extra_classes:
- fmt = '|{event} ({classes})| '
- else:
- fmt = '|{event}| '
- return fmt.format(classes=' '.join(extra_classes or []), event=event)
-
- def _simple_attached_period_renderer(begin, end):
- return '|{0.hour}:{0:%M} {1.hour}:{1:%M}| '.format(
- make_naive(begin.value), make_naive(end.value),
- )
-
- def _simple_columned_period_renderer(times, events):
- html = '|{}| '.format(
- ' '.join(
- '{0.hour}:{0:%M}'.format(make_naive(time.value))
- for time in times
- )
- )
- return html, len(events)
-
- mocker.patch.multiple(
- 'events.renderers',
- render_block=_simple_block_renderer,
- render_attached_period=_simple_attached_period_renderer,
- render_columned_period=_simple_columned_period_renderer,
- )
-
-
-@pytest.mark.usefixtures('simple_renderer')
-def test_render_belt_events_row(parser, utils, keynote_belt_event):
- times = [keynote_belt_event.begin_time, keynote_belt_event.end_time]
- rendered = renderers.render_row(times, [keynote_belt_event])
- assert utils.is_safe(rendered)
- assert parser.arrange(rendered) == parser.arrange("""
- |9:00 10:00|
-
-
- |9:00 10:00|
- |Keynote: Amber Brown|
-
-
- """)
-
-
-@pytest.mark.usefixtures('simple_renderer')
-def test_render_partial_belt_events_row(
- parser, utils,
- partial_belt_begin_time, partial_belt_end_time, partial_belt_events):
- times = [partial_belt_begin_time, partial_belt_end_time]
- rendered = renderers.render_row(times, partial_belt_events)
- assert utils.is_safe(rendered)
- assert parser.arrange(rendered) == parser.arrange("""
- |1:00 2:00|
-
-
- |1:00 2:00|
- |Refreshment|
-
-
- """)
-
-
-@pytest.mark.usefixtures('simple_renderer')
-def test_render_partial_belt_block_events_row(
- parser, utils, partial_belt_block_begin_time,
- partial_belt_block_end_time, partial_belt_block_events):
- times = [partial_belt_block_begin_time, partial_belt_block_end_time]
- rendered = renderers.render_row(times, partial_belt_block_events)
- assert utils.is_safe(rendered)
- assert parser.arrange(rendered) == parser.arrange("""
- |3:00 4:00|
-
-
- |3:00 4:00|
- |Refreshment|
- |Free-market sub-orbital tattoo|
-
-
- """)
-
-
-@pytest.mark.usefixtures('simple_renderer')
-def test_render_partial_block_events_row(
- parser, utils, partial_block_begin_time,
- partial_block_end_time, partial_block_events):
- times = [partial_block_begin_time, partial_block_end_time]
- rendered = renderers.render_row(times, partial_block_events)
- assert utils.is_safe(rendered)
- assert parser.arrange(rendered) == parser.arrange("""
- |5:00 6:00|
-
-
- |5:00 6:00|
- |Boost Maintainability|
- |We Made the PyCon TW 2016 Website|
- |Deep Learning and Application in Python|
-
-
- """)
-
-
-@pytest.mark.usefixtures('simple_renderer')
-def test_render_block_events_row(
- parser, utils, block_begin_time, block_end_time, block_events):
- times = [block_begin_time, block_end_time]
- rendered = renderers.render_row(times, block_events)
- assert utils.is_safe(rendered)
- assert parser.arrange(rendered) == parser.arrange("""
- |7:00 8:00|
-
-
- |7:00 8:00|
- |Boost Maintainability|
- |We Made the PyCon TW 2016 Website|
- |Deep Learning and Application in Python|
- |Free-market sub-orbital tattoo|
-
-
- """)
-
-
-@pytest.mark.usefixtures('simple_renderer')
-def test_render_mismatch_block_events_row(
- parser, utils, mismatch_block_begin_time, mismatch_block_mid_time,
- mismatch_block_end_time, mismatch_block_events):
- times = [
- mismatch_block_begin_time,
- mismatch_block_mid_time,
- mismatch_block_end_time,
- ]
- rendered = renderers.render_row(times, mismatch_block_events)
- assert utils.is_safe(rendered)
- assert parser.arrange(rendered) == parser.arrange("""
- |9:00 10:00 11:00|
-
-
- |9:00 11:00|
- |Refreshment|
- |9:00 10:00|
- |Free-market sub-orbital tattoo|
-
-
- """)
-
-
-@pytest.mark.usefixtures('simple_renderer')
-def test_render_multirow_block_events_row(
- parser, utils, multirow_block_begin_time, multirow_block_mid_time,
- multirow_block_end_time, multirow_block_events):
- times = [
- multirow_block_begin_time,
- multirow_block_mid_time,
- multirow_block_end_time,
- ]
- rendered = renderers.render_row(times, multirow_block_events)
- assert utils.is_safe(rendered)
- assert parser.arrange(rendered) == parser.arrange("""
- |12:00 13:00 14:00|
-
-
- |12:00 13:00|
- |Boost Maintainability|
- |We Made the PyCon TW 2016 Website|
- |Deep Learning and Application in Python|
- |12:00 14:00|
- |Free-market sub-orbital tattoo (pull-right)|
- |13:00 14:00|
- |Refreshment|
-
-
- """)
diff --git a/src/events/urls.py b/src/events/urls.py
index a32dbdee2..4c112d9d3 100644
--- a/src/events/urls.py
+++ b/src/events/urls.py
@@ -4,10 +4,6 @@
from events import views
urlpatterns = [
- url(r'^schedule/$', views.ScheduleView.as_view(), name='events_schedule'),
- url(r'^schedule/new/$', views.ScheduleCreateView.as_view()),
-
-
url(r'^talks/$', views.TalkListView.as_view(),
name='events_talk_list'),
url(r'^tutorials/$', views.TutorialListView.as_view(),
diff --git a/src/events/views.py b/src/events/views.py
index dc4f5ed7a..9604dfbf7 100644
--- a/src/events/views.py
+++ b/src/events/views.py
@@ -1,33 +1,12 @@
-import collections
-import itertools
import logging
-from django.conf import settings
-from django.contrib.auth.mixins import PermissionRequiredMixin
from django.db.models import Count, Prefetch
-from django.http import HttpResponseNotFound, HttpResponseRedirect
-from django.urls import reverse
-from django.utils import translation
-from django.utils.translation import gettext_lazy as _
-from django.views.generic import CreateView, DetailView, ListView, TemplateView
+from django.views.generic import DetailView, ListView
-from core.mixins import FormValidMessageMixin
from core.utils import OrderedDefaultDict, TemplateExistanceStatusResponse
from proposals.models import AdditionalSpeaker, TalkProposal, TutorialProposal
-from .forms import ScheduleCreationForm
-from .models import (
- EVENT_ROOMS,
- CustomEvent,
- KeynoteEvent,
- Location,
- ProposedTalkEvent,
- ProposedTutorialEvent,
- Schedule,
- SponsoredEvent,
- Time,
-)
-from .renderers import render_all
+from .models import ProposedTalkEvent, ProposedTutorialEvent, SponsoredEvent
logger = logging.getLogger(__name__)
@@ -96,143 +75,6 @@ def get_queryset(self):
return qs
-class ScheduleView(TemplateView):
- template_name = 'events/schedule.html'
- response_class = TemplateExistanceStatusResponse
-
- def get(self, request, *args, **kwargs):
- try:
- self.schedule = Schedule.objects.latest()
- except Schedule.DoesNotExist:
- redirect_url = settings.SCHEDULE_REDIRECT_URL
- if not redirect_url:
- return HttpResponseNotFound()
- return HttpResponseRedirect(redirect_url)
- return super().get(request, *args, **kwargs)
-
- def get_context_data(self, **kwargs):
- return super().get_context_data(
- schedule_html=self.schedule.html,
- schedule_days=collections.OrderedDict([
- (key, {'name': value})
- for key, value in settings.EVENTS_DAY_NAMES.items()
- ]),
- **kwargs
- )
-
-
-class ScheduleCreateMixin:
- form_class = ScheduleCreationForm
- form_valid_message = _('New talk schedule generated successfully.')
- permission_required = ['events.add_schedule']
- template_name = 'events/schedule_create.html'
- response_class = TemplateExistanceStatusResponse
-
- def get_success_url(self):
- return reverse('events_schedule')
-
-
-class ScheduleCreate2016View(
- ScheduleCreateMixin, FormValidMessageMixin, PermissionRequiredMixin, CreateView
-):
- def get_context_data(self, **kwargs):
- return super().get_context_data(content=render_all(), **kwargs)
-
-
-def _room_sort_key(room):
- return room.split('-', 1)[-1]
-
-
-class ScheduleCreateView(
- ScheduleCreateMixin, FormValidMessageMixin, PermissionRequiredMixin, CreateView
-):
- event_querysets = [
- CustomEvent.objects.all().exclude(location=Location.OTHER),
- KeynoteEvent.objects.all().exclude(location=Location.OTHER),
- (
- ProposedTalkEvent.objects
- .select_related('proposal__submitter')
- .annotate(_additional_speaker_count=Count(
- 'proposal__additionalspeaker_set',
- )).exclude(location=Location.OTHER)
- ),
- SponsoredEvent.objects.select_related('host').exclude(location=Location.OTHER),
- (
- ProposedTutorialEvent.objects
- .select_related('proposal__submitter')
- .annotate(_additional_speaker_count=Count(
- 'proposal__additionalspeaker_set',
- )).exclude(location=Location.OTHER)
- ),
- ]
-
- def get_day_grouped_events(self):
- begin_time_event_dict = collections.defaultdict(set)
- for qs in self.event_querysets:
- for event in qs.select_related('begin_time', 'end_time'):
- begin_time_event_dict[event.begin_time].add(event)
-
- day_info_dict = collections.OrderedDict(
- (date, {
- 'name': name,
- 'rooms': set(),
- 'slots': {},
- 'slots_mobile': {},
- 'timeline': {},
- }) for date, name in settings.EVENTS_DAY_NAMES.items()
- )
-
- times = list(Time.objects.order_by('value'))
- end_time_iter = iter(times)
- next(end_time_iter, None)
-
- for begin, __ in zip(times, end_time_iter):
- try:
- day_info = day_info_dict[begin.value.date()]
- except KeyError:
- logger.warn(f'Invalid time sot dropped: {begin}')
- continue
- for event in begin_time_event_dict[begin]:
- location = event.location
- day_info['slots'].setdefault(location, [])
- day_info['slots_mobile'].setdefault(event.begin_time, [])
- day_info['timeline'].setdefault('begin', event.begin_time)
- day_info['timeline'].setdefault('end', event.end_time)
-
- day_info['slots'][location].append(event)
- day_info['slots_mobile'][event.begin_time].append(event)
- day_info['timeline']['begin'] = min(
- day_info['timeline']['begin'],
- event.begin_time
- )
- day_info['timeline']['end'] = max(
- day_info['timeline']['end'],
- event.end_time
- )
- if location in EVENT_ROOMS:
- day_info['rooms'].add(location)
-
- for info in day_info_dict.values():
- # Sort rooms.
- info['rooms'] = sorted(info['rooms'], key=_room_sort_key)
-
- return day_info_dict
-
- def get_context_data(self, **kwargs):
- with translation.override('en-us'):
- schedule_days = self.get_day_grouped_events()
-
- all_rooms = sorted(set(itertools.chain.from_iterable(
- info['rooms'] for info in schedule_days.values()
- )), key=_room_sort_key)
-
- return super().get_context_data(
- schedule_days=schedule_days,
- all_rooms=all_rooms,
- **kwargs
- )
-
-
class EventInfoMixin:
def is_event_sponsored(self):
diff --git a/src/locale/en_US/LC_MESSAGES/django.po b/src/locale/en_US/LC_MESSAGES/django.po
index ab539105a..97ad6e3ca 100644
--- a/src/locale/en_US/LC_MESSAGES/django.po
+++ b/src/locale/en_US/LC_MESSAGES/django.po
@@ -13,7 +13,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PyCon TW\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-02-24 10:21+0800\n"
+"POT-Creation-Date: 2024-04-15 16:57+0800\n"
"PO-Revision-Date: 2022-07-11 02:23+0800\n"
"Last-Translator: Tom Chen \n"
"Language-Team: English (United States) (http://www.transifex.com/pycon-"
@@ -29,11 +29,11 @@ msgstr ""
msgid "token"
msgstr "token"
-#: attendee/models.py:7 users/models.py:166
+#: attendee/models.py:7 users/models.py:164
msgid "verified"
msgstr "verified"
-#: attendee/models.py:9 core/models.py:162 events/models.py:469
+#: attendee/models.py:9 core/models.py:162
msgid "created at"
msgstr "created at"
@@ -49,46 +49,45 @@ msgstr "attendee"
msgid "attendees"
msgstr "attendees"
-#: ccip/views.py:209
+#: ccip/views.py:210
msgctxt "CCIP event type"
msgid "event"
msgstr "event"
-#: ccip/views.py:217
+#: ccip/views.py:218
msgctxt "CCIP event type"
msgid "break"
msgstr "break"
-#: ccip/views.py:225
+#: ccip/views.py:226
msgctxt "CCIP event type"
msgid "keynote"
msgstr "keynote"
-#: ccip/views.py:231
+#: ccip/views.py:232
msgctxt "CCIP event type"
msgid "sponsored"
msgstr "sponsored"
-#: ccip/views.py:239
+#: ccip/views.py:240
msgctxt "CCIP event type"
msgid "tutorial"
msgstr "tutorial"
-#: ccip/views.py:251
+#: ccip/views.py:252
msgctxt "CCIP event type"
msgid "talk"
msgstr "talk"
-#: core/admin.py:15
+#: core/admin.py:14
msgid "Current"
msgstr ""
-#: core/admin.py:16 core/choices.py:24 events/models.py:157
-#: events/templatetags/events.py:21
+#: core/admin.py:15 core/choices.py:24 events/models.py:151
msgid "Other"
msgstr "Other"
-#: core/admin.py:17
+#: core/admin.py:16
msgid "All"
msgstr ""
@@ -214,29 +213,13 @@ msgstr "Intermediate"
msgid "Experienced"
msgstr "Experienced"
-#: core/choices.py:41
-msgid "Day 1, September 21st, 2024 Morning"
-msgstr "Day 1, September 21st, 2024 Morning with Taiwan Standard Time(UTC+8)"
-
-#: core/choices.py:42
-msgid "Day 1, September 21st, 2024 Afternoon"
-msgstr "Day 1, September 21st, 2024 Afternoon with Taiwan Standard Time(UTC+8)"
-
-#: core/choices.py:43
-msgid "Day 2, September 22nd, 2024 Morning"
-msgstr "Day 2, September 22nd, 2024 Morning with Taiwan Standard Time(UTC+8)"
-
-#: core/choices.py:44
-msgid "Day 2, September 22nd, 2024 Afternoon"
-msgstr "Day 2, September 22nd, 2024 Afternoon with Taiwan Standard Time(UTC+8)"
-
-#: core/choices.py:48 core/choices.py:53 core/choices.py:58 core/choices.py:63
-#: core/choices.py:68 reviews/models.py:122 reviews/models.py:137
+#: core/choices.py:41 core/choices.py:46 core/choices.py:51 core/choices.py:56
+#: core/choices.py:61 reviews/models.py:121 reviews/models.py:136
msgid "Yes"
msgstr "Yes"
-#: core/choices.py:49 core/choices.py:54 core/choices.py:59 core/choices.py:64
-#: core/choices.py:69 reviews/models.py:123 reviews/models.py:138
+#: core/choices.py:42 core/choices.py:47 core/choices.py:52 core/choices.py:57
+#: core/choices.py:62 reviews/models.py:122 reviews/models.py:137
msgid "No"
msgstr "No"
@@ -249,7 +232,7 @@ msgstr "{model_name_cap} creation requires a request object."
msgid "conference"
msgstr "conference"
-#: core/models.py:93 events/models.py:193
+#: core/models.py:93 events/models.py:187
msgid "title"
msgstr "title"
@@ -293,7 +276,7 @@ msgstr "slide link"
msgid "slido embed link"
msgstr "Slido embed link"
-#: core/models.py:156 events/models.py:258
+#: core/models.py:156 events/models.py:252
msgid "HackMD embed link"
msgstr "HackMD embed link"
@@ -313,8 +296,8 @@ msgstr "attending PyCon TW in person"
msgid "Key"
msgstr "Key"
-#: core/models.py:215 proposals/models.py:62 users/models.py:198
-#: users/models.py:313
+#: core/models.py:215 proposals/models.py:67 users/models.py:196
+#: users/models.py:311
msgid "user"
msgstr "user"
@@ -330,99 +313,96 @@ msgstr ""
msgid "Tokens"
msgstr ""
-#: events/admin.py:26
+#: events/admin.py:34
msgid "time value"
msgstr "time value"
-#: events/admin.py:100 events/models.py:173
+#: events/admin.py:108 events/models.py:167
msgid "begin time"
msgstr "begin time"
-#: events/admin.py:106 events/models.py:182
+#: events/admin.py:114 events/models.py:176
msgid "end time"
msgstr "end time"
-#: events/admin.py:128
+#: events/admin.py:136
#: templates/default/users/_includes/dashboard_proposal_table.html:32
msgid "Edit"
msgstr "Edit"
-#: events/models.py:73
+#: events/models.py:70
msgid "value"
msgstr "value"
-#: events/models.py:80
+#: events/models.py:77
msgid "time"
msgstr "time"
-#: events/models.py:81
+#: events/models.py:78
msgid "times"
msgstr "times"
-#: events/models.py:144
+#: events/models.py:138
msgid "All rooms"
msgstr "All rooms"
-#: events/models.py:145
+#: events/models.py:139
msgid "R0, R1, R2"
msgstr "R1, R2, R3"
-#: events/models.py:146
+#: events/models.py:140
msgid "R0"
msgstr "R0"
-#: events/models.py:147
+#: events/models.py:141
msgid "R0_1"
msgstr "R0_1"
-#: events/models.py:148
+#: events/models.py:142
msgid "R0_2"
msgstr "R0_2"
-# #: events/models.py:122
-# msgid "R0"
-# msgstr "R0"
-#: events/models.py:149 events/templatetags/events.py:15
+#: events/models.py:143
msgid "R1"
msgstr "R1"
-#: events/models.py:150
+#: events/models.py:144
msgid "R1_1"
msgstr "R1_1"
-#: events/models.py:151
+#: events/models.py:145
msgid "R1_2"
msgstr "R1_2"
-#: events/models.py:152 events/templatetags/events.py:16
+#: events/models.py:146
msgid "R2"
msgstr "R2"
-#: events/models.py:153
+#: events/models.py:147
msgid "R2_1"
msgstr "R2_1"
-#: events/models.py:154
+#: events/models.py:148
msgid "R2_2"
msgstr "R2_2"
-#: events/models.py:155 events/templatetags/events.py:17
+#: events/models.py:149
msgid "R3"
msgstr "R3"
-#: events/models.py:156
+#: events/models.py:150
msgid "Open Space"
msgstr "Open Spaces"
-#: events/models.py:165
+#: events/models.py:159
msgid "location"
msgstr "location"
-#: events/models.py:197
+#: events/models.py:191
msgid "is break event"
msgstr "is break event"
-#: events/models.py:200
+#: events/models.py:194
msgid ""
"Whether this event is displays as a break. A break can be visually "
"distinguished from \"real\" conference sessions, such as keynotes, talks, "
@@ -431,71 +411,71 @@ msgstr ""
"Whether this event is displays as a break. A break can be visually "
"distinguished from “real” conference sessions, such as keynotes, talks, etc."
-#: events/models.py:206
+#: events/models.py:200
msgid "event description"
msgstr "event description"
-#: events/models.py:209
+#: events/models.py:203
msgid "link path"
msgstr "link path"
-#: events/models.py:214
+#: events/models.py:208
msgid "custom event"
msgstr "custom event"
-#: events/models.py:215
+#: events/models.py:209
msgid "custom events"
msgstr "custom events"
-#: events/models.py:224 users/models.py:118
+#: events/models.py:218 users/models.py:116
msgid "speaker name"
msgstr "speaker name"
-#: events/models.py:229
+#: events/models.py:223
msgid "speaker bio"
msgstr "speaker bio"
-#: events/models.py:233
+#: events/models.py:227
msgid "speaker photo"
msgstr "speaker photo"
-#: events/models.py:237
+#: events/models.py:231
msgid "Raster format of the speaker's photo, e.g. PNG, JPEG."
msgstr "Raster format of the speaker's photo, e.g. PNG, JPEG."
-#: events/models.py:241
+#: events/models.py:235
msgid "keynote session title"
msgstr "keynote session title"
-#: events/models.py:246
+#: events/models.py:240
msgid "keynote session description"
msgstr "keynote session description"
-#: events/models.py:250
+#: events/models.py:244
msgid "session slides"
msgstr "session slides"
-#: events/models.py:254
+#: events/models.py:248
msgid "slido"
msgstr "Slido"
-#: events/models.py:263
+#: events/models.py:257
msgid "social linkedin"
msgstr "Linkedin"
-#: events/models.py:267
+#: events/models.py:261
msgid "social twitter"
msgstr "Twitter"
-#: events/models.py:271
+#: events/models.py:265
msgid "social github"
msgstr "Github"
-#: events/models.py:275 events/models.py:360
+#: events/models.py:269 events/models.py:354
msgid "slug"
msgstr "slug"
-#: events/models.py:277
+#: events/models.py:271
#, python-brace-format
msgid ""
"This is used to link to the speaker's introduction on the Keynote page, e.g. "
@@ -504,33 +484,33 @@ msgstr ""
"This is used to link to the speaker's introduction on the Keynote page, e.g. "
"'liang2' will link to '{link}#keynote-speaker-liang2'."
-#: events/models.py:284 events/models.py:403 events/models.py:439
+#: events/models.py:278 events/models.py:397 events/models.py:433
msgid "is remote"
msgstr "is remote"
-#: events/models.py:288 events/models.py:363 events/models.py:407
-#: events/models.py:443
+#: events/models.py:282 events/models.py:357 events/models.py:401
+#: events/models.py:437
msgid "youtube id"
msgstr "youtube id"
-#: events/models.py:294
+#: events/models.py:288
msgid "keynote event"
msgstr "keynote event"
-#: events/models.py:295
+#: events/models.py:289
msgid "keynote events"
msgstr "keynote events"
-#: events/models.py:298
+#: events/models.py:292
#, python-brace-format
msgid "Keynote: {speaker}"
msgstr "Keynote: {speaker}"
-#: events/models.py:337 sponsors/models.py:101 sponsors/models.py:117
+#: events/models.py:331 sponsors/models.py:97 sponsors/models.py:113
msgid "sponsor"
msgstr "sponsor"
-#: events/models.py:342 events/models.py:343
+#: events/models.py:336 events/models.py:337
#: templates/pycontw-2020/_includes/menu.html:104
#: templates/pycontw-2020/contents/_default/events/job-listings.html:33
#: templates/pycontw-2021/contents/_default/events/job-listings.html:33
@@ -540,120 +520,48 @@ msgstr "sponsor"
msgid "Job Listings"
msgstr "Job Listings"
-#: events/models.py:346
+#: events/models.py:340
#, python-brace-format
msgid "Open Role of Sponsor: {sponsor}"
msgstr "Open Role of Sponsor: {sponsor}"
-#: events/models.py:355
+#: events/models.py:349
msgid "host"
msgstr "host"
-#: events/models.py:369
+#: events/models.py:363
msgid "sponsored event"
msgstr "sponsored event"
-#: events/models.py:370
+#: events/models.py:364
msgid "sponsored events"
msgstr "sponsored events"
-#: events/models.py:398 events/models.py:429 reviews/models.py:76
-#: reviews/models.py:197
+#: events/models.py:392 events/models.py:423 reviews/models.py:75
+#: reviews/models.py:196
msgid "proposal"
msgstr "proposal"
-#: events/models.py:415
+#: events/models.py:409
msgid "talk event"
msgstr "talk event"
-#: events/models.py:416
+#: events/models.py:410
msgid "talk events"
msgstr "talk events"
-#: events/models.py:434
+#: events/models.py:428
msgid "registration link"
msgstr "registration link"
-#: events/models.py:451
+#: events/models.py:445
msgid "tutorial event"
msgstr "tutorial event"
-#: events/models.py:452
+#: events/models.py:446
msgid "tutorial events"
msgstr "tutorial events"
-#: events/models.py:466
-msgid "HTML"
-msgstr "HTML"
-
-#: events/models.py:474 templates/pycontw-2016/_includes/nav/front_nav.html:40
-#: templates/pycontw-2016/events/schedule.html:6
-#: templates/pycontw-2016/events/schedule.html:8
-#: templates/pycontw-2017/contents/_default/portal.html:16
-#: templates/pycontw-2017/events/schedule.html:6
-#: templates/pycontw-2017/events/schedule.html:17
-#: templates/pycontw-2018/_includes/menu.html:54
-#: templates/pycontw-2018/contents/_default/portal.html:17
-#: templates/pycontw-2018/events/schedule.html:6
-#: templates/pycontw-2018/events/schedule.html:20
-#: templates/pycontw-2019/_includes/menu.html:59
-#: templates/pycontw-2019/_includes/portal.html:4
-#: templates/pycontw-2019/events/schedule.html:6
-#: templates/pycontw-2019/events/schedule.html:20
-#: templates/pycontw-2020/_includes/menu.html:80
-#: templates/pycontw-2020/_includes/portal.html:4
-#: templates/pycontw-2020/events/schedule.html:6
-#: templates/pycontw-2020/events/schedule.html:21
-#: templates/pycontw-2021/_includes/portal.html:4
-#: templates/pycontw-2021/events/schedule.html:6
-#: templates/pycontw-2021/events/schedule.html:21
-#: templates/pycontw-2022/_includes/portal.html:4
-#: templates/pycontw-2022/events/schedule.html:6
-#: templates/pycontw-2022/events/schedule.html:21
-#: templates/pycontw-2023/_includes/portal.html:4
-#: templates/pycontw-2023/events/schedule.html:6
-#: templates/pycontw-2023/events/schedule.html:21
-#: templates/pycontw-2024/_includes/portal.html:4
-#: templates/pycontw-2024/events/schedule.html:6
-#: templates/pycontw-2024/events/schedule.html:21
-msgid "Schedule"
-msgstr "Schedule"
-
-#: events/models.py:475
-msgid "Schedules"
-msgstr "Schedules"
-
-#: events/models.py:480
-msgid "Schedule created at {}"
-msgstr "Schedule created at {}"
-
-#: events/renderers.py:34
-msgid "Keynote"
-msgstr "Keynote"
-
-#: events/renderers.py:43
-msgid "EN Slides"
-msgstr "EN Slides"
-
-# #: events/models.py:125
-# msgid "R4"
-# msgstr "R4"
-#: events/templatetags/events.py:18
-msgid "Multifunction room"
-msgstr "Multifunction room"
-
-#: events/templatetags/events.py:19
-msgid "Goodideas Studio"
-msgstr "Goodideas Studio"
-
-#: events/templatetags/events.py:20
-msgid "R1, R2, R3"
-msgstr "R1, R2, R3"
-
-#: events/views.py:121
-msgid "New talk schedule generated successfully."
-msgstr "New talk schedule generated successfully."
-
#: proposals/forms/mixins.py:16
#, python-brace-format
msgid ""
@@ -852,11 +760,11 @@ msgstr ""
msgid "duration"
msgstr "Duration"
-#: proposals/forms/speakers.py:19
+#: proposals/forms/speakers.py:18
msgid "speaker email"
msgstr "speaker email"
-#: proposals/forms/speakers.py:21
+#: proposals/forms/speakers.py:20
msgid ""
"The speaker should have a registered account, and have completed both email "
"validation and the speaker profile."
@@ -864,7 +772,7 @@ msgstr ""
"The speaker should have a registered account, and have completed both email "
"validation and the speaker profile."
-#: proposals/forms/speakers.py:29
+#: proposals/forms/speakers.py:28
#, python-brace-format
msgid ""
"Only authenticated user with complete speaker profile may create an "
@@ -873,161 +781,161 @@ msgstr ""
"Only authenticated user with complete speaker profile may create an "
"{model_name}."
-#: proposals/forms/speakers.py:33
+#: proposals/forms/speakers.py:32
msgid "Additional speaker creation requires a proposal instance."
msgstr "Additional speaker creation requires a proposal instance."
-#: proposals/forms/speakers.py:36
+#: proposals/forms/speakers.py:35
msgid "User can only add additional speakers to owned proposals."
msgstr "User can only add additional speakers to owned proposals."
-#: proposals/forms/speakers.py:39
+#: proposals/forms/speakers.py:38
msgid "This user is already a speaker for the proposal."
msgstr "This user is already a speaker for the proposal."
-#: proposals/forms/speakers.py:41
+#: proposals/forms/speakers.py:40
msgid "No valid speaker found with your selection."
msgstr "No valid speaker found with your selection."
-#: proposals/models.py:55
+#: proposals/models.py:60
msgid "Proposal author"
msgstr "Proposal author"
-#: proposals/models.py:68
+#: proposals/models.py:73
msgid "proposal model type"
msgstr "proposal model type"
-#: proposals/models.py:72
+#: proposals/models.py:77
msgid "proposal ID"
msgstr "proposal ID"
-#: proposals/models.py:80
+#: proposals/models.py:85
msgid "Pending"
msgstr "Pending"
-#: proposals/models.py:81 proposals/models.py:160
+#: proposals/models.py:86 proposals/models.py:162
msgid "Accepted"
msgstr "Accepted"
-#: proposals/models.py:82
+#: proposals/models.py:87
msgid "Declined"
msgstr "Declined"
-#: proposals/models.py:91 proposals/models.py:153
+#: proposals/models.py:96 proposals/models.py:155
msgid "cancelled"
msgstr "cancelled"
-#: proposals/models.py:99
+#: proposals/models.py:104
msgid "additional speaker"
msgstr "additional speaker"
-#: proposals/models.py:100
+#: proposals/models.py:105
msgid "additional speakers"
msgstr "additional speakers"
-#: proposals/models.py:132
+#: proposals/models.py:134
msgid "submitter"
msgstr "submitter"
-#: proposals/models.py:137
+#: proposals/models.py:139
msgid "outline"
msgstr "outline"
-#: proposals/models.py:142
+#: proposals/models.py:144
msgid "objective"
msgstr "objective"
-#: proposals/models.py:147
+#: proposals/models.py:149
msgid "supplementary"
msgstr "supplementary"
-#: proposals/models.py:161
+#: proposals/models.py:163
msgid "Rejected"
msgstr "Rejected"
-#: proposals/models.py:164
+#: proposals/models.py:166
msgid "accepted"
msgstr "accepted"
-#: proposals/models.py:178
+#: proposals/models.py:180
msgid "labels"
msgstr ""
-#: proposals/models.py:263 proposals/models.py:317
+#: proposals/models.py:264 proposals/models.py:318
#: templates/default/_includes/proposal_example_modal.html:50
#: templates/default/_includes/proposal_example_modal.html:67
#: templates/default/reviews/review_form.html:40
msgid "Duration"
msgstr "Duration"
-#: proposals/models.py:268
+#: proposals/models.py:269
msgid "Yes, it is my first time speaking at PyCon Taiwan."
msgstr "Yes, it is my first time speaking at PyCon Taiwan."
-#: proposals/models.py:269
+#: proposals/models.py:270
msgid "No, I have given talks at PyCon Taiwan in the past."
msgstr "No, I have given talks at PyCon Taiwan in the past."
-#: proposals/models.py:273
+#: proposals/models.py:274
msgid "first time speaker"
msgstr "first time speaker"
-#: proposals/models.py:278
+#: proposals/models.py:279
msgid "talk proposal"
msgstr "talk proposal"
-#: proposals/models.py:279
+#: proposals/models.py:280
msgid "talk proposals"
msgstr "talk proposals"
-#: proposals/models.py:313
+#: proposals/models.py:314
msgid "1.5hr"
msgstr "1.5hr"
-#: proposals/models.py:324
+#: proposals/models.py:325
msgid "tutorial proposal"
msgstr "tutorial proposal"
-#: proposals/models.py:325
+#: proposals/models.py:326
msgid "tutorial proposals"
msgstr "tutorial proposals"
-#: proposals/utils.py:4
+#: proposals/utils.py:3
msgctxt "speaker name default separator"
msgid ", "
msgstr ", "
-#: proposals/utils.py:5
+#: proposals/utils.py:4
msgctxt "speaker name last separator"
msgid " and "
msgstr " and "
-#: proposals/views/cancel.py:36
+#: proposals/views/cancel.py:35
#, python-brace-format
msgid "Talk proposal {title} withdrawn."
msgstr "Talk proposal {title} withdrawn."
-#: proposals/views/cancel.py:40
+#: proposals/views/cancel.py:39
#, python-brace-format
msgid "Talk proposal {title} reactivated."
msgstr "Talk proposal {title} reactivated."
-#: proposals/views/cancel.py:58
+#: proposals/views/cancel.py:57
#, python-brace-format
msgid "Tutorial proposal {title} withdrawn."
msgstr "Tutorial proposal {title} withdrawn."
-#: proposals/views/cancel.py:62
+#: proposals/views/cancel.py:61
#, python-brace-format
msgid "Tutorial proposal {title} reactivated."
msgstr "Tutorial proposal {title} reactivated."
-#: proposals/views/create.py:42
+#: proposals/views/create.py:41
#, python-brace-format
msgid "Talk proposal {title} created."
msgstr "Talk proposal {title} created."
-#: proposals/views/create.py:52
+#: proposals/views/create.py:51
#, python-brace-format
msgid "Tutorial proposal {title} created."
msgstr "Tutorial proposal {title} created."
@@ -1235,62 +1143,60 @@ msgstr ""
" - Final resulting work\n"
"5. Q&A [5min]\n"
-#: proposals/views/update.py:61
+#: proposals/views/update.py:64
#, python-brace-format
msgid "Talk proposal {title} updated."
msgstr "Talk proposal {title} updated."
-#: proposals/views/update.py:70
+#: proposals/views/update.py:73
#, python-brace-format
msgid "Tutorial proposal {title} updated."
msgstr "Tutorial proposal {title} updated."
-#: pycontw2016/settings/base.py:301
+#: pycontw2016/settings/base.py:306
msgid "PyCon Taiwan 2016"
msgstr "PyCon Taiwan 2016"
-#: pycontw2016/settings/base.py:302 templates/pycontw-2017/index.html:6
+#: pycontw2016/settings/base.py:307 templates/pycontw-2017/index.html:6
msgid "PyCon Taiwan 2017"
msgstr "PyCon Taiwan 2017"
-#: pycontw2016/settings/base.py:303 templates/pycontw-2018/index.html:14
+#: pycontw2016/settings/base.py:308 templates/pycontw-2018/index.html:14
msgid "PyCon Taiwan 2018"
msgstr "PyCon Taiwan 2018"
-#: pycontw2016/settings/base.py:304 templates/pycontw-2019/index.html:14
+#: pycontw2016/settings/base.py:309 templates/pycontw-2019/index.html:14
msgid "PyCon Taiwan 2019"
msgstr "PyCon Taiwan 2019"
-#: pycontw2016/settings/base.py:305 templates/pycontw-2020/index.html:14
+#: pycontw2016/settings/base.py:310 templates/pycontw-2020/index.html:14
msgid "PyCon Taiwan 2020"
msgstr "PyCon Taiwan 2020"
-#: pycontw2016/settings/base.py:306 templates/pycontw-2021/index.html:14
+#: pycontw2016/settings/base.py:311 templates/pycontw-2021/index.html:14
#: templates/pycontw-2022/index.html:14 templates/pycontw-2023/index.html:14
#: templates/pycontw-2024/index.html:14
msgid "PyCon Taiwan 2021"
msgstr "PyCon Taiwan 2021"
-#: pycontw2016/settings/base.py:307
+#: pycontw2016/settings/base.py:312
msgid "PyCon Taiwan 2022"
msgstr "PyCon Taiwan 2022"
-#: pycontw2016/settings/base.py:308
+#: pycontw2016/settings/base.py:313
msgid "PyCon Taiwan 2023"
msgstr "PyCon Taiwan 2023"
-#: pycontw2016/settings/base.py:309
-#, fuzzy
-#| msgid "PyCon Taiwan 2020"
+#: pycontw2016/settings/base.py:314
msgid "PyCon Taiwan 2024"
-msgstr "PyCon Taiwan 2020"
+msgstr "PyCon Taiwan 2024"
-#: pycontw2016/settings/base.py:322
+#: pycontw2016/settings/base.py:327
#: pycontw2016/settings/production/pycontw2016.py:16
-#: pycontw2016/settings/production/pycontw2021.py:15
-#: pycontw2016/settings/production/pycontw2022.py:14
-#: pycontw2016/settings/production/pycontw2023.py:14
-#: pycontw2016/settings/production/pycontw2024.py:14
+#: pycontw2016/settings/production/pycontw2021.py:16
+#: pycontw2016/settings/production/pycontw2022.py:16
+#: pycontw2016/settings/production/pycontw2023.py:16
+#: pycontw2016/settings/production/pycontw2024.py:16
#: pycontw2016/settings/testing/pycontw2021.py:11
#: pycontw2016/settings/testing/pycontw2022.py:11
#: pycontw2016/settings/testing/pycontw2023.py:11
@@ -1298,11 +1204,11 @@ msgstr "PyCon Taiwan 2020"
msgid "No preference"
msgstr "No preference"
-#: pycontw2016/settings/base.py:323
-#: pycontw2016/settings/production/pycontw2021.py:16
-#: pycontw2016/settings/production/pycontw2022.py:15
-#: pycontw2016/settings/production/pycontw2023.py:15
-#: pycontw2016/settings/production/pycontw2024.py:15
+#: pycontw2016/settings/base.py:328
+#: pycontw2016/settings/production/pycontw2021.py:17
+#: pycontw2016/settings/production/pycontw2022.py:17
+#: pycontw2016/settings/production/pycontw2023.py:17
+#: pycontw2016/settings/production/pycontw2024.py:17
#: pycontw2016/settings/testing/pycontw2021.py:12
#: pycontw2016/settings/testing/pycontw2022.py:12
#: pycontw2016/settings/testing/pycontw2023.py:12
@@ -1310,11 +1216,11 @@ msgstr "No preference"
msgid "Prefer 15min"
msgstr "Prefer 15min"
-#: pycontw2016/settings/base.py:324
-#: pycontw2016/settings/production/pycontw2021.py:17
-#: pycontw2016/settings/production/pycontw2022.py:16
-#: pycontw2016/settings/production/pycontw2023.py:16
-#: pycontw2016/settings/production/pycontw2024.py:16
+#: pycontw2016/settings/base.py:329
+#: pycontw2016/settings/production/pycontw2021.py:18
+#: pycontw2016/settings/production/pycontw2022.py:18
+#: pycontw2016/settings/production/pycontw2023.py:18
+#: pycontw2016/settings/production/pycontw2024.py:18
#: pycontw2016/settings/testing/pycontw2021.py:13
#: pycontw2016/settings/testing/pycontw2022.py:13
#: pycontw2016/settings/testing/pycontw2023.py:13
@@ -1322,13 +1228,13 @@ msgstr "Prefer 15min"
msgid "Prefer 30min"
msgstr "Prefer 30min"
-#: pycontw2016/settings/base.py:328
+#: pycontw2016/settings/base.py:333
#: pycontw2016/settings/production/pycontw2016.py:22
#: templates/pycontw-2016/events/schedule.html:42
msgid "Day 1"
msgstr "Day 1"
-#: pycontw2016/settings/base.py:329
+#: pycontw2016/settings/base.py:334
#: pycontw2016/settings/production/pycontw2016.py:23
#: templates/pycontw-2016/events/schedule.html:45
msgid "Day 2"
@@ -1339,10 +1245,10 @@ msgid "Prefer 25min"
msgstr "Prefer 25min"
#: pycontw2016/settings/production/pycontw2016.py:18
-#: pycontw2016/settings/production/pycontw2021.py:18
-#: pycontw2016/settings/production/pycontw2022.py:17
-#: pycontw2016/settings/production/pycontw2023.py:17
-#: pycontw2016/settings/production/pycontw2024.py:17
+#: pycontw2016/settings/production/pycontw2021.py:19
+#: pycontw2016/settings/production/pycontw2022.py:19
+#: pycontw2016/settings/production/pycontw2023.py:19
+#: pycontw2016/settings/production/pycontw2024.py:19
#: pycontw2016/settings/testing/pycontw2021.py:14
#: pycontw2016/settings/testing/pycontw2022.py:14
#: pycontw2016/settings/testing/pycontw2023.py:14
@@ -1378,35 +1284,35 @@ msgstr "{model_name_cap} creation requires a proposal object."
msgid "Could not review proposal {proposal}."
msgstr "Could not review proposal {proposal}."
-#: reviews/models.py:66
+#: reviews/models.py:65
msgid "reviewer"
msgstr "reviewer"
-#: reviews/models.py:71 reviews/models.py:202
+#: reviews/models.py:70 reviews/models.py:201
msgid "stage"
msgstr "stage"
-#: reviews/models.py:90
+#: reviews/models.py:89
msgid "+1 (strong accept)"
msgstr "+1 (strong accept)"
-#: reviews/models.py:91
+#: reviews/models.py:90
msgid "+0 (weak accept)"
msgstr "+0 (weak accept)"
-#: reviews/models.py:92
+#: reviews/models.py:91
msgid "-0 (weak reject)"
msgstr "–0 (weak reject)"
-#: reviews/models.py:93
+#: reviews/models.py:92
msgid "-1 (strong reject)"
msgstr "–1 (strong reject)"
-#: reviews/models.py:102
+#: reviews/models.py:101
msgid "vote"
msgstr "vote"
-#: reviews/models.py:104
+#: reviews/models.py:103
msgid ""
"Your vote to accept or reject this talk. More information about the scoring "
"and acceptance criteria can be found at the GitBook Review Guideline"
"a>."
-#: reviews/models.py:113
+#: reviews/models.py:112
msgid "comment"
msgstr "comment"
-#: reviews/models.py:115
+#: reviews/models.py:114
msgid ""
"Comments to this proposal. This may be available for other reviewers in "
"later review stages, and you can choose whether or not to disclose it to the "
@@ -1432,11 +1338,11 @@ msgstr ""
"later review stages, and you can choose whether or not to disclose it to the "
"proposal's submitter."
-#: reviews/models.py:128
+#: reviews/models.py:127
msgid "discloses comment to proposal submitter"
msgstr "discloses comment to proposal submitter"
-#: reviews/models.py:130
+#: reviews/models.py:129
msgid ""
"Whether the proposal submitter can read you comments. We will include your "
"comments in the proposal acceptance/rejection notice sent to the submitter "
@@ -1446,11 +1352,11 @@ msgstr ""
"comments in the proposal acceptance/rejection notice sent to the submitter "
"if you allow us to."
-#: reviews/models.py:143
+#: reviews/models.py:142
msgid "is appropriate"
msgstr "is appropriate"
-#: reviews/models.py:145
+#: reviews/models.py:144
msgid ""
"Administrators can use this field to hide a review from submitters, even if "
"the reviewer enables disclosure. The review may be shown to the submitter "
@@ -1460,11 +1366,11 @@ msgstr ""
"the reviewer enables disclosure. The review may be shown to the submitter "
"only if this is set to True."
-#: reviews/models.py:153
+#: reviews/models.py:152
msgid "note"
msgstr "note"
-#: reviews/models.py:155
+#: reviews/models.py:154
msgid ""
"Personal notes about this proposal. You can use this field to record "
"anything you like during the review process. We promise to never disclose "
@@ -1474,65 +1380,65 @@ msgstr ""
"anything you like during the review process. We promise to never disclose "
"them to anyone."
-#: reviews/models.py:163
+#: reviews/models.py:162
msgid "updated"
msgstr "updated"
-#: reviews/models.py:168
+#: reviews/models.py:167
msgid "review"
msgstr "review"
-#: reviews/models.py:169
+#: reviews/models.py:168
msgid "reviews"
msgstr "reviews"
-#: reviews/models.py:174
+#: reviews/models.py:173
#, python-brace-format
msgid "Review {proposal} by {reviewer}: {vote}"
msgstr "Review {proposal} by {reviewer}: {vote}"
-#: reviews/models.py:206
+#: reviews/models.py:205
msgid "dumped JSON"
msgstr "dumped JSON"
-#: reviews/models.py:210
+#: reviews/models.py:209
msgid "dumped at"
msgstr "dumped at"
-#: reviews/models.py:215
+#: reviews/models.py:214
msgid "talk proposal snapshot"
msgstr "talk proposal snapshot"
-#: reviews/models.py:216
+#: reviews/models.py:215
msgid "talk proposal snapshots"
msgstr "talk proposal snapshots"
-#: reviews/models.py:220
+#: reviews/models.py:219
#, python-brace-format
msgid "Stage {stage} dump for {proposal}"
msgstr "Stage {stage} dump for {proposal}"
-#: sponsors/models.py:27
+#: sponsors/models.py:23
msgid "name"
msgstr "name"
-#: sponsors/models.py:31
+#: sponsors/models.py:27
msgid "website URL"
msgstr "website URL"
-#: sponsors/models.py:35
+#: sponsors/models.py:31
msgid "introduction"
msgstr "introduction"
-#: sponsors/models.py:38
+#: sponsors/models.py:34
msgid "subtitle"
msgstr "subtitle"
-#: sponsors/models.py:43
+#: sponsors/models.py:39
msgid "logo (SVG)"
msgstr "logo (SVG)"
-#: sponsors/models.py:46
+#: sponsors/models.py:42
msgid ""
"Vector format of the logo, in SVG. This takes precedence to the raster "
"format, if available."
@@ -1540,11 +1446,11 @@ msgstr ""
"Vector format of the logo, in SVG. This takes precedence to the raster "
"format, if available."
-#: sponsors/models.py:51
+#: sponsors/models.py:47
msgid "logo (image)"
msgstr "logo (image)"
-#: sponsors/models.py:55
+#: sponsors/models.py:51
msgid ""
"Raster format of the logo, e.g. PNG, JPEG. This is used as fallback when the "
"SVG file is not available."
@@ -1552,71 +1458,71 @@ msgstr ""
"Raster format of the logo, e.g. PNG, JPEG. This is used as fallback when the "
"SVG file is not available."
-#: sponsors/models.py:60
+#: sponsors/models.py:56
msgid "order"
msgstr "order"
-#: sponsors/models.py:80
+#: sponsors/models.py:76
msgid "platinum"
msgstr "platinum"
-#: sponsors/models.py:81
+#: sponsors/models.py:77
msgid "gold"
msgstr "gold"
-#: sponsors/models.py:82
+#: sponsors/models.py:78
msgid "silver"
msgstr "silver"
-#: sponsors/models.py:83
+#: sponsors/models.py:79
msgid "bronze"
msgstr "bronze"
-#: sponsors/models.py:84
+#: sponsors/models.py:80
msgid "special"
msgstr "special"
-#: sponsors/models.py:85
+#: sponsors/models.py:81
msgid "special-thanks"
msgstr "special-thanks"
-#: sponsors/models.py:86
+#: sponsors/models.py:82
msgid "organizer"
msgstr "organizer"
-#: sponsors/models.py:87
+#: sponsors/models.py:83
msgid "co-organizer"
msgstr "co-organizer"
-#: sponsors/models.py:96
+#: sponsors/models.py:92
msgid "level"
msgstr "level"
-#: sponsors/models.py:102
+#: sponsors/models.py:98
msgid "sponsors"
msgstr "sponsors"
-#: sponsors/models.py:122
+#: sponsors/models.py:118
msgid "open role name"
msgstr "open role name"
-#: sponsors/models.py:127
+#: sponsors/models.py:123
msgid "open role descsription"
msgstr "open role description"
-#: sponsors/models.py:131
+#: sponsors/models.py:127
msgid "open role requirements"
msgstr "open role requirements"
-#: sponsors/models.py:136
+#: sponsors/models.py:132
msgid "open role URL"
msgstr "open role URL"
-#: sponsors/models.py:141
+#: sponsors/models.py:137
msgid "open role"
msgstr "open role"
-#: sponsors/models.py:142
+#: sponsors/models.py:138
msgid "open Roles"
msgstr "open Roles"
@@ -1875,7 +1781,7 @@ msgstr "Edit Tutorial Proposal %(proposal_title)s"
#: templates/default/registration/login.html:5
#: templates/default/registration/login.html:7
#: templates/pycontw-2016/registration/login.html:7
-#: templates/pycontw-2016/registration/login.html:21 users/forms.py:261
+#: templates/pycontw-2016/registration/login.html:21 users/forms.py:265
msgid "Log In"
msgstr "Log In"
@@ -1978,7 +1884,7 @@ msgid "Stage"
msgstr "Stage"
#: templates/default/reviews/_includes/previous_review_table.html:7
-#: users/models.py:223
+#: users/models.py:221
msgid "Reviewer ID"
msgstr "Reviewer ID"
@@ -2349,10 +2255,10 @@ msgstr "We screwed up. It’s not your fault."
#: templates/pycontw-2018/_includes/menu.html:29
#: templates/pycontw-2019/_includes/menu.html:31
#: templates/pycontw-2020/_includes/menu.html:42
-#: templates/pycontw-2021/_includes/menu.html:42
-#: templates/pycontw-2022/_includes/menu.html:42
-#: templates/pycontw-2023/_includes/menu.html:42
-#: templates/pycontw-2024/_includes/menu.html:42
+#: templates/pycontw-2021/_includes/menu.html:41
+#: templates/pycontw-2022/_includes/menu.html:41
+#: templates/pycontw-2023/_includes/menu.html:41
+#: templates/pycontw-2024/_includes/menu.html:41
msgid "About"
msgstr "About"
@@ -2419,6 +2325,27 @@ msgstr "Events"
msgid "Overview"
msgstr "Overview"
+#: templates/pycontw-2016/_includes/nav/front_nav.html:40
+#: templates/pycontw-2016/events/schedule.html:6
+#: templates/pycontw-2016/events/schedule.html:8
+#: templates/pycontw-2017/contents/_default/portal.html:16
+#: templates/pycontw-2017/events/schedule.html:6
+#: templates/pycontw-2017/events/schedule.html:17
+#: templates/pycontw-2018/_includes/menu.html:54
+#: templates/pycontw-2018/contents/_default/portal.html:17
+#: templates/pycontw-2018/events/schedule.html:6
+#: templates/pycontw-2018/events/schedule.html:20
+#: templates/pycontw-2019/_includes/menu.html:59
+#: templates/pycontw-2019/_includes/portal.html:4
+#: templates/pycontw-2019/events/schedule.html:6
+#: templates/pycontw-2019/events/schedule.html:20
+#: templates/pycontw-2020/_includes/menu.html:80
+#: templates/pycontw-2020/_includes/portal.html:4
+#: templates/pycontw-2020/events/schedule.html:6
+#: templates/pycontw-2020/events/schedule.html:21
+msgid "Schedule"
+msgstr "Schedule"
+
#: templates/pycontw-2016/_includes/nav/front_nav.html:41
#: templates/pycontw-2018/_includes/menu.html:55
#: templates/pycontw-2018/contents/en/events/overview.html:38
@@ -2616,10 +2543,6 @@ msgstr "Quick Jump"
#: templates/pycontw-2018/events/schedule_create.html:64
#: templates/pycontw-2019/events/schedule_create.html:60
#: templates/pycontw-2020/events/schedule_create.html:100
-#: templates/pycontw-2021/events/schedule_create.html:100
-#: templates/pycontw-2022/events/schedule_create.html:100
-#: templates/pycontw-2023/events/schedule_create.html:100
-#: templates/pycontw-2024/events/schedule_create.html:100
msgid "Schedule table generation requires JavaScript. Please. (´・_・`)"
msgstr "Schedule table generation requires JavaScript. Please. (´・_・`)"
@@ -2628,10 +2551,6 @@ msgstr "Schedule table generation requires JavaScript. Please. (´・_・`)"
#: templates/pycontw-2018/events/schedule_create.html:73
#: templates/pycontw-2019/events/schedule_create.html:69
#: templates/pycontw-2020/events/schedule_create.html:109
-#: templates/pycontw-2021/events/schedule_create.html:109
-#: templates/pycontw-2022/events/schedule_create.html:109
-#: templates/pycontw-2023/events/schedule_create.html:109
-#: templates/pycontw-2024/events/schedule_create.html:109
msgid "Generate"
msgstr "Generate"
@@ -2966,10 +2885,10 @@ msgstr "Portal"
#: templates/pycontw-2018/contents/_default/portal.html:21
#: templates/pycontw-2019/_includes/portal.html:8
#: templates/pycontw-2020/_includes/portal.html:8
-#: templates/pycontw-2021/_includes/portal.html:8
-#: templates/pycontw-2022/_includes/portal.html:8
-#: templates/pycontw-2023/_includes/portal.html:8
-#: templates/pycontw-2024/_includes/portal.html:8
+#: templates/pycontw-2021/_includes/portal.html:4
+#: templates/pycontw-2022/_includes/portal.html:4
+#: templates/pycontw-2023/_includes/portal.html:4
+#: templates/pycontw-2024/_includes/portal.html:4
msgid "Collaborative Notes"
msgstr "Collaborative Notes"
@@ -2988,10 +2907,10 @@ msgstr "Quiz Leaderboard"
#: templates/pycontw-2018/contents/_default/portal.html:29
#: templates/pycontw-2019/_includes/portal.html:16
#: templates/pycontw-2020/_includes/portal.html:16
-#: templates/pycontw-2021/_includes/portal.html:16
-#: templates/pycontw-2022/_includes/portal.html:16
-#: templates/pycontw-2023/_includes/portal.html:16
-#: templates/pycontw-2024/_includes/portal.html:16
+#: templates/pycontw-2021/_includes/portal.html:12
+#: templates/pycontw-2022/_includes/portal.html:12
+#: templates/pycontw-2023/_includes/portal.html:12
+#: templates/pycontw-2024/_includes/portal.html:12
msgid "Facebook Fan Page"
msgstr "Facebook Fan Page"
@@ -3031,14 +2950,6 @@ msgstr "Unconference"
#: templates/pycontw-2019/events/schedule_create.html:15
#: templates/pycontw-2020/events/schedule_create.html:7
#: templates/pycontw-2020/events/schedule_create.html:16
-#: templates/pycontw-2021/events/schedule_create.html:7
-#: templates/pycontw-2021/events/schedule_create.html:16
-#: templates/pycontw-2022/events/schedule_create.html:7
-#: templates/pycontw-2022/events/schedule_create.html:16
-#: templates/pycontw-2023/events/schedule_create.html:7
-#: templates/pycontw-2023/events/schedule_create.html:16
-#: templates/pycontw-2024/events/schedule_create.html:7
-#: templates/pycontw-2024/events/schedule_create.html:16
msgid "Export New Schedule"
msgstr "Export New Schedule"
@@ -3255,10 +3166,10 @@ msgstr "Registration"
#: templates/pycontw-2018/_includes/menu.html:89
#: templates/pycontw-2019/_includes/menu.html:94
#: templates/pycontw-2020/_includes/menu.html:138
-#: templates/pycontw-2021/_includes/menu.html:152
-#: templates/pycontw-2022/_includes/menu.html:152
-#: templates/pycontw-2023/_includes/menu.html:152
-#: templates/pycontw-2024/_includes/menu.html:152
+#: templates/pycontw-2021/_includes/menu.html:150
+#: templates/pycontw-2022/_includes/menu.html:150
+#: templates/pycontw-2023/_includes/menu.html:150
+#: templates/pycontw-2024/_includes/menu.html:150
msgid "My PyCon"
msgstr "My PyCon"
@@ -4424,27 +4335,27 @@ msgid "Warm-Up Session"
msgstr "Warm-Up Session"
#: templates/pycontw-2020/_includes/menu.html:132
-#: templates/pycontw-2021/_includes/menu.html:146
-#: templates/pycontw-2022/_includes/menu.html:146
-#: templates/pycontw-2023/_includes/menu.html:146
-#: templates/pycontw-2024/_includes/menu.html:146
+#: templates/pycontw-2021/_includes/menu.html:144
+#: templates/pycontw-2022/_includes/menu.html:144
+#: templates/pycontw-2023/_includes/menu.html:144
+#: templates/pycontw-2024/_includes/menu.html:144
msgctxt "COVID-19 Guidelines"
msgid "COVID-19 Guidelines"
msgstr "COVID-19 Guidelines"
#: templates/pycontw-2020/_includes/portal.html:12
-#: templates/pycontw-2021/_includes/portal.html:12
-#: templates/pycontw-2022/_includes/portal.html:12
-#: templates/pycontw-2023/_includes/portal.html:12
-#: templates/pycontw-2024/_includes/portal.html:12
+#: templates/pycontw-2021/_includes/portal.html:8
+#: templates/pycontw-2022/_includes/portal.html:8
+#: templates/pycontw-2023/_includes/portal.html:8
+#: templates/pycontw-2024/_includes/portal.html:8
msgid "Discord Chat Room"
msgstr "Discord 聊天室"
#: templates/pycontw-2020/_includes/portal.html:20
-#: templates/pycontw-2021/_includes/portal.html:20
-#: templates/pycontw-2022/_includes/portal.html:20
-#: templates/pycontw-2023/_includes/portal.html:20
-#: templates/pycontw-2024/_includes/portal.html:20
+#: templates/pycontw-2021/_includes/portal.html:16
+#: templates/pycontw-2022/_includes/portal.html:16
+#: templates/pycontw-2023/_includes/portal.html:16
+#: templates/pycontw-2024/_includes/portal.html:16
msgid "Open space"
msgstr "Open space"
@@ -5035,40 +4946,40 @@ msgstr "Permissions"
msgid "Important dates"
msgstr "Important dates"
-#: users/forms.py:28
+#: users/forms.py:32
msgid "A user with that email already exists."
msgstr "A user with that email already exists."
-#: users/forms.py:29
+#: users/forms.py:33
msgid "The two password fields didn't match."
msgstr "The two password fields didn’t match."
-#: users/forms.py:32 users/forms.py:201
+#: users/forms.py:36 users/forms.py:205
msgid "Password"
msgstr "Password"
-#: users/forms.py:36
+#: users/forms.py:40
msgid "Password confirmation"
msgstr "Password confirmation"
-#: users/forms.py:117
+#: users/forms.py:121
msgid "Create Account"
msgstr "Create Account"
-#: users/forms.py:148
+#: users/forms.py:152
#, python-brace-format
msgid "Your image is too small ({width}\\u00d7{height} pixels)."
msgstr "Your image is too small ({width}\\u00d7{height} pixels)."
-#: users/forms.py:150
+#: users/forms.py:154
msgid "The image you provided is not quadrate."
msgstr "The image you provided is not quadrate."
-#: users/forms.py:151
+#: users/forms.py:155
msgid "Your image size is too big (>10M)"
msgstr "Your image size is too big (>10M)"
-#: users/forms.py:202
+#: users/forms.py:206
msgid ""
"Raw passwords are not stored, so there is no way to see this user's "
"password, but you can change the password using this "
@@ -5078,61 +4989,61 @@ msgstr ""
"password, but you can change the password using this "
"form."
-#: users/forms.py:251
+#: users/forms.py:255
#, python-brace-format
msgid ""
"Forgot Password"
msgstr ""
"Forgot Password"
-#: users/forms.py:255
+#: users/forms.py:259
#, python-brace-format
msgid "Sign up now"
msgstr "Sign up now"
-#: users/forms.py:272
+#: users/forms.py:276
msgid "Email Address"
msgstr "Email Address"
-#: users/forms.py:291
+#: users/forms.py:295
msgid "Request Password Reset"
msgstr "Request Password Reset"
-#: users/forms.py:327
+#: users/forms.py:331
msgid "Set Password"
msgstr "Set Password"
-#: users/forms.py:339
+#: users/forms.py:343
msgid "I agree to the code of conduct."
msgstr "I agree to the code of conduct."
-#: users/forms.py:344
+#: users/forms.py:348
msgid "You must agree to continue."
msgstr "You must agree to continue."
-#: users/models.py:114
+#: users/models.py:112
msgid "email address"
msgstr "email address"
-#: users/models.py:122
+#: users/models.py:120
msgid "biography"
msgstr "biography"
-#: users/models.py:125
+#: users/models.py:123
msgid ""
"Describe yourself with 1000 characters or less. There will be no formatting."
msgstr ""
"Describe yourself with 1000 characters or less. There will be no formatting."
-#: users/models.py:130
+#: users/models.py:128
msgid "photo"
msgstr "photo"
-#: users/models.py:134
+#: users/models.py:132
msgid "Facebook"
msgstr "Facebook"
-#: users/models.py:137
+#: users/models.py:135
msgid ""
"Link to your Facebook profile page. This will be shown when we display your "
"public information. If you do not know what your profile page link is, click "
@@ -5144,11 +5055,11 @@ msgstr ""
"on this link, and "
"copy-paste the URL of the page opened. Remember to log in to Facebook first!"
-#: users/models.py:146
+#: users/models.py:144
msgid "Twitter"
msgstr "Twitter"
-#: users/models.py:151
+#: users/models.py:149
msgid ""
"Your Twitter handle, without the \"@\" sign. This will be shown when we "
"display your public information."
@@ -5156,11 +5067,11 @@ msgstr ""
"Your Twitter handle, without the “@” sign. This will be shown when we "
"display your public information."
-#: users/models.py:156
+#: users/models.py:154
msgid "GitHub"
msgstr "GitHub"
-#: users/models.py:161
+#: users/models.py:159
msgid ""
"Your GitHub account, without the \"@\" sign. This will be shown when we "
"display your public information."
@@ -5168,23 +5079,23 @@ msgstr ""
"Your GitHub account, without the “@” sign. This will be shown when we "
"display your public information."
-#: users/models.py:169
+#: users/models.py:167
msgid "Designates whether the user has verified email ownership."
msgstr "Designates whether the user has verified email ownership."
-#: users/models.py:173
+#: users/models.py:171
msgid "staff status"
msgstr "staff status"
-#: users/models.py:176
+#: users/models.py:174
msgid "Designates whether the user can log into this admin site."
msgstr "Designates whether the user can log into this admin site."
-#: users/models.py:180
+#: users/models.py:178
msgid "active"
msgstr "active"
-#: users/models.py:183
+#: users/models.py:181
msgid ""
"Designates whether this user should be treated as active. Unselect this "
"instead of deleting accounts."
@@ -5192,49 +5103,49 @@ msgstr ""
"Designates whether this user should be treated as active. Unselect this "
"instead of deleting accounts."
-#: users/models.py:188
+#: users/models.py:186
msgid "date joined"
msgstr "date joined"
-#: users/models.py:199
+#: users/models.py:197
msgid "users"
msgstr "users"
-#: users/models.py:299
+#: users/models.py:297
#, python-brace-format
msgid "Verify your email address on {host}"
msgstr "Verify your email address on {host}"
-#: users/models.py:317
+#: users/models.py:315
msgid "latest agreed CoC version"
msgstr "latest agreed CoC version"
-#: users/models.py:321
+#: users/models.py:319
msgid "agreed at"
msgstr "agreed at"
-#: users/views.py:42
+#: users/views.py:44
msgid "Sign up successful. You are now logged in."
msgstr "Sign up successful. You are now logged in."
-#: users/views.py:59
+#: users/views.py:61
msgid "Email verification successful."
msgstr "Email verification successful."
-#: users/views.py:71
+#: users/views.py:73
#, python-brace-format
msgid "A verification email has been sent to {email}"
msgstr "A verification email has been sent to {email}"
-#: users/views.py:101
+#: users/views.py:103
msgid "Your profile has been updated successfully."
msgstr "Your profile has been updated successfully."
-#: users/views.py:114
+#: users/views.py:116
msgid "Your new password has been applied successfully."
msgstr "Your new password has been applied successfully."
-#: users/views.py:121
+#: users/views.py:123
msgid ""
"An email is sent to your email account. Please check your inbox for furthur "
"instructions to reset your password."
@@ -5242,6 +5153,18 @@ msgstr ""
"An email is sent to your email account. Please check your inbox for furthur "
"instructions to reset your password."
-#: users/views.py:129
+#: users/views.py:131
msgid "Password reset successful. You can now login."
msgstr "Password reset successful. You can now login."
+
+#~ msgid "HTML"
+#~ msgstr "HTML"
+
+#~ msgid "Keynote"
+#~ msgstr "Keynote"
+
+#~ msgid "EN Slides"
+#~ msgstr "EN Slides"
+
+#~ msgid "R1, R2, R3"
+#~ msgstr "R1, R2, R3"
diff --git a/src/locale/zh_Hant/LC_MESSAGES/django.po b/src/locale/zh_Hant/LC_MESSAGES/django.po
index 52a8c64b8..2cd23cbca 100644
--- a/src/locale/zh_Hant/LC_MESSAGES/django.po
+++ b/src/locale/zh_Hant/LC_MESSAGES/django.po
@@ -20,7 +20,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PyCon TW\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-02-24 10:21+0800\n"
+"POT-Creation-Date: 2024-04-15 16:57+0800\n"
"PO-Revision-Date: 2022-07-11 02:25+0800\n"
"Last-Translator: Tom Chen \n"
"Language-Team: Chinese Traditional (http://www.transifex.com/pycon-taiwan/"
@@ -36,11 +36,11 @@ msgstr ""
msgid "token"
msgstr "驗證碼"
-#: attendee/models.py:7 users/models.py:166
+#: attendee/models.py:7 users/models.py:164
msgid "verified"
msgstr "已認證"
-#: attendee/models.py:9 core/models.py:162 events/models.py:469
+#: attendee/models.py:9 core/models.py:162
msgid "created at"
msgstr "建立時間"
@@ -56,46 +56,45 @@ msgstr "會眾"
msgid "attendees"
msgstr "會眾"
-#: ccip/views.py:209
+#: ccip/views.py:210
msgctxt "CCIP event type"
msgid "event"
msgstr "活動"
-#: ccip/views.py:217
+#: ccip/views.py:218
msgctxt "CCIP event type"
msgid "break"
msgstr "休息"
-#: ccip/views.py:225
+#: ccip/views.py:226
msgctxt "CCIP event type"
msgid "keynote"
msgstr "基調演講"
-#: ccip/views.py:231
+#: ccip/views.py:232
msgctxt "CCIP event type"
msgid "sponsored"
msgstr "贊助"
-#: ccip/views.py:239
+#: ccip/views.py:240
msgctxt "CCIP event type"
msgid "tutorial"
msgstr "專業課程"
-#: ccip/views.py:251
+#: ccip/views.py:252
msgctxt "CCIP event type"
msgid "talk"
msgstr "演講"
-#: core/admin.py:15
+#: core/admin.py:14
msgid "Current"
msgstr ""
-#: core/admin.py:16 core/choices.py:24 events/models.py:157
-#: events/templatetags/events.py:21
+#: core/admin.py:15 core/choices.py:24 events/models.py:151
msgid "Other"
msgstr "其他"
-#: core/admin.py:17
+#: core/admin.py:16
msgid "All"
msgstr ""
@@ -221,29 +220,13 @@ msgstr "中階"
msgid "Experienced"
msgstr "進階"
-#: core/choices.py:41
-msgid "Day 1, September 21st, 2024 Morning"
-msgstr "第一天(2024/09/21)早上"
-
-#: core/choices.py:42
-msgid "Day 1, September 21st, 2024 Afternoon"
-msgstr "第一天(2024/09/21)下午"
-
-#: core/choices.py:43
-msgid "Day 2, September 22nd, 2024 Morning"
-msgstr "第二天(2024/09/22)早上"
-
-#: core/choices.py:44
-msgid "Day 2, September 22nd, 2024 Afternoon"
-msgstr "第二天(2024/09/22)下午"
-
-#: core/choices.py:48 core/choices.py:53 core/choices.py:58 core/choices.py:63
-#: core/choices.py:68 reviews/models.py:122 reviews/models.py:137
+#: core/choices.py:41 core/choices.py:46 core/choices.py:51 core/choices.py:56
+#: core/choices.py:61 reviews/models.py:121 reviews/models.py:136
msgid "Yes"
msgstr "是"
-#: core/choices.py:49 core/choices.py:54 core/choices.py:59 core/choices.py:64
-#: core/choices.py:69 reviews/models.py:123 reviews/models.py:138
+#: core/choices.py:42 core/choices.py:47 core/choices.py:52 core/choices.py:57
+#: core/choices.py:62 reviews/models.py:122 reviews/models.py:137
msgid "No"
msgstr "否"
@@ -256,7 +239,7 @@ msgstr "建立 {model_name_cap} 需要一個 request 物件。"
msgid "conference"
msgstr "會議"
-#: core/models.py:93 events/models.py:193
+#: core/models.py:93 events/models.py:187
msgid "title"
msgstr "題目"
@@ -300,7 +283,7 @@ msgstr "投影片連結"
msgid "slido embed link"
msgstr "Slido 嵌入連結"
-#: core/models.py:156 events/models.py:258
+#: core/models.py:156 events/models.py:252
msgid "HackMD embed link"
msgstr "HackMD 嵌入連結"
@@ -320,8 +303,8 @@ msgstr "是否願意以實體方式與會"
msgid "Key"
msgstr "鍵"
-#: core/models.py:215 proposals/models.py:62 users/models.py:198
-#: users/models.py:313
+#: core/models.py:215 proposals/models.py:67 users/models.py:196
+#: users/models.py:311
msgid "user"
msgstr "使用者"
@@ -337,99 +320,96 @@ msgstr "驗證碼"
msgid "Tokens"
msgstr "驗證碼"
-#: events/admin.py:26
+#: events/admin.py:34
msgid "time value"
msgstr "時間值"
-#: events/admin.py:100 events/models.py:173
+#: events/admin.py:108 events/models.py:167
msgid "begin time"
msgstr "開始時間"
-#: events/admin.py:106 events/models.py:182
+#: events/admin.py:114 events/models.py:176
msgid "end time"
msgstr "結束時間"
-#: events/admin.py:128
+#: events/admin.py:136
#: templates/default/users/_includes/dashboard_proposal_table.html:32
msgid "Edit"
msgstr "編輯"
-#: events/models.py:73
+#: events/models.py:70
msgid "value"
msgstr "值"
-#: events/models.py:80
+#: events/models.py:77
msgid "time"
msgstr "時間"
-#: events/models.py:81
+#: events/models.py:78
msgid "times"
msgstr "時間"
-#: events/models.py:144
+#: events/models.py:138
msgid "All rooms"
msgstr "全體會議"
-#: events/models.py:145
+#: events/models.py:139
msgid "R0, R1, R2"
msgstr "R1、R2、R3"
-#: events/models.py:146
+#: events/models.py:140
msgid "R0"
msgstr ""
-#: events/models.py:147
+#: events/models.py:141
msgid "R0_1"
msgstr ""
-#: events/models.py:148
+#: events/models.py:142
msgid "R0_2"
msgstr ""
-# #: events/models.py:122
-# msgid "R0"
-# msgstr "R0"
-#: events/models.py:149 events/templatetags/events.py:15
+#: events/models.py:143
msgid "R1"
msgstr "R1"
-#: events/models.py:150
+#: events/models.py:144
msgid "R1_1"
msgstr ""
-#: events/models.py:151
+#: events/models.py:145
msgid "R1_2"
msgstr ""
-#: events/models.py:152 events/templatetags/events.py:16
+#: events/models.py:146
msgid "R2"
msgstr "R2"
-#: events/models.py:153
+#: events/models.py:147
msgid "R2_1"
msgstr ""
-#: events/models.py:154
+#: events/models.py:148
msgid "R2_2"
msgstr ""
-#: events/models.py:155 events/templatetags/events.py:17
+#: events/models.py:149
msgid "R3"
msgstr "R3"
-#: events/models.py:156
+#: events/models.py:150
msgid "Open Space"
msgstr "開放空間"
-#: events/models.py:165
+#: events/models.py:159
msgid "location"
msgstr "地點"
-#: events/models.py:197
+#: events/models.py:191
msgid "is break event"
msgstr "休息"
-#: events/models.py:200
+#: events/models.py:194
msgid ""
"Whether this event is displays as a break. A break can be visually "
"distinguished from \"real\" conference sessions, such as keynotes, talks, "
@@ -438,71 +418,71 @@ msgstr ""
"此活動是否應該被顯示為休息時間。休息時間的顯示會與「真的」會議時段,例如基調"
"演講、一般演講不同。"
-#: events/models.py:206
+#: events/models.py:200
msgid "event description"
msgstr "活動說明"
-#: events/models.py:209
+#: events/models.py:203
msgid "link path"
msgstr "連結路徑"
-#: events/models.py:214
+#: events/models.py:208
msgid "custom event"
msgstr "自訂活動"
-#: events/models.py:215
+#: events/models.py:209
msgid "custom events"
msgstr "自訂活動"
-#: events/models.py:224 users/models.py:118
+#: events/models.py:218 users/models.py:116
msgid "speaker name"
msgstr "演講者"
-#: events/models.py:229
+#: events/models.py:223
msgid "speaker bio"
msgstr "演講者介紹"
-#: events/models.py:233
+#: events/models.py:227
msgid "speaker photo"
msgstr "演講者照片"
-#: events/models.py:237
+#: events/models.py:231
msgid "Raster format of the speaker's photo, e.g. PNG, JPEG."
msgstr "演講者照片使用點陣格式,例如PNG, JPEG"
-#: events/models.py:241
+#: events/models.py:235
msgid "keynote session title"
msgstr "演講標題"
-#: events/models.py:246
+#: events/models.py:240
msgid "keynote session description"
msgstr "演講介紹"
-#: events/models.py:250
+#: events/models.py:244
msgid "session slides"
msgstr "演講投影片"
-#: events/models.py:254
+#: events/models.py:248
msgid "slido"
msgstr "Slido"
-#: events/models.py:263
+#: events/models.py:257
msgid "social linkedin"
msgstr "Linkedin"
-#: events/models.py:267
+#: events/models.py:261
msgid "social twitter"
msgstr "Twitter"
-#: events/models.py:271
+#: events/models.py:265
msgid "social github"
msgstr "Github"
-#: events/models.py:275 events/models.py:360
+#: events/models.py:269 events/models.py:354
msgid "slug"
msgstr "網址文字"
-#: events/models.py:277
+#: events/models.py:271
#, python-brace-format
msgid ""
"This is used to link to the speaker's introduction on the Keynote page, e.g. "
@@ -511,33 +491,33 @@ msgstr ""
"用來連結至基調演講頁面上的講者簡介,如「liang2」可連結至「{link}#keynote-"
"speaker-liang2」。"
-#: events/models.py:284 events/models.py:403 events/models.py:439
+#: events/models.py:278 events/models.py:397 events/models.py:433
msgid "is remote"
msgstr "遠端演講"
-#: events/models.py:288 events/models.py:363 events/models.py:407
-#: events/models.py:443
+#: events/models.py:282 events/models.py:357 events/models.py:401
+#: events/models.py:437
msgid "youtube id"
msgstr "youtube id"
-#: events/models.py:294
+#: events/models.py:288
msgid "keynote event"
msgstr "基調演講"
-#: events/models.py:295
+#: events/models.py:289
msgid "keynote events"
msgstr "基調演講"
-#: events/models.py:298
+#: events/models.py:292
#, python-brace-format
msgid "Keynote: {speaker}"
msgstr "基調演講:{speaker}"
-#: events/models.py:337 sponsors/models.py:101 sponsors/models.py:117
+#: events/models.py:331 sponsors/models.py:97 sponsors/models.py:113
msgid "sponsor"
msgstr "贊助"
-#: events/models.py:342 events/models.py:343
+#: events/models.py:336 events/models.py:337
#: templates/pycontw-2020/_includes/menu.html:104
#: templates/pycontw-2020/contents/_default/events/job-listings.html:33
#: templates/pycontw-2021/contents/_default/events/job-listings.html:33
@@ -547,120 +527,48 @@ msgstr "贊助"
msgid "Job Listings"
msgstr "徵才資訊"
-#: events/models.py:346
+#: events/models.py:340
#, python-brace-format
msgid "Open Role of Sponsor: {sponsor}"
msgstr "徵才中贊助商: {sponsor}"
-#: events/models.py:355
+#: events/models.py:349
msgid "host"
msgstr "主持人"
-#: events/models.py:369
+#: events/models.py:363
msgid "sponsored event"
msgstr "贊助演講"
-#: events/models.py:370
+#: events/models.py:364
msgid "sponsored events"
msgstr "贊助演講"
-#: events/models.py:398 events/models.py:429 reviews/models.py:76
-#: reviews/models.py:197
+#: events/models.py:392 events/models.py:423 reviews/models.py:75
+#: reviews/models.py:196
msgid "proposal"
msgstr "提案"
-#: events/models.py:415
+#: events/models.py:409
msgid "talk event"
msgstr "演講"
-#: events/models.py:416
+#: events/models.py:410
msgid "talk events"
msgstr "演講"
-#: events/models.py:434
+#: events/models.py:428
msgid "registration link"
msgstr "報名連結"
-#: events/models.py:451
+#: events/models.py:445
msgid "tutorial event"
msgstr "專業課程"
-#: events/models.py:452
+#: events/models.py:446
msgid "tutorial events"
msgstr "專業課程"
-#: events/models.py:466
-msgid "HTML"
-msgstr "HTML"
-
-#: events/models.py:474 templates/pycontw-2016/_includes/nav/front_nav.html:40
-#: templates/pycontw-2016/events/schedule.html:6
-#: templates/pycontw-2016/events/schedule.html:8
-#: templates/pycontw-2017/contents/_default/portal.html:16
-#: templates/pycontw-2017/events/schedule.html:6
-#: templates/pycontw-2017/events/schedule.html:17
-#: templates/pycontw-2018/_includes/menu.html:54
-#: templates/pycontw-2018/contents/_default/portal.html:17
-#: templates/pycontw-2018/events/schedule.html:6
-#: templates/pycontw-2018/events/schedule.html:20
-#: templates/pycontw-2019/_includes/menu.html:59
-#: templates/pycontw-2019/_includes/portal.html:4
-#: templates/pycontw-2019/events/schedule.html:6
-#: templates/pycontw-2019/events/schedule.html:20
-#: templates/pycontw-2020/_includes/menu.html:80
-#: templates/pycontw-2020/_includes/portal.html:4
-#: templates/pycontw-2020/events/schedule.html:6
-#: templates/pycontw-2020/events/schedule.html:21
-#: templates/pycontw-2021/_includes/portal.html:4
-#: templates/pycontw-2021/events/schedule.html:6
-#: templates/pycontw-2021/events/schedule.html:21
-#: templates/pycontw-2022/_includes/portal.html:4
-#: templates/pycontw-2022/events/schedule.html:6
-#: templates/pycontw-2022/events/schedule.html:21
-#: templates/pycontw-2023/_includes/portal.html:4
-#: templates/pycontw-2023/events/schedule.html:6
-#: templates/pycontw-2023/events/schedule.html:21
-#: templates/pycontw-2024/_includes/portal.html:4
-#: templates/pycontw-2024/events/schedule.html:6
-#: templates/pycontw-2024/events/schedule.html:21
-msgid "Schedule"
-msgstr "時間表"
-
-#: events/models.py:475
-msgid "Schedules"
-msgstr "時間表"
-
-#: events/models.py:480
-msgid "Schedule created at {}"
-msgstr "時間表建立於 {}"
-
-#: events/renderers.py:34
-msgid "Keynote"
-msgstr "基調演講"
-
-#: events/renderers.py:43
-msgid "EN Slides"
-msgstr "英文投影片"
-
-# #: events/models.py:126
-# msgid "R4"
-# msgstr "R4"
-#: events/templatetags/events.py:18
-msgid "Multifunction room"
-msgstr "多功能廳"
-
-#: events/templatetags/events.py:19
-msgid "Goodideas Studio"
-msgstr "好想工作室"
-
-#: events/templatetags/events.py:20
-msgid "R1, R2, R3"
-msgstr "R1、R2、R3"
-
-#: events/views.py:121
-msgid "New talk schedule generated successfully."
-msgstr "演講時間表產生成功。"
-
#: proposals/forms/mixins.py:16
#, python-brace-format
msgid ""
@@ -840,178 +748,178 @@ msgstr ""
msgid "duration"
msgstr "時間長度"
-#: proposals/forms/speakers.py:19
+#: proposals/forms/speakers.py:18
msgid "speaker email"
msgstr "講者 email"
-#: proposals/forms/speakers.py:21
+#: proposals/forms/speakers.py:20
msgid ""
"The speaker should have a registered account, and have completed both email "
"validation and the speaker profile."
msgstr "講者應該已於 tw.pycon.org 註冊帳號,並且完成 email 認證與講者資訊。"
-#: proposals/forms/speakers.py:29
+#: proposals/forms/speakers.py:28
#, python-brace-format
msgid ""
"Only authenticated user with complete speaker profile may create an "
"{model_name}."
msgstr "只有完成認證並填寫講者資訊的人才能建立 {model_name}。"
-#: proposals/forms/speakers.py:33
+#: proposals/forms/speakers.py:32
msgid "Additional speaker creation requires a proposal instance."
msgstr "增加額外講者需要一個已建立的提案。"
-#: proposals/forms/speakers.py:36
+#: proposals/forms/speakers.py:35
msgid "User can only add additional speakers to owned proposals."
msgstr "使用者只能對自己的提案增加額外講者。"
-#: proposals/forms/speakers.py:39
+#: proposals/forms/speakers.py:38
msgid "This user is already a speaker for the proposal."
msgstr "本使用者已經是此提案的講者。"
-#: proposals/forms/speakers.py:41
+#: proposals/forms/speakers.py:40
msgid "No valid speaker found with your selection."
msgstr "您的選擇找不到對應講者。"
-#: proposals/models.py:55
+#: proposals/models.py:60
msgid "Proposal author"
msgstr "提案人"
-#: proposals/models.py:68
+#: proposals/models.py:73
msgid "proposal model type"
msgstr "提案 model 類型"
-#: proposals/models.py:72
+#: proposals/models.py:77
msgid "proposal ID"
msgstr "提案 ID"
-#: proposals/models.py:80
+#: proposals/models.py:85
msgid "Pending"
msgstr "待回覆"
-#: proposals/models.py:81 proposals/models.py:160
+#: proposals/models.py:86 proposals/models.py:162
msgid "Accepted"
msgstr "已接受"
-#: proposals/models.py:82
+#: proposals/models.py:87
msgid "Declined"
msgstr "被拒絕"
-#: proposals/models.py:91 proposals/models.py:153
+#: proposals/models.py:96 proposals/models.py:155
msgid "cancelled"
msgstr "已取消"
-#: proposals/models.py:99
+#: proposals/models.py:104
msgid "additional speaker"
msgstr "額外講者"
-#: proposals/models.py:100
+#: proposals/models.py:105
msgid "additional speakers"
msgstr "額外講者"
-#: proposals/models.py:132
+#: proposals/models.py:134
msgid "submitter"
msgstr "提案人"
-#: proposals/models.py:137
+#: proposals/models.py:139
msgid "outline"
msgstr "大綱"
-#: proposals/models.py:142
+#: proposals/models.py:144
msgid "objective"
msgstr "演講目標"
-#: proposals/models.py:147
+#: proposals/models.py:149
msgid "supplementary"
msgstr "補充資訊"
-#: proposals/models.py:161
+#: proposals/models.py:163
msgid "Rejected"
msgstr "已拒絕"
-#: proposals/models.py:164
+#: proposals/models.py:166
msgid "accepted"
msgstr "已接受"
-#: proposals/models.py:178
+#: proposals/models.py:180
msgid "labels"
msgstr ""
-#: proposals/models.py:263 proposals/models.py:317
+#: proposals/models.py:264 proposals/models.py:318
#: templates/default/_includes/proposal_example_modal.html:50
#: templates/default/_includes/proposal_example_modal.html:67
#: templates/default/reviews/review_form.html:40
msgid "Duration"
msgstr "時間長度"
-#: proposals/models.py:268
+#: proposals/models.py:269
msgid "Yes, it is my first time speaking at PyCon Taiwan."
msgstr "是,我第一次擔任 PyCon Taiwan 講者"
-#: proposals/models.py:269
+#: proposals/models.py:270
msgid "No, I have given talks at PyCon Taiwan in the past."
msgstr "否,我過去曾擔任過 PyCon Taiwan 講者"
-#: proposals/models.py:273
+#: proposals/models.py:274
msgid "first time speaker"
msgstr "第一次擔任講者"
-#: proposals/models.py:278
+#: proposals/models.py:279
msgid "talk proposal"
msgstr "演講提案"
-#: proposals/models.py:279
+#: proposals/models.py:280
msgid "talk proposals"
msgstr "演講提案"
-#: proposals/models.py:313
+#: proposals/models.py:314
msgid "1.5hr"
msgstr "1.5 小時"
-#: proposals/models.py:324
+#: proposals/models.py:325
msgid "tutorial proposal"
msgstr "專業課程提案"
-#: proposals/models.py:325
+#: proposals/models.py:326
msgid "tutorial proposals"
msgstr "專業課程提案"
-#: proposals/utils.py:4
+#: proposals/utils.py:3
msgctxt "speaker name default separator"
msgid ", "
msgstr "、"
-#: proposals/utils.py:5
+#: proposals/utils.py:4
msgctxt "speaker name last separator"
msgid " and "
msgstr "、"
-#: proposals/views/cancel.py:36
+#: proposals/views/cancel.py:35
#, python-brace-format
msgid "Talk proposal {title} withdrawn."
msgstr "《{title}》演講提案已撤回。"
-#: proposals/views/cancel.py:40
+#: proposals/views/cancel.py:39
#, python-brace-format
msgid "Talk proposal {title} reactivated."
msgstr "《{title}》演講提案已重新提交。"
-#: proposals/views/cancel.py:58
+#: proposals/views/cancel.py:57
#, python-brace-format
msgid "Tutorial proposal {title} withdrawn."
msgstr "《{title}》專業課程提案已撤回。"
-#: proposals/views/cancel.py:62
+#: proposals/views/cancel.py:61
#, python-brace-format
msgid "Tutorial proposal {title} reactivated."
msgstr "《{title}》專業課程提案已重新提交。"
-#: proposals/views/create.py:42
+#: proposals/views/create.py:41
#, python-brace-format
msgid "Talk proposal {title} created."
msgstr "已建立題目為《{title}》的演講提案。"
-#: proposals/views/create.py:52
+#: proposals/views/create.py:51
#, python-brace-format
msgid "Tutorial proposal {title} created."
msgstr "已建立題目為《{title}》的專業課程提案。"
@@ -1197,62 +1105,60 @@ msgstr ""
"5. Q&A [5min]\n"
"\n"
-#: proposals/views/update.py:61
+#: proposals/views/update.py:64
#, python-brace-format
msgid "Talk proposal {title} updated."
msgstr "已完成更新《{title}》演講提案。"
-#: proposals/views/update.py:70
+#: proposals/views/update.py:73
#, python-brace-format
msgid "Tutorial proposal {title} updated."
msgstr "已完成更新《{title}》專業課程提案。"
-#: pycontw2016/settings/base.py:301
+#: pycontw2016/settings/base.py:306
msgid "PyCon Taiwan 2016"
msgstr "PyCon Taiwan 2016"
-#: pycontw2016/settings/base.py:302 templates/pycontw-2017/index.html:6
+#: pycontw2016/settings/base.py:307 templates/pycontw-2017/index.html:6
msgid "PyCon Taiwan 2017"
msgstr "PyCon Taiwan 2017"
-#: pycontw2016/settings/base.py:303 templates/pycontw-2018/index.html:14
+#: pycontw2016/settings/base.py:308 templates/pycontw-2018/index.html:14
msgid "PyCon Taiwan 2018"
msgstr "PyCon Taiwan 2018"
-#: pycontw2016/settings/base.py:304 templates/pycontw-2019/index.html:14
+#: pycontw2016/settings/base.py:309 templates/pycontw-2019/index.html:14
msgid "PyCon Taiwan 2019"
msgstr "PyCon Taiwan 2019"
-#: pycontw2016/settings/base.py:305 templates/pycontw-2020/index.html:14
+#: pycontw2016/settings/base.py:310 templates/pycontw-2020/index.html:14
msgid "PyCon Taiwan 2020"
msgstr "PyCon Taiwan 2020"
-#: pycontw2016/settings/base.py:306 templates/pycontw-2021/index.html:14
+#: pycontw2016/settings/base.py:311 templates/pycontw-2021/index.html:14
#: templates/pycontw-2022/index.html:14 templates/pycontw-2023/index.html:14
#: templates/pycontw-2024/index.html:14
msgid "PyCon Taiwan 2021"
msgstr "PyCon Taiwan 2021"
-#: pycontw2016/settings/base.py:307
+#: pycontw2016/settings/base.py:312
msgid "PyCon Taiwan 2022"
msgstr "PyCon Taiwan 2022"
-#: pycontw2016/settings/base.py:308
+#: pycontw2016/settings/base.py:313
msgid "PyCon Taiwan 2023"
msgstr "PyCon Taiwan 2023"
-#: pycontw2016/settings/base.py:309
-#, fuzzy
-#| msgid "PyCon Taiwan 2020"
+#: pycontw2016/settings/base.py:314
msgid "PyCon Taiwan 2024"
-msgstr "PyCon Taiwan 2020"
+msgstr "PyCon Taiwan 2024"
-#: pycontw2016/settings/base.py:322
+#: pycontw2016/settings/base.py:327
#: pycontw2016/settings/production/pycontw2016.py:16
-#: pycontw2016/settings/production/pycontw2021.py:15
-#: pycontw2016/settings/production/pycontw2022.py:14
-#: pycontw2016/settings/production/pycontw2023.py:14
-#: pycontw2016/settings/production/pycontw2024.py:14
+#: pycontw2016/settings/production/pycontw2021.py:16
+#: pycontw2016/settings/production/pycontw2022.py:16
+#: pycontw2016/settings/production/pycontw2023.py:16
+#: pycontw2016/settings/production/pycontw2024.py:16
#: pycontw2016/settings/testing/pycontw2021.py:11
#: pycontw2016/settings/testing/pycontw2022.py:11
#: pycontw2016/settings/testing/pycontw2023.py:11
@@ -1260,11 +1166,11 @@ msgstr "PyCon Taiwan 2020"
msgid "No preference"
msgstr "無偏好"
-#: pycontw2016/settings/base.py:323
-#: pycontw2016/settings/production/pycontw2021.py:16
-#: pycontw2016/settings/production/pycontw2022.py:15
-#: pycontw2016/settings/production/pycontw2023.py:15
-#: pycontw2016/settings/production/pycontw2024.py:15
+#: pycontw2016/settings/base.py:328
+#: pycontw2016/settings/production/pycontw2021.py:17
+#: pycontw2016/settings/production/pycontw2022.py:17
+#: pycontw2016/settings/production/pycontw2023.py:17
+#: pycontw2016/settings/production/pycontw2024.py:17
#: pycontw2016/settings/testing/pycontw2021.py:12
#: pycontw2016/settings/testing/pycontw2022.py:12
#: pycontw2016/settings/testing/pycontw2023.py:12
@@ -1272,11 +1178,11 @@ msgstr "無偏好"
msgid "Prefer 15min"
msgstr "偏好 15 分鐘"
-#: pycontw2016/settings/base.py:324
-#: pycontw2016/settings/production/pycontw2021.py:17
-#: pycontw2016/settings/production/pycontw2022.py:16
-#: pycontw2016/settings/production/pycontw2023.py:16
-#: pycontw2016/settings/production/pycontw2024.py:16
+#: pycontw2016/settings/base.py:329
+#: pycontw2016/settings/production/pycontw2021.py:18
+#: pycontw2016/settings/production/pycontw2022.py:18
+#: pycontw2016/settings/production/pycontw2023.py:18
+#: pycontw2016/settings/production/pycontw2024.py:18
#: pycontw2016/settings/testing/pycontw2021.py:13
#: pycontw2016/settings/testing/pycontw2022.py:13
#: pycontw2016/settings/testing/pycontw2023.py:13
@@ -1284,13 +1190,13 @@ msgstr "偏好 15 分鐘"
msgid "Prefer 30min"
msgstr "偏好 30 分鐘"
-#: pycontw2016/settings/base.py:328
+#: pycontw2016/settings/base.py:333
#: pycontw2016/settings/production/pycontw2016.py:22
#: templates/pycontw-2016/events/schedule.html:42
msgid "Day 1"
msgstr "第 1 天"
-#: pycontw2016/settings/base.py:329
+#: pycontw2016/settings/base.py:334
#: pycontw2016/settings/production/pycontw2016.py:23
#: templates/pycontw-2016/events/schedule.html:45
msgid "Day 2"
@@ -1301,10 +1207,10 @@ msgid "Prefer 25min"
msgstr "偏好 25 分鐘"
#: pycontw2016/settings/production/pycontw2016.py:18
-#: pycontw2016/settings/production/pycontw2021.py:18
-#: pycontw2016/settings/production/pycontw2022.py:17
-#: pycontw2016/settings/production/pycontw2023.py:17
-#: pycontw2016/settings/production/pycontw2024.py:17
+#: pycontw2016/settings/production/pycontw2021.py:19
+#: pycontw2016/settings/production/pycontw2022.py:19
+#: pycontw2016/settings/production/pycontw2023.py:19
+#: pycontw2016/settings/production/pycontw2024.py:19
#: pycontw2016/settings/testing/pycontw2021.py:14
#: pycontw2016/settings/testing/pycontw2022.py:14
#: pycontw2016/settings/testing/pycontw2023.py:14
@@ -1340,35 +1246,35 @@ msgstr "{model_name_cap} 的建立需要一個 proposal 物件。"
msgid "Could not review proposal {proposal}."
msgstr "無法審查提案 {proposal}。"
-#: reviews/models.py:66
+#: reviews/models.py:65
msgid "reviewer"
msgstr "審查者"
-#: reviews/models.py:71 reviews/models.py:202
+#: reviews/models.py:70 reviews/models.py:201
msgid "stage"
msgstr "階段"
-#: reviews/models.py:90
+#: reviews/models.py:89
msgid "+1 (strong accept)"
msgstr "+1(高度肯定地接受)"
-#: reviews/models.py:91
+#: reviews/models.py:90
msgid "+0 (weak accept)"
msgstr "+0(輕微贊成)"
-#: reviews/models.py:92
+#: reviews/models.py:91
msgid "-0 (weak reject)"
msgstr "−0(輕微反對)"
-#: reviews/models.py:93
+#: reviews/models.py:92
msgid "-1 (strong reject)"
msgstr "–1(高度肯定地拒絕)"
-#: reviews/models.py:102
+#: reviews/models.py:101
msgid "vote"
msgstr "投票"
-#: reviews/models.py:104
+#: reviews/models.py:103
msgid ""
"Your vote to accept or reject this talk. More information about the scoring "
"and acceptance criteria can be found at the GitBook 審稿指南 "
"GitBook。"
-#: reviews/models.py:113
+#: reviews/models.py:112
msgid "comment"
msgstr "評論"
-#: reviews/models.py:115
+#: reviews/models.py:114
msgid ""
"Comments to this proposal. This may be available for other reviewers in "
"later review stages, and you can choose whether or not to disclose it to the "
@@ -1392,11 +1298,11 @@ msgstr ""
"對於本提案的評論。本欄位內容將在之後的審查階段讓其他審查者看見,您可以選擇要"
"或不要讓本提案的投稿者知道您的評論。 "
-#: reviews/models.py:128
+#: reviews/models.py:127
msgid "discloses comment to proposal submitter"
msgstr "讓本提案的投稿者知道您的評論"
-#: reviews/models.py:130
+#: reviews/models.py:129
msgid ""
"Whether the proposal submitter can read you comments. We will include your "
"comments in the proposal acceptance/rejection notice sent to the submitter "
@@ -1405,11 +1311,11 @@ msgstr ""
"本提案的投稿者是否能知道您的評論。我們會在通知提案接受或拒絕結果時,如果您同"
"意讓投稿者知道,將這些評論加在通知結果中。"
-#: reviews/models.py:143
+#: reviews/models.py:142
msgid "is appropriate"
msgstr "認可"
-#: reviews/models.py:145
+#: reviews/models.py:144
msgid ""
"Administrators can use this field to hide a review from submitters, even if "
"the reviewer enables disclosure. The review may be shown to the submitter "
@@ -1418,11 +1324,11 @@ msgstr ""
"管理員可以用這個欄位隱藏某個審稿意見,不讓投稿人看見(即使審稿人要求揭露)。"
"只有當這個欄位設為 True 時,審稿意見才會被顯示給投稿人。"
-#: reviews/models.py:153
+#: reviews/models.py:152
msgid "note"
msgstr "附註"
-#: reviews/models.py:155
+#: reviews/models.py:154
msgid ""
"Personal notes about this proposal. You can use this field to record "
"anything you like during the review process. We promise to never disclose "
@@ -1431,146 +1337,146 @@ msgstr ""
"個人對本投稿的附註。您可以使用這個欄位記錄有關審查的任何資訊。我們不會把這欄"
"位的資訊公布給任何人,只有您知道。"
-#: reviews/models.py:163
+#: reviews/models.py:162
msgid "updated"
msgstr "更新時間"
-#: reviews/models.py:168
+#: reviews/models.py:167
msgid "review"
msgstr "審查"
-#: reviews/models.py:169
+#: reviews/models.py:168
msgid "reviews"
msgstr "審查"
-#: reviews/models.py:174
+#: reviews/models.py:173
#, python-brace-format
msgid "Review {proposal} by {reviewer}: {vote}"
msgstr "提案《{proposal}》審查:{vote}({reviewer})"
-#: reviews/models.py:206
+#: reviews/models.py:205
msgid "dumped JSON"
msgstr "匯出 JSON"
-#: reviews/models.py:210
+#: reviews/models.py:209
msgid "dumped at"
msgstr "匯出於"
-#: reviews/models.py:215
+#: reviews/models.py:214
msgid "talk proposal snapshot"
msgstr "演講投稿封存"
-#: reviews/models.py:216
+#: reviews/models.py:215
msgid "talk proposal snapshots"
msgstr "演講投稿封存"
-#: reviews/models.py:220
+#: reviews/models.py:219
#, python-brace-format
msgid "Stage {stage} dump for {proposal}"
msgstr "第 {stage} 階段封存《{proposal}》"
-#: sponsors/models.py:27
+#: sponsors/models.py:23
msgid "name"
msgstr "名稱"
-#: sponsors/models.py:31
+#: sponsors/models.py:27
msgid "website URL"
msgstr "網站 URL"
-#: sponsors/models.py:35
+#: sponsors/models.py:31
msgid "introduction"
msgstr "簡介"
-#: sponsors/models.py:38
+#: sponsors/models.py:34
msgid "subtitle"
msgstr "副標題"
-#: sponsors/models.py:43
+#: sponsors/models.py:39
msgid "logo (SVG)"
msgstr "logo (SVG)"
-#: sponsors/models.py:46
+#: sponsors/models.py:42
msgid ""
"Vector format of the logo, in SVG. This takes precedence to the raster "
"format, if available."
msgstr "即使有點陣式圖形格式的圖標,還是會優先採用向量式SVG格式的圖標。"
-#: sponsors/models.py:51
+#: sponsors/models.py:47
msgid "logo (image)"
msgstr "logo (圖片)"
-#: sponsors/models.py:55
+#: sponsors/models.py:51
msgid ""
"Raster format of the logo, e.g. PNG, JPEG. This is used as fallback when the "
"SVG file is not available."
msgstr ""
"當SVG格式的檔案不存在時,會回過頭來使用點陣式格式的圖標,例如PNG、JPEG。"
-#: sponsors/models.py:60
+#: sponsors/models.py:56
msgid "order"
msgstr "排序"
-#: sponsors/models.py:80
+#: sponsors/models.py:76
msgid "platinum"
msgstr "白金級"
-#: sponsors/models.py:81
+#: sponsors/models.py:77
msgid "gold"
msgstr "金級"
-#: sponsors/models.py:82
+#: sponsors/models.py:78
msgid "silver"
msgstr "銀級"
-#: sponsors/models.py:83
+#: sponsors/models.py:79
msgid "bronze"
msgstr "銅級"
-#: sponsors/models.py:84
+#: sponsors/models.py:80
msgid "special"
msgstr "特別贊助"
-#: sponsors/models.py:85
+#: sponsors/models.py:81
msgid "special-thanks"
msgstr "特別感謝"
-#: sponsors/models.py:86
+#: sponsors/models.py:82
msgid "organizer"
msgstr "主辦單位"
-#: sponsors/models.py:87
+#: sponsors/models.py:83
msgid "co-organizer"
msgstr "協辦單位"
-#: sponsors/models.py:96
+#: sponsors/models.py:92
msgid "level"
msgstr "等級"
-#: sponsors/models.py:102
+#: sponsors/models.py:98
msgid "sponsors"
msgstr "贊助"
-#: sponsors/models.py:122
+#: sponsors/models.py:118
msgid "open role name"
msgstr "職缺名稱"
-#: sponsors/models.py:127
+#: sponsors/models.py:123
msgid "open role descsription"
msgstr "職缺說明"
-#: sponsors/models.py:131
+#: sponsors/models.py:127
msgid "open role requirements"
msgstr "職缺需求"
-#: sponsors/models.py:136
+#: sponsors/models.py:132
msgid "open role URL"
msgstr "職缺 URL"
-#: sponsors/models.py:141
+#: sponsors/models.py:137
msgid "open role"
msgstr "職缺"
-#: sponsors/models.py:142
+#: sponsors/models.py:138
msgid "open Roles"
msgstr "職缺"
@@ -1823,7 +1729,7 @@ msgstr "修改專業課程提案《%(proposal_title)s》"
#: templates/default/registration/login.html:5
#: templates/default/registration/login.html:7
#: templates/pycontw-2016/registration/login.html:7
-#: templates/pycontw-2016/registration/login.html:21 users/forms.py:261
+#: templates/pycontw-2016/registration/login.html:21 users/forms.py:265
msgid "Log In"
msgstr "登入"
@@ -1923,7 +1829,7 @@ msgid "Stage"
msgstr "階段"
#: templates/default/reviews/_includes/previous_review_table.html:7
-#: users/models.py:223
+#: users/models.py:221
msgid "Reviewer ID"
msgstr "審稿人代號"
@@ -2287,10 +2193,10 @@ msgstr "我們搞砸了,這不是你的問題。"
#: templates/pycontw-2018/_includes/menu.html:29
#: templates/pycontw-2019/_includes/menu.html:31
#: templates/pycontw-2020/_includes/menu.html:42
-#: templates/pycontw-2021/_includes/menu.html:42
-#: templates/pycontw-2022/_includes/menu.html:42
-#: templates/pycontw-2023/_includes/menu.html:42
-#: templates/pycontw-2024/_includes/menu.html:42
+#: templates/pycontw-2021/_includes/menu.html:41
+#: templates/pycontw-2022/_includes/menu.html:41
+#: templates/pycontw-2023/_includes/menu.html:41
+#: templates/pycontw-2024/_includes/menu.html:41
msgid "About"
msgstr "關於"
@@ -2357,6 +2263,27 @@ msgstr "活動"
msgid "Overview"
msgstr "總覽"
+#: templates/pycontw-2016/_includes/nav/front_nav.html:40
+#: templates/pycontw-2016/events/schedule.html:6
+#: templates/pycontw-2016/events/schedule.html:8
+#: templates/pycontw-2017/contents/_default/portal.html:16
+#: templates/pycontw-2017/events/schedule.html:6
+#: templates/pycontw-2017/events/schedule.html:17
+#: templates/pycontw-2018/_includes/menu.html:54
+#: templates/pycontw-2018/contents/_default/portal.html:17
+#: templates/pycontw-2018/events/schedule.html:6
+#: templates/pycontw-2018/events/schedule.html:20
+#: templates/pycontw-2019/_includes/menu.html:59
+#: templates/pycontw-2019/_includes/portal.html:4
+#: templates/pycontw-2019/events/schedule.html:6
+#: templates/pycontw-2019/events/schedule.html:20
+#: templates/pycontw-2020/_includes/menu.html:80
+#: templates/pycontw-2020/_includes/portal.html:4
+#: templates/pycontw-2020/events/schedule.html:6
+#: templates/pycontw-2020/events/schedule.html:21
+msgid "Schedule"
+msgstr "時間表"
+
#: templates/pycontw-2016/_includes/nav/front_nav.html:41
#: templates/pycontw-2018/_includes/menu.html:55
#: templates/pycontw-2018/contents/en/events/overview.html:38
@@ -2553,10 +2480,6 @@ msgstr "跳至"
#: templates/pycontw-2018/events/schedule_create.html:64
#: templates/pycontw-2019/events/schedule_create.html:60
#: templates/pycontw-2020/events/schedule_create.html:100
-#: templates/pycontw-2021/events/schedule_create.html:100
-#: templates/pycontw-2022/events/schedule_create.html:100
-#: templates/pycontw-2023/events/schedule_create.html:100
-#: templates/pycontw-2024/events/schedule_create.html:100
msgid "Schedule table generation requires JavaScript. Please. (´・_・`)"
msgstr "需要 JavaScript 才能產生時間表。求求你~ (´・_・`)"
@@ -2565,10 +2488,6 @@ msgstr "需要 JavaScript 才能產生時間表。求求你~ (´・_・`)"
#: templates/pycontw-2018/events/schedule_create.html:73
#: templates/pycontw-2019/events/schedule_create.html:69
#: templates/pycontw-2020/events/schedule_create.html:109
-#: templates/pycontw-2021/events/schedule_create.html:109
-#: templates/pycontw-2022/events/schedule_create.html:109
-#: templates/pycontw-2023/events/schedule_create.html:109
-#: templates/pycontw-2024/events/schedule_create.html:109
msgid "Generate"
msgstr "產生"
@@ -2899,10 +2818,10 @@ msgstr "傳送門"
#: templates/pycontw-2018/contents/_default/portal.html:21
#: templates/pycontw-2019/_includes/portal.html:8
#: templates/pycontw-2020/_includes/portal.html:8
-#: templates/pycontw-2021/_includes/portal.html:8
-#: templates/pycontw-2022/_includes/portal.html:8
-#: templates/pycontw-2023/_includes/portal.html:8
-#: templates/pycontw-2024/_includes/portal.html:8
+#: templates/pycontw-2021/_includes/portal.html:4
+#: templates/pycontw-2022/_includes/portal.html:4
+#: templates/pycontw-2023/_includes/portal.html:4
+#: templates/pycontw-2024/_includes/portal.html:4
msgid "Collaborative Notes"
msgstr "共筆"
@@ -2921,10 +2840,10 @@ msgstr "猜謎排行榜"
#: templates/pycontw-2018/contents/_default/portal.html:29
#: templates/pycontw-2019/_includes/portal.html:16
#: templates/pycontw-2020/_includes/portal.html:16
-#: templates/pycontw-2021/_includes/portal.html:16
-#: templates/pycontw-2022/_includes/portal.html:16
-#: templates/pycontw-2023/_includes/portal.html:16
-#: templates/pycontw-2024/_includes/portal.html:16
+#: templates/pycontw-2021/_includes/portal.html:12
+#: templates/pycontw-2022/_includes/portal.html:12
+#: templates/pycontw-2023/_includes/portal.html:12
+#: templates/pycontw-2024/_includes/portal.html:12
msgid "Facebook Fan Page"
msgstr "Facebook 粉絲頁"
@@ -2964,14 +2883,6 @@ msgstr "非正式會議"
#: templates/pycontw-2019/events/schedule_create.html:15
#: templates/pycontw-2020/events/schedule_create.html:7
#: templates/pycontw-2020/events/schedule_create.html:16
-#: templates/pycontw-2021/events/schedule_create.html:7
-#: templates/pycontw-2021/events/schedule_create.html:16
-#: templates/pycontw-2022/events/schedule_create.html:7
-#: templates/pycontw-2022/events/schedule_create.html:16
-#: templates/pycontw-2023/events/schedule_create.html:7
-#: templates/pycontw-2023/events/schedule_create.html:16
-#: templates/pycontw-2024/events/schedule_create.html:7
-#: templates/pycontw-2024/events/schedule_create.html:16
msgid "Export New Schedule"
msgstr "輸出新時間表"
@@ -3180,10 +3091,10 @@ msgstr "報到"
#: templates/pycontw-2018/_includes/menu.html:89
#: templates/pycontw-2019/_includes/menu.html:94
#: templates/pycontw-2020/_includes/menu.html:138
-#: templates/pycontw-2021/_includes/menu.html:152
-#: templates/pycontw-2022/_includes/menu.html:152
-#: templates/pycontw-2023/_includes/menu.html:152
-#: templates/pycontw-2024/_includes/menu.html:152
+#: templates/pycontw-2021/_includes/menu.html:150
+#: templates/pycontw-2022/_includes/menu.html:150
+#: templates/pycontw-2023/_includes/menu.html:150
+#: templates/pycontw-2024/_includes/menu.html:150
msgid "My PyCon"
msgstr "My PyCon"
@@ -4310,27 +4221,27 @@ msgid "Warm-Up Session"
msgstr "暖身活動"
#: templates/pycontw-2020/_includes/menu.html:132
-#: templates/pycontw-2021/_includes/menu.html:146
-#: templates/pycontw-2022/_includes/menu.html:146
-#: templates/pycontw-2023/_includes/menu.html:146
-#: templates/pycontw-2024/_includes/menu.html:146
+#: templates/pycontw-2021/_includes/menu.html:144
+#: templates/pycontw-2022/_includes/menu.html:144
+#: templates/pycontw-2023/_includes/menu.html:144
+#: templates/pycontw-2024/_includes/menu.html:144
msgctxt "COVID-19 Guidelines"
msgid "COVID-19 Guidelines"
msgstr "COVID-19 防疫守則"
#: templates/pycontw-2020/_includes/portal.html:12
-#: templates/pycontw-2021/_includes/portal.html:12
-#: templates/pycontw-2022/_includes/portal.html:12
-#: templates/pycontw-2023/_includes/portal.html:12
-#: templates/pycontw-2024/_includes/portal.html:12
+#: templates/pycontw-2021/_includes/portal.html:8
+#: templates/pycontw-2022/_includes/portal.html:8
+#: templates/pycontw-2023/_includes/portal.html:8
+#: templates/pycontw-2024/_includes/portal.html:8
msgid "Discord Chat Room"
msgstr "Discord 聊天室"
#: templates/pycontw-2020/_includes/portal.html:20
-#: templates/pycontw-2021/_includes/portal.html:20
-#: templates/pycontw-2022/_includes/portal.html:20
-#: templates/pycontw-2023/_includes/portal.html:20
-#: templates/pycontw-2024/_includes/portal.html:20
+#: templates/pycontw-2021/_includes/portal.html:16
+#: templates/pycontw-2022/_includes/portal.html:16
+#: templates/pycontw-2023/_includes/portal.html:16
+#: templates/pycontw-2024/_includes/portal.html:16
msgid "Open space"
msgstr "Open space 揪團"
@@ -4920,40 +4831,40 @@ msgstr "權限"
msgid "Important dates"
msgstr "重要日期"
-#: users/forms.py:28
+#: users/forms.py:32
msgid "A user with that email already exists."
msgstr "此電子郵件信箱已被使用。"
-#: users/forms.py:29
+#: users/forms.py:33
msgid "The two password fields didn't match."
msgstr "密碼欄位內容不一致。"
-#: users/forms.py:32 users/forms.py:201
+#: users/forms.py:36 users/forms.py:205
msgid "Password"
msgstr "密碼"
-#: users/forms.py:36
+#: users/forms.py:40
msgid "Password confirmation"
msgstr "密碼確認"
-#: users/forms.py:117
+#: users/forms.py:121
msgid "Create Account"
msgstr "新建帳號"
-#: users/forms.py:148
+#: users/forms.py:152
#, python-brace-format
msgid "Your image is too small ({width}\\u00d7{height} pixels)."
msgstr "提供的圖片過小({width}\\u00d7{height} 像素)。"
-#: users/forms.py:150
+#: users/forms.py:154
msgid "The image you provided is not quadrate."
msgstr "提供的圖片並非方型。"
-#: users/forms.py:151
+#: users/forms.py:155
msgid "Your image size is too big (>10M)"
msgstr "提供的圖片檔案過大 (>10M)"
-#: users/forms.py:202
+#: users/forms.py:206
msgid ""
"Raw passwords are not stored, so there is no way to see this user's "
"password, but you can change the password using this "
@@ -4962,59 +4873,59 @@ msgstr ""
"原始的密碼並沒有儲存在資料庫中,因此沒有任何方法能看到本使用者的密碼,但你可"
"以透過本表單更改密碼。"
-#: users/forms.py:251
+#: users/forms.py:255
#, python-brace-format
msgid ""
"Forgot Password"
msgstr "忘記密碼"
-#: users/forms.py:255
+#: users/forms.py:259
#, python-brace-format
msgid "Sign up now"
msgstr "註冊帳號"
-#: users/forms.py:272
+#: users/forms.py:276
msgid "Email Address"
msgstr "電子郵件信箱"
-#: users/forms.py:291
+#: users/forms.py:295
msgid "Request Password Reset"
msgstr "申請密碼重設"
-#: users/forms.py:327
+#: users/forms.py:331
msgid "Set Password"
msgstr "設定密碼"
-#: users/forms.py:339
+#: users/forms.py:343
msgid "I agree to the code of conduct."
msgstr "我同意行為準則。"
-#: users/forms.py:344
+#: users/forms.py:348
msgid "You must agree to continue."
msgstr "你必須同意才能繼續下一步。"
-#: users/models.py:114
+#: users/models.py:112
msgid "email address"
msgstr "電子郵件信箱"
-#: users/models.py:122
+#: users/models.py:120
msgid "biography"
msgstr "自我介紹"
-#: users/models.py:125
+#: users/models.py:123
msgid ""
"Describe yourself with 1000 characters or less. There will be no formatting."
msgstr "使用少於 1000 個字元介紹您自己。本欄位不含任何格式。"
-#: users/models.py:130
+#: users/models.py:128
msgid "photo"
msgstr "個人照"
-#: users/models.py:134
+#: users/models.py:132
msgid "Facebook"
msgstr "Facebook"
-#: users/models.py:137
+#: users/models.py:135
msgid ""
"Link to your Facebook profile page. This will be shown when we display your "
"public information. If you do not know what your profile page link is, click "
@@ -5025,97 +4936,109 @@ msgstr ""
"人頁連結為何,可以使用"
"這個連結,並將開啟頁面的網址列內容貼至本欄位。記得先登入 Facebook!"
-#: users/models.py:146
+#: users/models.py:144
msgid "Twitter"
msgstr "Twitter"
-#: users/models.py:151
+#: users/models.py:149
msgid ""
"Your Twitter handle, without the \"@\" sign. This will be shown when we "
"display your public information."
msgstr "您的 Twitter 帳號,不包含 @ 符號。這個名稱會出現在您的公開資料中。"
-#: users/models.py:156
+#: users/models.py:154
msgid "GitHub"
msgstr "GitHub"
-#: users/models.py:161
+#: users/models.py:159
msgid ""
"Your GitHub account, without the \"@\" sign. This will be shown when we "
"display your public information."
msgstr "您的 GitHub 帳號,不包含 @ 符號。這個名稱會出現在您的公開資料中。"
-#: users/models.py:169
+#: users/models.py:167
msgid "Designates whether the user has verified email ownership."
msgstr "指定本使用者是否已完成 email 認證。"
-#: users/models.py:173
+#: users/models.py:171
msgid "staff status"
msgstr "管理員權限"
-#: users/models.py:176
+#: users/models.py:174
msgid "Designates whether the user can log into this admin site."
msgstr "指定本使用者是否可以登入本管理站台。"
-#: users/models.py:180
+#: users/models.py:178
msgid "active"
msgstr "已啟用"
-#: users/models.py:183
+#: users/models.py:181
msgid ""
"Designates whether this user should be treated as active. Unselect this "
"instead of deleting accounts."
msgstr "指定本使用者是否被視為 active。取消勾選此選項而非刪除帳號。"
-#: users/models.py:188
+#: users/models.py:186
msgid "date joined"
msgstr "註冊日期"
-#: users/models.py:199
+#: users/models.py:197
msgid "users"
msgstr "使用者"
-#: users/models.py:299
+#: users/models.py:297
#, python-brace-format
msgid "Verify your email address on {host}"
msgstr "於 {host} 認證您的信箱地址"
-#: users/models.py:317
+#: users/models.py:315
msgid "latest agreed CoC version"
msgstr "最後同意的 CoC 版本"
-#: users/models.py:321
+#: users/models.py:319
msgid "agreed at"
msgstr "同意於"
-#: users/views.py:42
+#: users/views.py:44
msgid "Sign up successful. You are now logged in."
msgstr "註冊成功。您現在已被登入。"
-#: users/views.py:59
+#: users/views.py:61
msgid "Email verification successful."
msgstr "Email 認證成功。"
-#: users/views.py:71
+#: users/views.py:73
#, python-brace-format
msgid "A verification email has been sent to {email}"
msgstr "一封認證用的 email 已經寄送至 {email}"
-#: users/views.py:101
+#: users/views.py:103
msgid "Your profile has been updated successfully."
msgstr "您的講者資訊已成功地更新。"
-#: users/views.py:114
+#: users/views.py:116
msgid "Your new password has been applied successfully."
msgstr "已成功地使用您的新密碼。"
-#: users/views.py:121
+#: users/views.py:123
msgid ""
"An email is sent to your email account. Please check your inbox for furthur "
"instructions to reset your password."
msgstr ""
"一封電子郵件已經發送至您的信箱。請檢查您的收件匣並參照信中指示重設密碼。"
-#: users/views.py:129
+#: users/views.py:131
msgid "Password reset successful. You can now login."
msgstr "密碼重設成功。您現在即可登入。"
+
+#~ msgid "HTML"
+#~ msgstr "HTML"
+
+#~ msgid "Keynote"
+#~ msgstr "基調演講"
+
+#~ msgid "EN Slides"
+#~ msgstr "英文投影片"
+
+#~ msgid "R1, R2, R3"
+#~ msgstr "R1、R2、R3"
diff --git a/src/pycontw2016/settings/base.py b/src/pycontw2016/settings/base.py
index bcd6f63e6..e4f61fde9 100644
--- a/src/pycontw2016/settings/base.py
+++ b/src/pycontw2016/settings/base.py
@@ -44,7 +44,6 @@
'django.template.context_processors.request',
'core.context_processors.script_prefix',
'core.context_processors.pycontw',
- 'core.context_processors.events',
'core.context_processors.sponsors',
'core.context_processors.frontend_host',
],
@@ -335,8 +334,6 @@ def node_bin(name):
(datetime.date(2024, 9, 22), _('Day 2')),
])
-SCHEDULE_REDIRECT_URL = None
-
COC_VERSION = '2024.0'
# Since 2021, pycon.tw has indivisual server hosting the attendee-facing pages
diff --git a/src/static/css/main.scss b/src/static/css/main.scss
index 565e2116f..4656ab34a 100644
--- a/src/static/css/main.scss
+++ b/src/static/css/main.scss
@@ -32,7 +32,6 @@
@import "pages/sponsors";
@import "pages/talks";
@import "pages/venue";
-@import "pages/schedule";
body {
@@ -64,42 +63,6 @@ body {
text-align: center;
}
-// schedule
-
-.schedule-wrapper {
- @include make-xs-column(10);
- @include make-xs-column-offset(1);
- @include make-sm-column(12);
- @include make-sm-column-offset(0);
- @include make-md-column(10);
- @include make-md-column-offset(1);
-}
-.schedule {
- background-color: #fff;
- padding-top: .6em;
- border-radius: 4px;
-}
-
-.schedule-block {
- @include make-sm-column(4);
- text-align: center;
- color: $schedule-color;
- font-size:24px;
-}
-
-// visible in viewport > xs
-.schedule__title--lg {
- @include make-sm-column(4);
- @extend .hidden-xs;
- text-align: center;
- color: $schedule__title-color;
-}
-
-// visible when viewport < xs
-.schedule__title {
- @extend .visible-xs-block;
- padding-top: .5em;
-}
// latest-news
@@ -348,14 +311,6 @@ body {
background-color: white;
}
-@media (max-width: $screen-sm){
- .schedule{
- padding-top: 0;
- }
- .schedule__title{
- padding-top: 0;
- }
-}
@media (max-width: $screen-xs) {
.sponsorship__call{
diff --git a/src/static/js/events/schedule.js b/src/static/js/events/schedule.js
deleted file mode 100644
index 42b10e081..000000000
--- a/src/static/js/events/schedule.js
+++ /dev/null
@@ -1,28 +0,0 @@
-(function () {
-
-// $('.talk__title').addClass('hyphenate');
-// Hyphenator.config({useCSS3hyphenation: true});
-// Hyphenator.run();
-
-$('.talk__title').dotdotdot({wrap: 'word', watch: true, fallbackToLetter: true});
-
-// Scrollspy-like behavior to update the hash on scroll.
-if (window.history && window.history.replaceState) {
- $(document).on('scroll', function () {
- var $document = $(this);
- var ids = ['2016-06-05', '2016-06-04', '2016-06-03'];
- var hash = '';
- $.each(ids, function () {
- if ($document.scrollTop() >= $('#' + this).offset().top - 1) {
- hash = '#' + this;
- return false;
- }
- });
- if (hash !== window.location.hash) {
- var nurl = window.location.pathname + window.location.search;
- window.history.replaceState('', '', nurl + hash);
- }
- });
-}
-
-})();
diff --git a/src/static/js/events/schedule_gen.js b/src/static/js/events/schedule_gen.js
deleted file mode 100644
index 103de3296..000000000
--- a/src/static/js/events/schedule_gen.js
+++ /dev/null
@@ -1,7 +0,0 @@
-(function ($) {
-
-var $form = $('.generation-form');
-$form.removeClass('hide');
-$form.find('input[name="html"]').val($('.schedule').html());
-
-})(jQuery);
diff --git a/src/static/pycontw-2021/_includes/base/_variables.scss b/src/static/pycontw-2021/_includes/base/_variables.scss
index e45223f99..d52a722a0 100644
--- a/src/static/pycontw-2021/_includes/base/_variables.scss
+++ b/src/static/pycontw-2021/_includes/base/_variables.scss
@@ -10,10 +10,6 @@ $quote-color: $theme-focus;
// section
$section__heading-color: $theme-color;
-// schedule
-$schedule-color: #F58454;
-$schedule__title-color: #ADCDFF;
-
// intro
$intro__heading-color: $theme-color;
$intro-color: #686868;
diff --git a/src/static/pycontw-2021/scripts/schedule.js b/src/static/pycontw-2021/scripts/schedule.js
deleted file mode 100644
index 478756338..000000000
--- a/src/static/pycontw-2021/scripts/schedule.js
+++ /dev/null
@@ -1,32 +0,0 @@
-// Schedule generation.
-const form = document.querySelector('.generation-form')
-if (form) {
- const html = document.querySelector('.schedule-content').innerHTML
- form.querySelector('input[name="html"]').value = html
- form.style.display = 'block'
-}
-
-
-// Replace localed URL with current locale prefix.
-const I18N = JSON.parse(document.getElementById('i18n_variables').textContent)
-function findPrefix(u) {
- const possiblePrefixes = []
- for (const prefix of I18N.LANGUAGE_PREFIXES) {
- if (u.startsWith(prefix)) {
- return prefix
- }
- }
- return ''
-}
-for (const el of document.querySelectorAll('.localed-url')) {
- const original = el.getAttribute('href')
- if (!original) {
- continue
- }
- const prefix = findPrefix(original)
- if (!prefix) {
- continue
- }
- const sub = original.substr(prefix.length)
- el.setAttribute('href', `${I18N.LANGUAGE_PREFIX}${sub}`)
-}
diff --git a/src/static/pycontw-2021/styles/page.scss b/src/static/pycontw-2021/styles/page.scss
index afab90859..0c57b6300 100644
--- a/src/static/pycontw-2021/styles/page.scss
+++ b/src/static/pycontw-2021/styles/page.scss
@@ -407,7 +407,6 @@ ul.custom_ul {
@import "pages/ticket-info";
@import "pages/tutorials";
@import "pages/venue";
-@import "pages/schedule";
@import "pages/staff";
@import "pages/portal";
@import "pages/sponsorship-prospectus";
diff --git a/src/static/pycontw-2021/styles/pages/_events.scss b/src/static/pycontw-2021/styles/pages/_events.scss
index 33fbad935..a343665ca 100644
--- a/src/static/pycontw-2021/styles/pages/_events.scss
+++ b/src/static/pycontw-2021/styles/pages/_events.scss
@@ -228,236 +228,3 @@ $icon-height: 26px;
@include rooms();
}
-
-@mixin toggleTimetableOrTimeList() {
- @media (min-width: 901px) {
- .py-schedule-timetable {
- display: initial;
-
- .room-tag {
- display: none;
- }
- }
-
- .py-schedule-time-list {
- display: none;
- }
- }
-
- @media (max-width: 900px) {
- .py-schedule-timetable {
- display: none;
- }
-
- .py-schedule-time-list {
- display: initial;
-
- .room-tag::before {
- white-space: initial;
- }
- }
-
- .py-schedule-tabs__tab {
- padding: 24px;
- }
- }
-}
-
-main {
- max-width: unset;
- margin: unset;
- padding: 24px;
-
- .py-schedule {
- &-tabs {
- display: flex;
- justify-content: center;
- align-items: center;
-
- &__tab {
- margin: 0 24px 24px;
- padding: 24px 72px;
- font-family: $header-font-family;
- font-size: 20px;
- border-radius: 8px;
- cursor: default;
-
- &:hover:not(.--active) {
- color: red;
- cursor: pointer;
- }
-
- &.--active {
- color: $jinger-bread;
- background: $salmon;
- }
- }
- }
-
- &-timetable {
- &-header {
- position: sticky;
- top: 0;
- z-index: 1000;
- display: flex;
- background: transparentize($brick, 0.3);
-
- &-column {
- @include roomDisplayStyle();
-
- margin: 8px 8px 8px 0;
- padding: 12px 0;
- font-size: 18px;
-
- &:last-child {
- margin-right: 0;
- }
-
- &.--timeline {
- flex: 1;
- background: transparent;
- }
-
- &.--room {
- flex: 2;
- }
- }
-
- @include rooms();
-
- &:not(.--active) {
- display: none;
- }
- }
-
- &-body {
- display: grid;
- grid-template-columns:
- [times] 1fr
- [room-4-r0-start] 2fr
- [room-4-r0-end room-5-r1-start] 2fr
- [room-5-r1-end room-6-r2-start] 2fr
- [room-6-r2-end room-8-r3-start] 2fr
- [room-8-r3-end room-7-r4-start] 2fr
- [room-7-r4-end];
- grid-column-gap: 8px;
- grid-row-gap: 8px;
-
- &-item {
- @include eventItem();
- }
-
- .room-3-r012 {
- grid-column: room-4-r0-start / room-6-r2-end;
- }
-
- .room-4-r0 {
- grid-column: room-4-r0-start / room-4-r0-end;
- }
-
- .room-5-r1 {
- grid-column: room-5-r1-start / room-5-r1-end;
- }
-
- .room-6-r2 {
- grid-column: room-6-r2-start / room-6-r2-end;
- }
-
- .room-7-r4 {
- grid-column: room-7-r4-start / room-7-r4-end;
- }
-
- .room-1-r3 {
- grid-column: room-8-r3-start / room-8-r3-end;
- }
-
- .room-2-all {
- grid-column: room-4-r0-start / room-8-r3-end;
- }
-
- .timeline {
- grid-column: times;
- justify-self: center;
- @include time();
-
- &.--hour {
- font-size: 20px;
- font-weight: 500;
- }
-
- &.--half-an-hour {
- font-size: 18px;
- }
- }
-
- &:not(.--active) {
- display: none;
- }
- }
- }
-
- &-time-list {
- &-section {
- &-header {
- position: sticky;
- position: -webkit-sticky;
- top: 0;
- z-index: 10;
- padding: 16px 0;
- background: $brick;
-
- & > .time {
- @include time();
-
- font-size: 22px;
- }
- }
-
- &-items {
- display: flex;
- flex-direction: column;
- margin: 12px 0;
-
- &-item {
- @include eventItem();
- margin-bottom: 12px;
- }
-
- .room-2-all {
- order: 1;
- }
-
- .room-3-r012 {
- order: 2;
- }
-
- .room-4-r0 {
- order: 3;
- }
-
- .room-5-r1 {
- order: 4;
- }
-
- .room-6-r2 {
- order: 5;
- }
-
- .room-1-r3 {
- order: 6;
- }
-
- .room-7-r4 {
- order: 7;
- }
- }
- }
-
- &:not(.--active) {
- display: none;
- }
- }
- }
-
- @include toggleTimetableOrTimeList();
-}
diff --git a/src/static/pycontw-2021/styles/pages/_schedule.scss b/src/static/pycontw-2021/styles/pages/_schedule.scss
deleted file mode 100644
index 96379039b..000000000
--- a/src/static/pycontw-2021/styles/pages/_schedule.scss
+++ /dev/null
@@ -1,342 +0,0 @@
-.schedule-page {
- form .button-round {
- @include button(276deg, $dark-peach, $maize);
- width: 160px;
- margin: 24px 0;
- text-align: center;
- cursor: pointer;
- }
-
- // Icon
- $icon-width: 26px;
- $icon-height: 26px;
-
- @mixin icon($name, $margin-top: 0, $x: left, $width: $icon-width) {
- &::before {
- width: $width;
- margin-top: $margin-top; // Visual compensation.
- background: url('../assets/icon-#{$name}.svg') top $x no-repeat;
- }
- }
-
- @mixin icon-info() {
- ul {
- @include list-reset();
- text-indent: 0;
- li {
-
- display: flex;
- align-content: baseline;
- align-items: flex-start;
-
- > * {
- flex: 1;
- min-height: 24px;
- margin-bottom: 12px;
- }
-
- &:before {
- content: ' ';
- height: $icon-height;
- margin-right: 1rem;
- }
-
- &.speech-en {
- @include icon('language-enen', -1px);
- }
- &.speech-zh {
- @include icon('language-zhzh', -1px);
- }
- &.slides-en {
- @include icon('language-enen', 0, right);
- }
- &.slides-zh {
- @include icon('language-zhzh', 0, right);
- }
- &.language-enen {
- @include icon('language-enen', 0, center, 2 * $icon-width);
- }
- &.language-zhen {
- @include icon('language-zhen', 0, center, 2 * $icon-width);
- }
- &.language-zhzh {
- @include icon('language-zhzh', 0, center, 2 * $icon-width);
- }
- &.python-level-novice {
- @include icon('level-1');
- }
- &.python-level-intermediate {
- @include icon('level-2');
- }
- &.python-level-experienced {
- @include icon('level-3');
- }
- &.no-recording {
- @include icon('no-recording', 1px);
- }
- &.room-R0 {
- @include icon('room-R0', 1px, center);
- }
- &.room-R1 {
- @include icon('room-R1', 1px, center);
- }
- &.room-R2 {
- @include icon('room-R2', 1px, center);
- }
- &.room-R3 {
- @include icon('room-R3', 1px, center);
- }
- &.grass {
- @include icon('grass-group', 0, right);
- &:before {
- background-size: contain;
- @include on-desktop() {
- height: $icon-height * 1.5;
- width: $icon-width * 1.5;
- }
- }
- }
- &.snake {
- @include icon('snake', 0, right);
- &:before {
- background-size: contain;
- @include on-desktop() {
- height: $icon-height * 1.5;
- width: $icon-width * 1.5;
- }
- }
- }
- }
- }
- }
-
- .schedule-legend {
-
- background: $pale-grey;
- word-wrap: break-word;
- padding: 32px 24px;
- border-radius: 2px;
-
- @include on-desktop() {
- padding: 32px;
- columns: 2;
- }
- @include icon-info();
- }
-
- .time-table {
- width: calc(100% + 50px);
- margin: -16px;
- background: transparent;
- text-align: center;
-
- // Fonts.
- @include text(16px);
- tbody, tr, th, td, ul {
- // display: block;
- @include text(16px);
- font-weight: normal;
- }
-
- $border-color: $pale-grey;
- // Round corners.
- th, td, .time-stack-ribbon[class~='2-all'] + .event-info {
- border-radius: 0;
- }
- .time-stack-ribbon + .event-info {
- border-radius: 0;
- }
- .time-stack-ribbon {
- border-radius: 0;
- }
-
- // Not actually a table!
- display: block;
- thead, tfoot {
- display: none;
- }
- tbody, tr, th, td, ul {
- display: block;
- }
-
- th, td, tr{
- display: flex;
- align-items: stretch;
- justify-content: left;
- margin: 0;
- padding: 0;
- }
-
- tr {
- align-items: flex-start;
-
- &.slot {
- min-height: 100px;
-
- border-style: solid;
- border-width: 0px;
- border-left-width: 3px;
- border-color: transparent;
- border-radius: 4px;
- background: $ghost-gray;
-
- &.custom {
- background-color: $light-grayish-yellow;
- border: 0px;
- }
-
- &.first-slot {
- border-top-color: $gold-yellow;
- border-top-width: 1px;
- margin-top: 10px;
- }
- &:hover {
- border-left-color: $gold-yellow;
- }
-
- @each $name, $color in ("R123": $gold-yellow, "R0": $gold-yellow, "R1": $egyptian-blue, "R2": $forest-green, "R3": $dark-orange) {
- &.slot-room-#{$name} {
- &.first-slot {
- border-top-color: $gold-yellow;
- }
- &:hover {
- border-left-color: $color;
- background: $white;
- }
- }
- }
- }
- }
-
-
- td {
- $slot-padding-top: 20px;
-
- &.event {
- flex: 3;
-
- .event-info {
- display: flex;
- flex: 1;
- text-align: left;
- flex-direction: column;
- padding-top: $slot-padding-top;
- font-weight: 400;
-
- a:hover {
- text-decoration: none;
- }
-
- .talk-info {
- flex: 3;
- p {
- &.talk-speakers {
- color: $light-gray;
- }
- }
- }
-
- .talk-tags {
- @include list-reset();
- display: flex;
- justify-content: left;
- margin: 0.375rem;
- text-indent: 0;
- flex: 1;
-
- > li::before {
- content: ' ';
- margin-right: 5px;
- }
- @include on-desktop() {
- justify-content: center;
- }
- }
-
- @include on-desktop() {
- flex-direction: row;
- }
- }
-
- &.event-not-first {
- border-top: dashed 1px $light-gray;
- }
-
- &.talk, &.tutorial {
- @include list-reset();
- @include icon-info();
- }
- &.keynote {
- .event-info {
- flex-direction: column;
- }
- }
- &.custom {
- align-self: center;
- width: 100%;
- .event-info {
- @include list-reset();
- @include icon-info();
- padding-top: 0;
- justify-content: center;
- ul {
- display: flex;
- justify-content: center;
- align-items: center;
- li {
- margin: 0px 5px 0px 0px;
- font-weight: 500;
- &:before {
- margin-right: 0;
- }
- &.info {
- text-align: center;
- &:before {
- content: none;
- }
- }
- @include on-desktop {
- margin: 0px 10px 0px 0px;
- }
- }
- }
- }
- }
- }
-
- &.time-table-time {
- flex: 1;
- flex-direction: column;
- @include list-reset();
- @include icon-info();
- padding-top: $slot-padding-top;
- padding-left: 20px;
- &.custom {
- display: none;
- }
- ul {
- margin: 0;
- li {
- &.time {
- text-align: left;
- font-size: 16px;
- &:before {
- content: none;
- }
- }
- }
- }
- }
- }
- }
-
- .room-info {
- @include icon-info();
- ul {
- margin: 0;
- display: flex;
- justify-content: center;
- text-indent: $icon-width;
- }
- }
-}
diff --git a/src/static/pycontw-2022/_includes/base/_variables.scss b/src/static/pycontw-2022/_includes/base/_variables.scss
index 56fb3a410..e297dde3b 100644
--- a/src/static/pycontw-2022/_includes/base/_variables.scss
+++ b/src/static/pycontw-2022/_includes/base/_variables.scss
@@ -10,10 +10,6 @@ $quote-color: $theme-focus;
// section
$section__heading-color: $theme-color;
-// schedule
-$schedule-color: #F58454;
-$schedule__title-color: #ADCDFF;
-
// intro
$intro__heading-color: $theme-color;
$intro-color: #686868;
diff --git a/src/static/pycontw-2022/scripts/schedule.js b/src/static/pycontw-2022/scripts/schedule.js
deleted file mode 100644
index 478756338..000000000
--- a/src/static/pycontw-2022/scripts/schedule.js
+++ /dev/null
@@ -1,32 +0,0 @@
-// Schedule generation.
-const form = document.querySelector('.generation-form')
-if (form) {
- const html = document.querySelector('.schedule-content').innerHTML
- form.querySelector('input[name="html"]').value = html
- form.style.display = 'block'
-}
-
-
-// Replace localed URL with current locale prefix.
-const I18N = JSON.parse(document.getElementById('i18n_variables').textContent)
-function findPrefix(u) {
- const possiblePrefixes = []
- for (const prefix of I18N.LANGUAGE_PREFIXES) {
- if (u.startsWith(prefix)) {
- return prefix
- }
- }
- return ''
-}
-for (const el of document.querySelectorAll('.localed-url')) {
- const original = el.getAttribute('href')
- if (!original) {
- continue
- }
- const prefix = findPrefix(original)
- if (!prefix) {
- continue
- }
- const sub = original.substr(prefix.length)
- el.setAttribute('href', `${I18N.LANGUAGE_PREFIX}${sub}`)
-}
diff --git a/src/static/pycontw-2022/styles/page.scss b/src/static/pycontw-2022/styles/page.scss
index afab90859..0c57b6300 100644
--- a/src/static/pycontw-2022/styles/page.scss
+++ b/src/static/pycontw-2022/styles/page.scss
@@ -407,7 +407,6 @@ ul.custom_ul {
@import "pages/ticket-info";
@import "pages/tutorials";
@import "pages/venue";
-@import "pages/schedule";
@import "pages/staff";
@import "pages/portal";
@import "pages/sponsorship-prospectus";
diff --git a/src/static/pycontw-2022/styles/pages/_events.scss b/src/static/pycontw-2022/styles/pages/_events.scss
index 33fbad935..a343665ca 100644
--- a/src/static/pycontw-2022/styles/pages/_events.scss
+++ b/src/static/pycontw-2022/styles/pages/_events.scss
@@ -228,236 +228,3 @@ $icon-height: 26px;
@include rooms();
}
-
-@mixin toggleTimetableOrTimeList() {
- @media (min-width: 901px) {
- .py-schedule-timetable {
- display: initial;
-
- .room-tag {
- display: none;
- }
- }
-
- .py-schedule-time-list {
- display: none;
- }
- }
-
- @media (max-width: 900px) {
- .py-schedule-timetable {
- display: none;
- }
-
- .py-schedule-time-list {
- display: initial;
-
- .room-tag::before {
- white-space: initial;
- }
- }
-
- .py-schedule-tabs__tab {
- padding: 24px;
- }
- }
-}
-
-main {
- max-width: unset;
- margin: unset;
- padding: 24px;
-
- .py-schedule {
- &-tabs {
- display: flex;
- justify-content: center;
- align-items: center;
-
- &__tab {
- margin: 0 24px 24px;
- padding: 24px 72px;
- font-family: $header-font-family;
- font-size: 20px;
- border-radius: 8px;
- cursor: default;
-
- &:hover:not(.--active) {
- color: red;
- cursor: pointer;
- }
-
- &.--active {
- color: $jinger-bread;
- background: $salmon;
- }
- }
- }
-
- &-timetable {
- &-header {
- position: sticky;
- top: 0;
- z-index: 1000;
- display: flex;
- background: transparentize($brick, 0.3);
-
- &-column {
- @include roomDisplayStyle();
-
- margin: 8px 8px 8px 0;
- padding: 12px 0;
- font-size: 18px;
-
- &:last-child {
- margin-right: 0;
- }
-
- &.--timeline {
- flex: 1;
- background: transparent;
- }
-
- &.--room {
- flex: 2;
- }
- }
-
- @include rooms();
-
- &:not(.--active) {
- display: none;
- }
- }
-
- &-body {
- display: grid;
- grid-template-columns:
- [times] 1fr
- [room-4-r0-start] 2fr
- [room-4-r0-end room-5-r1-start] 2fr
- [room-5-r1-end room-6-r2-start] 2fr
- [room-6-r2-end room-8-r3-start] 2fr
- [room-8-r3-end room-7-r4-start] 2fr
- [room-7-r4-end];
- grid-column-gap: 8px;
- grid-row-gap: 8px;
-
- &-item {
- @include eventItem();
- }
-
- .room-3-r012 {
- grid-column: room-4-r0-start / room-6-r2-end;
- }
-
- .room-4-r0 {
- grid-column: room-4-r0-start / room-4-r0-end;
- }
-
- .room-5-r1 {
- grid-column: room-5-r1-start / room-5-r1-end;
- }
-
- .room-6-r2 {
- grid-column: room-6-r2-start / room-6-r2-end;
- }
-
- .room-7-r4 {
- grid-column: room-7-r4-start / room-7-r4-end;
- }
-
- .room-1-r3 {
- grid-column: room-8-r3-start / room-8-r3-end;
- }
-
- .room-2-all {
- grid-column: room-4-r0-start / room-8-r3-end;
- }
-
- .timeline {
- grid-column: times;
- justify-self: center;
- @include time();
-
- &.--hour {
- font-size: 20px;
- font-weight: 500;
- }
-
- &.--half-an-hour {
- font-size: 18px;
- }
- }
-
- &:not(.--active) {
- display: none;
- }
- }
- }
-
- &-time-list {
- &-section {
- &-header {
- position: sticky;
- position: -webkit-sticky;
- top: 0;
- z-index: 10;
- padding: 16px 0;
- background: $brick;
-
- & > .time {
- @include time();
-
- font-size: 22px;
- }
- }
-
- &-items {
- display: flex;
- flex-direction: column;
- margin: 12px 0;
-
- &-item {
- @include eventItem();
- margin-bottom: 12px;
- }
-
- .room-2-all {
- order: 1;
- }
-
- .room-3-r012 {
- order: 2;
- }
-
- .room-4-r0 {
- order: 3;
- }
-
- .room-5-r1 {
- order: 4;
- }
-
- .room-6-r2 {
- order: 5;
- }
-
- .room-1-r3 {
- order: 6;
- }
-
- .room-7-r4 {
- order: 7;
- }
- }
- }
-
- &:not(.--active) {
- display: none;
- }
- }
- }
-
- @include toggleTimetableOrTimeList();
-}
diff --git a/src/static/pycontw-2022/styles/pages/_schedule.scss b/src/static/pycontw-2022/styles/pages/_schedule.scss
deleted file mode 100644
index 96379039b..000000000
--- a/src/static/pycontw-2022/styles/pages/_schedule.scss
+++ /dev/null
@@ -1,342 +0,0 @@
-.schedule-page {
- form .button-round {
- @include button(276deg, $dark-peach, $maize);
- width: 160px;
- margin: 24px 0;
- text-align: center;
- cursor: pointer;
- }
-
- // Icon
- $icon-width: 26px;
- $icon-height: 26px;
-
- @mixin icon($name, $margin-top: 0, $x: left, $width: $icon-width) {
- &::before {
- width: $width;
- margin-top: $margin-top; // Visual compensation.
- background: url('../assets/icon-#{$name}.svg') top $x no-repeat;
- }
- }
-
- @mixin icon-info() {
- ul {
- @include list-reset();
- text-indent: 0;
- li {
-
- display: flex;
- align-content: baseline;
- align-items: flex-start;
-
- > * {
- flex: 1;
- min-height: 24px;
- margin-bottom: 12px;
- }
-
- &:before {
- content: ' ';
- height: $icon-height;
- margin-right: 1rem;
- }
-
- &.speech-en {
- @include icon('language-enen', -1px);
- }
- &.speech-zh {
- @include icon('language-zhzh', -1px);
- }
- &.slides-en {
- @include icon('language-enen', 0, right);
- }
- &.slides-zh {
- @include icon('language-zhzh', 0, right);
- }
- &.language-enen {
- @include icon('language-enen', 0, center, 2 * $icon-width);
- }
- &.language-zhen {
- @include icon('language-zhen', 0, center, 2 * $icon-width);
- }
- &.language-zhzh {
- @include icon('language-zhzh', 0, center, 2 * $icon-width);
- }
- &.python-level-novice {
- @include icon('level-1');
- }
- &.python-level-intermediate {
- @include icon('level-2');
- }
- &.python-level-experienced {
- @include icon('level-3');
- }
- &.no-recording {
- @include icon('no-recording', 1px);
- }
- &.room-R0 {
- @include icon('room-R0', 1px, center);
- }
- &.room-R1 {
- @include icon('room-R1', 1px, center);
- }
- &.room-R2 {
- @include icon('room-R2', 1px, center);
- }
- &.room-R3 {
- @include icon('room-R3', 1px, center);
- }
- &.grass {
- @include icon('grass-group', 0, right);
- &:before {
- background-size: contain;
- @include on-desktop() {
- height: $icon-height * 1.5;
- width: $icon-width * 1.5;
- }
- }
- }
- &.snake {
- @include icon('snake', 0, right);
- &:before {
- background-size: contain;
- @include on-desktop() {
- height: $icon-height * 1.5;
- width: $icon-width * 1.5;
- }
- }
- }
- }
- }
- }
-
- .schedule-legend {
-
- background: $pale-grey;
- word-wrap: break-word;
- padding: 32px 24px;
- border-radius: 2px;
-
- @include on-desktop() {
- padding: 32px;
- columns: 2;
- }
- @include icon-info();
- }
-
- .time-table {
- width: calc(100% + 50px);
- margin: -16px;
- background: transparent;
- text-align: center;
-
- // Fonts.
- @include text(16px);
- tbody, tr, th, td, ul {
- // display: block;
- @include text(16px);
- font-weight: normal;
- }
-
- $border-color: $pale-grey;
- // Round corners.
- th, td, .time-stack-ribbon[class~='2-all'] + .event-info {
- border-radius: 0;
- }
- .time-stack-ribbon + .event-info {
- border-radius: 0;
- }
- .time-stack-ribbon {
- border-radius: 0;
- }
-
- // Not actually a table!
- display: block;
- thead, tfoot {
- display: none;
- }
- tbody, tr, th, td, ul {
- display: block;
- }
-
- th, td, tr{
- display: flex;
- align-items: stretch;
- justify-content: left;
- margin: 0;
- padding: 0;
- }
-
- tr {
- align-items: flex-start;
-
- &.slot {
- min-height: 100px;
-
- border-style: solid;
- border-width: 0px;
- border-left-width: 3px;
- border-color: transparent;
- border-radius: 4px;
- background: $ghost-gray;
-
- &.custom {
- background-color: $light-grayish-yellow;
- border: 0px;
- }
-
- &.first-slot {
- border-top-color: $gold-yellow;
- border-top-width: 1px;
- margin-top: 10px;
- }
- &:hover {
- border-left-color: $gold-yellow;
- }
-
- @each $name, $color in ("R123": $gold-yellow, "R0": $gold-yellow, "R1": $egyptian-blue, "R2": $forest-green, "R3": $dark-orange) {
- &.slot-room-#{$name} {
- &.first-slot {
- border-top-color: $gold-yellow;
- }
- &:hover {
- border-left-color: $color;
- background: $white;
- }
- }
- }
- }
- }
-
-
- td {
- $slot-padding-top: 20px;
-
- &.event {
- flex: 3;
-
- .event-info {
- display: flex;
- flex: 1;
- text-align: left;
- flex-direction: column;
- padding-top: $slot-padding-top;
- font-weight: 400;
-
- a:hover {
- text-decoration: none;
- }
-
- .talk-info {
- flex: 3;
- p {
- &.talk-speakers {
- color: $light-gray;
- }
- }
- }
-
- .talk-tags {
- @include list-reset();
- display: flex;
- justify-content: left;
- margin: 0.375rem;
- text-indent: 0;
- flex: 1;
-
- > li::before {
- content: ' ';
- margin-right: 5px;
- }
- @include on-desktop() {
- justify-content: center;
- }
- }
-
- @include on-desktop() {
- flex-direction: row;
- }
- }
-
- &.event-not-first {
- border-top: dashed 1px $light-gray;
- }
-
- &.talk, &.tutorial {
- @include list-reset();
- @include icon-info();
- }
- &.keynote {
- .event-info {
- flex-direction: column;
- }
- }
- &.custom {
- align-self: center;
- width: 100%;
- .event-info {
- @include list-reset();
- @include icon-info();
- padding-top: 0;
- justify-content: center;
- ul {
- display: flex;
- justify-content: center;
- align-items: center;
- li {
- margin: 0px 5px 0px 0px;
- font-weight: 500;
- &:before {
- margin-right: 0;
- }
- &.info {
- text-align: center;
- &:before {
- content: none;
- }
- }
- @include on-desktop {
- margin: 0px 10px 0px 0px;
- }
- }
- }
- }
- }
- }
-
- &.time-table-time {
- flex: 1;
- flex-direction: column;
- @include list-reset();
- @include icon-info();
- padding-top: $slot-padding-top;
- padding-left: 20px;
- &.custom {
- display: none;
- }
- ul {
- margin: 0;
- li {
- &.time {
- text-align: left;
- font-size: 16px;
- &:before {
- content: none;
- }
- }
- }
- }
- }
- }
- }
-
- .room-info {
- @include icon-info();
- ul {
- margin: 0;
- display: flex;
- justify-content: center;
- text-indent: $icon-width;
- }
- }
-}
diff --git a/src/static/pycontw-2023/_includes/base/_variables.scss b/src/static/pycontw-2023/_includes/base/_variables.scss
index 2af268b93..be9d2ed16 100644
--- a/src/static/pycontw-2023/_includes/base/_variables.scss
+++ b/src/static/pycontw-2023/_includes/base/_variables.scss
@@ -10,10 +10,6 @@ $quote-color: $theme-focus;
// section
$section__heading-color: $theme-color;
-// schedule
-$schedule-color: #F58454;
-$schedule__title-color: #ADCDFF;
-
// intro
$intro__heading-color: $theme-color;
$intro-color: #686868;
diff --git a/src/static/pycontw-2023/scripts/schedule.js b/src/static/pycontw-2023/scripts/schedule.js
deleted file mode 100644
index 478756338..000000000
--- a/src/static/pycontw-2023/scripts/schedule.js
+++ /dev/null
@@ -1,32 +0,0 @@
-// Schedule generation.
-const form = document.querySelector('.generation-form')
-if (form) {
- const html = document.querySelector('.schedule-content').innerHTML
- form.querySelector('input[name="html"]').value = html
- form.style.display = 'block'
-}
-
-
-// Replace localed URL with current locale prefix.
-const I18N = JSON.parse(document.getElementById('i18n_variables').textContent)
-function findPrefix(u) {
- const possiblePrefixes = []
- for (const prefix of I18N.LANGUAGE_PREFIXES) {
- if (u.startsWith(prefix)) {
- return prefix
- }
- }
- return ''
-}
-for (const el of document.querySelectorAll('.localed-url')) {
- const original = el.getAttribute('href')
- if (!original) {
- continue
- }
- const prefix = findPrefix(original)
- if (!prefix) {
- continue
- }
- const sub = original.substr(prefix.length)
- el.setAttribute('href', `${I18N.LANGUAGE_PREFIX}${sub}`)
-}
diff --git a/src/static/pycontw-2023/styles/page.scss b/src/static/pycontw-2023/styles/page.scss
index 7c9b94569..3261152c0 100644
--- a/src/static/pycontw-2023/styles/page.scss
+++ b/src/static/pycontw-2023/styles/page.scss
@@ -407,7 +407,6 @@ ul.custom_ul {
@import "pages/ticket-info";
@import "pages/tutorials";
@import "pages/venue";
-@import "pages/schedule";
@import "pages/staff";
@import "pages/portal";
@import "pages/sponsorship-prospectus";
diff --git a/src/static/pycontw-2023/styles/pages/_events.scss b/src/static/pycontw-2023/styles/pages/_events.scss
index 33fbad935..a343665ca 100644
--- a/src/static/pycontw-2023/styles/pages/_events.scss
+++ b/src/static/pycontw-2023/styles/pages/_events.scss
@@ -228,236 +228,3 @@ $icon-height: 26px;
@include rooms();
}
-
-@mixin toggleTimetableOrTimeList() {
- @media (min-width: 901px) {
- .py-schedule-timetable {
- display: initial;
-
- .room-tag {
- display: none;
- }
- }
-
- .py-schedule-time-list {
- display: none;
- }
- }
-
- @media (max-width: 900px) {
- .py-schedule-timetable {
- display: none;
- }
-
- .py-schedule-time-list {
- display: initial;
-
- .room-tag::before {
- white-space: initial;
- }
- }
-
- .py-schedule-tabs__tab {
- padding: 24px;
- }
- }
-}
-
-main {
- max-width: unset;
- margin: unset;
- padding: 24px;
-
- .py-schedule {
- &-tabs {
- display: flex;
- justify-content: center;
- align-items: center;
-
- &__tab {
- margin: 0 24px 24px;
- padding: 24px 72px;
- font-family: $header-font-family;
- font-size: 20px;
- border-radius: 8px;
- cursor: default;
-
- &:hover:not(.--active) {
- color: red;
- cursor: pointer;
- }
-
- &.--active {
- color: $jinger-bread;
- background: $salmon;
- }
- }
- }
-
- &-timetable {
- &-header {
- position: sticky;
- top: 0;
- z-index: 1000;
- display: flex;
- background: transparentize($brick, 0.3);
-
- &-column {
- @include roomDisplayStyle();
-
- margin: 8px 8px 8px 0;
- padding: 12px 0;
- font-size: 18px;
-
- &:last-child {
- margin-right: 0;
- }
-
- &.--timeline {
- flex: 1;
- background: transparent;
- }
-
- &.--room {
- flex: 2;
- }
- }
-
- @include rooms();
-
- &:not(.--active) {
- display: none;
- }
- }
-
- &-body {
- display: grid;
- grid-template-columns:
- [times] 1fr
- [room-4-r0-start] 2fr
- [room-4-r0-end room-5-r1-start] 2fr
- [room-5-r1-end room-6-r2-start] 2fr
- [room-6-r2-end room-8-r3-start] 2fr
- [room-8-r3-end room-7-r4-start] 2fr
- [room-7-r4-end];
- grid-column-gap: 8px;
- grid-row-gap: 8px;
-
- &-item {
- @include eventItem();
- }
-
- .room-3-r012 {
- grid-column: room-4-r0-start / room-6-r2-end;
- }
-
- .room-4-r0 {
- grid-column: room-4-r0-start / room-4-r0-end;
- }
-
- .room-5-r1 {
- grid-column: room-5-r1-start / room-5-r1-end;
- }
-
- .room-6-r2 {
- grid-column: room-6-r2-start / room-6-r2-end;
- }
-
- .room-7-r4 {
- grid-column: room-7-r4-start / room-7-r4-end;
- }
-
- .room-1-r3 {
- grid-column: room-8-r3-start / room-8-r3-end;
- }
-
- .room-2-all {
- grid-column: room-4-r0-start / room-8-r3-end;
- }
-
- .timeline {
- grid-column: times;
- justify-self: center;
- @include time();
-
- &.--hour {
- font-size: 20px;
- font-weight: 500;
- }
-
- &.--half-an-hour {
- font-size: 18px;
- }
- }
-
- &:not(.--active) {
- display: none;
- }
- }
- }
-
- &-time-list {
- &-section {
- &-header {
- position: sticky;
- position: -webkit-sticky;
- top: 0;
- z-index: 10;
- padding: 16px 0;
- background: $brick;
-
- & > .time {
- @include time();
-
- font-size: 22px;
- }
- }
-
- &-items {
- display: flex;
- flex-direction: column;
- margin: 12px 0;
-
- &-item {
- @include eventItem();
- margin-bottom: 12px;
- }
-
- .room-2-all {
- order: 1;
- }
-
- .room-3-r012 {
- order: 2;
- }
-
- .room-4-r0 {
- order: 3;
- }
-
- .room-5-r1 {
- order: 4;
- }
-
- .room-6-r2 {
- order: 5;
- }
-
- .room-1-r3 {
- order: 6;
- }
-
- .room-7-r4 {
- order: 7;
- }
- }
- }
-
- &:not(.--active) {
- display: none;
- }
- }
- }
-
- @include toggleTimetableOrTimeList();
-}
diff --git a/src/static/pycontw-2023/styles/pages/_schedule.scss b/src/static/pycontw-2023/styles/pages/_schedule.scss
deleted file mode 100644
index 96379039b..000000000
--- a/src/static/pycontw-2023/styles/pages/_schedule.scss
+++ /dev/null
@@ -1,342 +0,0 @@
-.schedule-page {
- form .button-round {
- @include button(276deg, $dark-peach, $maize);
- width: 160px;
- margin: 24px 0;
- text-align: center;
- cursor: pointer;
- }
-
- // Icon
- $icon-width: 26px;
- $icon-height: 26px;
-
- @mixin icon($name, $margin-top: 0, $x: left, $width: $icon-width) {
- &::before {
- width: $width;
- margin-top: $margin-top; // Visual compensation.
- background: url('../assets/icon-#{$name}.svg') top $x no-repeat;
- }
- }
-
- @mixin icon-info() {
- ul {
- @include list-reset();
- text-indent: 0;
- li {
-
- display: flex;
- align-content: baseline;
- align-items: flex-start;
-
- > * {
- flex: 1;
- min-height: 24px;
- margin-bottom: 12px;
- }
-
- &:before {
- content: ' ';
- height: $icon-height;
- margin-right: 1rem;
- }
-
- &.speech-en {
- @include icon('language-enen', -1px);
- }
- &.speech-zh {
- @include icon('language-zhzh', -1px);
- }
- &.slides-en {
- @include icon('language-enen', 0, right);
- }
- &.slides-zh {
- @include icon('language-zhzh', 0, right);
- }
- &.language-enen {
- @include icon('language-enen', 0, center, 2 * $icon-width);
- }
- &.language-zhen {
- @include icon('language-zhen', 0, center, 2 * $icon-width);
- }
- &.language-zhzh {
- @include icon('language-zhzh', 0, center, 2 * $icon-width);
- }
- &.python-level-novice {
- @include icon('level-1');
- }
- &.python-level-intermediate {
- @include icon('level-2');
- }
- &.python-level-experienced {
- @include icon('level-3');
- }
- &.no-recording {
- @include icon('no-recording', 1px);
- }
- &.room-R0 {
- @include icon('room-R0', 1px, center);
- }
- &.room-R1 {
- @include icon('room-R1', 1px, center);
- }
- &.room-R2 {
- @include icon('room-R2', 1px, center);
- }
- &.room-R3 {
- @include icon('room-R3', 1px, center);
- }
- &.grass {
- @include icon('grass-group', 0, right);
- &:before {
- background-size: contain;
- @include on-desktop() {
- height: $icon-height * 1.5;
- width: $icon-width * 1.5;
- }
- }
- }
- &.snake {
- @include icon('snake', 0, right);
- &:before {
- background-size: contain;
- @include on-desktop() {
- height: $icon-height * 1.5;
- width: $icon-width * 1.5;
- }
- }
- }
- }
- }
- }
-
- .schedule-legend {
-
- background: $pale-grey;
- word-wrap: break-word;
- padding: 32px 24px;
- border-radius: 2px;
-
- @include on-desktop() {
- padding: 32px;
- columns: 2;
- }
- @include icon-info();
- }
-
- .time-table {
- width: calc(100% + 50px);
- margin: -16px;
- background: transparent;
- text-align: center;
-
- // Fonts.
- @include text(16px);
- tbody, tr, th, td, ul {
- // display: block;
- @include text(16px);
- font-weight: normal;
- }
-
- $border-color: $pale-grey;
- // Round corners.
- th, td, .time-stack-ribbon[class~='2-all'] + .event-info {
- border-radius: 0;
- }
- .time-stack-ribbon + .event-info {
- border-radius: 0;
- }
- .time-stack-ribbon {
- border-radius: 0;
- }
-
- // Not actually a table!
- display: block;
- thead, tfoot {
- display: none;
- }
- tbody, tr, th, td, ul {
- display: block;
- }
-
- th, td, tr{
- display: flex;
- align-items: stretch;
- justify-content: left;
- margin: 0;
- padding: 0;
- }
-
- tr {
- align-items: flex-start;
-
- &.slot {
- min-height: 100px;
-
- border-style: solid;
- border-width: 0px;
- border-left-width: 3px;
- border-color: transparent;
- border-radius: 4px;
- background: $ghost-gray;
-
- &.custom {
- background-color: $light-grayish-yellow;
- border: 0px;
- }
-
- &.first-slot {
- border-top-color: $gold-yellow;
- border-top-width: 1px;
- margin-top: 10px;
- }
- &:hover {
- border-left-color: $gold-yellow;
- }
-
- @each $name, $color in ("R123": $gold-yellow, "R0": $gold-yellow, "R1": $egyptian-blue, "R2": $forest-green, "R3": $dark-orange) {
- &.slot-room-#{$name} {
- &.first-slot {
- border-top-color: $gold-yellow;
- }
- &:hover {
- border-left-color: $color;
- background: $white;
- }
- }
- }
- }
- }
-
-
- td {
- $slot-padding-top: 20px;
-
- &.event {
- flex: 3;
-
- .event-info {
- display: flex;
- flex: 1;
- text-align: left;
- flex-direction: column;
- padding-top: $slot-padding-top;
- font-weight: 400;
-
- a:hover {
- text-decoration: none;
- }
-
- .talk-info {
- flex: 3;
- p {
- &.talk-speakers {
- color: $light-gray;
- }
- }
- }
-
- .talk-tags {
- @include list-reset();
- display: flex;
- justify-content: left;
- margin: 0.375rem;
- text-indent: 0;
- flex: 1;
-
- > li::before {
- content: ' ';
- margin-right: 5px;
- }
- @include on-desktop() {
- justify-content: center;
- }
- }
-
- @include on-desktop() {
- flex-direction: row;
- }
- }
-
- &.event-not-first {
- border-top: dashed 1px $light-gray;
- }
-
- &.talk, &.tutorial {
- @include list-reset();
- @include icon-info();
- }
- &.keynote {
- .event-info {
- flex-direction: column;
- }
- }
- &.custom {
- align-self: center;
- width: 100%;
- .event-info {
- @include list-reset();
- @include icon-info();
- padding-top: 0;
- justify-content: center;
- ul {
- display: flex;
- justify-content: center;
- align-items: center;
- li {
- margin: 0px 5px 0px 0px;
- font-weight: 500;
- &:before {
- margin-right: 0;
- }
- &.info {
- text-align: center;
- &:before {
- content: none;
- }
- }
- @include on-desktop {
- margin: 0px 10px 0px 0px;
- }
- }
- }
- }
- }
- }
-
- &.time-table-time {
- flex: 1;
- flex-direction: column;
- @include list-reset();
- @include icon-info();
- padding-top: $slot-padding-top;
- padding-left: 20px;
- &.custom {
- display: none;
- }
- ul {
- margin: 0;
- li {
- &.time {
- text-align: left;
- font-size: 16px;
- &:before {
- content: none;
- }
- }
- }
- }
- }
- }
- }
-
- .room-info {
- @include icon-info();
- ul {
- margin: 0;
- display: flex;
- justify-content: center;
- text-indent: $icon-width;
- }
- }
-}
diff --git a/src/static/pycontw-2024/_includes/base/_variables.scss b/src/static/pycontw-2024/_includes/base/_variables.scss
index 0133d965a..e899cb95d 100644
--- a/src/static/pycontw-2024/_includes/base/_variables.scss
+++ b/src/static/pycontw-2024/_includes/base/_variables.scss
@@ -10,10 +10,6 @@ $quote-color: $theme-focus;
// section
$section__heading-color: $theme-color;
-// schedule
-$schedule-color: #F58454;
-$schedule__title-color: #ADCDFF;
-
// intro
$intro__heading-color: $theme-color;
$intro-color: #686868;
diff --git a/src/static/pycontw-2024/scripts/schedule.js b/src/static/pycontw-2024/scripts/schedule.js
deleted file mode 100644
index 478756338..000000000
--- a/src/static/pycontw-2024/scripts/schedule.js
+++ /dev/null
@@ -1,32 +0,0 @@
-// Schedule generation.
-const form = document.querySelector('.generation-form')
-if (form) {
- const html = document.querySelector('.schedule-content').innerHTML
- form.querySelector('input[name="html"]').value = html
- form.style.display = 'block'
-}
-
-
-// Replace localed URL with current locale prefix.
-const I18N = JSON.parse(document.getElementById('i18n_variables').textContent)
-function findPrefix(u) {
- const possiblePrefixes = []
- for (const prefix of I18N.LANGUAGE_PREFIXES) {
- if (u.startsWith(prefix)) {
- return prefix
- }
- }
- return ''
-}
-for (const el of document.querySelectorAll('.localed-url')) {
- const original = el.getAttribute('href')
- if (!original) {
- continue
- }
- const prefix = findPrefix(original)
- if (!prefix) {
- continue
- }
- const sub = original.substr(prefix.length)
- el.setAttribute('href', `${I18N.LANGUAGE_PREFIX}${sub}`)
-}
diff --git a/src/static/pycontw-2024/styles/page.scss b/src/static/pycontw-2024/styles/page.scss
index 7c9b94569..3261152c0 100644
--- a/src/static/pycontw-2024/styles/page.scss
+++ b/src/static/pycontw-2024/styles/page.scss
@@ -407,7 +407,6 @@ ul.custom_ul {
@import "pages/ticket-info";
@import "pages/tutorials";
@import "pages/venue";
-@import "pages/schedule";
@import "pages/staff";
@import "pages/portal";
@import "pages/sponsorship-prospectus";
diff --git a/src/static/pycontw-2024/styles/pages/_events.scss b/src/static/pycontw-2024/styles/pages/_events.scss
index 33fbad935..a343665ca 100644
--- a/src/static/pycontw-2024/styles/pages/_events.scss
+++ b/src/static/pycontw-2024/styles/pages/_events.scss
@@ -228,236 +228,3 @@ $icon-height: 26px;
@include rooms();
}
-
-@mixin toggleTimetableOrTimeList() {
- @media (min-width: 901px) {
- .py-schedule-timetable {
- display: initial;
-
- .room-tag {
- display: none;
- }
- }
-
- .py-schedule-time-list {
- display: none;
- }
- }
-
- @media (max-width: 900px) {
- .py-schedule-timetable {
- display: none;
- }
-
- .py-schedule-time-list {
- display: initial;
-
- .room-tag::before {
- white-space: initial;
- }
- }
-
- .py-schedule-tabs__tab {
- padding: 24px;
- }
- }
-}
-
-main {
- max-width: unset;
- margin: unset;
- padding: 24px;
-
- .py-schedule {
- &-tabs {
- display: flex;
- justify-content: center;
- align-items: center;
-
- &__tab {
- margin: 0 24px 24px;
- padding: 24px 72px;
- font-family: $header-font-family;
- font-size: 20px;
- border-radius: 8px;
- cursor: default;
-
- &:hover:not(.--active) {
- color: red;
- cursor: pointer;
- }
-
- &.--active {
- color: $jinger-bread;
- background: $salmon;
- }
- }
- }
-
- &-timetable {
- &-header {
- position: sticky;
- top: 0;
- z-index: 1000;
- display: flex;
- background: transparentize($brick, 0.3);
-
- &-column {
- @include roomDisplayStyle();
-
- margin: 8px 8px 8px 0;
- padding: 12px 0;
- font-size: 18px;
-
- &:last-child {
- margin-right: 0;
- }
-
- &.--timeline {
- flex: 1;
- background: transparent;
- }
-
- &.--room {
- flex: 2;
- }
- }
-
- @include rooms();
-
- &:not(.--active) {
- display: none;
- }
- }
-
- &-body {
- display: grid;
- grid-template-columns:
- [times] 1fr
- [room-4-r0-start] 2fr
- [room-4-r0-end room-5-r1-start] 2fr
- [room-5-r1-end room-6-r2-start] 2fr
- [room-6-r2-end room-8-r3-start] 2fr
- [room-8-r3-end room-7-r4-start] 2fr
- [room-7-r4-end];
- grid-column-gap: 8px;
- grid-row-gap: 8px;
-
- &-item {
- @include eventItem();
- }
-
- .room-3-r012 {
- grid-column: room-4-r0-start / room-6-r2-end;
- }
-
- .room-4-r0 {
- grid-column: room-4-r0-start / room-4-r0-end;
- }
-
- .room-5-r1 {
- grid-column: room-5-r1-start / room-5-r1-end;
- }
-
- .room-6-r2 {
- grid-column: room-6-r2-start / room-6-r2-end;
- }
-
- .room-7-r4 {
- grid-column: room-7-r4-start / room-7-r4-end;
- }
-
- .room-1-r3 {
- grid-column: room-8-r3-start / room-8-r3-end;
- }
-
- .room-2-all {
- grid-column: room-4-r0-start / room-8-r3-end;
- }
-
- .timeline {
- grid-column: times;
- justify-self: center;
- @include time();
-
- &.--hour {
- font-size: 20px;
- font-weight: 500;
- }
-
- &.--half-an-hour {
- font-size: 18px;
- }
- }
-
- &:not(.--active) {
- display: none;
- }
- }
- }
-
- &-time-list {
- &-section {
- &-header {
- position: sticky;
- position: -webkit-sticky;
- top: 0;
- z-index: 10;
- padding: 16px 0;
- background: $brick;
-
- & > .time {
- @include time();
-
- font-size: 22px;
- }
- }
-
- &-items {
- display: flex;
- flex-direction: column;
- margin: 12px 0;
-
- &-item {
- @include eventItem();
- margin-bottom: 12px;
- }
-
- .room-2-all {
- order: 1;
- }
-
- .room-3-r012 {
- order: 2;
- }
-
- .room-4-r0 {
- order: 3;
- }
-
- .room-5-r1 {
- order: 4;
- }
-
- .room-6-r2 {
- order: 5;
- }
-
- .room-1-r3 {
- order: 6;
- }
-
- .room-7-r4 {
- order: 7;
- }
- }
- }
-
- &:not(.--active) {
- display: none;
- }
- }
- }
-
- @include toggleTimetableOrTimeList();
-}
diff --git a/src/static/pycontw-2024/styles/pages/_schedule.scss b/src/static/pycontw-2024/styles/pages/_schedule.scss
deleted file mode 100644
index 96379039b..000000000
--- a/src/static/pycontw-2024/styles/pages/_schedule.scss
+++ /dev/null
@@ -1,342 +0,0 @@
-.schedule-page {
- form .button-round {
- @include button(276deg, $dark-peach, $maize);
- width: 160px;
- margin: 24px 0;
- text-align: center;
- cursor: pointer;
- }
-
- // Icon
- $icon-width: 26px;
- $icon-height: 26px;
-
- @mixin icon($name, $margin-top: 0, $x: left, $width: $icon-width) {
- &::before {
- width: $width;
- margin-top: $margin-top; // Visual compensation.
- background: url('../assets/icon-#{$name}.svg') top $x no-repeat;
- }
- }
-
- @mixin icon-info() {
- ul {
- @include list-reset();
- text-indent: 0;
- li {
-
- display: flex;
- align-content: baseline;
- align-items: flex-start;
-
- > * {
- flex: 1;
- min-height: 24px;
- margin-bottom: 12px;
- }
-
- &:before {
- content: ' ';
- height: $icon-height;
- margin-right: 1rem;
- }
-
- &.speech-en {
- @include icon('language-enen', -1px);
- }
- &.speech-zh {
- @include icon('language-zhzh', -1px);
- }
- &.slides-en {
- @include icon('language-enen', 0, right);
- }
- &.slides-zh {
- @include icon('language-zhzh', 0, right);
- }
- &.language-enen {
- @include icon('language-enen', 0, center, 2 * $icon-width);
- }
- &.language-zhen {
- @include icon('language-zhen', 0, center, 2 * $icon-width);
- }
- &.language-zhzh {
- @include icon('language-zhzh', 0, center, 2 * $icon-width);
- }
- &.python-level-novice {
- @include icon('level-1');
- }
- &.python-level-intermediate {
- @include icon('level-2');
- }
- &.python-level-experienced {
- @include icon('level-3');
- }
- &.no-recording {
- @include icon('no-recording', 1px);
- }
- &.room-R0 {
- @include icon('room-R0', 1px, center);
- }
- &.room-R1 {
- @include icon('room-R1', 1px, center);
- }
- &.room-R2 {
- @include icon('room-R2', 1px, center);
- }
- &.room-R3 {
- @include icon('room-R3', 1px, center);
- }
- &.grass {
- @include icon('grass-group', 0, right);
- &:before {
- background-size: contain;
- @include on-desktop() {
- height: $icon-height * 1.5;
- width: $icon-width * 1.5;
- }
- }
- }
- &.snake {
- @include icon('snake', 0, right);
- &:before {
- background-size: contain;
- @include on-desktop() {
- height: $icon-height * 1.5;
- width: $icon-width * 1.5;
- }
- }
- }
- }
- }
- }
-
- .schedule-legend {
-
- background: $pale-grey;
- word-wrap: break-word;
- padding: 32px 24px;
- border-radius: 2px;
-
- @include on-desktop() {
- padding: 32px;
- columns: 2;
- }
- @include icon-info();
- }
-
- .time-table {
- width: calc(100% + 50px);
- margin: -16px;
- background: transparent;
- text-align: center;
-
- // Fonts.
- @include text(16px);
- tbody, tr, th, td, ul {
- // display: block;
- @include text(16px);
- font-weight: normal;
- }
-
- $border-color: $pale-grey;
- // Round corners.
- th, td, .time-stack-ribbon[class~='2-all'] + .event-info {
- border-radius: 0;
- }
- .time-stack-ribbon + .event-info {
- border-radius: 0;
- }
- .time-stack-ribbon {
- border-radius: 0;
- }
-
- // Not actually a table!
- display: block;
- thead, tfoot {
- display: none;
- }
- tbody, tr, th, td, ul {
- display: block;
- }
-
- th, td, tr{
- display: flex;
- align-items: stretch;
- justify-content: left;
- margin: 0;
- padding: 0;
- }
-
- tr {
- align-items: flex-start;
-
- &.slot {
- min-height: 100px;
-
- border-style: solid;
- border-width: 0px;
- border-left-width: 3px;
- border-color: transparent;
- border-radius: 4px;
- background: $ghost-gray;
-
- &.custom {
- background-color: $light-grayish-yellow;
- border: 0px;
- }
-
- &.first-slot {
- border-top-color: $gold-yellow;
- border-top-width: 1px;
- margin-top: 10px;
- }
- &:hover {
- border-left-color: $gold-yellow;
- }
-
- @each $name, $color in ("R123": $gold-yellow, "R0": $gold-yellow, "R1": $egyptian-blue, "R2": $forest-green, "R3": $dark-orange) {
- &.slot-room-#{$name} {
- &.first-slot {
- border-top-color: $gold-yellow;
- }
- &:hover {
- border-left-color: $color;
- background: $white;
- }
- }
- }
- }
- }
-
-
- td {
- $slot-padding-top: 20px;
-
- &.event {
- flex: 3;
-
- .event-info {
- display: flex;
- flex: 1;
- text-align: left;
- flex-direction: column;
- padding-top: $slot-padding-top;
- font-weight: 400;
-
- a:hover {
- text-decoration: none;
- }
-
- .talk-info {
- flex: 3;
- p {
- &.talk-speakers {
- color: $light-gray;
- }
- }
- }
-
- .talk-tags {
- @include list-reset();
- display: flex;
- justify-content: left;
- margin: 0.375rem;
- text-indent: 0;
- flex: 1;
-
- > li::before {
- content: ' ';
- margin-right: 5px;
- }
- @include on-desktop() {
- justify-content: center;
- }
- }
-
- @include on-desktop() {
- flex-direction: row;
- }
- }
-
- &.event-not-first {
- border-top: dashed 1px $light-gray;
- }
-
- &.talk, &.tutorial {
- @include list-reset();
- @include icon-info();
- }
- &.keynote {
- .event-info {
- flex-direction: column;
- }
- }
- &.custom {
- align-self: center;
- width: 100%;
- .event-info {
- @include list-reset();
- @include icon-info();
- padding-top: 0;
- justify-content: center;
- ul {
- display: flex;
- justify-content: center;
- align-items: center;
- li {
- margin: 0px 5px 0px 0px;
- font-weight: 500;
- &:before {
- margin-right: 0;
- }
- &.info {
- text-align: center;
- &:before {
- content: none;
- }
- }
- @include on-desktop {
- margin: 0px 10px 0px 0px;
- }
- }
- }
- }
- }
- }
-
- &.time-table-time {
- flex: 1;
- flex-direction: column;
- @include list-reset();
- @include icon-info();
- padding-top: $slot-padding-top;
- padding-left: 20px;
- &.custom {
- display: none;
- }
- ul {
- margin: 0;
- li {
- &.time {
- text-align: left;
- font-size: 16px;
- &:before {
- content: none;
- }
- }
- }
- }
- }
- }
- }
-
- .room-info {
- @include icon-info();
- ul {
- margin: 0;
- display: flex;
- justify-content: center;
- text-indent: $icon-width;
- }
- }
-}
diff --git a/src/template_tests/conftest.py b/src/template_tests/conftest.py
deleted file mode 100644
index 3138a844f..000000000
--- a/src/template_tests/conftest.py
+++ /dev/null
@@ -1,8 +0,0 @@
-import pytest
-
-from events.models import Schedule
-
-
-@pytest.fixture
-def schedule(db):
- return Schedule.objects.create(html='mmm')
diff --git a/src/template_tests/test_pycontw2016.py b/src/template_tests/test_pycontw2016.py
index c85a50c40..90314bb64 100644
--- a/src/template_tests/test_pycontw2016.py
+++ b/src/template_tests/test_pycontw2016.py
@@ -15,17 +15,6 @@
User = get_user_model()
-def test_schedule(client, parser, schedule):
- r = client.get('/en-us/events/schedule/')
- body = parser.parse(r)
-
- # Make sure every cross-page intra-site link is good.
- link_tags = body.cssselect('a[href^="/"]')
- assert len(link_tags) != 0
- for tag in link_tags:
- assert client.get(tag.get('href'), follow=True).status_code == 200
-
-
@pytest.mark.django_db
@override_settings( # Make sure we don't really send an email.
SECRET_KEY='Footage order-flow long-chain hydrocarbons hacker',
diff --git a/src/templates/pycontw-2021/_includes/menu.html b/src/templates/pycontw-2021/_includes/menu.html
index a55756f5c..ab336e69a 100644
--- a/src/templates/pycontw-2021/_includes/menu.html
+++ b/src/templates/pycontw-2021/_includes/menu.html
@@ -13,7 +13,6 @@
{% url 'page' path='speaking/tutorial' as speaking_tutorial_url %}
{% url 'page' path='speaking/recording' as speaking_recording_url %}
-{% url 'events_schedule' as events_schedule_url %}
{% url 'page' path='conference/keynotes' as events_keynote_url %}
{% url 'events_talk_list' as events_talk_list_url %}
{% url 'events_tutorial_list' as events_tutorial_list_url %}
@@ -78,7 +77,6 @@