Skip to content

Commit 012d51b

Browse files
committed
Simplified the view that lets the secretariat see and change timeslot types. Fixes ietf-tools#2313. Commit ready for merge.
- Legacy-Id: 14634
1 parent 0306f0a commit 012d51b

5 files changed

Lines changed: 105 additions & 185 deletions

File tree

ietf/meeting/tests_views.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
from ietf.person.factories import PersonFactory
3535
from ietf.group.factories import GroupFactory, GroupEventFactory
3636
from ietf.meeting.factories import ( SessionFactory, SessionPresentationFactory, ScheduleFactory,
37-
MeetingFactory, FloorPlanFactory )
37+
MeetingFactory, FloorPlanFactory, TimeSlotFactory )
3838
from ietf.doc.factories import DocumentFactory
3939

4040

@@ -536,6 +536,17 @@ def test_edit_timeslots(self):
536536
self.assertEqual(r.status_code, 200)
537537
self.assertTrue(meeting.room_set.all().first().name in unicontent(r))
538538

539+
def test_edit_timeslot_type(self):
540+
timeslot = TimeSlotFactory()
541+
url = urlreverse('ietf.meeting.views.edit_timeslot_type', kwargs=dict(num=timeslot.meeting.number,slot_id=timeslot.id))
542+
login_testing_unauthorized(self,"secretary",url)
543+
r = self.client.get(url)
544+
self.assertEqual(r.status_code, 200)
545+
r = self.client.post(url,{'type':'other',})
546+
self.assertEqual(r.status_code, 302)
547+
timeslot = TimeSlot.objects.get(id=timeslot.id)
548+
self.assertEqual(timeslot.type.slug,'other')
549+
539550
def test_slot_to_the_right(self):
540551
meeting = make_meeting_test_data()
541552
session = Session.objects.filter(meeting=meeting, group__acronym="mars").first()

ietf/meeting/urls.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
url(r'^agendas/list$', views.list_agendas),
4242
url(r'^agendas/edit$', RedirectView.as_view(pattern_name='ietf.meeting.views.list_agendas', permanent=True)),
4343
url(r'^timeslots/edit$', views.edit_timeslots),
44+
url(r'^timeslot/(?P<slot_id>\d+)/edittype$', views.edit_timeslot_type),
4445
url(r'^rooms$', ajax.timeslot_roomsurl),
4546
url(r'^room/(?P<roomid>\d+).json$', ajax.timeslot_roomurl),
4647
url(r'^timeslots$', ajax.timeslot_slotsurl),

ietf/meeting/views.py

Lines changed: 34 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import tarfile
77
import urllib
88
from tempfile import mkstemp
9-
from collections import OrderedDict, Counter
9+
from collections import OrderedDict, Counter, deque
1010
import csv
1111
import json
1212
import pytz
@@ -39,7 +39,7 @@
3939
from ietf.group.models import Group
4040
from ietf.group.utils import can_manage_materials
4141
from ietf.ietfauth.utils import role_required, has_role
42-
from ietf.meeting.models import Meeting, Session, Schedule, FloorPlan, SessionPresentation
42+
from ietf.meeting.models import Meeting, Session, Schedule, FloorPlan, SessionPresentation, TimeSlot
4343
from ietf.meeting.helpers import get_areas, get_person_by_email, get_schedule_by_name
4444
from ietf.meeting.helpers import build_all_agenda_slices, get_wg_name_list
4545
from ietf.meeting.helpers import get_all_assignments_from_schedule
@@ -266,41 +266,29 @@ def agenda_create(request, num=None, owner=None, name=None):
266266

267267

268268
@role_required('Secretariat')
269-
@ensure_csrf_cookie
270269
def edit_timeslots(request, num=None):
271270

272271
meeting = get_meeting(num)
273-
timeslots = meeting.timeslot_set.exclude(location=None).select_related("location", "type")
274272

275273
time_slices,date_slices,slots = meeting.build_timeslices()
276274

277-
meeting_base_url = request.build_absolute_uri(meeting.base_url())
278-
site_base_url = request.build_absolute_uri('/')[:-1] # skip the trailing slash
279-
280-
rooms = meeting.room_set.order_by("capacity")
281-
282-
# this import locate here to break cyclic loop.
283-
from ietf.meeting.ajax import timeslot_roomsurl, AddRoomForm, timeslot_slotsurl, AddSlotForm
284-
roomsurl = reverse(timeslot_roomsurl, args=[meeting.number])
285-
adddayurl = reverse(timeslot_slotsurl, args=[meeting.number])
275+
ts_list = deque()
276+
rooms = meeting.room_set.order_by("capacity","name","id")
277+
for room in rooms:
278+
for day in time_slices:
279+
for slice in date_slices[day]:
280+
ts_list.append(room.timeslot_set.filter(time=slice[0],duration=datetime.timedelta(seconds=slice[2])).first())
281+
286282

287283
return render(request, "meeting/timeslot_edit.html",
288-
{"timeslots": timeslots,
289-
"meeting_base_url": meeting_base_url,
290-
"site_base_url": site_base_url,
291-
"rooms":rooms,
292-
"addroom": AddRoomForm(),
293-
"roomsurl": roomsurl,
294-
"addday": AddSlotForm(),
295-
"adddayurl":adddayurl,
284+
{"rooms":rooms,
296285
"time_slices":time_slices,
297286
"slot_slices": slots,
298287
"date_slices":date_slices,
299288
"meeting":meeting,
300-
"hide_menu": True,
289+
"ts_list":ts_list,
301290
})
302291

303-
304292
##############################################################################
305293
#@role_required('Area Director','Secretariat')
306294
# disable the above security for now, check it below.
@@ -2204,3 +2192,26 @@ def important_dates(request, num=None):
22042192

22052193
context={'meetings':meetings}
22062194
return render(request, 'meeting/important-dates.html', context)
2195+
2196+
TimeSlotTypeForm = modelform_factory(TimeSlot, fields=('type',))
2197+
2198+
@role_required('Secretariat')
2199+
def edit_timeslot_type(request, num, slot_id):
2200+
timeslot = get_object_or_404(TimeSlot,id=slot_id)
2201+
meeting = get_object_or_404(Meeting,number=num)
2202+
if timeslot.meeting!=meeting:
2203+
raise Http404()
2204+
if request.method=='POST':
2205+
form = TimeSlotTypeForm(instance=timeslot,data=request.POST)
2206+
if form.is_valid():
2207+
form.save()
2208+
return HttpResponseRedirect(reverse('ietf.meeting.views.edit_timeslots',kwargs={'num':num}))
2209+
2210+
else:
2211+
form = TimeSlotTypeForm(instance=timeslot)
2212+
2213+
sessions = timeslot.sessions.filter(timeslotassignments__schedule=meeting.agenda)
2214+
2215+
return render(request, 'meeting/edit_timeslot_type.html', {'timeslot':timeslot,'form':form,'sessions':sessions})
2216+
2217+
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{% extends "base.html" %}
2+
{# Copyright The IETF Trust 2018, All Rights Reserved #}
3+
{% load origin %}
4+
{% load bootstrap3 %}
5+
6+
{% block title %}Edit timeslot type for {{timeslot}}{% endblock %}
7+
8+
{% block content %}
9+
{% origin %}
10+
<h1>Edit timeslot type for {{timeslot}}</h1>
11+
{% if sessions %}
12+
<div class="alert alert-warning">
13+
This timeslot currently has the following sessions assigned to it:
14+
{% for s in sessions %}
15+
<div>{{s}}</div>
16+
{% endfor %}
17+
</div>
18+
{% endif %}
19+
<form method="post">
20+
{% csrf_token %}
21+
{% bootstrap_form form %}
22+
{% buttons %}
23+
<button type="submit" class="btn btn-primary">Save</button>
24+
<a class="btn btn-default" href="{% url 'ietf.meeting.views.edit_timeslots' num=timeslot.meeting.number %}">Cancel</a>
25+
{% endbuttons %}
26+
</form>
27+
{% endblock %}
Lines changed: 31 additions & 161 deletions
Original file line numberDiff line numberDiff line change
@@ -1,187 +1,57 @@
11
{% extends "base.html" %}
22
{# Copyright The IETF Trust 2015, All Rights Reserved #}
33
{% load origin %}
4-
{% load staticfiles %}
5-
{% load ietf_filters %}
6-
{% load humanize %}
7-
8-
{% block title %}IETF {{ meeting.number }} Meeting Agenda: Timeslot/Room Availability{% endblock %}
94
{% load agenda_custom_tags %}
10-
{% block pagehead %}
11-
<link rel='stylesheet' type='text/css' href="{% static 'ietf/css/agenda/jquery-ui-themes/jquery-ui-1.8.11.custom.css' %}" />
12-
<link rel='stylesheet' type='text/css' href="{% static 'ietf/css/agenda/base2.css' %}" />
13-
<link rel='stylesheet' type='text/css' href="{% static 'ietf/css/agenda/agenda.css' %}" />
14-
{% endblock pagehead %}
15-
16-
{% block js %}
17-
<script type="text/javascript" src="{% static 'ietf/js/agenda/jquery-1.8.2.min.js' %}"></script>
18-
<script src="{% static 'jquery.cookie/jquery.cookie.js' %}"></script>
19-
<script>
20-
jQuery.ajaxSetup({
21-
crossDomain: false, // obviates need for sameOrigin test
22-
beforeSend: function(xhr, settings) {
23-
if (!csrfSafeMethod(settings.type)) {
24-
xhr.setRequestHeader("X-CSRFToken", $.cookie('csrftoken'));
25-
}
26-
}
27-
});
28-
</script>
29-
<script type='text/javascript' src="{% static 'ietf/js/agenda/jquery-ui-1.9.0.custom/jquery-ui.custom.js' %}"></script>
30-
<script type='text/javascript' src="{% static 'ietf/js/agenda/jquery-ui-1.9.0.custom/jquery.ui.widget.js' %}"></script>
31-
<script type='text/javascript' src="{% static 'ietf/js/agenda/jquery-ui-1.9.0.custom/jquery.ui.droppable.js' %}"></script>
32-
<script type='text/javascript' src="{% static 'ietf/js/agenda/jquery-ui-1.9.0.custom/jquery.ui.sortable.js' %}"></script>
33-
<script type='text/javascript' src="{% static 'ietf/js/agenda/jquery-ui-1.9.0.custom/jquery.ui.accordion.js' %}"></script>
34-
<script type='text/javascript' src="{% static 'ietf/js/agenda/jquery-ui-1.9.0.custom/jquery.ui.draggable.js' %}"></script>
35-
<script type='text/javascript' src="{% static 'ietf/js/agenda/jquery-ui-1.9.0.custom/jquery.ui.datepicker.js' %}"></script>
36-
37-
<!-- source (MIT License) http://momentjs.com/ https://github.com/moment/moment/ -->
38-
<script type='text/javascript' src="{% static 'ietf/js/agenda/moment.min.js' %}"></script>
39-
40-
<!-- source (MIT License) : https://github.com/trentrichardson/jQuery-Timepicker-Addon -->
41-
<script type='text/javascript' src="{% static 'ietf/js/agenda/jquery-ui-timepicker/jquery-ui-timepicker-addon.js' %}"></script>
42-
<script type='text/javascript' src="{% static 'ietf/js/agenda/jquery-ui-timepicker/jquery-ui-sliderAccess.js' %}"></script>
43-
<link rel='stylesheet' type='text/css' href="{% static 'ietf/css/agenda/jquery-ui-timepicker-addon.css' %}" />
44-
45-
<script type='text/javascript' src="{% static 'spin.js/spin.min.js' %}"></script>
46-
<script type='text/javascript' src="{% static 'ietf/js/agenda/timeslot_edit.js' %}"></script>
47-
<script type='text/javascript' src="{% static 'ietf/js/agenda/agenda_objects.js' %}"></script>
48-
<script type='text/javascript' src="{% static 'ietf/js/agenda/agenda_helpers.js' %}"></script>
49-
<script type='text/javascript' src="{% static 'ietf/js/agenda/agenda_listeners.js' %}"></script>
50-
515

52-
<script type='text/javascript'>
53-
54-
var meeting_number = "{{ meeting.number }}";
55-
var meeting_base_url = "{{ meeting_base_url }}";
56-
var site_base_url = "{{ site_base_url }}";
57-
var meeting_slots_href = "{% url "ietf.meeting.ajax.timeslot_slotsurl" meeting.number %}";
58-
total_days = {{time_slices|length}};
59-
total_rooms = {{rooms|length}};
60-
61-
first_day = new Date("{% with timeslots|first as day %} {{ day.time }} {% endwith %}"); /* needed for the datepicker */
62-
63-
function setup_slots(promiselist){
64-
var ts_promise = load_timeslots(meeting_slots_href);
65-
promiselist.push(ts_promise);
66-
67-
{% for day in time_slices %}
68-
days.push("{{day}}");
69-
{% endfor %}
70-
console.log("setup_slots run");
71-
}
6+
{% block title %}IETF {{ meeting.number }} Meeting Agenda: Timeslot/Room Availability{% endblock %}
727

73-
</script>
74-
{% endblock js %}
8+
{% block morecss %}
9+
.tstable { width: 100%;}
10+
.tstable th { white-space: nowrap;}
11+
.tstable td { white-space: nowrap;}
12+
.capacity { font-size:80%; font-weight: normal;}
7513

14+
.tstable .tstype_unavail {background-color:#666;}
15+
{% endblock %}
7616

7717
{% block content %}
7818
{% origin %}
79-
<div class="wrapper custom_text_stuff">
80-
<div style="ui-icon ui-icon-arrow-1-w" id="close_ietf_menubar">
81-
&lt;
82-
</div>
8319

84-
<div class="agenda_div">
8520

86-
<table id="meetings" class="ietf-navbar" style="width:100%">
87-
<th class="schedule_title"><div id="pageloaded" style="display:none"><span id="schedule_name">name: {{meeting.number}}</span></div>
88-
<div id="spinner"><!-- spinney goes here --></div>
89-
</th>
90-
<th><!-- resources --></th>
91-
{% for day in time_slices %}
92-
<th colspan="{{date_slices|colWidth:day}}" id="{{day|date:'Y-m-d'}}-btn" class=" day_{{day}} agenda_slot_title">
93-
<div style="display: none;" class="delete delete_day bottom_left" id="delete_{{day|date:'Y-m-d'}}">X</div>
94-
{{day|date:'D'}}&nbsp;({{day}})
95-
96-
</th>
97-
<th class="day_{{day}} spacer {{day|date:'Y-m-d'}}-spacer" id="">
98-
<div class="ui-widget-content ui-resizable" id="resize-{{day|date:'Y-m-d'}}-spacer">
99-
<div class="spacer_grip ui-resizable-handle ui-resizable-e"></div>
100-
</div>
101-
</th>
102-
{% endfor %}
103-
<tr>
104-
<th>
105-
<div class="addbutton" id="add_room">+ROOM</div>
106-
<div class="addbutton" id="add_day">+DAY</div>
107-
</th>
108-
<th><!-- resources --></th>
21+
<table class="tstable table table-striped table-compact table-bordered">
22+
<thead>
23+
<tr>
24+
<th></th>
25+
{% for day in time_slices %}
26+
<th colspan="{{date_slices|colWidth:day}}">
27+
{{day|date:'D'}}&nbsp;({{day}})
28+
</th>
29+
{% endfor %}
30+
</tr>
31+
<tr>
32+
<th></th>
10933
{% for day in time_slices %}
11034
{% for slot in slot_slices|lookup:day %}
111-
<th class="day_{{day}} room_title">
112-
<div
113-
href="{{slot.json_url}}"
114-
timeslot_id="{{slot.pk}}"
115-
class="delete delete_slot bottom_left"
116-
id="delete_{{day|date:'Y-m-d'}}_{{slot.time|date:'Hi'}}">X</div>
117-
{{slot.time|date:'Hi'}}-{{slot.end_time|date:'Hi'}}
35+
<th>
36+
{{slot.time|date:'Hi'}}-{{slot.end_time|date:'Hi'}}
11837
</th>
119-
12038
{% endfor %}
121-
<th class="day_{{day}} spacer {{day|date:'Y-m-d'}}-spacer">
122-
</div></th>
123-
12439
{% endfor %}
40+
</tr>
41+
</thead>
12542

126-
{% for r in rooms %}
127-
<tr id="{{r.name|to_acceptable_id}}" class="agenda_slot_row">
128-
<th class="vert_time">
129-
<div class="delete delete_room bottom_left"
130-
id="delete_{{r.name|to_acceptable_id}}"
131-
href="{{r.json_url}}"
132-
roomid="{{r.pk}}">X</div>
133-
<div class="right room_name"><a class="edit_room editlink"
134-
href="/meeting/{{ meeting.number }}/room/{{r.pk}}.html" >{{r.name}} <span class="capacity">({{r.capacity}})</span></a></div>
135-
136-
<!-- <span class="hide_room light_blue_border">X</span><span class="left">{{r.name}}</span></th> -->
137-
<th class="room_features">
138-
<div class="resource_list">
139-
{% for resource in r.resources.all %}
140-
<span class="resource_image">
141-
{% if resource.id == "project" %} <img src="{% static 'ietf/images/projector.png' %}" height=24 alt="{{resource.desc}}" title="{{resource.desc}}"/>{% endif %}
142-
{% if resource.id == "proj2" %} <img src="{% static 'ietf/images/projector2.png' %}" height=24 alt="{{resource.desc}}" title="{{resource.desc}}"/>{% endif %}
143-
{% if resource.id == "meetecho" %} <img src="{% static 'ietf/images/meetecho-mini.png' %}" height=24 alt="{{resource.desc}}" title="{{resource.desc}}"/>{% endif %}
144-
{% if resource.id == "boardroom" %} <img src="{% static 'ietf/images/projector.png' %}" height=24 alt="{{resource.desc}}" title="{{resource.desc}}"/>{% endif %}
145-
</span>
146-
{% endfor %}
147-
</div>
148-
</th>
43+
{% for room in rooms %}
44+
<tr>
45+
<th>{{room.name}}<span class='capacity'>{% if room.capacity %} ({{room.capacity}}){% endif %}</th>
14946
{% for day in time_slices %}
150-
{% for slot in date_slices|lookup:day %}
151-
<td slot_time="{{day}} {{slot.0|date:'H:i:s'}}" slot_duration="{{slot.2}}" slot_room="{{r.pk}}" id="{{r.dom_id}}_{{day}}_{{slot.0|date:'Hi'}}" class="day_{{day}} agenda-column-{{day}}-{{slot.0|date:'Hi'}} agenda_slot {% cycle 'agenda_slot_alt' '' %} agenda_slot_unavailable" ></td>
47+
{% for slice in date_slices|lookup:day %}
48+
{% with ts=ts_list.popleft %}
49+
<td{% if ts %} class="tstype_{{ts.type.slug}}"{% endif %}>{% if ts %}<a href="{% url 'ietf.meeting.views.edit_timeslot_type' num=meeting.number slot_id=ts.id %}">{{ts.type.slug}}</a>{% endif %}</td>
50+
{% endwith %}
15251
{% endfor %}
153-
<td class="day_{{day}} spacer {{day|date:'Y-m-d'}}-spacer"></td>
15452
{% endfor %}
15553
</tr>
15654
{% endfor %}
15755
</table>
15856

159-
</div>
160-
<div class="dialog" id="add_room_dialog">
161-
<form action="{{roomsurl}}" method="post">{% csrf_token %}
162-
<table>
163-
{{ addroom.as_table }}
164-
<tr><td><input type="submit" name="addroom" value="addroom"></td></tr>
165-
</table>
166-
</form>
167-
</div>
168-
<div class="dialog" id="add_day_dialog">
169-
<table>
170-
<form action="{{adddayurl}}" method="post">{% csrf_token %}
171-
{{ addday }}
172-
<tr><th><label>Duration</label></th><td><input type="text" id="duration_time"></td></tr>
173-
<tr><th></th><td><div id="timespan"></div></td></tr>
174-
<tr><td><input type="submit" name="addday" value="addday"></td></tr>
175-
</form>
176-
</table>
177-
</div>
178-
179-
<div class="dialog" id="room_delete_dialog">
180-
Are you sure you want to delete this room?
181-
</div>
182-
183-
<div class="dialog" id="slot_delete_dialog">
184-
Are you sure you want to delete this entire timeslot?
185-
</div>
186-
18757
{% endblock %}

0 commit comments

Comments
 (0)