Merged in [18712] from jennifer@painless-security.com:
Add timezone support to agenda weekview; display UTC on UTC agenda page. Fixes #3111.
- Legacy-Id: 18796
Note: SVN reference [18712] has been migrated to Git commit d29553c0bc
This commit is contained in:
commit
159b8fe37c
|
@ -1,5 +1,7 @@
|
|||
# -*- conf-mode -*-
|
||||
|
||||
/personal/kivinen/7.22.1.dev0@18689 # Hold for revision based on timezone-aware code
|
||||
|
||||
/personal/rcross/7.19.1.dev0@18663
|
||||
/personal/rcross/7.19.1.dev0@18662
|
||||
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
"js-cookie": "~2",
|
||||
"jquery": "~1",
|
||||
"jquery.tablesorter": "~2",
|
||||
"moment": "~2",
|
||||
"moment-timezone": "~0",
|
||||
"respond": "~1",
|
||||
"select2": "~3",
|
||||
"select2-bootstrap-css": "~1",
|
||||
|
@ -33,6 +35,11 @@
|
|||
"./fonts/*"
|
||||
]
|
||||
},
|
||||
"moment": {
|
||||
"main": [
|
||||
"min/moment.min.js"
|
||||
]
|
||||
},
|
||||
"tablesorter": {
|
||||
"main": [
|
||||
"dist/js/jquery.tablesorter.combined.min.js",
|
||||
|
|
1
ietf/externals/static/moment-timezone/builds/moment-timezone-with-data-10-year-range.min.js
vendored
Normal file
1
ietf/externals/static/moment-timezone/builds/moment-timezone-with-data-10-year-range.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
2
ietf/externals/static/moment/min/moment.min.js
vendored
Normal file
2
ietf/externals/static/moment/min/moment.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
|
@ -13,6 +13,7 @@ import django
|
|||
from django.urls import reverse as urlreverse
|
||||
from django.utils.text import slugify
|
||||
from django.db.models import F
|
||||
from pytz import timezone
|
||||
#from django.test.utils import override_settings
|
||||
|
||||
import debug # pyflakes:ignore
|
||||
|
@ -23,7 +24,7 @@ from ietf.group import colors
|
|||
from ietf.person.models import Person
|
||||
from ietf.group.models import Group
|
||||
from ietf.group.factories import GroupFactory
|
||||
from ietf.meeting.factories import SessionFactory
|
||||
from ietf.meeting.factories import SessionFactory, TimeSlotFactory
|
||||
from ietf.meeting.test_data import make_meeting_test_data, make_interim_meeting
|
||||
from ietf.meeting.models import (Schedule, SchedTimeSessAssignment, Session,
|
||||
Room, TimeSlot, Constraint, ConstraintName,
|
||||
|
@ -904,6 +905,200 @@ class AgendaTests(MeetingTestCase):
|
|||
self.driver.find_element_by_xpath('//a[text()="%s"]' % slide.title)
|
||||
|
||||
|
||||
@skipIf(skip_selenium, skip_message)
|
||||
class WeekviewTests(MeetingTestCase):
|
||||
def setUp(self):
|
||||
super(WeekviewTests, self).setUp()
|
||||
self.meeting = make_meeting_test_data()
|
||||
|
||||
def get_expected_items(self):
|
||||
expected_items = self.meeting.schedule.assignments.exclude(timeslot__type__in=['lead', 'offagenda'])
|
||||
self.assertGreater(len(expected_items), 0, 'Test setup generated an empty schedule')
|
||||
return expected_items
|
||||
|
||||
def test_timezone_default(self):
|
||||
"""Week view should show local times by default"""
|
||||
self.assertNotEqual(self.meeting.time_zone.lower(), 'utc',
|
||||
'Cannot test local time weekview because meeting is using UTC time.')
|
||||
self.login()
|
||||
self.driver.get(self.absreverse('ietf.meeting.views.week_view'))
|
||||
for item in self.get_expected_items():
|
||||
if item.session.name:
|
||||
expected_name = item.session.name
|
||||
elif item.timeslot.type_id == 'break':
|
||||
expected_name = item.timeslot.name
|
||||
else:
|
||||
expected_name = item.session.group.name
|
||||
expected_time = '-'.join([item.timeslot.local_start_time().strftime('%H%M'),
|
||||
item.timeslot.local_end_time().strftime('%H%M')])
|
||||
WebDriverWait(self.driver, 2).until(
|
||||
expected_conditions.presence_of_element_located(
|
||||
(By.XPATH,
|
||||
'//div/div[contains(text(), "%s")]/span[contains(text(), "%s")]' % (
|
||||
expected_time, expected_name))
|
||||
)
|
||||
)
|
||||
|
||||
def test_timezone_selection(self):
|
||||
"""Week view should show time zones when requested"""
|
||||
# Must test utc; others are picked arbitrarily
|
||||
zones_to_test = ['utc', 'America/Halifax', 'Asia/Bangkok', 'Africa/Dakar', 'Europe/Dublin']
|
||||
self.login()
|
||||
for zone_name in zones_to_test:
|
||||
zone = timezone(zone_name)
|
||||
self.driver.get(self.absreverse('ietf.meeting.views.week_view') + '?tz=' + zone_name)
|
||||
for item in self.get_expected_items():
|
||||
if item.session.name:
|
||||
expected_name = item.session.name
|
||||
elif item.timeslot.type_id == 'break':
|
||||
expected_name = item.timeslot.name
|
||||
else:
|
||||
expected_name = item.session.group.name
|
||||
|
||||
start_time = item.timeslot.utc_start_time().astimezone(zone)
|
||||
end_time = item.timeslot.utc_end_time().astimezone(zone)
|
||||
expected_time = '-'.join([start_time.strftime('%H%M'),
|
||||
end_time.strftime('%H%M')])
|
||||
|
||||
WebDriverWait(self.driver, 2).until(
|
||||
expected_conditions.presence_of_element_located(
|
||||
(By.XPATH,
|
||||
'//div/div[contains(text(), "%s")]/span[contains(text(), "%s")]' % (
|
||||
expected_time, expected_name))
|
||||
),
|
||||
'Could not find event "%s" at %s for time zone %s' % (expected_name,
|
||||
expected_time,
|
||||
zone_name),
|
||||
)
|
||||
|
||||
def test_event_wrapping(self):
|
||||
"""Events that overlap midnight should be shown on both days
|
||||
|
||||
This assumes that the meeting is in America/New_York timezone.
|
||||
"""
|
||||
def _assert_wrapped(displayed, expected_time_string):
|
||||
self.assertEqual(len(displayed), 2)
|
||||
first = displayed[0]
|
||||
first_parent = first.find_element_by_xpath('..')
|
||||
second = displayed[1]
|
||||
second_parent = second.find_element_by_xpath('..')
|
||||
self.assertNotIn('continued', first.text)
|
||||
self.assertIn(expected_time_string, first_parent.text)
|
||||
self.assertIn('continued', second.text)
|
||||
self.assertIn(expected_time_string, second_parent.text)
|
||||
|
||||
def _assert_not_wrapped(displayed, expected_time_string):
|
||||
self.assertEqual(len(displayed), 1)
|
||||
first = displayed[0]
|
||||
first_parent = first.find_element_by_xpath('..')
|
||||
self.assertNotIn('continued', first.text)
|
||||
self.assertIn(expected_time_string, first_parent.text)
|
||||
|
||||
duration = datetime.timedelta(minutes=120) # minutes
|
||||
|
||||
# Session during a single day in meeting local time but multi-day UTC
|
||||
# Compute a time that overlaps midnight, UTC, but won't when shifted to a local time zone
|
||||
start_time_utc = timezone('UTC').localize(
|
||||
datetime.datetime.combine(self.meeting.date, datetime.time(23,0))
|
||||
)
|
||||
start_time_local = start_time_utc.astimezone(timezone(self.meeting.time_zone))
|
||||
|
||||
daytime_session = SessionFactory(
|
||||
meeting=self.meeting,
|
||||
name='Single Day Session for Wrapping Test',
|
||||
add_to_schedule=False,
|
||||
)
|
||||
daytime_timeslot = TimeSlotFactory(
|
||||
meeting=self.meeting,
|
||||
time=start_time_local.replace(tzinfo=None), # drop timezone for Django
|
||||
duration=duration,
|
||||
)
|
||||
daytime_session.timeslotassignments.create(timeslot=daytime_timeslot, schedule=self.meeting.schedule)
|
||||
|
||||
# Session that overlaps midnight in meeting local time
|
||||
overnight_session = SessionFactory(
|
||||
meeting=self.meeting,
|
||||
name='Overnight Session for Wrapping Test',
|
||||
add_to_schedule=False,
|
||||
)
|
||||
overnight_timeslot = TimeSlotFactory(
|
||||
meeting=self.meeting,
|
||||
time=datetime.datetime.combine(self.meeting.date, datetime.time(23,0)),
|
||||
duration=duration,
|
||||
)
|
||||
overnight_session.timeslotassignments.create(timeslot=overnight_timeslot, schedule=self.meeting.schedule)
|
||||
|
||||
# Check assumptions about events overlapping midnight
|
||||
self.assertEqual(daytime_timeslot.local_start_time().day,
|
||||
daytime_timeslot.local_end_time().day,
|
||||
'Daytime event should not overlap midnight in local time')
|
||||
self.assertNotEqual(daytime_timeslot.utc_start_time().day,
|
||||
daytime_timeslot.utc_end_time().day,
|
||||
'Daytime event should overlap midnight in UTC')
|
||||
|
||||
self.assertNotEqual(overnight_timeslot.local_start_time().day,
|
||||
overnight_timeslot.local_end_time().day,
|
||||
'Overnight event should overlap midnight in local time')
|
||||
self.assertEqual(overnight_timeslot.utc_start_time().day,
|
||||
overnight_timeslot.utc_end_time().day,
|
||||
'Overnight event should not overlap midnight in UTC')
|
||||
|
||||
self.login()
|
||||
|
||||
# Test in meeting local time
|
||||
self.driver.get(self.absreverse('ietf.meeting.views.week_view'))
|
||||
|
||||
time_string = '-'.join([daytime_timeslot.local_start_time().strftime('%H%M'),
|
||||
daytime_timeslot.local_end_time().strftime('%H%M')])
|
||||
displayed = WebDriverWait(self.driver, 2).until(
|
||||
expected_conditions.presence_of_all_elements_located(
|
||||
(By.XPATH,
|
||||
'//div/div[contains(text(), "%s")]/span[contains(text(), "%s")]' % (
|
||||
time_string,
|
||||
daytime_session.name))
|
||||
)
|
||||
)
|
||||
_assert_not_wrapped(displayed, time_string)
|
||||
|
||||
time_string = '-'.join([overnight_timeslot.local_start_time().strftime('%H%M'),
|
||||
overnight_timeslot.local_end_time().strftime('%H%M')])
|
||||
displayed = WebDriverWait(self.driver, 2).until(
|
||||
expected_conditions.presence_of_all_elements_located(
|
||||
(By.XPATH,
|
||||
'//div/div[contains(text(), "%s")]/span[contains(text(), "%s")]' % (
|
||||
time_string,
|
||||
overnight_session.name))
|
||||
)
|
||||
)
|
||||
_assert_wrapped(displayed, time_string)
|
||||
|
||||
# Test in utc time
|
||||
self.driver.get(self.absreverse('ietf.meeting.views.week_view') + '?tz=utc')
|
||||
|
||||
time_string = '-'.join([daytime_timeslot.utc_start_time().strftime('%H%M'),
|
||||
daytime_timeslot.utc_end_time().strftime('%H%M')])
|
||||
displayed = WebDriverWait(self.driver, 2).until(
|
||||
expected_conditions.presence_of_all_elements_located(
|
||||
(By.XPATH,
|
||||
'//div/div[contains(text(), "%s")]/span[contains(text(), "%s")]' % (
|
||||
time_string,
|
||||
daytime_session.name))
|
||||
)
|
||||
)
|
||||
_assert_wrapped(displayed, time_string)
|
||||
|
||||
time_string = '-'.join([overnight_timeslot.utc_start_time().strftime('%H%M'),
|
||||
overnight_timeslot.utc_end_time().strftime('%H%M')])
|
||||
displayed = WebDriverWait(self.driver, 2).until(
|
||||
expected_conditions.presence_of_all_elements_located(
|
||||
(By.XPATH,
|
||||
'//div/div[contains(text(), "%s")]/span[contains(text(), "%s")]' % (
|
||||
time_string,
|
||||
overnight_session.name))
|
||||
)
|
||||
)
|
||||
_assert_not_wrapped(displayed, time_string)
|
||||
|
||||
@skipIf(skip_selenium, skip_message)
|
||||
class InterimTests(MeetingTestCase):
|
||||
def setUp(self):
|
||||
|
|
|
@ -360,11 +360,17 @@ class MeetingTests(TestCase):
|
|||
|
||||
def test_agenda_week_view(self):
|
||||
meeting = make_meeting_test_data()
|
||||
url = urlreverse("ietf.meeting.views.week_view",kwargs=dict(num=meeting.number)) + "#farfut"
|
||||
url = urlreverse("ietf.meeting.views.week_view",kwargs=dict(num=meeting.number)) + "?show=farfut"
|
||||
r = self.client.get(url)
|
||||
self.assertEqual(r.status_code,200)
|
||||
self.assertTrue(all([x in unicontent(r) for x in ['var all_items', 'maximize', 'draw_calendar', ]]))
|
||||
|
||||
# Specifying a time zone should not change the output (time zones are handled by the JS)
|
||||
url = urlreverse("ietf.meeting.views.week_view",kwargs=dict(num=meeting.number)) + "?show=farfut&tz=Asia/Bangkok"
|
||||
r_with_tz = self.client.get(url)
|
||||
self.assertEqual(r_with_tz.status_code,200)
|
||||
self.assertEqual(r.content, r_with_tz.content)
|
||||
|
||||
@override_settings(MEETING_MATERIALS_SERVE_LOCALLY=False, MEETING_DOC_HREFS = settings.MEETING_DOC_CDN_HREFS)
|
||||
def test_materials_through_cdn(self):
|
||||
meeting = make_meeting_test_data(create_interims=True)
|
||||
|
@ -686,9 +692,6 @@ class MeetingTests(TestCase):
|
|||
|
||||
self.assertIsNone(parse_agenda_filter_params(QueryDict('')))
|
||||
|
||||
self.assertRaises(ValueError, parse_agenda_filter_params, QueryDict('unknown')) # unknown param
|
||||
self.assertRaises(ValueError, parse_agenda_filter_params, QueryDict('unknown=x')) # unknown param
|
||||
|
||||
# test valid combos (not exhaustive)
|
||||
for qstr, expected in (
|
||||
('show=', _r()), ('hide=', _r()), ('showtypes=', _r()), ('hidetypes=', _r()),
|
||||
|
@ -708,16 +711,6 @@ class MeetingTests(TestCase):
|
|||
'Parsed "%s" incorrectly' % qstr,
|
||||
)
|
||||
|
||||
def test_ical_filter_invalid_syntaxes(self):
|
||||
meeting = make_meeting_test_data()
|
||||
url = urlreverse('ietf.meeting.views.agenda_ical', kwargs={'num':meeting.number})
|
||||
|
||||
r = self.client.get(url + '?unknownparam=mars')
|
||||
self.assertEqual(r.status_code, 400, 'Unknown parameter should be rejected')
|
||||
|
||||
r = self.client.get(url + '?mars')
|
||||
self.assertEqual(r.status_code, 400, 'Missing parameter name should be rejected')
|
||||
|
||||
def do_ical_filter_test(self, meeting, querystring, expected_session_summaries):
|
||||
url = urlreverse('ietf.meeting.views.agenda_ical', kwargs={'num':meeting.number})
|
||||
r = self.client.get(url + querystring)
|
||||
|
@ -2328,16 +2321,6 @@ class InterimTests(TestCase):
|
|||
expected_event_count=2)
|
||||
|
||||
|
||||
def test_upcoming_ical_filter_invalid_syntaxes(self):
|
||||
make_meeting_test_data()
|
||||
url = urlreverse('ietf.meeting.views.upcoming_ical')
|
||||
|
||||
r = self.client.get(url + '?unknownparam=mars')
|
||||
self.assertEqual(r.status_code, 400, 'Unknown parameter should be rejected')
|
||||
|
||||
r = self.client.get(url + '?mars')
|
||||
self.assertEqual(r.status_code, 400, 'Missing parameter name should be rejected')
|
||||
|
||||
def test_upcoming_json(self):
|
||||
make_meeting_test_data(create_interims=True)
|
||||
url = urlreverse("ietf.meeting.views.upcoming_json")
|
||||
|
|
|
@ -100,7 +100,6 @@ from ietf.utils.pipe import pipe
|
|||
from ietf.utils.pdf import pdf_pages
|
||||
from ietf.utils.response import permission_denied
|
||||
from ietf.utils.text import xslugify
|
||||
from ietf.utils.timezone import date2datetime
|
||||
|
||||
from .forms import (InterimMeetingModelForm, InterimAnnounceForm, InterimSessionModelForm,
|
||||
InterimCancelForm, InterimSessionInlineFormSet, FileUploadForm, RequestMinutesForm,)
|
||||
|
@ -1719,13 +1718,6 @@ def week_view(request, num=None, name=None, owner=None):
|
|||
schedule__in=[schedule, schedule.base],
|
||||
timeslot__type__private=False,
|
||||
)
|
||||
# Only show assignments from the traditional meeting "week" (Sat-Fri).
|
||||
# We'll determine this using the saturday before the first scheduled regular session.
|
||||
first_regular_session = meeting.schedule.qs_assignments_with_sessions.filter(session__type_id='regular').order_by('timeslot__time').first()
|
||||
first_regular_session_time = first_regular_session.timeslot.time if first_regular_session else date2datetime(meeting.date)
|
||||
saturday_before = first_regular_session_time.replace(hour=0, minute=0, second=0, microsecond=0) - datetime.timedelta(days=(first_regular_session_time.weekday() - 5)%7)
|
||||
# saturday_after = saturday_before + datetime.timedelta(days=7)
|
||||
# filtered_assignments = filtered_assignments.filter(timeslot__time__gte=saturday_before,timeslot__time__lt=saturday_after)
|
||||
filtered_assignments = preprocess_assignments_for_agenda(filtered_assignments, meeting)
|
||||
tag_assignments_with_filter_keywords(filtered_assignments)
|
||||
|
||||
|
@ -1734,16 +1726,8 @@ def week_view(request, num=None, name=None, owner=None):
|
|||
# we don't HTML escape any of these as the week-view code is using createTextNode
|
||||
item = {
|
||||
"key": str(a.timeslot.pk),
|
||||
"day": (a.timeslot.time - saturday_before).days - 1,
|
||||
"time": a.timeslot.time.strftime("%H%M") + "-" + a.timeslot.end_time().strftime("%H%M"),
|
||||
"utc_time": a.timeslot.utc_start_time().strftime("%Y%m%dT%H%MZ"), # ISO8601 compliant
|
||||
"duration": a.timeslot.duration.seconds,
|
||||
"time_id": a.timeslot.time.strftime("%m%d%H%M"),
|
||||
"dayname": "{weekday}, {month} {day_of_month}, {year}".format(
|
||||
weekday=a.timeslot.time.strftime("%A").upper(),
|
||||
month=a.timeslot.time.strftime("%B"),
|
||||
day_of_month=a.timeslot.time.strftime("%d").lstrip("0"),
|
||||
year=a.timeslot.time.strftime("%Y"),
|
||||
),
|
||||
"type": a.timeslot.type.name,
|
||||
"filter_keywords": ",".join(a.filter_keywords),
|
||||
}
|
||||
|
@ -1780,6 +1764,7 @@ def week_view(request, num=None, name=None, owner=None):
|
|||
|
||||
return render(request, "meeting/week-view.html", {
|
||||
"items": json.dumps(items),
|
||||
"timezone": meeting.time_zone,
|
||||
})
|
||||
|
||||
@role_required('Area Director','Secretariat','IAB')
|
||||
|
@ -1866,17 +1851,11 @@ def parse_agenda_filter_params(querydict):
|
|||
if len(querydict) == 0:
|
||||
return None
|
||||
|
||||
# Parse group filters from GET parameters. The keys in this dict define the
|
||||
# allowed querystring parameters.
|
||||
# Parse group filters from GET parameters. Other params are ignored.
|
||||
filt_params = {'show': set(), 'hide': set(), 'showtypes': set(), 'hidetypes': set()}
|
||||
|
||||
for key, value in querydict.items():
|
||||
if key not in filt_params:
|
||||
raise ValueError('Unrecognized parameter "%s"' % key)
|
||||
if value is None:
|
||||
return ValueError(
|
||||
'Parameter "%s" is not assigned a value (use "key=" for an empty value)' % key
|
||||
)
|
||||
if key in filt_params:
|
||||
vals = unquote(value).lower().split(',')
|
||||
vals = [v.strip() for v in vals]
|
||||
filt_params[key] = set([v for v in vals if len(v) > 0]) # remove empty strings
|
||||
|
|
|
@ -373,10 +373,18 @@
|
|||
|
||||
var wv_iframe = document.getElementById('weekview');
|
||||
var wv_window = wv_iframe.contentWindow;
|
||||
var new_url = 'week-view.html' + window.location.search;
|
||||
var queryparams = window.location.search;
|
||||
{% if "-utc" in request.path %}
|
||||
if (queryparams) {
|
||||
queryparams += '&tz=utc';
|
||||
} else {
|
||||
queryparams = '?tz=utc';
|
||||
}
|
||||
{% endif %}
|
||||
var new_url = 'week-view.html' + queryparams;
|
||||
if (wv_iframe.src && wv_window.history && wv_window.history.replaceState) {
|
||||
wv_window.history.replaceState({}, '', new_url);
|
||||
wv_window.draw_calendar()
|
||||
wv_window.redraw_weekview();
|
||||
} else {
|
||||
// either have not yet loaded the iframe or we do not support history replacement
|
||||
wv_iframe.src = new_url;
|
||||
|
|
|
@ -3,17 +3,12 @@
|
|||
{% load static %}
|
||||
<html> <head>
|
||||
<script src="{% static 'ietf/js/agenda/agenda_filter.js' %}"></script>
|
||||
<script src="{% static 'moment/min/moment.min.js' %}"></script>
|
||||
<script src="{% static 'moment-timezone/builds/moment-timezone-with-data-10-year-range.min.js' %}"></script>
|
||||
<script type="text/javascript">
|
||||
|
||||
var all_items = {{ items|safe }};
|
||||
|
||||
/* Also, process clock times to "minutes past midnight" */
|
||||
all_items.forEach(function(item) {
|
||||
item.start_time = parseInt(item.time.substr(0,2),10) * 60 +
|
||||
parseInt(item.time.substr(2,2),10);
|
||||
item.end_time = item.start_time + (item.duration / 60)
|
||||
});
|
||||
|
||||
var color = {
|
||||
'app': { fg: "#008", bg: "#eef"},
|
||||
'art': { fg: "#808", bg: "#fef"},
|
||||
|
@ -123,15 +118,11 @@
|
|||
|
||||
//===========================================================================
|
||||
|
||||
function draw_calendar() {
|
||||
function draw_calendar(items, filter_params) {
|
||||
var width = document.body.clientWidth;
|
||||
var height = document.body.clientHeight;
|
||||
|
||||
var visible_items = all_items;
|
||||
var filter_params = agenda_filter.get_filter_params(
|
||||
agenda_filter.parse_query_params(window.location.search)
|
||||
);
|
||||
|
||||
var visible_items = items;
|
||||
if (agenda_filter.filtering_is_enabled(filter_params)) {
|
||||
visible_items = visible_items.filter(is_visible(filter_params));
|
||||
}
|
||||
|
@ -143,8 +134,8 @@
|
|||
day_start = visible_items[0].start_time;
|
||||
} else {
|
||||
// fallback in case all items were filtered
|
||||
start_day = all_items[0].day;
|
||||
day_start = all_items[0].start_time;
|
||||
start_day = items[0].day;
|
||||
day_start = items[0].start_time;
|
||||
}
|
||||
var end_day = start_day;
|
||||
var day_end = 0;
|
||||
|
@ -496,10 +487,124 @@
|
|||
e.style.fontSize="8pt";
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
function get_first_item(items, type) {
|
||||
var earliest;
|
||||
for (var ii=0; ii < items.length; ii++) {
|
||||
var this_item = items[ii];
|
||||
if (type && (this_item.type !== type)) {
|
||||
continue;
|
||||
}
|
||||
// Update earliest if we don't have an earliest item yet or this_item is earlier
|
||||
if (!earliest || (items[ii].utc_time < earliest.utc_time)) {
|
||||
earliest = items[ii];
|
||||
}
|
||||
}
|
||||
return earliest;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
function prepare_items(items, timezone_name) {
|
||||
function make_display_item(item) {
|
||||
return {
|
||||
name: item.name,
|
||||
group: item.group,
|
||||
area: item.area,
|
||||
room: item.room,
|
||||
agenda: item.agenda,
|
||||
key: item.key,
|
||||
type: item.type,
|
||||
filter_keywords: item.filter_keywords
|
||||
}
|
||||
};
|
||||
|
||||
/* Ported from Django view, which had the following comment:
|
||||
* Only show assignments from the traditional meeting "week" (Sat-Fri).
|
||||
* We'll determine this using the saturday before the first scheduled regular session. */
|
||||
var first_session = get_first_item(items, 'Regular');
|
||||
if (!first_session) {
|
||||
first_session = get_first_item(items); // any type
|
||||
}
|
||||
var first_session_time = moment(first_session.utc_time).utc();
|
||||
if (timezone_name) {
|
||||
first_session_time.tz(timezone_name); // mutates the moment
|
||||
}
|
||||
|
||||
// Moment.js day() uses 0 == Sunday, 6 == Saturday
|
||||
days_since_saturday = first_session_time.day() - 6;
|
||||
if (days_since_saturday < 0) {
|
||||
days_since_saturday += 7;
|
||||
}
|
||||
saturday_before = first_session_time.clone().startOf('day').subtract(days_since_saturday, 'days');
|
||||
|
||||
var display_items = [];
|
||||
for (var ii = 0; ii < items.length; ii++) {
|
||||
var this_item = items[ii];
|
||||
|
||||
/* It's possible an event overlaps the moment of a daylight savings shift.
|
||||
* Calculate the end_moment in utc() time, which has no DST. Once we switch
|
||||
* to a time zone, end time minus start time may not equal duration. */
|
||||
var start_moment = moment(this_item.utc_time).utc();
|
||||
var end_moment = start_moment.clone().add(this_item.duration, 'seconds');
|
||||
if (timezone_name) {
|
||||
start_moment.tz(timezone_name);
|
||||
end_moment.tz(timezone_name);
|
||||
}
|
||||
// Avoid off-by-one day number calculations if a session ends at midnight
|
||||
var just_before_end_moment = end_moment.clone().subtract(1, 'millisecond');
|
||||
|
||||
var start_day = start_moment.diff(saturday_before, 'days') - 1; // shift so sunday = 0
|
||||
var end_day = just_before_end_moment.diff(saturday_before, 'days') - 1; // shift so sunday = 0
|
||||
|
||||
// Generate display items - create multiple if item ends on different day than starts
|
||||
for (var day = start_day; day <= end_day; day++) {
|
||||
var display_item = make_display_item(this_item);
|
||||
display_item.day = day;
|
||||
if (day === start_day) {
|
||||
// First day of session - compute start time
|
||||
display_item.start_time = start_moment.diff(
|
||||
start_moment.clone().startOf('day'),
|
||||
'minutes'
|
||||
);
|
||||
} else {
|
||||
// Not first day, start at midnight
|
||||
display_item.start_time = 0;
|
||||
display_item.name += " - continued";
|
||||
}
|
||||
if (day === end_day) {
|
||||
// Last day of session - compute end time
|
||||
display_item.end_time = end_moment.diff(
|
||||
just_before_end_moment.clone().startOf('day'),
|
||||
'minutes'
|
||||
);
|
||||
} else {
|
||||
/* Not last day, use full day. Calculate this on the fly to account for
|
||||
* daylight savings shifts, when a calendar day is not 24*60 minutes long. */
|
||||
display_item.end_time = just_before_end_moment.clone().endOf('day').diff(
|
||||
just_before_end_moment.clone().startOf('day'),
|
||||
'minutes'
|
||||
);
|
||||
}
|
||||
|
||||
display_item.time = start_moment.format('HHmm') + '-' + end_moment.format('HHmm');
|
||||
display_item.dayname = start_moment.format('dddd, ').toUpperCase() +
|
||||
start_moment.format('MMMM D, Y');
|
||||
display_items.push(display_item);
|
||||
}
|
||||
}
|
||||
return display_items;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
// Set up events for drawing the calendar
|
||||
function redraw_weekview() {
|
||||
draw_calendar();
|
||||
var query_params = agenda_filter.parse_query_params(window.location.search);
|
||||
var timezone_name = query_params.tz || '{{ timezone }}';
|
||||
|
||||
items = prepare_items(all_items, timezone_name);
|
||||
draw_calendar(items, agenda_filter.get_filter_params(query_params));
|
||||
}
|
||||
|
||||
window.addEventListener("resize", redraw_weekview, false);
|
||||
|
|
Loading…
Reference in a new issue