Merged in [19841] from jennifer@painless-security.com:

Link to the timeslot editor when meeting has no timeslots. Fixes #3511.
 - Legacy-Id: 19852
Note: SVN reference [19841] has been migrated to Git commit 64e904804d
This commit is contained in:
Robert Sparks 2022-01-14 20:40:03 +00:00
commit 6f682c313c
3 changed files with 110 additions and 83 deletions

View file

@ -1677,6 +1677,23 @@ class EditMeetingScheduleTests(TestCase):
self.assertEqual(r.status_code, 200) self.assertEqual(r.status_code, 200)
self.assertTrue(self._decode_json_response(r)['success']) self.assertTrue(self._decode_json_response(r)['success'])
def test_editor_with_no_timeslots(self):
"""Schedule editor should not crash when there are no timeslots"""
meeting = MeetingFactory(
type_id='ietf',
date=datetime.date.today() + datetime.timedelta(days=7),
populate_schedule=False,
)
meeting.schedule = ScheduleFactory(meeting=meeting)
meeting.save()
SessionFactory(meeting=meeting, add_to_schedule=False)
self.assertEqual(meeting.timeslot_set.count(), 0, 'Test problem - meeting should not have any timeslots')
url = urlreverse('ietf.meeting.views.edit_meeting_schedule', kwargs={'num': meeting.number})
self.assertTrue(self.client.login(username='secretary', password='secretary+password'))
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
self.assertContains(r, 'No timeslots exist')
self.assertContains(r, urlreverse('ietf.meeting.views.edit_timeslots', kwargs={'num': meeting.number}))
class EditTimeslotsTests(TestCase): class EditTimeslotsTests(TestCase):

View file

@ -506,8 +506,8 @@ def edit_meeting_schedule(request, num=None, owner=None, name=None):
min_duration = min(t.duration for t in timeslots_qs) min_duration = min(t.duration for t in timeslots_qs)
max_duration = max(t.duration for t in timeslots_qs) max_duration = max(t.duration for t in timeslots_qs)
else: else:
min_duration = 1 min_duration = datetime.timedelta(minutes=30)
max_duration = 2 max_duration = datetime.timedelta(minutes=120)
def timedelta_to_css_ems(timedelta): def timedelta_to_css_ems(timedelta):
# we scale the session and slots a bit according to their # we scale the session and slots a bit according to their

View file

@ -75,96 +75,106 @@
{% endif %} {% endif %}
</p> </p>
<div class="edit-grid {% if not can_edit %}read-only{% endif %}"> {% if timeslot_groups|length == 0 %}
<p>
{# using the same markup in both room labels and the actual days ensures they are aligned #} No timeslots exist for this meeting yet.
<div class="room-label-column"> </p>
{% for day_data in days.values %} <p>
<div class="day"> <a href="{% url "ietf.meeting.views.edit_timeslots" num=meeting.number %}">
<div class="day-label"> Edit timeslots.
<strong>&nbsp;</strong><br> </a>
&nbsp; </p>
</div> {% else %}
<div class="edit-grid {% if not can_edit %}read-only{% endif %}">
{% for rgroup in day_data %} {# using the same markup in both room labels and the actual days ensures they are aligned #}
<div class="room-group"> <div class="room-label-column">
<div class="time-header"><div class="time-label"></div></div> {% for day_data in days.values %}
{% for room_data in rgroup %}{% with room_data.room as room %} <div class="day">
<div class="timeslots"> <div class="day-label">
<div class="room-name"> <strong>&nbsp;</strong><br>
<strong>{{ room.name }}</strong><br> &nbsp;
{% if room.capacity %}{{ room.capacity }} <i class="fa fa-user-o"></i>{% endif %}
</div>
</div>
{% endwith %}{% endfor %}
</div> </div>
{% endfor %}
</div>
{% endfor %}
</div>
<div class="day-flow"> {% for rgroup in day_data %}
{% for day, day_data in days.items %} <div class="room-group">
<div class="day"> <div class="time-header"><div class="time-label"></div></div>
<div class="day-label"> {% for room_data in rgroup %}{% with room_data.room as room %}
<strong>{{ day|date:"l" }}</strong> <div class="timeslots">
<i class="fa fa-exchange swap-days" <div class="room-name">
data-dayid="{{ day.isoformat }}" <strong>{{ room.name }}</strong><br>
data-start="{{ day.isoformat }}"></i> {% if room.capacity %}{{ room.capacity }} <i class="fa fa-user-o"></i>{% endif %}
<br> </div>
{{ day|date:"N j, Y" }}
</div>
{% for rgroup in day_data %}
<div class="room-group"
data-index="{{ forloop.counter0 }}"
data-rooms="{% for r in rgroup %}{{ r.room.pk }}{% if not forloop.last %},{% endif %}{% endfor %}">
<div class="time-header">
{# All rooms in a group have same timeslots; grab the first for the labels #}
{% for t in rgroup.0.timeslots %}
<div class="time-label" style="width: {{ t.layout_width }}rem">
<span>
{{ t.time|date:"G:i" }} - {{ t.end_time|date:"G:i" }}
<i class="fa fa-exchange swap-timeslot-col"
data-origin-label="{{ day|date:"l, N j" }}, {{ t.time|date:"G:i" }}-{{ t.end_time|date:"G:i" }}"
data-start="{{ t.utc_start_time.isoformat }}"
data-timeslot-pk="{{ t.pk }}"></i>
</span>
</div> </div>
{% endfor %} {% endwith %}{% endfor %}
</div> </div>
{% for room_data in rgroup %}{% with room_data.room as room %} {% endfor %}
<div class="timeslots" data-roomcapacity="{{ room.capacity }}"> </div>
{% for t in room_data.timeslots %} {% endfor %}
<div id="timeslot{{ t.pk }}" </div>
class="timeslot {{ t.start_end_group }}"
data-start="{{ t.utc_start_time.isoformat }}"
data-end="{{ t.utc_end_time.isoformat }}"
data-duration="{{ t.duration.total_seconds }}"
data-scheduledatlabel="{{ t.time|date:"l G:i" }}-{{ t.end_time|date:"G:i" }}"
data-type="{{ t.type.slug }}"
style="width: {{ t.layout_width }}rem;">
<div class="time-label">
<div class="past-flag">&nbsp;{# blank div keeps time centered vertically #}</div>
<div>{{ t.time|date:"G:i" }} - {{ t.end_time|date:"G:i" }}</div>
<div class="past-flag">Past</div>
</div>
<div class="drop-target"> <div class="day-flow">
{% for assignment, session in t.session_assignments %} {% for day, day_data in days.items %}
{% include "meeting/edit_meeting_schedule_session.html" %} <div class="day">
{% endfor %} <div class="day-label">
</div> <strong>{{ day|date:"l" }}</strong>
<i class="fa fa-exchange swap-days"
data-dayid="{{ day.isoformat }}"
data-start="{{ day.isoformat }}"></i>
<br>
{{ day|date:"N j, Y" }}
</div>
{% for rgroup in day_data %}
<div class="room-group"
data-index="{{ forloop.counter0 }}"
data-rooms="{% for r in rgroup %}{{ r.room.pk }}{% if not forloop.last %},{% endif %}{% endfor %}">
<div class="time-header">
{# All rooms in a group have same timeslots; grab the first for the labels #}
{% for t in rgroup.0.timeslots %}
<div class="time-label" style="width: {{ t.layout_width }}rem">
<span>
{{ t.time|date:"G:i" }} - {{ t.end_time|date:"G:i" }}
<i class="fa fa-exchange swap-timeslot-col"
data-origin-label="{{ day|date:"l, N j" }}, {{ t.time|date:"G:i" }}-{{ t.end_time|date:"G:i" }}"
data-start="{{ t.utc_start_time.isoformat }}"
data-timeslot-pk="{{ t.pk }}"></i>
</span>
</div> </div>
{% endfor %} {% endfor %}
</div> </div>
{% endwith %}{% endfor %} {% for room_data in rgroup %}{% with room_data.room as room %}
</div> <div class="timeslots" data-roomcapacity="{{ room.capacity }}">
{% endfor %} {% for t in room_data.timeslots %}
</div> <div id="timeslot{{ t.pk }}"
{% endfor %} class="timeslot {{ t.start_end_group }}"
data-start="{{ t.utc_start_time.isoformat }}"
data-end="{{ t.utc_end_time.isoformat }}"
data-duration="{{ t.duration.total_seconds }}"
data-scheduledatlabel="{{ t.time|date:"l G:i" }}-{{ t.end_time|date:"G:i" }}"
data-type="{{ t.type.slug }}"
style="width: {{ t.layout_width }}rem;">
<div class="time-label">
<div class="past-flag">&nbsp;{# blank div keeps time centered vertically #}</div>
<div>{{ t.time|date:"G:i" }} - {{ t.end_time|date:"G:i" }}</div>
<div class="past-flag">Past</div>
</div>
<div class="drop-target">
{% for assignment, session in t.session_assignments %}
{% include "meeting/edit_meeting_schedule_session.html" %}
{% endfor %}
</div>
</div>
{% endfor %}
</div>
{% endwith %}{% endfor %}
</div>
{% endfor %}
</div>
{% endfor %}
</div>
</div> </div>
</div> {% endif %}
<div class="scheduling-panel"> <div class="scheduling-panel">
<div class="unassigned-container"> <div class="unassigned-container">