Simplified the view that lets the secretariat see and change timeslot types. Fixes #2313. Commit ready for merge.
- Legacy-Id: 14634
This commit is contained in:
parent
0306f0ad57
commit
012d51b63c
|
@ -34,7 +34,7 @@ from ietf.utils.text import xslugify
|
|||
from ietf.person.factories import PersonFactory
|
||||
from ietf.group.factories import GroupFactory, GroupEventFactory
|
||||
from ietf.meeting.factories import ( SessionFactory, SessionPresentationFactory, ScheduleFactory,
|
||||
MeetingFactory, FloorPlanFactory )
|
||||
MeetingFactory, FloorPlanFactory, TimeSlotFactory )
|
||||
from ietf.doc.factories import DocumentFactory
|
||||
|
||||
|
||||
|
@ -536,6 +536,17 @@ class EditTests(TestCase):
|
|||
self.assertEqual(r.status_code, 200)
|
||||
self.assertTrue(meeting.room_set.all().first().name in unicontent(r))
|
||||
|
||||
def test_edit_timeslot_type(self):
|
||||
timeslot = TimeSlotFactory()
|
||||
url = urlreverse('ietf.meeting.views.edit_timeslot_type', kwargs=dict(num=timeslot.meeting.number,slot_id=timeslot.id))
|
||||
login_testing_unauthorized(self,"secretary",url)
|
||||
r = self.client.get(url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
r = self.client.post(url,{'type':'other',})
|
||||
self.assertEqual(r.status_code, 302)
|
||||
timeslot = TimeSlot.objects.get(id=timeslot.id)
|
||||
self.assertEqual(timeslot.type.slug,'other')
|
||||
|
||||
def test_slot_to_the_right(self):
|
||||
meeting = make_meeting_test_data()
|
||||
session = Session.objects.filter(meeting=meeting, group__acronym="mars").first()
|
||||
|
|
|
@ -41,6 +41,7 @@ type_ietf_only_patterns = [
|
|||
url(r'^agendas/list$', views.list_agendas),
|
||||
url(r'^agendas/edit$', RedirectView.as_view(pattern_name='ietf.meeting.views.list_agendas', permanent=True)),
|
||||
url(r'^timeslots/edit$', views.edit_timeslots),
|
||||
url(r'^timeslot/(?P<slot_id>\d+)/edittype$', views.edit_timeslot_type),
|
||||
url(r'^rooms$', ajax.timeslot_roomsurl),
|
||||
url(r'^room/(?P<roomid>\d+).json$', ajax.timeslot_roomurl),
|
||||
url(r'^timeslots$', ajax.timeslot_slotsurl),
|
||||
|
|
|
@ -6,7 +6,7 @@ import re
|
|||
import tarfile
|
||||
import urllib
|
||||
from tempfile import mkstemp
|
||||
from collections import OrderedDict, Counter
|
||||
from collections import OrderedDict, Counter, deque
|
||||
import csv
|
||||
import json
|
||||
import pytz
|
||||
|
@ -39,7 +39,7 @@ from ietf.doc.models import Document, State, DocEvent, NewRevisionDocEvent
|
|||
from ietf.group.models import Group
|
||||
from ietf.group.utils import can_manage_materials
|
||||
from ietf.ietfauth.utils import role_required, has_role
|
||||
from ietf.meeting.models import Meeting, Session, Schedule, FloorPlan, SessionPresentation
|
||||
from ietf.meeting.models import Meeting, Session, Schedule, FloorPlan, SessionPresentation, TimeSlot
|
||||
from ietf.meeting.helpers import get_areas, get_person_by_email, get_schedule_by_name
|
||||
from ietf.meeting.helpers import build_all_agenda_slices, get_wg_name_list
|
||||
from ietf.meeting.helpers import get_all_assignments_from_schedule
|
||||
|
@ -266,41 +266,29 @@ def agenda_create(request, num=None, owner=None, name=None):
|
|||
|
||||
|
||||
@role_required('Secretariat')
|
||||
@ensure_csrf_cookie
|
||||
def edit_timeslots(request, num=None):
|
||||
|
||||
meeting = get_meeting(num)
|
||||
timeslots = meeting.timeslot_set.exclude(location=None).select_related("location", "type")
|
||||
|
||||
time_slices,date_slices,slots = meeting.build_timeslices()
|
||||
|
||||
meeting_base_url = request.build_absolute_uri(meeting.base_url())
|
||||
site_base_url = request.build_absolute_uri('/')[:-1] # skip the trailing slash
|
||||
|
||||
rooms = meeting.room_set.order_by("capacity")
|
||||
|
||||
# this import locate here to break cyclic loop.
|
||||
from ietf.meeting.ajax import timeslot_roomsurl, AddRoomForm, timeslot_slotsurl, AddSlotForm
|
||||
roomsurl = reverse(timeslot_roomsurl, args=[meeting.number])
|
||||
adddayurl = reverse(timeslot_slotsurl, args=[meeting.number])
|
||||
ts_list = deque()
|
||||
rooms = meeting.room_set.order_by("capacity","name","id")
|
||||
for room in rooms:
|
||||
for day in time_slices:
|
||||
for slice in date_slices[day]:
|
||||
ts_list.append(room.timeslot_set.filter(time=slice[0],duration=datetime.timedelta(seconds=slice[2])).first())
|
||||
|
||||
|
||||
return render(request, "meeting/timeslot_edit.html",
|
||||
{"timeslots": timeslots,
|
||||
"meeting_base_url": meeting_base_url,
|
||||
"site_base_url": site_base_url,
|
||||
"rooms":rooms,
|
||||
"addroom": AddRoomForm(),
|
||||
"roomsurl": roomsurl,
|
||||
"addday": AddSlotForm(),
|
||||
"adddayurl":adddayurl,
|
||||
{"rooms":rooms,
|
||||
"time_slices":time_slices,
|
||||
"slot_slices": slots,
|
||||
"date_slices":date_slices,
|
||||
"meeting":meeting,
|
||||
"hide_menu": True,
|
||||
"ts_list":ts_list,
|
||||
})
|
||||
|
||||
|
||||
##############################################################################
|
||||
#@role_required('Area Director','Secretariat')
|
||||
# disable the above security for now, check it below.
|
||||
|
@ -2204,3 +2192,26 @@ def important_dates(request, num=None):
|
|||
|
||||
context={'meetings':meetings}
|
||||
return render(request, 'meeting/important-dates.html', context)
|
||||
|
||||
TimeSlotTypeForm = modelform_factory(TimeSlot, fields=('type',))
|
||||
|
||||
@role_required('Secretariat')
|
||||
def edit_timeslot_type(request, num, slot_id):
|
||||
timeslot = get_object_or_404(TimeSlot,id=slot_id)
|
||||
meeting = get_object_or_404(Meeting,number=num)
|
||||
if timeslot.meeting!=meeting:
|
||||
raise Http404()
|
||||
if request.method=='POST':
|
||||
form = TimeSlotTypeForm(instance=timeslot,data=request.POST)
|
||||
if form.is_valid():
|
||||
form.save()
|
||||
return HttpResponseRedirect(reverse('ietf.meeting.views.edit_timeslots',kwargs={'num':num}))
|
||||
|
||||
else:
|
||||
form = TimeSlotTypeForm(instance=timeslot)
|
||||
|
||||
sessions = timeslot.sessions.filter(timeslotassignments__schedule=meeting.agenda)
|
||||
|
||||
return render(request, 'meeting/edit_timeslot_type.html', {'timeslot':timeslot,'form':form,'sessions':sessions})
|
||||
|
||||
|
||||
|
|
27
ietf/templates/meeting/edit_timeslot_type.html
Normal file
27
ietf/templates/meeting/edit_timeslot_type.html
Normal file
|
@ -0,0 +1,27 @@
|
|||
{% extends "base.html" %}
|
||||
{# Copyright The IETF Trust 2018, All Rights Reserved #}
|
||||
{% load origin %}
|
||||
{% load bootstrap3 %}
|
||||
|
||||
{% block title %}Edit timeslot type for {{timeslot}}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% origin %}
|
||||
<h1>Edit timeslot type for {{timeslot}}</h1>
|
||||
{% if sessions %}
|
||||
<div class="alert alert-warning">
|
||||
This timeslot currently has the following sessions assigned to it:
|
||||
{% for s in sessions %}
|
||||
<div>{{s}}</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
{% bootstrap_form form %}
|
||||
{% buttons %}
|
||||
<button type="submit" class="btn btn-primary">Save</button>
|
||||
<a class="btn btn-default" href="{% url 'ietf.meeting.views.edit_timeslots' num=timeslot.meeting.number %}">Cancel</a>
|
||||
{% endbuttons %}
|
||||
</form>
|
||||
{% endblock %}
|
|
@ -1,187 +1,57 @@
|
|||
{% extends "base.html" %}
|
||||
{# Copyright The IETF Trust 2015, All Rights Reserved #}
|
||||
{% load origin %}
|
||||
{% load staticfiles %}
|
||||
{% load ietf_filters %}
|
||||
{% load humanize %}
|
||||
{% load agenda_custom_tags %}
|
||||
|
||||
{% block title %}IETF {{ meeting.number }} Meeting Agenda: Timeslot/Room Availability{% endblock %}
|
||||
{% load agenda_custom_tags %}
|
||||
{% block pagehead %}
|
||||
<link rel='stylesheet' type='text/css' href="{% static 'ietf/css/agenda/jquery-ui-themes/jquery-ui-1.8.11.custom.css' %}" />
|
||||
<link rel='stylesheet' type='text/css' href="{% static 'ietf/css/agenda/base2.css' %}" />
|
||||
<link rel='stylesheet' type='text/css' href="{% static 'ietf/css/agenda/agenda.css' %}" />
|
||||
{% endblock pagehead %}
|
||||
|
||||
{% block js %}
|
||||
<script type="text/javascript" src="{% static 'ietf/js/agenda/jquery-1.8.2.min.js' %}"></script>
|
||||
<script src="{% static 'jquery.cookie/jquery.cookie.js' %}"></script>
|
||||
<script>
|
||||
jQuery.ajaxSetup({
|
||||
crossDomain: false, // obviates need for sameOrigin test
|
||||
beforeSend: function(xhr, settings) {
|
||||
if (!csrfSafeMethod(settings.type)) {
|
||||
xhr.setRequestHeader("X-CSRFToken", $.cookie('csrftoken'));
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<script type='text/javascript' src="{% static 'ietf/js/agenda/jquery-ui-1.9.0.custom/jquery-ui.custom.js' %}"></script>
|
||||
<script type='text/javascript' src="{% static 'ietf/js/agenda/jquery-ui-1.9.0.custom/jquery.ui.widget.js' %}"></script>
|
||||
<script type='text/javascript' src="{% static 'ietf/js/agenda/jquery-ui-1.9.0.custom/jquery.ui.droppable.js' %}"></script>
|
||||
<script type='text/javascript' src="{% static 'ietf/js/agenda/jquery-ui-1.9.0.custom/jquery.ui.sortable.js' %}"></script>
|
||||
<script type='text/javascript' src="{% static 'ietf/js/agenda/jquery-ui-1.9.0.custom/jquery.ui.accordion.js' %}"></script>
|
||||
<script type='text/javascript' src="{% static 'ietf/js/agenda/jquery-ui-1.9.0.custom/jquery.ui.draggable.js' %}"></script>
|
||||
<script type='text/javascript' src="{% static 'ietf/js/agenda/jquery-ui-1.9.0.custom/jquery.ui.datepicker.js' %}"></script>
|
||||
|
||||
<!-- source (MIT License) http://momentjs.com/ https://github.com/moment/moment/ -->
|
||||
<script type='text/javascript' src="{% static 'ietf/js/agenda/moment.min.js' %}"></script>
|
||||
|
||||
<!-- source (MIT License) : https://github.com/trentrichardson/jQuery-Timepicker-Addon -->
|
||||
<script type='text/javascript' src="{% static 'ietf/js/agenda/jquery-ui-timepicker/jquery-ui-timepicker-addon.js' %}"></script>
|
||||
<script type='text/javascript' src="{% static 'ietf/js/agenda/jquery-ui-timepicker/jquery-ui-sliderAccess.js' %}"></script>
|
||||
<link rel='stylesheet' type='text/css' href="{% static 'ietf/css/agenda/jquery-ui-timepicker-addon.css' %}" />
|
||||
|
||||
<script type='text/javascript' src="{% static 'spin.js/spin.min.js' %}"></script>
|
||||
<script type='text/javascript' src="{% static 'ietf/js/agenda/timeslot_edit.js' %}"></script>
|
||||
<script type='text/javascript' src="{% static 'ietf/js/agenda/agenda_objects.js' %}"></script>
|
||||
<script type='text/javascript' src="{% static 'ietf/js/agenda/agenda_helpers.js' %}"></script>
|
||||
<script type='text/javascript' src="{% static 'ietf/js/agenda/agenda_listeners.js' %}"></script>
|
||||
|
||||
|
||||
<script type='text/javascript'>
|
||||
|
||||
var meeting_number = "{{ meeting.number }}";
|
||||
var meeting_base_url = "{{ meeting_base_url }}";
|
||||
var site_base_url = "{{ site_base_url }}";
|
||||
var meeting_slots_href = "{% url "ietf.meeting.ajax.timeslot_slotsurl" meeting.number %}";
|
||||
total_days = {{time_slices|length}};
|
||||
total_rooms = {{rooms|length}};
|
||||
|
||||
first_day = new Date("{% with timeslots|first as day %} {{ day.time }} {% endwith %}"); /* needed for the datepicker */
|
||||
|
||||
function setup_slots(promiselist){
|
||||
var ts_promise = load_timeslots(meeting_slots_href);
|
||||
promiselist.push(ts_promise);
|
||||
|
||||
{% for day in time_slices %}
|
||||
days.push("{{day}}");
|
||||
{% endfor %}
|
||||
console.log("setup_slots run");
|
||||
}
|
||||
|
||||
</script>
|
||||
{% endblock js %}
|
||||
{% block morecss %}
|
||||
.tstable { width: 100%;}
|
||||
.tstable th { white-space: nowrap;}
|
||||
.tstable td { white-space: nowrap;}
|
||||
.capacity { font-size:80%; font-weight: normal;}
|
||||
|
||||
.tstable .tstype_unavail {background-color:#666;}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% origin %}
|
||||
<div class="wrapper custom_text_stuff">
|
||||
<div style="ui-icon ui-icon-arrow-1-w" id="close_ietf_menubar">
|
||||
<
|
||||
</div>
|
||||
|
||||
<div class="agenda_div">
|
||||
|
||||
<table id="meetings" class="ietf-navbar" style="width:100%">
|
||||
<th class="schedule_title"><div id="pageloaded" style="display:none"><span id="schedule_name">name: {{meeting.number}}</span></div>
|
||||
<div id="spinner"><!-- spinney goes here --></div>
|
||||
</th>
|
||||
<th><!-- resources --></th>
|
||||
{% for day in time_slices %}
|
||||
<th colspan="{{date_slices|colWidth:day}}" id="{{day|date:'Y-m-d'}}-btn" class=" day_{{day}} agenda_slot_title">
|
||||
<div style="display: none;" class="delete delete_day bottom_left" id="delete_{{day|date:'Y-m-d'}}">X</div>
|
||||
{{day|date:'D'}} ({{day}})
|
||||
|
||||
</th>
|
||||
<th class="day_{{day}} spacer {{day|date:'Y-m-d'}}-spacer" id="">
|
||||
<div class="ui-widget-content ui-resizable" id="resize-{{day|date:'Y-m-d'}}-spacer">
|
||||
<div class="spacer_grip ui-resizable-handle ui-resizable-e"></div>
|
||||
</div>
|
||||
</th>
|
||||
{% endfor %}
|
||||
<tr>
|
||||
<th>
|
||||
<div class="addbutton" id="add_room">+ROOM</div>
|
||||
<div class="addbutton" id="add_day">+DAY</div>
|
||||
</th>
|
||||
<th><!-- resources --></th>
|
||||
<table class="tstable table table-striped table-compact table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
{% for day in time_slices %}
|
||||
<th colspan="{{date_slices|colWidth:day}}">
|
||||
{{day|date:'D'}} ({{day}})
|
||||
</th>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
<tr>
|
||||
<th></th>
|
||||
{% for day in time_slices %}
|
||||
{% for slot in slot_slices|lookup:day %}
|
||||
<th class="day_{{day}} room_title">
|
||||
<div
|
||||
href="{{slot.json_url}}"
|
||||
timeslot_id="{{slot.pk}}"
|
||||
class="delete delete_slot bottom_left"
|
||||
id="delete_{{day|date:'Y-m-d'}}_{{slot.time|date:'Hi'}}">X</div>
|
||||
{{slot.time|date:'Hi'}}-{{slot.end_time|date:'Hi'}}
|
||||
<th>
|
||||
{{slot.time|date:'Hi'}}-{{slot.end_time|date:'Hi'}}
|
||||
</th>
|
||||
|
||||
{% endfor %}
|
||||
<th class="day_{{day}} spacer {{day|date:'Y-m-d'}}-spacer">
|
||||
</div></th>
|
||||
|
||||
{% endfor %}
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
{% for r in rooms %}
|
||||
<tr id="{{r.name|to_acceptable_id}}" class="agenda_slot_row">
|
||||
<th class="vert_time">
|
||||
<div class="delete delete_room bottom_left"
|
||||
id="delete_{{r.name|to_acceptable_id}}"
|
||||
href="{{r.json_url}}"
|
||||
roomid="{{r.pk}}">X</div>
|
||||
<div class="right room_name"><a class="edit_room editlink"
|
||||
href="/meeting/{{ meeting.number }}/room/{{r.pk}}.html" >{{r.name}} <span class="capacity">({{r.capacity}})</span></a></div>
|
||||
|
||||
<!-- <span class="hide_room light_blue_border">X</span><span class="left">{{r.name}}</span></th> -->
|
||||
<th class="room_features">
|
||||
<div class="resource_list">
|
||||
{% for resource in r.resources.all %}
|
||||
<span class="resource_image">
|
||||
{% if resource.id == "project" %} <img src="{% static 'ietf/images/projector.png' %}" height=24 alt="{{resource.desc}}" title="{{resource.desc}}"/>{% endif %}
|
||||
{% if resource.id == "proj2" %} <img src="{% static 'ietf/images/projector2.png' %}" height=24 alt="{{resource.desc}}" title="{{resource.desc}}"/>{% endif %}
|
||||
{% if resource.id == "meetecho" %} <img src="{% static 'ietf/images/meetecho-mini.png' %}" height=24 alt="{{resource.desc}}" title="{{resource.desc}}"/>{% endif %}
|
||||
{% if resource.id == "boardroom" %} <img src="{% static 'ietf/images/projector.png' %}" height=24 alt="{{resource.desc}}" title="{{resource.desc}}"/>{% endif %}
|
||||
</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</th>
|
||||
{% for room in rooms %}
|
||||
<tr>
|
||||
<th>{{room.name}}<span class='capacity'>{% if room.capacity %} ({{room.capacity}}){% endif %}</th>
|
||||
{% for day in time_slices %}
|
||||
{% for slot in date_slices|lookup:day %}
|
||||
<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>
|
||||
{% for slice in date_slices|lookup:day %}
|
||||
{% with ts=ts_list.popleft %}
|
||||
<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>
|
||||
{% endwith %}
|
||||
{% endfor %}
|
||||
<td class="day_{{day}} spacer {{day|date:'Y-m-d'}}-spacer"></td>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
|
||||
</div>
|
||||
<div class="dialog" id="add_room_dialog">
|
||||
<form action="{{roomsurl}}" method="post">{% csrf_token %}
|
||||
<table>
|
||||
{{ addroom.as_table }}
|
||||
<tr><td><input type="submit" name="addroom" value="addroom"></td></tr>
|
||||
</table>
|
||||
</form>
|
||||
</div>
|
||||
<div class="dialog" id="add_day_dialog">
|
||||
<table>
|
||||
<form action="{{adddayurl}}" method="post">{% csrf_token %}
|
||||
{{ addday }}
|
||||
<tr><th><label>Duration</label></th><td><input type="text" id="duration_time"></td></tr>
|
||||
<tr><th></th><td><div id="timespan"></div></td></tr>
|
||||
<tr><td><input type="submit" name="addday" value="addday"></td></tr>
|
||||
</form>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="dialog" id="room_delete_dialog">
|
||||
Are you sure you want to delete this room?
|
||||
</div>
|
||||
|
||||
<div class="dialog" id="slot_delete_dialog">
|
||||
Are you sure you want to delete this entire timeslot?
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
|
Loading…
Reference in a new issue