diff --git a/ietf/group/models.py b/ietf/group/models.py index 8e2c55f9c..d97b50ab4 100644 --- a/ietf/group/models.py +++ b/ietf/group/models.py @@ -8,16 +8,12 @@ import jsonfield import os import re -from urllib.parse import urljoin - from django.conf import settings from django.core.validators import RegexValidator from django.db import models from django.db.models.deletion import CASCADE, PROTECT from django.dispatch import receiver -#from simple_history.models import HistoricalRecords - import debug # pyflakes:ignore from ietf.group.colors import fg_group_colors, bg_group_colors @@ -168,30 +164,6 @@ class Group(GroupInfo): def bg_color(self): return bg_group_colors[self.upcase_acronym] - def json_url(self): - return "/group/%s.json" % (self.acronym,) - - def json_dict(self, host_scheme): - group1= dict() - group1['href'] = urljoin(host_scheme, self.json_url()) - group1['acronym'] = self.acronym - group1['name'] = self.name - group1['state'] = self.state.slug - group1['type'] = self.type.slug - if self.parent is not None: - group1['parent_href'] = urljoin(host_scheme, self.parent.json_url()) - # uncomment when people URL handle is created - try: - if self.ad_role() is not None: - group1['ad_href'] = urljoin(host_scheme, self.ad_role().person.json_url()) - except Person.DoesNotExist: - pass - group1['list_email'] = self.list_email - group1['list_subscribe'] = self.list_subscribe - group1['list_archive'] = self.list_archive - group1['comments'] = self.comments - return group1 - def has_tools_page(self): return self.type_id in ['wg', ] and self.state_id in ['active', 'dormant', 'replaced', 'conclude'] diff --git a/ietf/group/urls.py b/ietf/group/urls.py index a8d6a06e6..07eb2c4b6 100644 --- a/ietf/group/urls.py +++ b/ietf/group/urls.py @@ -54,7 +54,6 @@ info_detail_urls = [ group_urls = [ url(r'^$', views.active_groups), url(r'^groupmenu.json', views.group_menu_data, None, 'ietf.group.views.group_menu_data'), - url(r'^%(acronym)s.json$' % settings.URL_REGEXPS, views.group_json), url(r'^chartering/$', views.chartering_groups), url(r'^chartering/create/(?P(wg|rg))/$', views.edit, {'action': "charter"}), url(r'^concluded/$', views.concluded_groups), diff --git a/ietf/group/views.py b/ietf/group/views.py index d41a925f3..fcb3318ae 100644 --- a/ietf/group/views.py +++ b/ietf/group/views.py @@ -38,7 +38,6 @@ import copy import datetime import itertools import io -import json import markdown import math import os @@ -1299,13 +1298,6 @@ def stream_edit(request, acronym): ) -def group_json(request, acronym): - group = get_object_or_404(Group, acronym=acronym) - - return HttpResponse(json.dumps(group.json_dict(request.build_absolute_uri('/')), - sort_keys=True, indent=2), - content_type="application/json") - @cache_control(public=True, max_age=30*60) @cache_page(30 * 60) def group_menu_data(request): diff --git a/ietf/meeting/ajax.py b/ietf/meeting/ajax.py deleted file mode 100644 index b8a3c01a5..000000000 --- a/ietf/meeting/ajax.py +++ /dev/null @@ -1,629 +0,0 @@ -# Copyright The IETF Trust 2013-2019, All Rights Reserved -import json - -from django.shortcuts import get_object_or_404, redirect -from django.http import HttpResponse -from django.http import QueryDict -from django.http import Http404 -from django.views.decorators.http import require_POST - -from ietf.ietfauth.utils import role_required, has_role -from ietf.meeting.helpers import get_meeting, get_schedule, schedule_permissions, get_person_by_email, get_schedule_by_name -from ietf.meeting.models import TimeSlot, Session, Schedule, Room, Constraint, SchedTimeSessAssignment, ResourceAssociation -from ietf.meeting.views import edit_timeslots, edit_meeting_schedule - -import debug # pyflakes:ignore - -def is_truthy_enough(value): - return not (value == "0" or value == 0 or value=="false") - -# look up a schedule by number, owner and schedule name, returning an API error if it can not be found -def get_meeting_schedule(num, owner, name): - meeting = get_meeting(num) - person = get_person_by_email(owner) - schedule = get_schedule_by_name(meeting, person, name) - - if schedule is None or person is None or meeting is None: - meeting_pk = 0 - person_pk = 0 - schedule_pk =0 - # to make diagnostics more meaningful, log what we found - if meeting: - meeting_pk = meeting.pk - if person: - person_pk = person.pk - if schedule: - schedule_pk=schedule.pk - return HttpResponse(json.dumps({'error' : 'invalid meeting=%s/person=%s/schedule=%s' % (num,owner,name), - 'meeting': meeting_pk, - 'person': person_pk, - 'schedule': schedule_pk}), - content_type="application/json", - status=404); - return meeting, person, schedule - - - -# should asking if an schedule is read-only require any kind of permission? -def schedule_permission_api(request, num, owner, name): - meeting = get_meeting(num) - person = get_person_by_email(owner) - schedule = get_schedule_by_name(meeting, person, name) - - save_perm = False - secretariat = False - cansee = False - canedit = False - owner_href = "" - - if schedule is not None: - cansee,canedit,secretariat = schedule_permissions(meeting, schedule, request.user) - owner_href = request.build_absolute_uri(schedule.owner.json_url()) - - if has_role(request.user, "Area Director") or secretariat: - save_perm = True - - return HttpResponse(json.dumps({'secretariat': secretariat, - 'save_perm': save_perm, - 'read_only': canedit==False, - 'owner_href': owner_href}), - content_type="application/json") - -############################################################################# -## ROOM API -############################################################################# -from django.forms.models import modelform_factory -AddRoomForm = modelform_factory(Room, exclude=('meeting','time')) - -# no authorization required -def timeslot_roomlist(request, mtg): - rooms = mtg.room_set.all() - json_array=[] - for room in rooms: - json_array.append(room.json_dict(request.build_absolute_uri('/'))) - return HttpResponse(json.dumps(json_array), - content_type="application/json") - -@role_required('Secretariat') -def timeslot_addroom(request, meeting): - newroomform = AddRoomForm(request.POST) - if not newroomform.is_valid(): - return HttpResponse(status=404) - - newroom = newroomform.save(commit=False) - newroom.meeting = meeting - newroom.save() - newroom.create_timeslots() - - if "HTTP_ACCEPT" in request.META and "application/json" in request.META['HTTP_ACCEPT']: - return redirect(timeslot_roomurl, meeting.number, newroom.pk) - else: - return redirect(edit_timeslots, meeting.number) - -@role_required('Secretariat') -def timeslot_delroom(request, meeting, roomid): - room = get_object_or_404(meeting.room_set, pk=roomid) - - room.delete_timeslots() - room.delete() - return HttpResponse('{"error":"none"}', status = 200) - -@role_required('Secretariat') -def timeslot_updroom(request, meeting, roomid): - room = get_object_or_404(meeting.room_set, pk=roomid) - - if "name" in request.POST: - room.name = request.POST["name"] - - if "capacity" in request.POST: - room.capacity = request.POST["capacity"] - - if "resources" in request.POST: - new_resource_ids = request.POST["resources"] - new_resources = [ ResourceAssociation.objects.get(pk=a) - for a in new_resource_ids] - room.resources = new_resources - - room.save() - return HttpResponse('{"error":"none"}', status = 200) - -def timeslot_roomsurl(request, num=None): - meeting = get_meeting(num) - - if request.method == 'GET': - return timeslot_roomlist(request, meeting) - elif request.method == 'POST': - return timeslot_addroom(request, meeting) - - # unacceptable reply - return HttpResponse(status=406) - -def timeslot_roomurl(request, num=None, roomid=None): - meeting = get_meeting(num) - - if request.method == 'GET': - room = get_object_or_404(meeting.room_set, pk=roomid) - return HttpResponse(json.dumps(room.json_dict(request.build_absolute_uri('/'))), - content_type="application/json") - elif request.method == 'PUT': - return timeslot_updroom(request, meeting, roomid) - elif request.method == 'DELETE': - return timeslot_delroom(request, meeting, roomid) - -############################################################################# -## DAY/SLOT API -## -- this creates groups of timeslots, and associated schedtimesessassignments. -############################################################################# -AddSlotForm = modelform_factory(TimeSlot, exclude=('meeting','name','location','sessions', 'modified')) - -# no authorization required to list. -def timeslot_slotlist(request, mtg): - slots = mtg.timeslot_set.all() - # Restrict graphical editing to slots of type 'regular' for now - slots = slots.filter(type__slug='regular') - json_array=[] - for slot in slots: - json_array.append(slot.json_dict(request.build_absolute_uri('/'))) - return HttpResponse(json.dumps(json_array, sort_keys=True, indent=2), - content_type="application/json") - -@role_required('Secretariat') -def timeslot_addslot(request, meeting): - addslotform = AddSlotForm(request.POST) - #debug.log("newslot: %u" % ( addslotform.is_valid() )) - if not addslotform.is_valid(): - return HttpResponse(status=404) - - newslot = addslotform.save(commit=False) - newslot.meeting = meeting - newslot.save() - - # XXX FIXME: timeslot_dayurl is undefined. Placeholder: - # timeslot_dayurl = None - # XXX FIXME: newroom is undefined. Placeholder: - # newroom = None - values = newslot.json_dict(request.build_absolute_uri('/')) - response = HttpResponse(json.dumps(values), - content_type="application/json", - status=201) - response['Location'] = values['href'] - return response - -@role_required('Secretariat') -def timeslot_updslot(request, meeting, slotid): - slot = get_object_or_404(meeting.timeslot_set, pk=slotid) - - # at present, updates to the purpose only is supported. - # updates to time or duration would need likely need to be - # propogated to the entire vertical part of the grid, and nothing - # needs to do that yet. - if request.method == 'POST': - put_vars = request.POST - slot.type_id = put_vars["purpose"] - else: - put_vars = QueryDict(request.body) - slot.type_id = put_vars.get("purpose") - - slot.save() - - # WORKAROUND: Right now, if there are sessions scheduled in this timeslot - # when it is marked unavailable (or any other value besides 'regular') they - # become unreachable from the editing screen. The session is listed in the - # "unscheduled" block incorrectly, and drag-dropping it onto the a new - # timeslot produces erroneous results. To avoid this, we will silently - # unschedule any sessions in the timeslot that has just been made - # unavailable. - - if slot.type_id != 'regular': - slot.sessionassignments.all().delete() - - # ENDWORKAROUND - - # need to return the new object. - dict1 = slot.json_dict(request.build_absolute_uri('/')) - dict1['message'] = 'valid' - return HttpResponse(json.dumps(dict1), - content_type="application/json") - -@role_required('Secretariat') -def timeslot_delslot(request, meeting, slotid): - slot = get_object_or_404(meeting.timeslot_set, pk=slotid) - - # this will delete self as well. - slot.delete_concurrent_timeslots() - return HttpResponse('{"error":"none"}', status = 200) - -def timeslot_slotsurl(request, num=None): - meeting = get_meeting(num) - - if request.method == 'GET': - return timeslot_slotlist(request, meeting) - elif request.method == 'POST': - return timeslot_addslot(request, meeting) - - # unacceptable reply - return HttpResponse(status=406) - -def timeslot_sloturl(request, num=None, slotid=None): - meeting = get_meeting(num) - - if request.method == 'GET': - slot = get_object_or_404(meeting.timeslot_set, pk=slotid) - return HttpResponse(json.dumps(slot.json_dict(request.build_absolute_uri('/'))), - content_type="application/json") - elif request.method == 'POST' or request.method == 'PUT': - return timeslot_updslot(request, meeting, slotid) - elif request.method == 'DELETE': - return timeslot_delslot(request, meeting, slotid) - -############################################################################# -## Schedule List API -############################################################################# -ScheduleEntryForm = modelform_factory(Schedule, exclude=('meeting','owner')) -EditScheduleEntryForm = modelform_factory(Schedule, exclude=('meeting','owner', 'name')) - -@role_required('Area Director','Secretariat') -def schedule_list(request, mtg): - schedules = mtg.schedule_set.all() - json_array=[] - for schedule in schedules: - json_array.append(schedule.json_dict(request.build_absolute_uri('/'))) - return HttpResponse(json.dumps(json_array), - content_type="application/json") - -# duplicates save-as functionality below. -@role_required('Area Director','Secretariat') -def schedule_add(request, meeting): - newscheduleform = ScheduleEntryForm(request.POST) - if not newscheduleform.is_valid(): - return HttpResponse(status=404) - - newschedule = newscheduleform.save(commit=False) - newschedule.meeting = meeting - newschedule.owner = request.user.person - newschedule.save() - - if "HTTP_ACCEPT" in request.META and "application/json" in request.META['HTTP_ACCEPT']: - return redirect(schedule_infourl, meeting.number, newschedule.owner_email(), newschedule.name) - else: - return redirect(edit_meeting_schedule, meeting.number, newschedule.owner_email(), newschedule.name) - -@require_POST -def schedule_update(request, meeting, schedule): - # forms are completely useless for update actions that want to - # accept a subset of values. (huh? we could use required=False) - - user = request.user - - if not user.is_authenticated: - return HttpResponse({'error':'no permission'}, status=403) - - cansee,canedit,secretariat = schedule_permissions(meeting, schedule, request.user) - #read_only = not canedit ## not used - - # TODO: Secretariat should always get canedit - if not (canedit or secretariat): - return HttpResponse({'error':'no permission'}, status=403) - - if "public" in request.POST: - schedule.public = is_truthy_enough(request.POST["public"]) - - if "visible" in request.POST: - schedule.visible = is_truthy_enough(request.POST["visible"]) - - if "name" in request.POST: - schedule.name = request.POST["name"] - - schedule.save() - - # enforce that a non-public schedule can not be the public one. - if meeting.schedule == schedule and not schedule.public: - meeting.schedule = None - meeting.save() - - if "HTTP_ACCEPT" in request.META and "application/json" in request.META['HTTP_ACCEPT']: - return HttpResponse(json.dumps(schedule.json_dict(request.build_absolute_uri('/'))), - content_type="application/json") - else: - return redirect(edit_meeting_schedule, meeting.number, schedule.owner_email(), schedule.name) - -@role_required('Secretariat') -def schedule_del(request, meeting, schedule): - schedule.delete_assignments() - #debug.log("deleting meeting: %s schedule: %s" % (meeting, meeting.schedule)) - if meeting.schedule == schedule: - meeting.schedule = None - meeting.save() - schedule.delete() - return HttpResponse('{"error":"none"}', status = 200) - -def schedule_infosurl(request, num=None): - meeting = get_meeting(num) - - if request.method == 'GET': - return schedule_list(request, meeting) - elif request.method == 'POST': - return schedule_add(request, meeting) - - # unacceptable action - return HttpResponse(status=406) - -def schedule_infourl(request, num=None, owner=None, name=None): - meeting = get_meeting(num) - person = get_person_by_email(owner) - schedule = get_schedule_by_name(meeting, person, name) - if schedule is None: - raise Http404("No meeting information for meeting %s schedule %s available" % (num,name)) - - #debug.log("results in schedule: %u / %s" % (schedule.id, request.method)) - - if request.method == 'GET': - return HttpResponse(json.dumps(schedule.json_dict(request.build_absolute_uri('/'))), - content_type="application/json") - elif request.method == 'POST': - return schedule_update(request, meeting, schedule) - elif request.method == 'DELETE': - return schedule_del(request, meeting, schedule) - else: - return HttpResponse(status=406) - -############################################################################# -## Meeting API (very limited) -############################################################################# - -def meeting_get(request, meeting): - return HttpResponse(json.dumps(meeting.json_dict(request.build_absolute_uri('/')), - sort_keys=True, indent=2), - content_type="application/json") - -@role_required('Secretariat') -def meeting_update(request, meeting): - # at present, only the official schedule can be updated from this interface. - - #debug.log("1 meeting.schedule: %s / %s / %s" % (meeting.schedule, update_dict, request.body)) - if "schedule" in request.POST: - value = request.POST["schedule"] - #debug.log("4 meeting.schedule: %s" % (value)) - if not value or value == "None": # value == "None" is just weird, better with empty string - meeting.set_official_schedule(None) - else: - schedule = get_schedule(meeting, value) - if not schedule.public: - return HttpResponse(status = 406) - #debug.log("3 meeting.schedule: %s" % (schedule)) - meeting.set_official_schedule(schedule) - - #debug.log("2 meeting.schedule: %s" % (meeting.schedule)) - meeting.save() - return meeting_get(request, meeting) - -def meeting_json(request, num): - meeting = get_meeting(num) - - if request.method == 'GET': - return meeting_get(request, meeting) - elif request.method == 'POST': - return meeting_update(request, meeting) - else: - return HttpResponse(status=406) - - -############################################################################# -## Session details API functions -############################################################################# - -def session_json(request, num, sessionid): - meeting = get_meeting(num) - - try: - session = meeting.session_set.get(pk=int(sessionid)) - except Session.DoesNotExist: -# return json.dumps({'error':"no such session %s" % sessionid}) - return HttpResponse(json.dumps({'error':"no such session %s" % sessionid}), - status = 404, - content_type="application/json") - - sess1 = session.json_dict(request.build_absolute_uri('/')) - return HttpResponse(json.dumps(sess1, sort_keys=True, indent=2), - content_type="application/json") - -# get group of all sessions. -def sessions_json(request, num): - meeting = get_meeting(num) - - sessions = meeting.session_set.that_can_meet().with_requested_time().with_requested_by() - - sess1_dict = [ x.json_dict(request.build_absolute_uri('/')) for x in sessions ] - return HttpResponse(json.dumps(sess1_dict, sort_keys=True, indent=2), - content_type="application/json") - -############################################################################# -## Scheduledsesion -############################################################################# - -# this creates an entirely *NEW* schedtimesessassignment -def assignments_post(request, meeting, schedule): - cansee,canedit,secretariat = schedule_permissions(meeting, schedule, request.user) - if not canedit: - return HttpResponse(json.dumps({'error':'no permission to modify this schedule'}), - status = 403, - content_type="application/json") - - # get JSON out of raw body. XXX should check Content-Type! - newvalues = json.loads(request.body) - if not ("session_id" in newvalues) or not ("timeslot_id" in newvalues): - return HttpResponse(json.dumps({'error':'missing values, timeslot_id and session_id required'}), - status = 406, - content_type="application/json") - - try: - Session.objects.get(pk=newvalues["session_id"]) - except Session.DoesNotExist: - return HttpResponse(json.dumps({'error':'session has been deleted'}), - status = 406, - content_type="application/json") - - ss1 = SchedTimeSessAssignment(schedule = schedule, - session_id = newvalues["session_id"], - timeslot_id = newvalues["timeslot_id"]) - if("extendedfrom_id" in newvalues): - val = int(newvalues["extendedfrom_id"]) - try: - ss2 = schedule.assignments.get(pk = val) - ss1.extendedfrom = ss2 - except SchedTimeSessAssignment.DoesNotExist: - return HttpResponse(json.dumps({'error':'invalid extendedfrom value: %u' % val}), - status = 406, - content_type="application/json") - ss1.save() - ss1_dict = ss1.json_dict(request.build_absolute_uri('/')) - response = HttpResponse(json.dumps(ss1_dict), - status = 201, - content_type="application/json") - # 201 code needs a Location: header. - response['Location'] = ss1_dict["href"], - return response - -def assignments_get(request, num, schedule): - assignments = schedule.assignments.all() - - absolute_url = request.build_absolute_uri('/') - sess1_dict = [ x.json_dict(absolute_url) for x in assignments ] - return HttpResponse(json.dumps(sess1_dict, sort_keys=True, indent=2), - content_type="application/json") - -# this returns the list of scheduled sessions for the given named schedule -def assignments_json(request, num, owner, name): - info = get_meeting_schedule(num, owner, name) - # The return values from get_meeting_schedule() are silly, in that it - # is a tuple for non-error return, but a HTTPResponse when error, but - # work around that for the moment - if isinstance(info, HttpResponse): - return info - meeting, person, schedule = info - - if request.method == 'GET': - return assignments_get(request, meeting, schedule) - elif request.method == 'POST': - return assignments_post(request, meeting, schedule) - else: - return HttpResponse(json.dumps({'error':'inappropriate action: %s' % (request.method)}), - status = 406, - content_type="application/json") - -# accepts both POST and PUT in order to implement Postel Doctrine. -def assignment_update(request, meeting, schedule, ss): - cansee,canedit,secretariat = schedule_permissions(meeting, schedule, request.user) - if not canedit: - return HttpResponse(json.dumps({'error':'no permission to update this schedule'}), - status = 403, - content_type="application/json") - - if request.method == 'POST': - put_vars = request.POST - ss.pinned = is_truthy_enough(put_vars["pinned"]) - else: - put_vars = QueryDict(request.body) - ss.pinned = is_truthy_enough(put_vars.get("pinned")) - - ss.save() - return HttpResponse(json.dumps({'message':'valid'}), - content_type="application/json") - -def assignment_delete(request, meeting, schedule, ss): - cansee,canedit,secretariat = schedule_permissions(meeting, schedule, request.user) - if not canedit: - return HttpResponse(json.dumps({'error':'no permission to update this schedule'}), - status = 403, - content_type="application/json") - - # in case there is, somehow, more than one item with the same pk.. XXX - assignments = schedule.assignments.filter(pk = ss.pk) - if len(assignments) == 0: - return HttpResponse(json.dumps({'error':'no such object'}), - status = 404, - content_type="application/json") - count=0 - for ss in assignments: - ss.delete() - count += 1 - - return HttpResponse(json.dumps({'result':"%u objects deleted"%(count)}), - status = 200, - content_type="application/json") - -def assignment_get(request, meeting, schedule, ss): - cansee,canedit,secretariat = schedule_permissions(meeting, schedule, request.user) - - if not cansee: - return HttpResponse(json.dumps({'error':'no permission to see this schedule'}), - status = 403, - content_type="application/json") - - sess1_dict = ss.json_dict(request.build_absolute_uri('/')) - return HttpResponse(json.dumps(sess1_dict, sort_keys=True, indent=2), - content_type="application/json") - -# this return a specific session, updates a session or deletes a SPECIFIC scheduled session -def assignment_json(request, num, owner, name, assignment_id): - meeting, person, schedule = get_meeting_schedule(num, owner, name) - - assignments = schedule.assignments.filter(pk = assignment_id) - if len(assignments) == 0: - return HttpResponse(json.dumps({'error' : 'invalid assignment'}), - content_type="application/json", - status=404); - ss = assignments[0] - - if request.method == 'GET': - return assignment_get(request, meeting, schedule, ss) - elif request.method == 'PUT' or request.method=='POST': - return assignment_update(request, meeting, schedule, ss) - elif request.method == 'DELETE': - return assignment_delete(request, meeting, schedule, ss) - -############################################################################# -## Constraints API -############################################################################# - - -# Would like to cache for 1 day, but there are invalidation issues. -#@cache_page(86400) -def constraint_json(request, num, constraintid): - meeting = get_meeting(num) - - try: - constraint = meeting.constraint_set.get(pk=int(constraintid)) - except Constraint.DoesNotExist: - return HttpResponse(json.dumps({'error':"no such constraint %s" % constraintid}), - status = 404, - content_type="application/json") - - json1 = constraint.json_dict(request.build_absolute_uri('/')) - return HttpResponse(json.dumps(json1, sort_keys=True, indent=2), - content_type="application/json") - - -# Cache for 2 hour2 -#@cache_page(7200) -# caching is a problem if there Host: header changes. -# -def session_constraints(request, num, sessionid): - meeting = get_meeting(num) - - #print "Getting meeting=%s session contraints for %s" % (num, sessionid) - try: - session = meeting.session_set.get(pk=int(sessionid)) - except Session.DoesNotExist: - return json.dumps({"error":"no such session"}) - - constraint_list = session.constraints_dict(request.build_absolute_uri('/')) - - json_str = json.dumps(constraint_list, - sort_keys=True, indent=2), - #print " gives: %s" % (json_str) - - return HttpResponse(json_str, content_type="application/json") - - - diff --git a/ietf/meeting/helpers.py b/ietf/meeting/helpers.py index 0ca6e0d12..1a7a3d5c2 100644 --- a/ietf/meeting/helpers.py +++ b/ietf/meeting/helpers.py @@ -9,13 +9,11 @@ import os import re from tempfile import mkstemp -from django.http import HttpRequest, Http404 -from django.db.models import F, Max, Q, Prefetch +from django.http import Http404 +from django.db.models import F, Prefetch from django.conf import settings from django.contrib.auth.models import AnonymousUser -from django.core.cache import cache from django.urls import reverse -from django.utils.cache import get_cache_key from django.shortcuts import get_object_or_404 from django.template.loader import render_to_string @@ -32,95 +30,12 @@ from ietf.meeting.models import Meeting, Schedule, TimeSlot, SchedTimeSessAssign from ietf.meeting.utils import session_requested_by, add_event_info_to_session_qs from ietf.name.models import ImportantDateName, SessionPurposeName from ietf.utils import log -from ietf.utils.history import find_history_active_at, find_history_replacements_active_at +from ietf.utils.history import find_history_replacements_active_at from ietf.utils.mail import send_mail from ietf.utils.pipe import pipe from ietf.utils.text import xslugify -def find_ads_for_meeting(meeting): - ads = [] - meeting_time = datetime.datetime.combine(meeting.date, datetime.time(0, 0, 0)) - - num = 0 - # get list of ADs which are/were active at the time of the meeting. - # (previous [x for x in y] syntax changed to aid debugging) - for g in Group.objects.filter(type="area").order_by("acronym"): - history = find_history_active_at(g, meeting_time) - num = num +1 - if history and history != g: - #print " history[%u]: %s" % (num, history) - if history.state_id == "active": - for x in history.rolehistory_set.filter(name="ad",group__type='area').select_related('group', 'person', 'email'): - #print "xh[%u]: %s" % (num, x) - ads.append(x) - else: - #print " group[%u]: %s" % (num, g) - if g.state_id == "active": - for x in g.role_set.filter(name="ad",group__type='area').select_related('group', 'person', 'email'): - #print "xg[%u]: %s (#%u)" % (num, x, x.pk) - ads.append(x) - return ads - - -# get list of all areas, + IRTF + IETF (plenaries). -def get_pseudo_areas(): - return Group.objects.filter(Q(state="active", name="IRTF")| - Q(state="active", name="IETF")| - Q(state="active", type="area")).order_by('acronym') - -# get list of all areas, + IRTF. -def get_areas(): - return Group.objects.filter(Q(state="active", - name="IRTF")| - Q(state="active", type="area")).order_by('acronym') - -# get list of areas that are referenced. -def get_area_list_from_sessions(assignments, num): - return assignments.filter(timeslot__type = 'regular', - session__group__parent__isnull = False).order_by( - 'session__group__parent__acronym').distinct().values_list( - 'session__group__parent__acronym',flat=True) - -def build_all_agenda_slices(meeting): - time_slices = [] - date_slices = {} - - for ts in meeting.timeslot_set.filter(type__in=['regular',]).order_by('time','name'): - ymd = ts.time.date() - - if ymd not in date_slices and ts.location != None: - date_slices[ymd] = [] - time_slices.append(ymd) - - if ymd in date_slices: - if [ts.time, ts.time+ts.duration] not in date_slices[ymd]: # only keep unique entries - date_slices[ymd].append([ts.time, ts.time+ts.duration]) - - time_slices.sort() - return time_slices,date_slices - -def get_all_assignments_from_schedule(schedule): - ss = schedule.assignments.filter(timeslot__location__isnull = False) - ss = ss.filter(session__type__slug='regular') - ss = ss.order_by('timeslot__time','timeslot__name') - - return ss - -def get_modified_from_assignments(assignments): - return assignments.aggregate(Max('timeslot__modified'))['timeslot__modified__max'] - -def get_wg_name_list(assignments): - return assignments.filter(timeslot__type = 'regular', - session__group__isnull = False, - session__group__parent__isnull = False).order_by( - 'session__group__acronym').distinct().values_list( - 'session__group__acronym',flat=True) - -def get_wg_list(assignments): - wg_name_list = get_wg_name_list(assignments) - return Group.objects.filter(acronym__in = set(wg_name_list)).order_by('parent__acronym','acronym') - def get_meeting(num=None,type_in=['ietf',],days=28): meetings = Meeting.objects if type_in: @@ -799,15 +714,6 @@ def schedule_permissions(meeting, schedule, user): return cansee, canedit, secretariat -def session_constraint_expire(request,session): - from .ajax import session_constraints - path = reverse(session_constraints, args=[session.meeting.number, session.pk]) - temp_request = HttpRequest() - temp_request.path = path - temp_request.META['HTTP_HOST'] = request.META['HTTP_HOST'] - key = get_cache_key(temp_request) - if key is not None and key in cache: - cache.delete(key) # ------------------------------------------------- # Interim Meeting Helpers diff --git a/ietf/meeting/models.py b/ietf/meeting/models.py index ea394569e..7abe1b1c6 100644 --- a/ietf/meeting/models.py +++ b/ietf/meeting/models.py @@ -14,7 +14,6 @@ import string from collections import namedtuple from pathlib import Path -from urllib.parse import urljoin import debug # pyflakes:ignore @@ -23,9 +22,6 @@ from django.db import models from django.db.models import Max, Subquery, OuterRef, TextField, Value, Q from django.db.models.functions import Coalesce from django.conf import settings -# mostly used by json_dict() -#from django.template.defaultfilters import slugify, date as date_format, time as time_format -from django.template.defaultfilters import date as date_format from django.urls import reverse as urlreverse from django.utils.text import slugify from django.utils.safestring import mark_safe @@ -310,35 +306,9 @@ class Meeting(models.Model): slugs = ('conflict', 'conflic2', 'conflic3') return ConstraintName.objects.filter(slug__in=slugs) - def json_url(self): - return "/meeting/%s/json" % (self.number, ) - def base_url(self): return "/meeting/%s" % (self.number, ) - def json_dict(self, host_scheme): - # unfortunately, using the datetime aware json encoder seems impossible, - # so the dates are formatted as strings here. - agenda_url = "" - if self.schedule: - agenda_url = urljoin(host_scheme, self.schedule.base_url()) - return { - 'href': urljoin(host_scheme, self.json_url()), - 'name': self.number, - 'submission_start_date': fmt_date(self.get_submission_start_date()), - 'submission_cut_off_date': fmt_date(self.get_submission_cut_off_date()), - 'submission_correction_date': fmt_date(self.get_submission_correction_date()), - 'date': fmt_date(self.date), - 'agenda_href': agenda_url, - 'city': self.city, - 'country': self.country, - 'time_zone': self.time_zone, - 'venue_name': self.venue_name, - 'venue_addr': self.venue_addr, - 'break_area': self.break_area, - 'reg_area': self.reg_area - } - def build_timeslices(self): """Get unique day/time/timeslot data for meeting @@ -438,13 +408,6 @@ class ResourceAssociation(models.Model): def __str__(self): return self.desc - def json_dict(self, host_scheme): - res1 = dict() - res1['name'] = self.name.slug - res1['icon'] = "/images/%s" % (self.icon) - res1['desc'] = self.desc - res1['resource_id'] = self.pk - return res1 class Room(models.Model): meeting = ForeignKey(Meeting) @@ -487,15 +450,6 @@ class Room(models.Model): def dom_id(self): return "room%u" % (self.pk) - def json_url(self): - return "/meeting/%s/room/%s.json" % (self.meeting.number, self.id) - - def json_dict(self, host_scheme): - return { - 'href': urljoin(host_scheme, self.json_url()), - 'name': self.name, - 'capacity': self.capacity, - } # floorplan support def floorplan_url(self): mtg_num = self.meeting.get_number() @@ -700,29 +654,8 @@ class TimeSlot(models.Model): dom_id = self.location.dom_id() return "%s_%s_%s" % (dom_id, self.time.strftime('%Y-%m-%d'), self.time.strftime('%H%M')) - def json_dict(self, host_scheme): - ts = dict() - ts['timeslot_id'] = self.id - ts['href'] = urljoin(host_scheme, self.json_url()) - ts['room'] = self.get_location() - ts['roomtype'] = self.type.slug - if self.location is not None: - ts['capacity'] = self.location.capacity - ts["time"] = date_format(self.time, 'Hi') - ts["date"] = fmt_date(self.time) - ts["domid"] = self.js_identifier - following = self.slot_to_the_right - if following is not None: - ts["following_timeslot_id"] = following.id - return ts - - def json_url(self): - return "/meeting/%s/timeslot/%s.json" % (self.meeting.number, self.id) - - """ - This routine deletes all timeslots which are in the same time as this slot. - """ def delete_concurrent_timeslots(self): + """Delete all timeslots which are in the same time as this slot""" # can not include duration in filter, because there is no support # for having it a WHERE clause. # below will delete self as well. @@ -826,25 +759,6 @@ class Schedule(models.Model): def delete_assignments(self): self.assignments.all().delete() - def json_url(self): - return "%s.json" % self.base_url() - - def json_dict(self, host_scheme): - sch = dict() - sch['schedule_id'] = self.id - sch['href'] = urljoin(host_scheme, self.json_url()) - if self.visible: - sch['visible'] = "visible" - else: - sch['visible'] = "hidden" - if self.public: - sch['public'] = "public" - else: - sch['public'] = "private" - sch['owner'] = urljoin(host_scheme, self.owner.json_url()) - # should include href to list of assignments, but they have no direct API yet. - return sch - @property def qs_assignments_with_sessions(self): return self.assignments.filter(session__isnull=False) @@ -908,40 +822,6 @@ class SchedTimeSessAssignment(models.Model): """Get the TimeSlotTypeName that applies to this assignment""" return self.timeslot.type - def json_url(self): - if not hasattr(self, '_cached_json_url'): - self._cached_json_url = "/meeting/%s/agenda/%s/%s/session/%u.json" % ( - self.schedule.meeting.number, - self.schedule.owner_email(), - self.schedule.name, self.id ) - return self._cached_json_url - - def json_dict(self, host_scheme): - if not hasattr(self, '_cached_json_dict'): - ss = dict() - ss['assignment_id'] = self.id - ss['href'] = urljoin(host_scheme, self.json_url()) - ss['timeslot_id'] = self.timeslot.id - - efset = self.session.timeslotassignments.filter(schedule=self.schedule).order_by("timeslot__time") - if efset.count() > 1: - # now we know that there is some work to do finding the extendedfrom_id. - # loop through the list of items - previous = None - for efss in efset: - if efss.pk == self.pk: - extendedfrom = previous - break - previous = efss - if extendedfrom is not None: - ss['extendedfrom_id'] = extendedfrom.id - - if self.session: - ss['session_id'] = self.session.id - ss["pinned"] = self.pinned - self._cached_json_dict = ss - return self._cached_json_dict - def slug(self): """Return sensible id string for session, e.g. suitable for use as HTML anchor.""" components = [] @@ -1033,30 +913,6 @@ class Constraint(models.Model): elif not self.target and self.person: return "%s " % (self.person) - def json_url(self): - return "/meeting/%s/constraint/%s.json" % (self.meeting.number, self.id) - - def json_dict(self, host_scheme): - ct1 = dict() - ct1['constraint_id'] = self.id - ct1['href'] = urljoin(host_scheme, self.json_url()) - ct1['name'] = self.name.slug - if self.person is not None: - ct1['person_href'] = urljoin(host_scheme, self.person.json_url()) - if self.source is not None: - ct1['source_href'] = urljoin(host_scheme, self.source.json_url()) - if self.target is not None: - ct1['target_href'] = urljoin(host_scheme, self.target.json_url()) - ct1['meeting_href'] = urljoin(host_scheme, self.meeting.json_url()) - if self.time_relation: - ct1['time_relation'] = self.time_relation - ct1['time_relation_display'] = self.get_time_relation_display() - if self.timeranges.count(): - ct1['timeranges_cant_meet'] = [t.slug for t in self.timeranges.all()] - timeranges_str = ", ".join([t.desc for t in self.timeranges.all()]) - ct1['timeranges_display'] = "Can't meet %s" % timeranges_str - return ct1 - class SessionPresentation(models.Model): session = ForeignKey('Session') @@ -1367,92 +1223,10 @@ class Session(models.Model): def official_timeslotassignment(self): return self.timeslotassignments.filter(schedule__in=[self.meeting.schedule, self.meeting.schedule.base if self.meeting.schedule else None]).first() - def constraints_dict(self, host_scheme): - constraint_list = [] - for constraint in self.constraints(): - ct1 = constraint.json_dict(host_scheme) - constraint_list.append(ct1) - - for constraint in self.reverse_constraints(): - ct1 = constraint.json_dict(host_scheme) - constraint_list.append(ct1) - return constraint_list - @property def people_constraints(self): return self.group.constraint_source_set.filter(meeting=self.meeting, name='bethere') - def json_url(self): - return "/meeting/%s/session/%s.json" % (self.meeting.number, self.id) - - def json_dict(self, host_scheme): - sess1 = dict() - sess1['href'] = urljoin(host_scheme, self.json_url()) - if self.group is not None: - sess1['group'] = self.group.json_dict(host_scheme) - sess1['group_href'] = urljoin(host_scheme, self.group.json_url()) - if self.group.parent is not None: - sess1['area'] = self.group.parent.acronym.upper() - sess1['description'] = self.group.name - sess1['group_id'] = str(self.group.pk) - reslist = [] - for r in self.resources.all(): - reslist.append(r.json_dict(host_scheme)) - sess1['resources'] = reslist - sess1['session_id'] = str(self.pk) - sess1['name'] = self.name - sess1['title'] = self.short_name - sess1['short_name'] = self.short_name - sess1['bof'] = str(self.group.is_bof()) - sess1['agenda_note'] = self.agenda_note - sess1['attendees'] = str(self.attendees) - sess1['joint_with_groups'] = self.joint_with_groups_acronyms() - - # fish out scheduling information - eventually, we should pick - # this out in the caller instead - latest_event = None - first_event = None - - if self.pk is not None: - if not hasattr(self, 'current_status') or not hasattr(self, 'requested_time'): - events = list(SchedulingEvent.objects.filter(session=self.pk).order_by('time', 'id')) - if events: - first_event = events[0] - latest_event = events[-1] - - status_id = None - if hasattr(self, 'current_status'): - status_id = self.current_status - elif latest_event: - status_id = latest_event.status_id - - sess1['status'] = SessionStatusName.objects.get(slug=status_id).name if status_id else None - if self.comments is not None: - sess1['comments'] = self.comments - - requested_time = None - if hasattr(self, 'requested_time'): - requested_time = self.requested_time - elif first_event: - requested_time = first_event.time - sess1['requested_time'] = requested_time.strftime("%Y-%m-%d") if requested_time else None - - - requested_by = None - if hasattr(self, 'requested_by'): - requested_by = self.requested_by - elif first_event: - requested_by = first_event.by_id - - if requested_by is not None: - requested_by_person = Person.objects.filter(pk=requested_by).first() - if requested_by_person: - sess1['requested_by'] = str(requested_by_person) - - sess1['requested_duration']= "%.1f" % (float(self.requested_duration.seconds) / 3600) - sess1['special_request'] = str(self.special_request_token) - return sess1 - def agenda_text(self): doc = self.agenda() if doc: diff --git a/ietf/meeting/tests_api.py b/ietf/meeting/tests_api.py deleted file mode 100644 index c7d010dc5..000000000 --- a/ietf/meeting/tests_api.py +++ /dev/null @@ -1,501 +0,0 @@ -# Copyright The IETF Trust 2013-2020, All Rights Reserved -# -*- coding: utf-8 -*- - - -import datetime - -from urllib.parse import urlsplit - -from django.urls import reverse as urlreverse - -import debug # pyflakes:ignore - -from ietf.name.models import TimerangeName -from ietf.group.models import Group -from ietf.meeting.models import Schedule, TimeSlot, Session, SchedTimeSessAssignment, Meeting, Constraint -from ietf.meeting.test_data import make_meeting_test_data -from ietf.person.models import Person -from ietf.utils.test_utils import TestCase -from ietf.utils.mail import outbox - - -class ApiTests(TestCase): - def test_update_schedule(self): - meeting = make_meeting_test_data() - schedule = Schedule.objects.get(meeting__number=72,name="test-schedule") - mars_session = Session.objects.filter(meeting=meeting, group__acronym="mars").first() - ames_session = Session.objects.filter(meeting=meeting, group__acronym="ames").first() - - mars_scheduled = SchedTimeSessAssignment.objects.get(session=mars_session,schedule__name='test-schedule') - mars_slot = mars_scheduled.timeslot - - ames_scheduled = SchedTimeSessAssignment.objects.get(session=ames_session,schedule__name='test-schedule') - ames_slot = ames_scheduled.timeslot - - def do_unschedule(assignment): - url = urlreverse("ietf.meeting.ajax.assignment_json", - kwargs=dict(num=assignment.session.meeting.number, - owner=assignment.schedule.owner_email(), - name=assignment.schedule.name, - assignment_id=assignment.pk,)) - return self.client.delete(url) - - def do_schedule(schedule,session,timeslot): - url = urlreverse("ietf.meeting.ajax.assignments_json", - kwargs=dict(num=session.meeting.number, - owner=schedule.owner_email(), - name=schedule.name,)) - post_data = '{ "session_id": "%s", "timeslot_id": "%s" }'%(session.pk,timeslot.pk) - return self.client.post(url,post_data,content_type='application/x-www-form-urlencoded') - - def do_extend(schedule, assignment): - session = assignment.session - url = urlreverse("ietf.meeting.ajax.assignments_json", - kwargs=dict(num=session.meeting.number, - owner=schedule.owner_email(), - name=schedule.name,)) - post_data = '{ "session_id": "%s", "timeslot_id": "%s", "extendedfrom_id": "%s" }'%(session.pk,assignment.timeslot.slot_to_the_right.pk,assignment.pk) - - - return self.client.post(url,post_data,content_type='application/x-www-form-urlencoded') - - # not logged in - # faulty delete - r = do_unschedule(mars_scheduled) - self.assertEqual(r.status_code, 403) - self.assertEqual(SchedTimeSessAssignment.objects.get(pk=mars_scheduled.pk).session, mars_session) - # faulty post - r = do_schedule(schedule,ames_session,mars_slot) - self.assertEqual(r.status_code, 403) - - # logged in as non-owner - # faulty delete - self.client.login(username="ad", password="ad+password") - r = do_unschedule(mars_scheduled) - self.assertEqual(r.status_code, 403) - self.assertTrue("error" in r.json()) - # faulty post - r = do_schedule(schedule,ames_session,mars_slot) - self.assertEqual(r.status_code, 403) - - # Put ames in the same timeslot as mars - self.client.login(username="plain", password='plain+password') - r = do_unschedule(ames_scheduled) - self.assertEqual(r.status_code, 200) - self.assertNotIn("error", r.json()) - - r = do_schedule(schedule,ames_session,mars_slot) - self.assertEqual(r.status_code, 201) - - # Move the two timeslots close enough together for extension to work - ames_slot_qs=TimeSlot.objects.filter(id=ames_slot.id) - ames_slot_qs.update(time=mars_slot.time+mars_slot.duration+datetime.timedelta(minutes=10)) - - # Extend the mars session - r = do_extend(schedule,mars_scheduled) - self.assertEqual(r.status_code, 201) - self.assertTrue("error" not in r.json()) - self.assertEqual(mars_session.timeslotassignments.filter(schedule__name='test-schedule').count(),2) - - # Unschedule mars - r = do_unschedule(mars_scheduled) - self.assertEqual(r.status_code, 200) - self.assertNotIn("error", r.json()) - # Make sure it got both the original and extended session - self.assertEqual(mars_session.timeslotassignments.filter(schedule__name='test-schedule').count(),0) - - self.assertEqual(SchedTimeSessAssignment.objects.get(session=ames_session,schedule__name='test-schedule').timeslot, mars_slot) - - - def test_constraints_json(self): - meeting = make_meeting_test_data() - session = Session.objects.filter(meeting=meeting, group__acronym="mars").select_related("group").first() - c_ames = Constraint.objects.create(meeting=meeting, source=session.group, - target=Group.objects.get(acronym="ames"), - name_id="conflict") - - c_person = Constraint.objects.create(meeting=meeting, source=session.group, - person=Person.objects.get(user__username="ad"), - name_id="bethere") - - c_adjacent = Constraint.objects.create(meeting=meeting, source=session.group, - target=Group.objects.get(acronym="irg"), - name_id="wg_adjacent") - - c_time_relation = Constraint.objects.create(meeting=meeting, source=session.group, - time_relation='subsequent-days', - name_id="time_relation") - - c_timerange = Constraint.objects.create(meeting=meeting, source=session.group, - name_id="timerange") - c_timerange.timeranges.set(TimerangeName.objects.filter(slug__startswith='monday')) - - r = self.client.get(urlreverse("ietf.meeting.ajax.session_constraints", kwargs=dict(num=meeting.number, sessionid=session.pk))) - self.assertEqual(r.status_code, 200) - constraints = r.json() - expected_keys = set([c_ames.pk, c_person.pk, c_adjacent.pk, c_time_relation.pk, c_timerange.pk]) - self.assertEqual(expected_keys, set(c["constraint_id"] for c in constraints)) - - def test_meeting_json(self): - meeting = make_meeting_test_data() - - r = self.client.get(urlreverse("ietf.meeting.ajax.meeting_json", kwargs=dict(num=meeting.number))) - self.assertEqual(r.status_code, 200) - info = r.json() - self.assertEqual(info["name"], meeting.number) - - def test_get_room_json(self): - meeting = make_meeting_test_data() - room = meeting.room_set.first() - - r = self.client.get(urlreverse("ietf.meeting.ajax.timeslot_roomurl", kwargs=dict(num=meeting.number, roomid=room.pk))) - self.assertEqual(r.status_code, 200) - info = r.json() - self.assertEqual(info["name"], room.name) - - def test_create_new_room(self): - meeting = make_meeting_test_data() - timeslots_before = meeting.timeslot_set.filter(type='regular').count() - url = urlreverse("ietf.meeting.ajax.timeslot_roomsurl", kwargs=dict(num=meeting.number)) - - post_data = { "name": "new room", "capacity": "50" , "resources": [], "session_types":['regular']} - - # unauthorized post - r = self.client.post(url, post_data) - self.assertEqual(r.status_code, 302) - self.assertTrue(not meeting.room_set.filter(name="new room")) - - # create room - self.client.login(username="secretary", password="secretary+password") - r = self.client.post(url, post_data) - self.assertEqual(r.status_code, 302) - self.assertTrue(meeting.room_set.filter(name="new room")) - - timeslots_after = meeting.timeslot_set.filter(type='regular').count() - # It's not clear that what that ajax function is doing is the right thing to do, - # but it currently makes a new timeslot for any existing timeslot. - # The condition tested below relies on the timeslots before this test all having different start and end times - self.assertEqual( timeslots_after, 2 * timeslots_before) - - def test_delete_room(self): - meeting = make_meeting_test_data() - room = meeting.room_set.first() - timeslots_before = list(room.timeslot_set.values_list("pk", flat=True)) - - url = urlreverse("ietf.meeting.ajax.timeslot_roomurl", kwargs=dict(num=meeting.number, roomid=room.pk)) - - # unauthorized delete - r = self.client.delete(url) - self.assertEqual(r.status_code, 302) - self.assertTrue(meeting.room_set.filter(pk=room.pk)) - - # delete - self.client.login(username="secretary", password="secretary+password") - r = self.client.delete(url) - self.assertTrue(not meeting.room_set.filter(pk=room.pk)) - self.assertTrue(not TimeSlot.objects.filter(pk__in=timeslots_before)) - - # This really belongs in group tests - def test_group_json(self): - make_meeting_test_data() - group = Group.objects.get(acronym="mars") - - url = urlreverse("ietf.group.views.group_json", kwargs=dict(acronym=group.acronym)) - r = self.client.get(url) - self.assertEqual(r.status_code, 200) - info = r.json() - self.assertEqual(info["name"], group.name) - - # This really belongs in person tests - def test_person_json(self): - make_meeting_test_data() - person = Person.objects.get(user__username="ad") - - url = urlreverse("ietf.person.ajax.person_json", kwargs=dict(personid=person.pk)) - r = self.client.get(url) - self.assertEqual(r.status_code, 200) - info = r.json() - self.assertEqual(info["name"], person.name) - - def test_sessions_json(self): - meeting = make_meeting_test_data() - - url = urlreverse("ietf.meeting.ajax.sessions_json",kwargs=dict(num=meeting.number)) - r = self.client.get(url) - self.assertEqual(r.status_code, 200) - info = r.json() - self.assertEqual(set([x['short_name'] for x in info]),set([s.session.short_name for s in meeting.schedule.assignments.filter(session__type_id='regular')])) - - schedule = meeting.schedule - url = urlreverse("ietf.meeting.ajax.assignments_json", - kwargs=dict(num=meeting.number,owner=schedule.owner_email(),name=schedule.name)) - r = self.client.get(url) - self.assertEqual(r.status_code, 200) - info = r.json() - self.assertEqual(len(info),schedule.assignments.count()) - - - def test_slot_json(self): - meeting = make_meeting_test_data() - slot = meeting.timeslot_set.all()[0] - - url = urlreverse("ietf.meeting.ajax.timeslot_sloturl", - kwargs=dict(num=meeting.number, slotid=slot.pk)) - r = self.client.get(url) - self.assertEqual(r.status_code, 200) - info = r.json() - self.assertEqual(info["timeslot_id"], slot.pk) - - def test_create_new_slot(self): - meeting = make_meeting_test_data() - - slot_time = datetime.date.today() - - url = urlreverse("ietf.meeting.ajax.timeslot_slotsurl", - kwargs=dict(num=meeting.number)) - post_data = { - 'type' : 'plenary', - 'time' : slot_time.strftime("%Y-%m-%d"), - 'duration': '08:00:00', - } - - # unauthorized post - prior_slotcount = meeting.timeslot_set.count() - self.client.login(username="ad", password="ad+password") - r = self.client.post(url, post_data) - self.assertEqual(r.status_code, 403) - self.assertEqual(meeting.timeslot_set.count(),prior_slotcount) - - # create slot - self.client.login(username="secretary", password="secretary+password") - r = self.client.post(url, post_data) - self.assertEqual(r.status_code, 201) - self.assertTrue(meeting.timeslot_set.filter(time=slot_time)) - self.assertEqual(meeting.timeslot_set.count(),prior_slotcount+1) - - def test_delete_slot(self): - meeting = make_meeting_test_data() - slot = meeting.timeslot_set.all()[0] - - url = urlreverse("ietf.meeting.ajax.timeslot_sloturl", - kwargs=dict(num=meeting.number, slotid=slot.pk)) - - # unauthorized delete - self.client.login(username="ad", password="ad+password") - r = self.client.delete(url) - self.assertEqual(r.status_code, 403) - - # delete - self.client.login(username="secretary", password="secretary+password") - self.client.delete(url) - self.assertTrue(not meeting.timeslot_set.filter(pk=slot.pk)) - - def test_schedule_json(self): - meeting = make_meeting_test_data() - - url = urlreverse("ietf.meeting.ajax.schedule_infourl", - kwargs=dict(num=meeting.number, - owner=meeting.schedule.owner_email(), - name=meeting.schedule.name)) - - r = self.client.get(url) - info = r.json() - self.assertEqual(info["schedule_id"], meeting.schedule.pk) - - def test_create_new_schedule(self): - meeting = make_meeting_test_data() - - url = urlreverse("ietf.meeting.ajax.schedule_infosurl", - kwargs=dict(num=meeting.number)) - post_data = { - 'name': 'new-schedule', - } - - # unauthorized post - self.client.login(username="plain", password="plain+password") - r = self.client.post(url, post_data) - self.assertEqual(r.status_code, 403) - self.assertTrue(not meeting.schedule_set.filter(name='new-schedule')) - - # create new schedule - self.client.login(username="secretary", password="secretary+password") - r = self.client.post(url, post_data) - self.assertEqual(r.status_code, 302) - self.assertTrue(meeting.schedule_set.filter(name='new-schedule')) - - def test_update_meeting_schedule(self): - meeting = make_meeting_test_data() - - self.assertTrue(meeting.schedule.visible) - - url = urlreverse("ietf.meeting.ajax.schedule_infourl", - kwargs=dict(num=meeting.number, - owner=meeting.schedule.owner_email(), - name=meeting.schedule.name)) - - post_data = { - 'visible': 'false', - 'name': 'new-test-name', - } - - # unauthorized posts - self.client.logout() - r = self.client.post(url, post_data) - self.assertEqual(r.status_code, 403) - self.client.login(username="ad", password="ad+password") - r = self.client.post(url, post_data) - self.assertEqual(r.status_code, 403) - - # change schedule - self.client.login(username="secretary", password="secretary+password") - r = self.client.post(url, post_data) - self.assertEqual(r.status_code, 302) - changed_schedule = Schedule.objects.get(pk=meeting.schedule.pk) - self.assertTrue(not changed_schedule.visible) - self.assertEqual(changed_schedule.name, "new-test-name") - - def test_delete_schedule(self): - meeting = make_meeting_test_data() - - url = urlreverse("ietf.meeting.ajax.schedule_infourl", - kwargs=dict(num=meeting.number, - owner=meeting.schedule.owner_email(), - name=meeting.schedule.name)) - # unauthorized delete - self.client.login(username="plain", password="plain+password") - r = self.client.delete(url) - self.assertEqual(r.status_code, 403) - - # delete - self.client.login(username="secretary", password="secretary+password") - r = self.client.delete(url) - self.assertEqual(r.status_code, 200) - self.assertTrue(not Schedule.objects.filter(pk=meeting.schedule.pk)) - - def test_set_meeting_schedule(self): - meeting = make_meeting_test_data() - schedule = meeting.schedule - - url = urlreverse("ietf.meeting.ajax.meeting_json", - kwargs=dict(num=meeting.number)) - post_data = { - "schedule": "", - } - # unauthorized post - self.client.login(username="ad", password="ad+password") - r = self.client.post(url, post_data) - self.assertEqual(r.status_code, 403) - - # clear - self.client.login(username="secretary", password="secretary+password") - r = self.client.post(url, post_data) - self.assertEqual(r.status_code, 200) - self.assertTrue(not Meeting.objects.get(pk=meeting.pk).schedule) - - # set schedule - first fail with non-public - post_data = { - "schedule": schedule.name, - } - schedule.public = False - schedule.save() - - r = self.client.post(url, post_data) - self.assertTrue(r.status_code != 200) - self.assertTrue(not Meeting.objects.get(pk=meeting.pk).schedule) - - # then go through with public - schedule.public = True - schedule.save() - - # Setting a meeting as official no longer sends mail immediately - prior_length= len(outbox) - r = self.client.post(url, post_data) - self.assertEqual(r.status_code, 200) - self.assertEqual(Meeting.objects.get(pk=meeting.pk).schedule, schedule) - self.assertEqual(len(outbox),prior_length) - - def test_read_only(self): - meeting = make_meeting_test_data() - - # Secretariat - self.client.login(username="secretary", password="secretary+password") - url = '/meeting/%s/agenda/%s/%s/permissions' % (meeting.number, meeting.schedule.owner.email_address(), meeting.schedule.name); - r = self.client.get(url) - self.assertEqual(r.status_code, 200) - - info = r.json() - self.assertEqual(info['secretariat'], True) - self.assertEqual(urlsplit(info['owner_href'])[2], "/person/%s.json" % meeting.schedule.owner_id) - self.assertEqual(info['read_only'], True) - self.assertEqual(info['save_perm'], True) - - # owner - self.client.login(username=meeting.schedule.owner.user.username, - password=meeting.schedule.owner.user.username+"+password") - url = '/meeting/%s/agenda/%s/%s/permissions' % (meeting.number, meeting.schedule.owner.email_address(), meeting.schedule.name); - r = self.client.get(url) - self.assertEqual(r.status_code, 200) - - info = r.json() - self.assertEqual(info['secretariat'], False) - self.assertEqual(info['read_only'], False) - self.assertEqual(info['save_perm'], False) - - def test_update_timeslot_pinned(self): - meeting = make_meeting_test_data() - scheduled = SchedTimeSessAssignment.objects.filter( - session__meeting=meeting, session__group__acronym="mars").first() - - url = '/meeting/%s/agenda/%s/%s/session/%u.json' % (meeting.number, meeting.schedule.owner_email(), meeting.schedule.name, scheduled.pk) - - post_data = { - "pinned": True - } - - # unauthorized post gets failure (no redirect) - r = self.client.put(url, post_data) - self.assertEqual(r.status_code, 403, - "post to %s should have failed, no permission, got: %u/%s" % - (url, r.status_code, r.content)) - self.assertTrue(not SchedTimeSessAssignment.objects.get(pk=scheduled.pk).pinned) - - # set pinned - meeting.schedule.owner = Person.objects.get(user__username="secretary") - meeting.schedule.save() - - # need to rebuild URL, since the schedule owner has changed. - url = '/meeting/%s/agenda/%s/%s/session/%u.json' % (meeting.number, meeting.schedule.owner_email(), meeting.schedule.name, scheduled.pk) - - self.client.login(username="secretary", password="secretary+password") - r = self.client.put(url, post_data) - self.assertEqual(r.status_code, 200, - "post to %s should have worked, but got: %u/%s" % - (url, r.status_code, r.content)) - self.assertTrue(SchedTimeSessAssignment.objects.get(pk=scheduled.pk).pinned) - -class TimeSlotEditingApiTests(TestCase): - - def test_manipulate_timeslot(self): - meeting = make_meeting_test_data() - slot = meeting.timeslot_set.filter(type_id='regular')[0] - - url = urlreverse("ietf.meeting.ajax.timeslot_sloturl", - kwargs=dict(num=meeting.number, slotid=slot.pk)) - - modify_post_data = { - "purpose" : "plenary" - } - - # Fail as non-secretariat - self.client.login(username="plain", password="plain+password") - r = self.client.post(url, modify_post_data) - self.assertEqual(r.status_code, 403) - slot.refresh_from_db() - self.assertEqual(slot.type_id, 'regular') - - # Successful change of purpose - self.client.login(username="secretary", password="secretary+password") - r = self.client.post(url, modify_post_data) - self.assertEqual(r.status_code, 200) - slot.refresh_from_db() - self.assertEqual(slot.type_id, 'plenary') diff --git a/ietf/meeting/tests_js.py b/ietf/meeting/tests_js.py index 14dfaf5f0..7d2cc2d0a 100644 --- a/ietf/meeting/tests_js.py +++ b/ietf/meeting/tests_js.py @@ -854,7 +854,7 @@ class ScheduleEditTests(IetfSeleniumTestCase): ss = list(SchedTimeSessAssignment.objects.filter(session__meeting__number=72,session__group__acronym='mars',schedule__name='test-schedule')) # pyflakes:ignore self.login() - url = self.absreverse('ietf.meeting.views.edit_schedule',kwargs=dict(num='72',name='test-schedule',owner='plain@example.com')) + url = self.absreverse('ietf.meeting.views.edit_meeting_schedule',kwargs=dict(num='72',name='test-schedule',owner='plain@example.com')) self.driver.get(url) # driver.get() will wait for scripts to finish, but not ajax @@ -2770,5 +2770,5 @@ class EditTimeslotsTests(IetfSeleniumTestCase): # make_meeting_test_data() # # def testOpenSchedule(self): -# url = urlreverse('ietf.meeting.views.edit_schedule', kwargs=dict(num='72',name='test-schedule')) +# url = urlreverse('ietf.meeting.views.edit_meeting_schedule', kwargs=dict(num='72',name='test-schedule')) # r = self.client.get(url) diff --git a/ietf/meeting/tests_views.py b/ietf/meeting/tests_views.py index cae8d2a39..7dc1010c5 100644 --- a/ietf/meeting/tests_views.py +++ b/ietf/meeting/tests_views.py @@ -3053,6 +3053,7 @@ class ReorderSlidesTests(TestCase): class EditTests(TestCase): + """Test schedule edit operations""" def setUp(self): # make sure we have the colors of the area from ietf.group.colors import fg_group_colors, bg_group_colors @@ -3060,17 +3061,6 @@ class EditTests(TestCase): fg_group_colors[area_upper] = "#333" bg_group_colors[area_upper] = "#aaa" - def test_edit_schedule(self): - meeting = make_meeting_test_data() - - self.client.login(username="secretary", password="secretary+password") - r = self.client.get(urlreverse("ietf.meeting.views.edit_schedule", kwargs={'num': meeting.number})) - self.assertRedirects( - r, - urlreverse("ietf.meeting.views.edit_meeting_schedule", kwargs={'num': meeting.number}), - status_code=301, - ) - def test_official_record_schedule_is_read_only(self): def _set_date_offset_and_retrieve_page(meeting, days_offset, client): meeting.date = datetime.date.today() + datetime.timedelta(days=days_offset) @@ -3598,8 +3588,8 @@ class EditTests(TestCase): self.assertEqual(tostring(s2_constraints[1][0]), conf_label) # [0][0] is the innermost def test_new_meeting_schedule(self): + """Can create a meeting schedule from scratch""" meeting = make_meeting_test_data() - self.client.login(username="secretary", password="secretary+password") # new from scratch @@ -3623,7 +3613,11 @@ class EditTests(TestCase): self.assertEqual(new_schedule.origin, None) self.assertEqual(new_schedule.base_id, meeting.schedule.base_id) - # copy + def test_copy_meeting_schedule(self): + """Can create a copy of an existing meeting schedule""" + meeting = make_meeting_test_data() + self.client.login(username="secretary", password="secretary+password") + url = urlreverse("ietf.meeting.views.new_meeting_schedule", kwargs=dict(num=meeting.number, owner=meeting.schedule.owner_email(), name=meeting.schedule.name)) r = self.client.get(url) self.assertEqual(r.status_code, 200) @@ -3647,34 +3641,21 @@ class EditTests(TestCase): for a in SchedTimeSessAssignment.objects.filter(schedule=new_schedule): self.assertIn((a.session_id, a.timeslot_id), old_assignments) - def test_save_agenda_as_and_read_permissions(self): + def test_schedule_read_permissions(self): meeting = make_meeting_test_data() + schedule = meeting.schedule # try to get non-existing agenda - url = urlreverse("ietf.meeting.views.edit_schedule", kwargs=dict(num=meeting.number, - owner=meeting.schedule.owner_email(), - name="foo")) + url = urlreverse("ietf.meeting.views.edit_meeting_schedule", kwargs=dict(num=meeting.number, + owner=schedule.owner_email(), + name="foo")) r = self.client.get(url) self.assertEqual(r.status_code, 404) - # save as new name (requires valid existing agenda) - url = urlreverse("ietf.meeting.views.edit_schedule", kwargs=dict(num=meeting.number, - owner=meeting.schedule.owner_email(), - name=meeting.schedule.name)) - self.client.login(username="ad", password="ad+password") - r = self.client.post(url, { - 'savename': "foo", - 'saveas': "saveas", - }) - self.assertEqual(r.status_code, 302) - # Verify that we actually got redirected to a new place. - self.assertNotEqual(urlparse(r.url).path, url) - - # get - schedule = meeting.get_schedule_by_name("foo") - url = urlreverse("ietf.meeting.views.edit_schedule", kwargs=dict(num=meeting.number, - owner=schedule.owner_email(), - name="foo")) + url = urlreverse("ietf.meeting.views.edit_meeting_schedule", kwargs=dict(num=meeting.number, + owner=schedule.owner_email(), + name=schedule.name)) + self.client.login(username='ad', password='ad+password') r = self.client.get(url) self.assertEqual(r.status_code, 200) @@ -3701,39 +3682,73 @@ class EditTests(TestCase): r = self.client.get(url) self.assertEqual(r.status_code, 200) - def test_save_agenda_broken_names(self): + def test_new_meeting_schedule_rejects_invalid_names(self): meeting = make_meeting_test_data() - # save as new name (requires valid existing agenda) - url = urlreverse("ietf.meeting.views.edit_schedule", kwargs=dict(num=meeting.number, - owner=meeting.schedule.owner_email(), - name=meeting.schedule.name)) + orig_schedule_count = meeting.schedule_set.count() self.client.login(username="ad", password="ad+password") + url = urlreverse("ietf.meeting.views.new_meeting_schedule", kwargs=dict(num=meeting.number)) r = self.client.post(url, { - 'savename': "/no/this/should/not/work/it/is/too/long", - 'saveas': "saveas", - }) - self.assertEqual(r.status_code, 302) - self.assertEqual(urlparse(r.url).path, url) - # TODO: Verify that an error message was in fact returned. + 'name': "/no/this/should/not/work/it/is/too/long", + 'public': "on", + 'notes': "Name too long", + 'base': meeting.schedule.base_id, + }) + self.assertEqual(r.status_code, 200) + self.assertFormError(r, 'form', 'name', 'Enter a valid value.') + self.assertEqual(meeting.schedule_set.count(), orig_schedule_count, 'Schedule should not be created') r = self.client.post(url, { - 'savename': "/invalid/chars/", - 'saveas': "saveas", - }) - # TODO: Verify that an error message was in fact returned. - self.assertEqual(r.status_code, 302) - self.assertEqual(urlparse(r.url).path, url) + 'name': "/invalid/chars/", + 'public': "on", + 'notes': "Name too long", + 'base': meeting.schedule.base_id, + }) + self.assertEqual(r.status_code, 200) + self.assertFormError(r, 'form', 'name', 'Enter a valid value.') + self.assertEqual(meeting.schedule_set.count(), orig_schedule_count, 'Schedule should not be created') # Non-ASCII alphanumeric characters r = self.client.post(url, { - 'savename': "f\u00E9ling", - 'saveas': "saveas", - }) - # TODO: Verify that an error message was in fact returned. - self.assertEqual(r.status_code, 302) - self.assertEqual(urlparse(r.url).path, url) - + 'name': "f\u00E9ling", + 'public': "on", + 'notes': "Name too long", + 'base': meeting.schedule.base_id, + }) + self.assertEqual(r.status_code, 200) + self.assertFormError(r, 'form', 'name', 'Enter a valid value.') + self.assertEqual(meeting.schedule_set.count(), orig_schedule_count, 'Schedule should not be created') + + def test_edit_session(self): + session = SessionFactory(group__type_id='team') # type determines allowed session purposes + self.client.login(username='secretary', password='secretary+password') + url = urlreverse('ietf.meeting.views.edit_session', kwargs={'session_id': session.pk}) + r = self.client.get(url) + self.assertContains(r, 'Edit session', status_code=200) + r = self.client.post(url, { + 'name': 'this is a name', + 'short': 'tian', + 'purpose': 'coding', + 'type': 'other', + 'requested_duration': '3600', + 'on_agenda': True, + 'remote_instructions': 'Do this do that', + 'attendees': '103', + 'comments': 'So much to say', + }) + self.assertNoFormPostErrors(r) + self.assertRedirects(r, urlreverse('ietf.meeting.views.edit_meeting_schedule', + kwargs={'num': session.meeting.number})) + session = Session.objects.get(pk=session.pk) # refresh objects from DB + self.assertEqual(session.name, 'this is a name') + self.assertEqual(session.short, 'tian') + self.assertEqual(session.purpose_id, 'coding') + self.assertEqual(session.type_id, 'other') + self.assertEqual(session.requested_duration, datetime.timedelta(hours=1)) + self.assertEqual(session.on_agenda, True) + self.assertEqual(session.remote_instructions, 'Do this do that') + self.assertEqual(session.attendees, 103) + self.assertEqual(session.comments, 'So much to say') def test_edit_timeslots(self): meeting = make_meeting_test_data() diff --git a/ietf/meeting/urls.py b/ietf/meeting/urls.py index 7e42e1940..a051bd953 100644 --- a/ietf/meeting/urls.py +++ b/ietf/meeting/urls.py @@ -4,7 +4,7 @@ from django.conf.urls import include from django.views.generic import RedirectView from django.conf import settings -from ietf.meeting import views, ajax, views_proceedings +from ietf.meeting import views, views_proceedings from ietf.utils.urls import url safe_for_all_meeting_types = [ @@ -26,10 +26,7 @@ safe_for_all_meeting_types = [ type_ietf_only_patterns = [ - url(r'^agenda/%(owner)s/%(schedule_name)s/edit$' % settings.URL_REGEXPS, - RedirectView.as_view(pattern_name='ietf.meeting.views.edit_meeting_schedule', permanent=True), - name='ietf.meeting.views.edit_schedule'), - url(r'^agenda/%(owner)s/%(schedule_name)s/edit/$' % settings.URL_REGEXPS, views.edit_meeting_schedule), + url(r'^agenda/%(owner)s/%(schedule_name)s/edit/?$' % settings.URL_REGEXPS, views.edit_meeting_schedule), url(r'^agenda/%(owner)s/%(schedule_name)s/timeslots/$' % settings.URL_REGEXPS, views.edit_meeting_timeslots_and_misc_sessions), url(r'^agenda/%(owner)s/%(schedule_name)s/details$' % settings.URL_REGEXPS, views.edit_schedule_properties), url(r'^agenda/%(owner)s/%(schedule_name)s/delete$' % settings.URL_REGEXPS, views.delete_schedule), @@ -40,10 +37,6 @@ type_ietf_only_patterns = [ url(r'^agenda/%(owner)s/%(schedule_name)s/by-room/?$' % settings.URL_REGEXPS, views.agenda_by_room), url(r'^agenda/%(owner)s/%(schedule_name)s/by-type/?$' % settings.URL_REGEXPS, views.agenda_by_type), url(r'^agenda/%(owner)s/%(schedule_name)s/by-type/(?P[a-z]+)$' % settings.URL_REGEXPS, views.agenda_by_type), - url(r'^agenda/%(owner)s/%(schedule_name)s/permissions$' % settings.URL_REGEXPS, ajax.schedule_permission_api), - url(r'^agenda/%(owner)s/%(schedule_name)s/session/(?P\d+).json$' % settings.URL_REGEXPS, ajax.assignment_json), - url(r'^agenda/%(owner)s/%(schedule_name)s/sessions.json$' % settings.URL_REGEXPS, ajax.assignments_json), - url(r'^agenda/%(owner)s/%(schedule_name)s.json$' % settings.URL_REGEXPS, ajax.schedule_infourl), url(r'^agenda/%(owner)s/%(schedule_name)s/new/$' % settings.URL_REGEXPS, views.new_meeting_schedule), url(r'^agenda/by-room$', views.agenda_by_room), url(r'^agenda/by-type$', views.agenda_by_type), @@ -58,20 +51,8 @@ type_ietf_only_patterns = [ url(r'^timeslot/new$', views.create_timeslot), url(r'^timeslot/(?P\d+)/edit$', views.edit_timeslot), url(r'^timeslot/(?P\d+)/edittype$', views.edit_timeslot_type), - url(r'^rooms$', ajax.timeslot_roomsurl), - url(r'^room/(?P\d+).json$', ajax.timeslot_roomurl), - url(r'^timeslots$', ajax.timeslot_slotsurl), - url(r'^timeslots.json$', ajax.timeslot_slotsurl), - url(r'^timeslot/(?P\d+).json$', ajax.timeslot_sloturl), - url(r'^agendas$', ajax.schedule_infosurl), - url(r'^agendas.json$', ajax.schedule_infosurl), url(r'^agenda/(?P[-a-z0-9]+)-drafts.pdf$', views.session_draft_pdf), url(r'^agenda/(?P[-a-z0-9]+)-drafts.tgz$', views.session_draft_tarfile), - url(r'^sessions\.json$', ajax.sessions_json), - url(r'^session/(?P\d+).json', ajax.session_json), - url(r'^session/(?P\d+)/constraints.json', ajax.session_constraints), - url(r'^constraint/(?P\d+).json', ajax.constraint_json), - url(r'^json$', ajax.meeting_json), ] # This is a limited subset of the list above -- many of the views above won't work for interim meetings @@ -88,7 +69,7 @@ type_ietf_only_patterns_id_optional = [ url(r'^agenda(?P.csv)$', views.agenda), url(r'^agenda/edit$', RedirectView.as_view(pattern_name='ietf.meeting.views.edit_meeting_schedule', permanent=True), - name='ietf.meeting.views.edit_schedule'), + name='ietf.meeting.views.edit_meeting_schedule'), url(r'^agenda/edit/$', views.edit_meeting_schedule), url(r'^requests$', views.meeting_requests), url(r'^agenda/agenda\.ics$', views.agenda_ical), diff --git a/ietf/meeting/views.py b/ietf/meeting/views.py index fcadf6bfb..72161a16b 100644 --- a/ietf/meeting/views.py +++ b/ietf/meeting/views.py @@ -59,11 +59,7 @@ from ietf.meeting.models import Meeting, Session, Schedule, FloorPlan, SessionPr from ietf.meeting.models import SessionStatusName, SchedulingEvent, SchedTimeSessAssignment, Room, TimeSlotTypeName from ietf.meeting.forms import ( CustomDurationField, SwapDaysForm, SwapTimeslotsForm, TimeSlotCreateForm, TimeSlotEditForm, SessionEditForm ) -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 -from ietf.meeting.helpers import get_modified_from_assignments -from ietf.meeting.helpers import get_wg_list, find_ads_for_meeting +from ietf.meeting.helpers import get_person_by_email, get_schedule_by_name from ietf.meeting.helpers import get_meeting, get_ietf_meeting, get_current_ietf_meeting_num from ietf.meeting.helpers import get_schedule, schedule_permissions from ietf.meeting.helpers import preprocess_assignments_for_agenda, read_agenda_file @@ -280,86 +276,6 @@ def materials_editable_groups(request, num=None): return render(request, "meeting/materials_editable_groups.html", { 'meeting_num': meeting.number}) -def ascii_alphanumeric(string): - return re.match(r'^[a-zA-Z0-9]*$', string) - -class SaveAsForm(forms.Form): - savename = forms.CharField(max_length=16) - -@role_required('Area Director','Secretariat') -def schedule_create(request, num=None, owner=None, name=None): - meeting = get_meeting(num) - person = get_person_by_email(owner) - schedule = get_schedule_by_name(meeting, person, name) - - if schedule is None: - # here we have to return some ajax to display an error. - messages.error("Error: No meeting information for meeting %s owner %s schedule %s available" % (num, owner, name)) # pylint: disable=no-value-for-parameter - return redirect(edit_schedule, num=num, owner=owner, name=name) - - # authorization was enforced by the @group_require decorator above. - - saveasform = SaveAsForm(request.POST) - if not saveasform.is_valid(): - messages.info(request, "This name is not valid. Please choose another one.") - return redirect(edit_schedule, num=num, owner=owner, name=name) - - savedname = saveasform.cleaned_data['savename'] - - if not ascii_alphanumeric(savedname): - messages.info(request, "This name contains illegal characters. Please choose another one.") - return redirect(edit_schedule, num=num, owner=owner, name=name) - - # create the new schedule, and copy the assignments - try: - sched = meeting.schedule_set.get(name=savedname, owner=request.user.person) - if sched: - return redirect(edit_schedule, num=meeting.number, owner=sched.owner_email(), name=sched.name) - else: - messages.info(request, "Schedule creation failed. Please try again.") - return redirect(edit_schedule, num=num, owner=owner, name=name) - - except Schedule.DoesNotExist: - pass - - # must be done - newschedule = Schedule(name=savedname, - owner=request.user.person, - meeting=meeting, - base=schedule.base, - origin=schedule, - visible=False, - public=False) - - newschedule.save() - if newschedule is None: - return HttpResponse(status=500) - - # keep a mapping so that extendedfrom references can be chased. - mapping = {}; - for ss in schedule.assignments.all(): - # hack to copy the object, creating a new one - # just reset the key, and save it again. - oldid = ss.pk - ss.pk = None - ss.schedule=newschedule - ss.save() - mapping[oldid] = ss.pk - #print "Copying %u to %u" % (oldid, ss.pk) - - # now fix up any extendedfrom references to new set. - for ss in newschedule.assignments.all(): - if ss.extendedfrom is not None: - oldid = ss.extendedfrom.id - newid = mapping[oldid] - #print "Fixing %u to %u" % (oldid, newid) - ss.extendedfrom = newschedule.assignments.get(pk = newid) - ss.save() - - - # now redirect to this new schedule. - return redirect(edit_schedule, meeting.number, newschedule.owner_email(), newschedule.name) - @role_required('Secretariat') def edit_timeslots(request, num=None): @@ -1397,80 +1313,6 @@ def edit_meeting_timeslots_and_misc_sessions(request, num=None, owner=None, name }) -############################################################################## -#@role_required('Area Director','Secretariat') -# disable the above security for now, check it below. -@ensure_csrf_cookie -def edit_schedule(request, num=None, owner=None, name=None): - - if request.method == 'POST': - return schedule_create(request, num, owner, name) - - user = request.user - meeting = get_meeting(num) - person = get_person_by_email(owner) - if name is None: - schedule = meeting.schedule - else: - schedule = get_schedule_by_name(meeting, person, name) - if schedule is None: - raise Http404("No meeting information for meeting %s owner %s schedule %s available" % (num, owner, name)) - - 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.filter(session_types__slug='regular').distinct().order_by("capacity") - saveas = SaveAsForm() - saveasurl=reverse(edit_schedule, - args=[meeting.number, schedule.owner_email(), schedule.name]) - - can_see, can_edit,secretariat = schedule_permissions(meeting, schedule, user) - - if not can_see: - return render(request, "meeting/private_schedule.html", - {"schedule":schedule, - "meeting": meeting, - "meeting_base_url":meeting_base_url, - "hide_menu": True - }, status=403, content_type="text/html") - - assignments = get_all_assignments_from_schedule(schedule) - - # get_modified_from needs the query set, not the list - modified = get_modified_from_assignments(assignments) - - area_list = get_areas() - wg_name_list = get_wg_name_list(assignments) - wg_list = get_wg_list(wg_name_list) - ads = find_ads_for_meeting(meeting) - for ad in ads: - # set the default to avoid needing extra arguments in templates - # django 1.3+ - ad.default_hostscheme = site_base_url - - time_slices,date_slices = build_all_agenda_slices(meeting) - - return render(request, "meeting/landscape_edit.html", - {"schedule":schedule, - "saveas": saveas, - "saveasurl": saveasurl, - "meeting_base_url": meeting_base_url, - "site_base_url": site_base_url, - "rooms":rooms, - "time_slices":time_slices, - "date_slices":date_slices, - "modified": modified, - "meeting":meeting, - "area_list": area_list, - "area_directors" : ads, - "wg_list": wg_list , - "assignments": assignments, - "show_inline": set(["txt","htm","html"]), - "hide_menu": True, - "can_edit_properties": can_edit or secretariat, - }) - - class SchedulePropertiesForm(forms.ModelForm): class Meta: model = Schedule @@ -1504,7 +1346,7 @@ def edit_schedule_properties(request, num, owner, name): form.save() if request.GET.get('next'): return HttpResponseRedirect(request.GET.get('next')) - return redirect('ietf.meeting.views.edit_schedule', num=num, owner=owner, name=name) + return redirect('ietf.meeting.views.edit_meeting_schedule', num=num, owner=owner, name=name) else: form = SchedulePropertiesForm(meeting, instance=schedule) diff --git a/ietf/person/ajax.py b/ietf/person/ajax.py index ee4cbb0ea..e8a4f1699 100644 --- a/ietf/person/ajax.py +++ b/ietf/person/ajax.py @@ -6,14 +6,6 @@ from django.http import HttpResponse from ietf.ietfauth.utils import role_required from ietf.person.models import Person -def person_json(request, personid): - person = get_object_or_404(Person, pk=personid) - - return HttpResponse(json.dumps(person.json_dict(request.build_absolute_uri("/")), - sort_keys=True, indent=2), - content_type="application/json") - - @role_required('Secretariat') def person_email_json(request, personid): person = get_object_or_404(Person, pk=personid) diff --git a/ietf/person/models.py b/ietf/person/models.py index 47eaeb659..381fee968 100644 --- a/ietf/person/models.py +++ b/ietf/person/models.py @@ -228,18 +228,6 @@ class Person(models.Model): def defurl(self): return urljoin(self.default_hostscheme,self.json_url()) - def json_url(self): - return "/person/%s.json" % (self.id, ) - - # return info about the person - def json_dict(self, hostscheme): - ct1 = dict() - ct1['person_id'] = self.id - ct1['href'] = urljoin(hostscheme, self.json_url()) - ct1['name'] = self.name - ct1['ascii'] = self.ascii - return ct1 - def available_api_endpoints(self): from ietf.ietfauth.utils import has_role return list(set([ (v, n) for (v, n, r) in PERSON_API_KEY_VALUES if r==None or has_role(self.user, r) ])) diff --git a/ietf/person/urls.py b/ietf/person/urls.py index ffcda989d..5c894922f 100644 --- a/ietf/person/urls.py +++ b/ietf/person/urls.py @@ -4,7 +4,6 @@ from ietf.utils.urls import url urlpatterns = [ url(r'^merge/$', views.merge), url(r'^search/(?P(person|email))/$', views.ajax_select2_search), - url(r'^(?P[a-z0-9]+).json$', ajax.person_json), url(r'^(?P[a-z0-9]+)/email.json$', ajax.person_email_json), url(r'^(?P[^/]+)$', views.profile), url(r'^(?P[^/]+)/photo/?$', views.photo), diff --git a/ietf/secr/sreq/views.py b/ietf/secr/sreq/views.py index d5034eb25..7443e28f8 100644 --- a/ietf/secr/sreq/views.py +++ b/ietf/secr/sreq/views.py @@ -539,10 +539,6 @@ def edit(request, acronym, num=None): # send notification send_notification(group,meeting,login,form.cleaned_data,'update') - # nuke any cache that might be lingering around. - from ietf.meeting.helpers import session_constraint_expire - session_constraint_expire(request,session) - messages.success(request, 'Session Request updated') return redirect('ietf.secr.sreq.views.view', acronym=acronym) diff --git a/ietf/static/ietf/js/agenda/agenda_edit.js b/ietf/static/ietf/js/agenda/agenda_edit.js deleted file mode 100644 index 81bc58692..000000000 --- a/ietf/static/ietf/js/agenda/agenda_edit.js +++ /dev/null @@ -1,184 +0,0 @@ -/* -* -* FILE: agenda_edit.js -* Copyright (c) 2013, The IETF Trust. See ../../../LICENSE. -* -* www.credil.org: Project Orlando 2013 -* Author: Justin Hornosty ( justin@credil.org ) -* Michael Richardson -* -* Description: -* This is the main file for the agenda editing page. -* It contains the document read function that starts everything -* off, and uses functions and objects from agenda_*.js -* -*/ - - - - -//////////////-GLOBALS----//////////////////////////////////////// - -// these need to be setup in landscape_edit's setup_slots() inline function: -//var meeting_number = 0; // is the meeting name. -//var schedule_id = 0; // what is the schedule we are editing. -//var schedule_name; // what is the schedule we are editing. -//var schedule_owner_href = ''; // who owns this schedule -//var assignments_post_href; -//var meeting_base_url; -//var site_base_url; -//var total_rooms = 0; // the number of rooms -//var total_days = 0; // the number of days - -var is_secretariat = false; - -var agenda_globals; - -var area_directors = {}; // list of promises of area directors, index by href. - -var read_only = true; // it is true until we learn otherwise. -var days = []; -var legend_status = {}; // agenda area colors. -var load_conflicts = true; -var duplicate_sessions = {}; -/********* colors ************************************/ - -var dragging_color = "blue"; // color when draging events. -var none_color = ''; // when we reset the color. I believe doing '' will force it back to the stylesheet value. -var color_droppable_empty_slot = 'rgb(0, 102, 153)'; - -// these are used for debugging only. -var last_json_txt = ""; // last txt from a json call. -var last_json_reply = []; // last parsed content - -var hidden_rooms = []; -var hidden_days = []; - -/****************************************************/ - -/////////////-END-GLOBALS-/////////////////////////////////////// - -/* refactor this out into the html */ -$(document).ready(function() { - initStuff(); - - $("#close_ietf_menubar").click(); - -}); - -/* initStuff() - This is ran at page load and sets up the entire page. -*/ -function initStuff(){ - agenda_globals = new AgendaGlobals(); - //agenda_globals.__debug_session_move = true; - - log("initstuff() running..."); - var directorpromises = []; - - /* define a slot for unscheduled items */ - var unassigned = new ScheduledSlot(); - unassigned.make_unassigned(); - - setup_slots(directorpromises); - mark_area_directors(directorpromises); - log("setup_slots() ran"); - droppable(); - log("droppable() ran"); - - $.when.apply($,directorpromises).done(function() { - /* can not load events until area director info, - timeslots, sessions, and assignments - have been loaded - */ - log("loading/linking objects"); - load_events(); - log("load_events() ran"); - find_meeting_no_room(); - calculate_name_select_box(); - calculate_room_select_box(); - listeners(); - droppable(); - duplicate_sessions = find_double_timeslots(); - empty_info_table(); - count_sessions(); - - if(load_conflicts) { - recalculate(null); - } - }); - - static_listeners(); - log("listeners() ran"); - - start_spin(); - - read_only = true; - log("do read only check"); - read_only_check(); - stop_spin(); - - meeting_objs_length = Object.keys(agenda_globals.meeting_objs).length; - - /* Comment this out for fast loading */ - //load_conflicts = false; -} - -var __READ_ONLY; -function read_only_result(msg) { - __READ_ONLY = msg; - is_secretariat = msg.secretariat; - - read_only = msg.read_only; - console.log("read only", read_only); - - if(!read_only) { - $("#read_only").css("display", "none"); - } - - if(msg.save_perm) { - $(".agenda_save_box").css("display", "block"); - if(read_only) { - $(".agenda_save_box").css("position", "fixed"); - $(".agenda_save_box").css("top", "20px"); - $(".agenda_save_box").css("right", "10px"); - $(".agenda_save_box").css("bottom", "auto"); - $(".agenda_save_box").css("border", "3px solid blue"); - $(".agenda_save_box").css("z-index", "2000"); - } - } else { - $(".agenda_save_box").html("please login to save"); - } - - schedule_owner_href = msg.owner_href; - // XX go fetch the owner and display it. - console.log("owner href:", schedule_owner_href); - - $("#pageloaded").show(); - - listeners(); - droppable(); -} - -function read_only_check() { - var read_only_url = meeting_base_url + "/agenda/" + schedule_owner_email + "/" + schedule_name + "/permissions"; - console.log("Loading readonly status from: ", read_only_url); - var read_only_load = $.ajax(read_only_url); - - read_only_load.success(function(newobj, status, jqXHR) { - last_json_reply = newobj; - read_only_result(newobj); - }); -} - -function print_all_ss(objs){ - console.log(objs) -} - - -/* - * Local Variables: - * c-basic-offset:4 - * End: - */ - diff --git a/ietf/static/ietf/js/agenda/agenda_helpers.js b/ietf/static/ietf/js/agenda/agenda_helpers.js deleted file mode 100644 index 8cb36a0db..000000000 --- a/ietf/static/ietf/js/agenda/agenda_helpers.js +++ /dev/null @@ -1,626 +0,0 @@ -/* -* agenda_helpers.js -* -* Copyright (c) 2013, The IETF Trust. See ../../../LICENSE. -* -* www.credil.org: Project Orlando 2013 -* Author: Justin Hornosty ( justin@credil.org ) -* Michael Richardson -* -* Should contain miscellaneous commonly used functions. -* -* -*/ - -/* do_work: - when we are waiting for something to either resolve to true, or another similar job - this function should achieve this. - - result will be a function that when returns true will stop the work and then the callback - will be triggered. - - ex: - global_x = 0 - do_work(function(){ global_x++; return global_x > 100 }, function(){ console.log("resolved") }) -*/ -function do_work(result,callback){ - setTimeout(function(){ - if(!result()){ - setTimeout(arguments.callee,1); - } - else{ - callback(); - } - }); -} - - -function log(text){ - console.log(text); -} - -function print_all(){ - console.log("all"); - console.log(agenda_globals.meeting_objs.length); - for(var i=0; ititledata -*/ -function gen_tr_td(title,data){ - return ""+title+""+data+""; -} - -/* Mainly for the case where we didn't get any data back from the server */ -function empty_info_table(){ - $("#info_grp").html(name_select_html); - $("#info_name").html(""); - $("#info_area").html(""); - $("#info_duration").html(""); - - $(".agenda_selected_buttons").attr('disabled',true); - $(".agenda_double_slot").addClass("button_disabled"); - $(".agenda_double_slot").removeClass("button_enabled"); - - if(!read_only) { - $("#info_location").html(generate_select_box()+""); - $("#info_location_select").val(""); - $("#info_location_select").val($("#info_location_select_option_"+current_timeslot_id).val()); - } - $("#info_responsible").html(""); - $("#info_requestedby").html(""); - $("#agenda_requested_features").html(""); - - /* need to reset listeners, because we just changed the HTML */ - listeners(); -} - - -var temp_1; -/* creates the 'info' table that is located on the right side. - takes in a json. -*/ - -function compare_timeslot(a,b) { - //console.log("day: a,b", a.day, b.day); - - // sometimes (a.day==b.say)==false and (a.day===b.day)==false, - // for days that appear identical, but built from different strings, - // yet (a.day-b.day)==0. - if((a.day - b.day) == 0) { - //console.log("time: a,b", a.starttime, b.starttime); - if(a.starttime == b.starttime) { - //console.log("room: a,b", a.room, b.room, a.room < b.room); - if(a.room > b.room) { - return 1; - } else { - return -1; - } - }; - if(a.starttime > b.starttime) { - return 1; - } else { - return -1; - } - } - if(a.day > b.day) { - return 1; - } else { - return -1; - } -} - -var room_select_html = ""; -function calculate_room_select_box() { - var html = ""; - room_select_html = html; - return room_select_html; -} - -var name_select_html = undefined; -var temp_sorted = null; -function calculate_name_select_box(){ - var html = ""; - name_select_html = html; - return html; -} - - - -function generate_select_box(){ - if(!room_select_html) { - calculate_name_select_box(); - } - return room_select_html; -} - - - - -function insert_cell(js_room_id, text, replace){ - slot_id = ("#"+js_room_id); - try{ - var found; - if(replace) { - found = $(slot_id).html(text); - } else { - found = $(slot_id).append($(text)); - - } - $(slot_id).css('background',''); - $(slot_id).removeClass('free_slot'); - if(found.length == 0){ - // do something here, if length was zero... then? - } - - } - catch(err){ - log("error"); - log(err); - } -} - - -function find_meeting_no_room(){ - $.each(agenda_globals.meeting_objs, function(key){ - if(agenda_globals.meeting_objs[key].slot_status_key == null) { - session = agenda_globals.meeting_objs[key] - session.slot_status_key = null; - session.populate_event(bucketlist_id); - } - }) -} - - -/* in some cases we have sessions that span over two timeslots. - so we end up with two slot_status pointing to the same meeting_obj. - this this occures when someone requests a session that is extra long - which will then fill up the next timeslot. - - this functions finds those cases. - - returns a json{ 'ts': arr[time_slot_ids] } - -*/ -function find_double_timeslots(){ - var duplicate = {}; - - $.each(agenda_globals.slot_status, function(key){ - for(var i =0; i 1){ - dup[key] = duplicate[key]['ts']; - - } - }); - return dup; -} - - -var child = null; -/* removes a duplicate timeslot. completely. it's gone. */ -function remove_duplicate(timeslot_id, ss_id){ - children = $("#"+timeslot_id).children(); - child = children; - for(var i = 0; i< children.length; i++){ // loop to - if($(children[i]).attr('session_id') == ss_id) { // make sure we only remove duplicate. - try{ - $(children[i]).remove(); - }catch(exception){ - console.log("exception from remove_duplicate",exception); - } - } - } - -} - - - -function auto_remove(){ - dup = find_double_timeslots(); - $.each(dup, function(key){ - remove_duplicate(dup[key][1], key); - }) -} - - - -/* for the spinnner */ - -/* spinner code from: - http://fgnass.github.com/spin.js/ - - ex: $("#spinner").spin() < start the spin - $("#spinner").spin(false) < stop the spin - - http://gist.github.com/itsflorida < jquery functionality. - - lines: 30, // The number of lines to draw - length: 7, // The length of each line - width: 1, // The line thickness - radius: 20, // The radius of the inner circle - corners: 1, // Corner roundness (0..1) - rotate: 0, // The rotation offset - color: '#000', // #rgb or #rrggbb - speed: 1, // Rounds per second - trail: 60, // Afterglow percentage - shadow: false, // Whether to render a shadow - hwaccel: true, // Whether to use hardware acceleration - className: 'spinner', // The CSS class to assign to the spinner - zIndex: 2e9, // The zindex (defaults to 2000000000) - top: 'auto', // Top position relative to parent in px - left: 'auto' // Left position relative to parent in px - -*/ - -(function($) { - $.fn.spin = function(opts, color) { - if (Spinner) { - return this.each(function() { - var $this = $(this), - data = $this.data(); - - if (data.spinner) { - data.spinner.stop(); - delete data.spinner; - } - if (opts !== false) { - if (typeof opts === "string") { - if (opts in presets) { - opts = presets[opts]; - } else { - opts = {}; - } - if (color) { - opts.color = color; - } - } - data.spinner = new Spinner($.extend({color: $this.css('color')}, opts)).spin(this); - } - }); - } else { - throw "Spinner class not available."; - } - }; -})(jQuery); - - -function start_spin(opts){ -//spinner - // $("#schedule_name").hide(); - $("#spinner").show(); - $("#spinner").spin({lines:16, radius:8, length:16, width:4}); - $("#pageloaded").hide(); -} -function stop_spin(){ -//spinner - $("#schedule_name").show(); - $("#spinner").hide(); - $("#spinner").spin(false); - $("#pageloaded").show(); -} - -/* - * Local Variables: - * c-basic-offset:4 - * End: - */ diff --git a/ietf/static/ietf/js/agenda/agenda_listeners.js b/ietf/static/ietf/js/agenda/agenda_listeners.js deleted file mode 100644 index 3ee70b5de..000000000 --- a/ietf/static/ietf/js/agenda/agenda_listeners.js +++ /dev/null @@ -1,1266 +0,0 @@ -/* -* agenda_listeners.js -* -* Copyright (c) 2013, The IETF Trust. See ../../../LICENSE. -* -* www.credil.org: Project Orlando 2013 -* Author: Justin Hornosty ( justin@credil.org ) -* Michael Richardson -* -* This file should contain functions relating to -* jquery ui droppable ( http://jqueryui.com/droppable/ ) -* and other interactions. -* -*/ - -var bucketlist_id = "sortable-list" // for if/when the id for bucket list changes. - -function resize_listeners() { - for(i = 0; i 0){ - for(var i = 0; i< free_slots.length; i++){ - if(free_slots[i].capacity >= target_capacity) { - perfect_slots.push(free_slots[i]); - } - } - if(perfect_slots.length > 0){ - return perfect_slots[0]; - } - else{ - return free_slots[0]; // just return the first one. - } - } - else{ - return null; - } -} - -function extend_slot(event) { - // event is just the button push, ignore it. - - session = last_session; - - console.log("session", session.title, "sslot:", current_scheduledslot.assignment_id); - - /* bind current_timeslot into this function and continuations */ - var slot = current_timeslot; - - // determine if this slot can be extended. - if(current_timeslot.can_extend_right()) { - $("#can-extend-dialog").html("Extend "+session.title+" to slot "+slot.following_timeslot.domid); - $("#can-extend-dialog").dialog({ - resizable: true, - modal: true, - dialogClass: "extend-dialog", - buttons: { - "Yes": { - click: function() { - // need to create new assignment - var new_ss = make_ss({ "session_id" : session.session_id, - "timeslot_id": slot.following_timeslot.timeslot_id, - "extendedfrom_id" : current_scheduledslot.assignment_id}); - // make_ss also adds to slot_status. - new_ss.saveit(); - - slot.extendedto = slot.following_timeslot; - slot.extendedto.extendedfrom = slot; - session.double_wide = true; - session.repopulate_event(slot.domid); - - droppable(); - listeners(); - $( this ).dialog( "close" ); - - // may have caused some new conflicts!!!! - recalculate_conflicts_for_session(session, - [slot.column_class], - [slot.column_class, slot.extendedto.column_class]); - }, - text: "Yes", - id: "extend-yes"}, - "Cancel": { - click: function() { - $( this ).dialog( "close" ); - result = "cancel" - }, - text: "Cancel", - id: "extend-cancel"}, - } - }); - } else { - $( "#can-not-extend-dialog" ).dialog(); - } -} - -function find_free(){ - if(last_session) { - var empty_slot = find_empty_slot(last_session); - if(empty_slot != null){ - var domthing = $("#"+empty_slot.domid); - domthing.effect("highlight", {},3000); - if(current_item != null){ - $(current_item).addClass('ui-effects-transfer'); - $(current_item).effect("transfer", {to: domthing }, 1000); - } - $(current_item).removeClass('ui-effects-transfer'); - } - } -} - - -function expand_spacer(target) { - var current_width = $(target).css('min-width'); - current_width = current_width.substr(0,current_width.search("px")); - current_width = parseInt(current_width) + 20; - $(target).css('min-width',current_width); - $(target).css('width',current_width); - -} - -function sort_by_alphaname(a,b) { - am = agenda_globals.meeting_objs[$(a).attr('session_id')] - bm = agenda_globals.meeting_objs[$(b).attr('session_id')] - if(am.title < bm.title) { - return -1; - } else { - return 1; - } -} -function sort_by_area(a,b) { - am = agenda_globals.meeting_objs[$(a).attr('session_id')] - bm = agenda_globals.meeting_objs[$(b).attr('session_id')] - if(am.area < bm.area) { - return -1; - } else { - return 1; - } -} -function sort_by_duration(a,b) { - am = agenda_globals.meeting_objs[$(a).attr('session_id')] - bm = agenda_globals.meeting_objs[$(b).attr('session_id')] - if(am.requested_duration < bm.requested_duration) { - // sort duration biggest to smallest. - return 1; - } else if(am.requested_duration == bm.requested_duration && - am.title < bm.title) { - return 1; - } else { - return -1; - } -} -function sort_by_specialrequest(a,b) { - am = agenda_globals.meeting_objs[$(a).attr('session_id')] - bm = agenda_globals.meeting_objs[$(b).attr('session_id')] - if(am.special_request == '*' && bm.special_request == '') { - return -1; - } else if(am.special_request == '' && bm.special_request == '*') { - return 1; - } else if(am.title < bm.title) { - return -1; - } else { - return 1; - } -} - -function sort_unassigned() { - $('#'+bucketlist_id+" div.meeting_box_container").sort(unassigned_sort_function).appendTo('#'+bucketlist_id); -} - -var unassigned_sort_function = sort_by_alphaname; -function unassigned_sort_change(){ - var last_sort_method = unassigned_sort_function; - var sort_method= $("#unassigned_sort_button").attr('value'); - - if(sort_method == "alphaname") { - unassigned_sort_function = sort_by_alphaname; - } else if(sort_method == "area") { - unassigned_sort_function = sort_by_area; - } else if(sort_method == "duration") { - unassigned_sort_function = sort_by_duration; - } else if(sort_method == "special") { - unassigned_sort_function = sort_by_specialrequest; - } else { - unassigned_sort_function = sort_by_alphaname; - } - - if(unassigned_sort_function != last_sort_method) { - sort_unassigned(); - } -} - - -/* the functionality of these listeners will never change so they do not need to be run twice */ -function static_listeners(){ - $('#close_ietf_menubar').click(hide_ietf_menu_bar); - - $("#show_hidden_days").unbind('click'); - $("#show_hidden_days").click(show_hidden_days); - $("#show_hidden_rooms").unbind('click'); - $("#show_hidden_rooms").click(show_hidden_rooms); - - $('#unassigned_sort_button').unbind('change'); - $('#unassigned_sort_button').change(unassigned_sort_change); - $('#unassigned_sort_button').css('display','block'); - $("#unassigned_alpha").attr('selected',true); - sort_unassigned(); -} - -// recalculate all conflicts from scratch -function recalculate(event) { - start_spin(); - console.log("loading all conflicts"); - - var promise = get_all_conflicts(); - promise.done(function() { - stop_spin(); - console.log("showing all conflicts"); - show_all_conflicts(); - }); -} - -function color_legend_click(event){ - var clicked = $(event.target).attr('id'); - if(legend_status[clicked]){ - legend_status[clicked] = false; - } - else{ - legend_status[clicked] = true; - } - set_transparent(); -} - -var conflict_status = {}; - -function conflict_click(event){ - var clicked = $(event.target).attr('id'); - var constraint = find_conflict(clicked); - //console.log("7 fill", clicked, conflict_status[clicked]); - if(conflict_status[clicked] == true){ - //console.log("8 fill", constraint.href); - conflict_status[clicked] = false; - constraint.checked = "checked"; - } - else{ - //console.log("9 fill", constraint.href); - conflict_status[clicked] = true; - constraint.show_conflict_view(); - } -} - -function set_transparent(){ - $.each(legend_status, function(k){ - if(legend_status[k] != true){ - $("."+k+"-scheme.meeting_obj").parent().parent().parent().draggable("option","disabled",true); - }else{ - $("."+k+"-scheme.meeting_obj").parent().parent().parent().draggable("option","disabled",false); - } - - }); -} - -var __debug_meeting_click = false; -var clicked_event; -var __DEBUG__SESSION_OBJ; -var __DEBUG__SLOT_OBJ; -var __debug_click_slot_id; -var __debug_click_container; - -// current_item is the domid that was clicked -// last_session is the session active. -var current_item = null; -var current_timeslot = null; -var current_scheduledslot = null; -var current_timeslot_id = null; // global used by empty_info_table to move picker. -var meeting_clicked = false; -function meeting_event_click(event){ - //hide_all_conflicts(); - try{ - clear_highlight(find_friends(current_item)); - }catch(err){ } - - if(__debug_meeting_click) { - console.log("1 meeting_click:", event); - } - - // keep event from going up the chain. - event.preventDefault(); - meeting_clicked = true; - - var slot_id = $(event.target).closest('.agenda_slot').attr('id'); - var container = $(event.target).closest('.meeting_box_container'); - __debug_click_slot_id = slot_id; - __debug_click_container = container; - - if(container == undefined) { - return; - } - - var session_id = container.attr('session_id'); - var session = agenda_globals.meeting_objs[session_id]; - - select_session(session); - __DEBUG__SS_OBJ = current_scheduledslot; - __DEBUG__SLOT_OBJ = current_timeslot; - __DEBUG__SESSION_OBJ = session; -} - -function select_session(session) { - if(last_session != null) { - last_session.unselectit(); - } - last_session = session; - - empty_info_table(); - current_item = session.element(); - - /* clear set ot conflict views */ - clear_conflict_classes(); - conflict_classes = {}; - - current_timeslot = session.slot; - if(current_timeslot) { - current_timeslot_id = current_timeslot.timeslot_id; - } - current_scheduledslot = session.assignment; - if(__debug_meeting_click) { - console.log("2 meeting_click:", current_timeslot, session); - } - - fill_in_session_info(session, true, session.slot); -} - -var last_item = null; // used during location change we make the background color -// of the timeslot highlight because it is being set into that slot. -function info_location_select_change(){ - if(last_item != null){ - $(last_item).removeClass("selected_slot"); - } - - last_item = '#'+$('#info_location_select').val(); - $(last_item).addClass("selected_slot"); -} - -// called when the "Set" button is called, needs to perform a move, as if -// it was dragged and dropped. -function info_location_select_set() { - // figure out where the item was, and where it is going to. - var session = last_session; - var from_slot = session.slot; - - var id = $('#info_location_select').val(); - var to_slot = agenda_globals.timeslot_byid[id]; - - console.log("moved by select box from", from_slot, "to", to_slot); - - move_thing({ "session": session, - "to_slot_id": to_slot.domid, - "to_slot": to_slot, - "dom_obj": "#" + to_slot.domid, - "from_slot_id": from_slot.domid, - "from_slot": from_slot}); -} - -function move_thing(parameters) { - // hasn't moved don't do anything - if(parameters.from_slot_id == parameters.to_slot_id){ - $(parameters.dom_obj).removeClass('highlight_free_slot'); - return; - } - - parameters.too_small = false; - parameters.slot_occupied = false; - - var room_capacity = parameters.to_slot.capacity; - - if(parameters.session.session_attendees > room_capacity){ - parameters.too_small = true; - } - - parameters.bucket_list = (parameters.to_slot_id == bucketlist_id); - if(!parameters.bucket_list && !parameters.to_slot.empty){ - parameters.slot_occupied = true - } - - if(parameters.too_small || parameters.slot_occupied){ - toggle_dialog(parameters); - return - } - - clear_conflict_classes(); - // clear double wide setting for now. - // (could return if necessary) - parameters.session.double_wide = false; - - move_slot(parameters); -} - - - -var last_session = null; -var last_name_item = null; -function info_name_select_change(){ - if(last_session != null) { - console.log("unselecting:",last_session.title); - /* clear set ot conflict views */ - clear_conflict_classes(); - conflict_classes = {}; - last_session.unselectit(); - } - $(".same_group").removeClass("same_group"); - $(".selected_group").removeClass("selected_group"); - $(".selected_slot").removeClass("selected_slot"); - - if(last_item != null) { - $(last_item).removeClass("selected_slot"); - } - - var slot_id = $('#info_name_select').val(); - last_name_item = '#'+slot_id; - console.log("selecting group", slot_id); - - var ssk = agenda_globals.meeting_objs[slot_id].slot_status_key; - // ssk is null when item is in bucket list. - - current_item = "#session_"+slot_id; //slot_status_obj[0].session_id; - if(current_item != null){ - $(current_item).addClass("selected_slot"); - $(current_item).get(0).scrollIntoView(true); - } - - if(ssk != null){ - var slot_status_obj = agenda_globals.slot_status[ssk]; - current_timeslot = slot_status_obj[0].timeslot; - current_timeslot_id = slot_status_obj[0].timeslot_id; - ss = slot_status_obj[0]; - session = ss.session; - last_session = session; - last_session.selectit(); - // now set up the call back that might have to retrieve info. - fill_in_session_info(session, true, ss); - } - else { - ss = agenda_globals.meeting_objs[slot_id]; - last_session = ss; - last_session.selectit(); - fill_in_session_info(ss, true, ss.slot); - } - - console.log("selecting new item:", last_session.title); -} - -function set_pin_session_button(assignment) { - $("#pin_slot").unbind('click'); - if(assignment == undefined) { - console.log("pin not set, assignment undefined"); - return; - } - state = assignment.pinned; - //console.log("button set to: ",state); - $("#pin_slot").attr('disabled',false); - if(state) { - $("#pin_slot").html("unPin"); - $("#agenda_pin_slot").addClass("button_down"); - $("#agenda_pin_slot").removeClass("button_up"); - $("#pin_slot").click(function(event) { - update_pin_session(assignment, false); - }); - } else { - $("#pin_slot").html(" Pin "); - $("#agenda_pin_slot").addClass("button_up"); - $("#agenda_pin_slot").removeClass("button_down"); - $("#pin_slot").click(function(event) { - update_pin_session(assignment, true); - }); - } -} - -function update_pin_session(assignment, state) { - start_spin(); - assignment.set_pinned(state, function(assignment) { - stop_spin(); - session.repopulate_event(assignment.domid()); - set_pin_session_button(assignment); - }); -} - -function enable_button(divid, buttonid, func) { - $(buttonid).unbind('click'); - $(buttonid).click(func); - $(buttonid).attr('disabled',false); - - $(divid).removeClass("button_disabled"); - $(divid).addClass("button_enabled"); -} - -function disable_button(divid, buttonid) { - $(buttonid).unbind('click'); - $(buttonid).attr('disabled',true); - - $(divid).addClass("button_disabled"); - $(divid).removeClass("button_enabled"); -} - -function highlight_session(session) { - var element = session.element()[0]; - element.scrollIntoView(true); - session.element().parent().parent().parent().effect("pulsate", {color:"lightcoral"}, 10000); -} - -function fill_in_session_info(session, success, extra) { - var prev_session = null; - var next_session = null; - - if(session == null || session == "None" || !success){ - empty_info_table(); - return; - } - session.generate_info_table(); - - prev_session = session.prev_session; - next_session = session.next_session; - - if(!read_only) { - enable_button("#agenda_double_slot", "#double_slot", extend_slot); - - $("#agenda_pin_slot").removeClass("button_disabled"); - $("#agenda_pin_slot").addClass("button_enabled"); - set_pin_session_button(session.assignment); - } else { - $("#pin_slot").unbind('click'); - } - - enable_button("#agenda_show", "#show_session", show_all_session); - - if(prev_session) { - enable_button("#agenda_prev_session", "#prev_session", function(event) { - select_session(prev_session); - highlight_session(prev_session); - }); - } else { - disable_button("#agenda_prev_session", "#prev_session"); - } - if(next_session) { - enable_button("#agenda_next_session", "#next_session", function(event) { - select_session(next_session); - highlight_session(next_session); - }); - } else { - disable_button("#agenda_next_session", "#next_session"); - } - - $("#agenda_requested_features").html(""); - // fill in list of resources into #agenda_requested_features - if(session.resources != undefined) { - $.each(session.resources, function(index) { - resource = this; - - $("#agenda_requested_features").html(function(inbox, oldhtml) { - return "
"+ - "\""+"+ - "
"+ - oldhtml - }); - - console.log(session.title, "asks for resource", resource.desc); - }); - } - - - // here is where we would set up the session request edit button. - // $(".agenda_sreq_button").html(session.session_req_link); - - session.retrieve_constraints_by_session().success(function(newobj,status,jq) { - draw_constraints(session); - }); -} - -function group_name_or_empty(constraint) { - if(constraint == undefined) { - return ""; - } else { - return constraint.conflict_view(); - } -} - -function draw_constraints(session) { - - if("conflicts" in session) { - var display = agenda_globals.group_conflict_labels; - var group_icons = ""; - var group_set = {}; - $.each(session.conflicts, function(index) { - conflict = session.conflicts[index]; - conflict.build_othername(); - if(conflict.conflict_groupP()) { - if ( ! (conflict.othergroup_name in group_set) ) { - group_set[conflict.othergroup_name] = {}; - } - group_set[conflict.othergroup_name][conflict.direction]=display[conflict.conflict_type]; - // This had been in build_group_conflict_view - conflict.populate_conflict_classes(); - highlight_conflict(conflict); - } - }); - - - $.each(group_set, function(index) { - group = group_set[index]; - group_view = "
  • "; - group_view += "
    " - if ('ours' in group_set[index]) { - group_view += group_set[index].ours+"->" - } - group_view += "
    " - group_view += index; - group_view += "
    " - if ('theirs' in group_set[index]) { - group_view += "->"+group_set[index].theirs - } - group_view += "
    " - group_view += "
  • "; - group_icons += group_view; - }); - - if(group_icons == "") { - $("#conflict_group_list").html("none"); - } else { - $("#conflict_group_list").html("
      "+group_icons+"
    "); - } - } else { - $("#conflict_group_list").html("none"); - } - if("bethere" in session.constraints) { - var people_icons = ""; - - $.each(session.constraints.bethere, function(index) { - conflict = session.constraints.bethere[index]; - if(conflict.conflict_peopleP()) { - people_icons += "
  • "+conflict.conflict_view(); - } - }); - if(people_icons == "") { - $("#conflict_people_list").html("none"); - } else { - $("#conflict_people_list").html("
      "+people_icons+"
    "); - } - } else { - $("#conflict_people_list").html("none"); - } - -} - -function highlight_conflict(constraint) { - if(constraint != undefined) { - var clicked = constraint.dom_id; - //console.log("91 fill", constraint.href, constraint.othergroup.href); - conflict_status[clicked] = true; - constraint.show_conflict_view(); - } -} - -var menu_bar_hidden = false; -function hide_ietf_menu_bar(){ - $('#ietf_menubar').toggle('slide',"",100); - if(menu_bar_hidden){ - menu_bar_hidden = false; - $('.wrapper').css('width','auto'); - $('.wrapper').css('margin-left','160px'); - $('#close_ietf_menubar').html("<"); - - } - else{ - menu_bar_hidden = true; - $('.wrapper').css('width','auto'); - $('.wrapper').css('margin-left','0px'); - $('#close_ietf_menubar').html(">"); - } -} - - - -/* create the droppable */ -function droppable(){ - if(read_only) { - return; - } - $(function() { - /* the thing that is draggable */ - $( ".meeting_event").draggable({ - appendTo: "body", - helper: "clone", - drag: drag_drag, - start: drag_start, - stop: drag_stop, - }); - - $( "#sortable-list").droppable({ - over : drop_over, - activate: drop_activate, - out : drop_out, - drop : drop_drop, - start: drop_start, - }) - - $("#meetings td.ui-droppable").droppable({ - over :drop_over, - activate:drop_activate, - out :drop_out, - drop : drop_drop, - create: drop_create, - start: drop_start, - - }); // end $(#meetings td).droppable - }); // end function() -} // end droppable() - - -function update_to_slot(session_id, to_slot_id, force){ - //console.log("update_to_slot meeting_id:",session_id, to_slot_id); - if(to_slot_id == "sortable-list") { - /* must be bucket list */ - return true; - } - - var to_timeslot = agenda_globals.timeslot_bydomid[to_slot_id]; - if(to_timeslot != undefined && (to_timeslot.empty == true || force)) { - // add a new assignment for this, save it. - var new_ss = make_ss({ "session_id" : session_id, - "timeslot_id": to_timeslot.timeslot_id}); - // make_ss also adds to slot_status. - var save_promise = new_ss.saveit(); - - if(to_slot_id != bucketlist_id) { - to_timeslot.mark_occupied(); - } - - // update meeting_obj - agenda_globals.meeting_objs[session_id].placed(to_timeslot, true, new_ss); - - return save_promise; - } else { - console.log("update_to_slot failed", to_timeslot, force, "empty", to_timeslot.empty); - return false; - } -} - -function update_from_slot(session_id, from_slot_id) -{ - var from_timeslot = agenda_globals.timeslot_bydomid[from_slot_id]; - - /* this is a list of schedule-timeslot-session-assignments */ - var from_scheduledslots = agenda_globals.slot_status[from_slot_id]; - var delete_promises = []; - - // it will be null if it's coming from a bucketlist - if(from_slot_id != null && from_scheduledslots != undefined){ - //console.log("1 from_slot_id", from_slot_id, from_scheduledslots); - var count = from_scheduledslots.length; - var found = []; - for(var k = 0; k 0) { - remove_duplicate(parameters.from_slot_id, parameters.session.session_id); - // do something else? - } - else { - console.log("WARNING -- nothing to delete when updating from_slot", parameters.from_slot_id, agenda_globals.slot_status[parameters.from_slot_id]); - //return; - } - parameters.session.slot_status_key = parameters.to_slot_id; - - var eTemplate = parameters.session.event_template() - $(parameters.dom_obj).append(eTemplate); - - if(parameters.ui) { - parameters.ui.draggable.remove(); - } - - /* recalculate all the conflict classes given new slot */ - parameters.session.update_column_classes([parameters.to_slot], - parameters.bucket_list); - - /* set colours */ - $(parameters.dom_obj).removeClass('highlight_free_slot'); - if(parameters.to_slot.empty) { - $(parameters.dom_obj).removeClass('free_slot') - } - else{ - $(parameters.dom_obj).addClass('free_slot') - } - - if(parameters.from_slot != undefined && parameters.from_slot.empty){ - $("#"+parameters.from_slot_id).removeClass('free_slot'); - } - else{ - $("#"+parameters.from_slot_id).addClass('free_slot'); - } - $("#"+bucketlist_id).removeClass('free_slot'); - /******************************************************/ - - droppable(); - listeners(); - sort_unassigned(); - - delete_promises.push(save_promise); - return $.when.apply($,delete_promises); -} - -/* first thing that happens when we grab a meeting_event */ -function drop_activate(event, ui){ - //$(event.draggable).css("background",dragging_color); - $(event.currentTarget).addClass('highlight_current_moving'); -} - - -/* what happens when moving a meeting event over something that is 'droppable' */ -function drop_over(event, ui){ - if(check_free(this)){ - $(this).addClass('highlight_free_slot'); - } - $(event.draggable).addClass('highlight_current_selected'); - $(ui.draggable).addClass('highlight_current_selected'); - -// $(ui.draggable).css("background",dragging_color); -// $(event.draggable).css("background",dragging_color); -} - -/* when we have actually dropped the meeting event */ -function drop_out(event, ui){ - if(check_free(this)){ - if($(this).attr('id') != bucketlist_id){ - $(this).addClass("free_slot"); - } - } - $(this).removeClass('highlight_free_slot'); - $(event.draggable).removeClass('highlight_current_selected'); - $(ui.draggable).removeClass('highlight_current_selected'); - -} - -function drag_stop(event,ui){ - $(event.target).removeClass('highlight_current_selected'); - $(event.target).removeClass('highlight_current_moving'); -} - - -/* functions here are not used at the moment */ -function drop_create(event,ui){ -} - -function drop_start(event,ui){ -} - -function drag_drag(event, ui){ -} - -function drag_start(event, ui){ - return; -} - -/* - * Local Variables: - * c-basic-offset:4 - * End: - */ diff --git a/ietf/static/ietf/js/agenda/agenda_objects.js b/ietf/static/ietf/js/agenda/agenda_objects.js deleted file mode 100644 index 441f27b81..000000000 --- a/ietf/static/ietf/js/agenda/agenda_objects.js +++ /dev/null @@ -1,1935 +0,0 @@ -/* -* -* FILE: agenda_objects.js -* Copyright (c) 2013, The IETF Trust. See ../../../LICENSE. -* -* www.credil.org: Project Orlando 2013 -* Author: Justin Hornosty ( justin@credil.org ) -* Michael Richardson -* -* Description: -* Contains the objects relating to django's models. -* As much business logic as possible should be here. -* This file should be resuable by other than agenda_edit.js -* -* Display logic should be contained in agenda_listeners.js -* -* Functions: -* - check_delimiter(inp) -* - upperCaseWords(inp) -* -*/ - -function AgendaGlobals() { - this.group_objs = {}; - this.slot_status = {}; - this.slot_objs = {}; - this.meeting_objs = {}; - this.sessions_objs = {}; - this.timeslot_bydomid = {}; - this.timeslot_byid = {}; - this.assignment_promise = undefined; - this.timeslot_promise = undefined; - this.__debug_session_move = false; - /* Group_conflict_labels defines constraint names that are group conflicts and - * the symbols used to represent them. Shadow the old 1-2-3 labels to maintain - * behavior from before the new conflict types were introduced. This may need - * to be changed if the old editor is not phased out. */ - this.group_conflict_labels = { - 'conflict':'1' , - 'conflic2':'2' , - 'conflic3':'3', - 'chair_conflict': '1', - 'tech_overlap': '2', - 'key_participant': '3' - }; - -} - -function createLine(x1,y1, x2,y2){ - var length = Math.sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2)); - var angle = Math.atan2(y2 - y1, x2 - x1) * 180 / Math.PI; - var transform = 'rotate('+angle+'deg)'; - - var line = $('
    ') - .appendTo('#meetings') - .addClass('line') - .css({ - 'position': '', - 'transform': transform - }) - .width(length) - .offset({left: x1, top: y1}); - - return line; -} - - -function empty_callback(inp){ -// console.log('inp:', inp); -} - -function get_all_constraints(){ - for(s in agenda_globals.meeting_objs){ - show_non_conflicting_spots(s) - } - -} - -function show_all_conflicts(){ - console.log("showing all conflicts"); - for(sk in agenda_globals.meeting_objs) { - var s = agenda_globals.meeting_objs[sk]; - s.display_conflict(); - s.display_personconflict(); - } -} - -// not really used anymore -- just for debugging -function hide_all_conflicts(){ - for(sk in agenda_globals.meeting_objs) { - var s = agenda_globals.meeting_objs[sk]; - s.hide_conflict(); - } -} - -function get_all_conflicts(){ - var all_constraint_promises = []; - var one_constraint; - var sess1; - //console.log("get_all_conflicts()"); - for(var s in agenda_globals.meeting_objs){ - sess1 = agenda_globals.meeting_objs[s]; - sess1.clear_conflict(); - sess1.display_conflict(); - - one_constraint = sess1.retrieve_constraints_by_session(); - all_constraint_promises.push(one_constraint); - } - - /* now make a promise that ends when all the conflicts are loaded */ - var all_constraints = $.when.apply($,all_constraint_promises); - - all_constraints.done(function() { - for(var s in agenda_globals.meeting_objs) { - var sess2 = agenda_globals.meeting_objs[s]; - sess2.examine_people_conflicts(); - } - }); - return all_constraints; -} - -var __debug_conflict_calculate = false; -var __verbose_conflict_calculate = false; - -function calculate_real_conflict(conflict, vertical_location, room_tag, session_obj) { - if(__debug_conflict_calculate) { - console.log(" conflict check:", conflict.othergroup.acronym, "me:", vertical_location, room_tag); - } - - if(session_obj.group.href == conflict.othergroup.href) { - console.log(" session: ",session_obj.session_id, "lists conflict with self"); - return; - } - - var osessions = conflict.othergroup.all_sessions; - if(__debug_conflict_calculate) { - console.log(" ogroup: ", conflict.othergroup.href, "me: ", session_obj.group.href); - } - if(conflict.othergroup === session_obj.group) { - osessions = conflict.thisgroup.all_sessions; - } - if(osessions != null) { - $.each(osessions, function(index) { - osession = osessions[index]; - for(ccn in osession.column_class_list) { - var value = osession.column_class_list[ccn]; - if(value != undefined) { - if(__debug_conflict_calculate) { - console.log(" vs: ",index, "session_id:",osession.session_id," at: ",value.column_tag, value.room_tag); - } - if(value.column_tag == vertical_location && - value.room_tag != room_tag) { - if(__verbose_conflict_calculate || __debug_conflict_calculate) { - console.log("real conflict:",session_obj.title," with: ",conflict.othergroup.acronym, " #session_",session_obj.session_id, value.room_tag, room_tag, value.column_tag, vertical_location); - } - // there is a conflict! - __DEBUG_SHOW_CONSTRAINT = $("#"+value[0]).children()[0]; - session_obj.add_conflict(conflict); - } - } - } - }); - } -} - -var __DEBUG_SHOW_CONSTRAINT = null; -// can become a method now. -function find_and_populate_conflicts(session_obj) { - if(__debug_conflict_calculate) { - console.log("populating conflict:", session_obj.title, session_obj.column_class_list); - } - - var room_tag = null; - session_obj.reset_conflicts(); - - for(let ccn in session_obj.column_class_list) { - if (session_obj.column_class_list.hasOwnProperty(ccn)) { - var vertical_location = session_obj.column_class_list[ccn].column_tag; - var room_tag = session_obj.column_class_list[ccn].room_tag; - $.each(Object.keys(agenda_globals.group_conflict_labels), (i, conflict_name) => { - if (session_obj.constraints[conflict_name]) { - $.each(session_obj.constraints[conflict_name], (j, conflict) => { - calculate_real_conflict(conflict, vertical_location, room_tag, session_obj); - }); - } - if(session_obj.theirconstraints.conflict){ - $.each(session_obj.theirconstraints.conflict, (i, conflict) => { - calculate_real_conflict(conflict, vertical_location, room_tag, session_obj); - }); - } - } - ); - - /* bethere constraints are processed in another loop */ - } - } -} - -function show_non_conflicting_spots(ss_id){ - var conflict_spots = [] - $.each(conflict_classes, function(key){ - conflict_spots.push(conflict_classes[key].session.slot_status_key); - }); - var empty_slots = find_empty_slots(); - conflict_spots.forEach(function(val){ - empty_slots.forEach(function(s){ - if(val == s.key){ - } - }); - }); -} - -function find_empty_slots(){ - var empty_slots = []; - $.each(slot_status, function(key){ - for(var i =0; i 0 above. - session.group = json.group; - - if(session.requested_duration == undefined) { - session.requested_duration = session.duration; - } - - // make it a number. - session.attendees = parseInt(session.attendees); - - session.ogroup = session.group; - if(session.group != undefined) { - /* it has an inline group object, intern it, and redirect to interned object */ - //console.log(session.title, "using embedded group: ", - // session.group.acronym, session.group.href, session.group); - session.group = load_group_from_json(session.group); - session.group_href = session.group.href; - //console.log(session.title, "2 using embedded group: ", - // session.group.acronym, session.group.href, session.group); - } else if(session.group_href != undefined) { - console.log("session ",session.session_id, - "has no embedded group, load by href", session.group_href); - session.group = find_group_by_href(session.group_href, "session_load"); - } else { - // bogus - session.group_href = site_base_url+'/group/'+session.title+".json"; - } - - // keep a list of sessions by name - // this is mostly used for debug purposes only. - if(agenda_globals.sessions_objs[session.title] == undefined) { - agenda_globals.sessions_objs[session.title] = []; - } - agenda_globals.sessions_objs[session.title].push(session); // an array since there can be more than one session/wg - - agenda_globals.meeting_objs[session.session_id] = session; - - return session; -} - -/* feed this an array of sessions */ -function make_sessions(json, status, jqXHR) { - $.each(json, function(index) { - var thing = json[index]; - session_obj(thing); - }); -} - -function load_sessions(href) { - if(agenda_globals.session_promise == undefined) { - agenda_globals.session_promise = $.Deferred(); - - var ss = $.ajax(href); - ss.success(function(newobj, status, jqXHR) { - console.log("finished session promise"); - make_sessions(newobj); - agenda_globals.session_promise.resolve(newobj); - }); - } - return agenda_globals.session_promise; -} - -function count_sessions() { - $.each(agenda_globals.sessions_objs, function(title) { - //console.log("title", title, this); - var lastone = null; - var sessions = agenda_globals.sessions_objs[title]; - var num_sessions = sessions.length; - $.each(sessions, function(index) { - //console.log("session", index, this); - this.number = index; // a number - this.maxNum = num_sessions; - - this.prev_session = lastone; - this.next_session = null; - if(index < num_sessions) { - this.next_session = sessions[index+1]; - } - lastone = this; - }); - }); -} - - -// augument to jQuery.getJSON( url, [data], [callback] ) -Session.prototype.load_session_obj = function(andthen, arg) { - session = this; - if(this.loaded == undefined) { - start_spin(); - this.loaded = $.ajax(this.href); - } - - this.loaded.success(function(newobj, status, jqXHR) { - last_json_reply = newobj; - $.extend(session, newobj); - - if(andthen != undefined) { - andthen(session, true, arg); - } - stop_spin(); - }); - this.loaded.error(function(jqXHR, textStatus, errorThrown ) { - console.log("exception: ",textStatus,errorThrown); - if(andthen != undefined) { - andthen(session, false, arg); - } - stop_spin(); - }); -}; - -Session.prototype.find_responsible_ad = function() { - var session = this; - //console.log("session",this.title, this.session_id,"looking for ad",this.group.ad_href); - if(this.group && this.group.ad_href) { - find_person_by_href(session.group.ad_href).done(function(ad) { - //console.log("session",session.session_id,"found ", ad); - session.responsible_ad = ad; - }); - } -}; - -Session.prototype.element = function() { - return $("#session_"+this.session_id); -}; - -Session.prototype.personconflict_element = function() { - return this.element().parent().find(".personconflict"); -}; - -Session.prototype.selectit = function() { - clear_all_selections(); - // mark self as selected - if(this.group != undefined) { - $("." + this.group.acronym).addClass("same_group"); - } - this.element().removeClass("save_group"); - this.element().addClass("selected_group"); -}; -Session.prototype.unselectit = function() { - clear_all_selections(); -}; - -Session.prototype.on_bucket_list = function() { - this.is_placed = false; - this.column_class_list = []; - this.element().parent("div").addClass("meeting_box_bucket_list"); -}; -Session.prototype.placed = function(where, forceslot, assignment) { - this.is_placed = true; - - // forceslot is set on a move, but unset on initial placement, - // as placed might be called more than once for a double slot session. - if(forceslot || this.slot==undefined) { - this.slot = where; - this.assignment = assignment; - - /* we can not mark old slot as empty, because it - might have multiple sessions in it. - we can do the opposite: mark it was not empty when we fill it. - */ - - if(where != undefined) { - where.empty = false; - } - } - if(where != undefined) { - this.add_column_class(where.column_class); - } - //console.log("session:",session.title, "column_class", ssid.column_class()); - this.element().parent("div").removeClass("meeting_box_bucket_list"); - this.pinned = where.pinned; -}; - -Session.prototype.populate_event = function(js_room_id) { - var eTemplate = this.event_template() - insert_cell(js_room_id, eTemplate, false); -}; -Session.prototype.repopulate_event = function(js_room_id) { - var eTemplate = this.event_template() - insert_cell(js_room_id, eTemplate, true); - this.display_conflict(); -}; - -Session.prototype.visible_title = function() { - return this.special_request + this.title; -}; - -var _conflict_debug = false; -Session.prototype.mark_conflict = function(value) { - this.conflicted = value; -}; -Session.prototype.add_conflict = function(conflict) { - this.conflicted = true; - if (this.conflicted_direction==undefined) { - this.conflicted_direction={}; - } - this.conflicted_direction[conflict.direction] = true; - if (this.highest_conflict==undefined) { - this.highest_conflict={}; - } - if(this.highest_conflict[conflict.direction]==undefined) { - this.highest_conflict[conflict.direction] = conflict; - } else { - var oldhighest = this.highest_conflict[conflict.direction]; - this.highest_conflict[conflict.direction] = this.highest_conflict[conflict.direction].conflict_compare(conflict); - if(_conflict_debug) { - console.log("add conflict for", this.title, - oldhighest.conflict_type, ">?", conflict.conflict_type, - "=", this.highest_conflict[conflict.direction].conflict_type); - } - } - if (this.conflict_level==undefined){ - this.conflict_level={}; - } - this.conflict_level[conflict.direction] = this.highest_conflict[conflict.direction].conflict_type; -}; - -Session.prototype.clear_conflict = function() { - this.conflicted = false; -}; - -Session.prototype.clear_all_conflicts = function(old_column_classes) { - var session_obj = this; - this.clear_conflict(); - - if(old_column_classes != undefined) { - $.each(session_obj.constraints.bethere, function(i) { - var conflict = session_obj.constraints.bethere[i]; - var person = conflict.person; - - person.clear_session(session_obj, old_column_classes); - }); - } -}; - -Session.prototype.show_conflict = function() { - if(_conflict_debug) { - console.log("showing conflict for", this.title, this.conflict_level['ours'],this.conflict_level['theirs']); - } - var display = agenda_globals.group_conflict_labels; - if (this.conflicted) { - if ('ours' in this.conflict_level) { - this.element().find('.ourconflicts').text('->'+display[this.conflict_level.ours]); - } - if ('theirs' in this.conflict_level) { - this.element().find('.theirconflicts').text(display[this.conflict_level.theirs]+'->'); - } - } -}; - -Session.prototype.hide_conflict = function() { - if(_conflict_debug) { - console.log("removing conflict for", this.title); - } - this.element().find('.ourconflicts').text(''); - this.element().find('.theirconflicts').text(''); -}; - -Session.prototype.display_conflict = function() { - if(this.conflicted || this.theirconflicted) { - this.show_conflict(); - } else { - this.hide_conflict(); - } -}; - -Session.prototype.reset_conflicts = function() { - this.conflict_level = undefined; - this.highest_conflict = undefined; - this.conflicted = false; - this.conflicted_direction = undefined; - this.theirconflict_level = undefined; - this.highest_theirconflict = undefined; - this.theirconflicted = false; -}; - -Session.prototype.show_personconflict = function() { - if(_conflict_debug) { - console.log("showing person conflict for", this.title, this.conflict_level.ours); - } - this.personconflict_element().removeClass("hidepersonconflict"); - this.personconflict_element().addClass("showpersonconflict"); -}; -Session.prototype.hide_personconflict = function() { - if(_conflict_debug) { - console.log("removing person conflict for", this.title); - } - this.personconflict_element().addClass("hidepersonconflict"); - this.personconflict_element().removeClass("showpersonconflict"); -}; -Session.prototype.display_personconflict = function() { - if(this.person_conflicted) { - this.show_personconflict(); - } else { - this.hide_personconflict(); - } -}; -Session.prototype.add_personconflict = function(conflict) { - this.person_conflicted = true; -}; - -Session.prototype.examine_people_conflicts = function() { - /* - * the scan for people conflicts has to be done after the fill_in_constraints - * because we don't know which sessions the people will need to attend until - * all the constraints have been examined. - */ - var session_obj = this; - - // reset people conflicts. - session_obj.person_conflicted = false; - - for(ccn in session_obj.column_class_list) { - var vertical_location = session_obj.column_class_list[ccn].column_tag; - var room_tag = session_obj.column_class_list[ccn].room_tag; - - if(session_obj.constraints.bethere != null) { - if(_person_bethere_debug) { - console.log("examining bethere constraints for", session_obj.title); - } - $.each(session_obj.constraints.bethere, function(i) { - var conflict = session_obj.constraints.bethere[i]; - find_person_by_href(conflict.person_href).done(function(person) { - conflict.person = person; - if(_person_bethere_debug) { - console.log("examining", person.ascii," bethere constraints for", session_obj.title); - } - if(person.conflicted_time(vertical_location)) { - session_obj.add_personconflict(conflict); - } - }); - }); - } - } -} - - -Session.prototype.area_scheme = function() { - return this.area.toUpperCase() + "-scheme"; -}; - -Session.prototype.is_bof = function() { - return this.bof == "True"; -} -Session.prototype.wg_scheme = function() { - if(this.is_bof()) { - return "bof_style"; - } else { - return "wg_style"; - } -}; - -Session.prototype.add_column_class = function(column_class) { - if(__column_class_debug) { - console.log("adding:",column_class, "to ", this.title); - } - this.column_class_list.push(column_class); -}; - -var _LAST_MOVED_OLD; -var _LAST_MOVED_NEW; -// timeslot_list is a list of slots where the session has been located. -// bucket_list is a boolean. -Session.prototype.update_column_classes = function(timeslot_list, bucket_list) { - - // COLUMN CLASSES MUST BE A LIST because of multiple slot use - console.log("updating column_classes for ", this.title); - - var old_column_classes = this.column_class_list; - if(old_column_classes.length == 0) { - console.log("old column class was undefined for session:", session.title); - old_column_classes = [new ColumnClass()]; - } - - // zero out list. - this.column_class_list = []; - var new_column_tag = "none"; - if(bucket_list) { - this.on_bucket_list(); - - } else { - for(tsn in timeslot_list) { - var ts = timeslot_list[tsn]; - console.log("timeslot_list", tsn, ts); - this.add_column_class(ts.column_class); - } - new_column_tag = this.column_class_list[0].column_tag; - } - - var old_column_class_name = "none"; - if(old_column_classes != undefined && - old_column_classes[0] != undefined) { - old_colum_class_name = old_column_classes[0].column_tag; - } - - console.log("setting column_class for ",this.title," to ", - new_column_tag, "was: ", old_column_class_name); - - console.log("unset conflict for ",this.title," is ", this.conflicted); - - _LAST_MOVED_OLD = old_column_classes; - _LAST_MOVED_NEW = this.column_class_list; - - this.group.del_column_classes(old_column_classes); - this.group.add_column_classes(this.column_class_list); - recalculate_conflicts_for_session(this, old_column_classes, this.column_class_list); -}; - - -// utility/debug function, draws all events. -function update_all_templates() { - for(key in agenda_globals.meeting_objs) { - session = agenda_globals.meeting_objs[key]; - var slot = session.slot_status_key; - if(slot != null) { - session.repopulate_event(slot); - } - } -} - -Session.prototype.event_template = function() { - // the extra div is present so that the table can have a border which does not - // affect the total height of the box. The border otherwise screws up the height, - // causing things to the right to avoid this box. - var area_mark = ""; - if(this.responsible_ad != undefined) { - area_mark = this.responsible_ad.area_mark; - if(area_mark == undefined) { - // This doesn't seem to do the right thing - // area_mark = "ad:" + this.responsible_ad.href; - area_mark = ""; - } - } - - var bucket_list_style = "meeting_box_bucket_list" - if(this.is_placed) { - bucket_list_style = ""; - area_mark = ""; /* no mark for unplaced items: it goes to the wrong place */ - } - if(this.double_wide) { - bucket_list_style = bucket_list_style + " meeting_box_double"; - } - - var pinned = ""; - if(this.pinned) { - bucket_list_style = bucket_list_style + " meeting_box_pinned"; - pinned="P"; - } - - var groupacronym = "nogroup"; - if(this.group != undefined) { - groupacronym = this.group.acronym; - } - //console.log("acronym", groupacronym, this.group.acronym, this.visible_title()); - - var durationstring=""; - if (this.requested_duration!="0.0") { - durationstring = " ("+this.requested_duration+")" - } - // see comment in ietf.ccs, and - // http://stackoverflow.com/questions/5148041/does-firefox-support-position-relative-on-table-elements - return "
    "+pinned+"
    "+ - this.visible_title()+ - "" + durationstring + "" + - "
    "+ area_mark +"
    "; -}; - -function andthen_alert(object, result, arg) { - alert("result: "+result+" on obj: "+object); -}; - -Session.prototype.generate_info_table = function() { - $("#info_grp").html(name_select_html); - if(this.is_bof()) { - $("#grp_type").html("BOF"); - } else { - $("#grp_type").html("WG"); - } - - $("#info_name_select").val($("#info_name_select_option_"+this.session_id).val()); - if(this.description.length > 33) { - $("#info_name").html(""+this.description.substring(0,35)+"..."); - } else { - $("#info_name").html(this.description); - } - $("#info_area").html(""+this.area+""); - $("#info_duration").html(this.requested_duration); - if(this.attendees == "None") { - $("#info_capacity").text("size unknown"); - } else { - $("#info_capacity").text(this.attendees + " people"); - } - - if(!read_only) { - $("#info_location").html(generate_select_box()+""); - } - - var special_requests_text = ''; - if(this.joint_with_groups) { - special_requests_text += 'Joint session with ' + this.joint_with_groups.join(', ') + '. '; - } - if(this.constraints.wg_adjacent) { - for (var target_href in this.constraints.wg_adjacent) { - if (this.constraints.wg_adjacent.hasOwnProperty(target_href)) { - special_requests_text += 'Schedule adjacent with ' + this.constraints.wg_adjacent[target_href].othergroup.acronym + '. '; - } - } - } - if(this.constraints.time_relation) { - special_requests_text += this.constraints.time_relation.time_relation.time_relation_display + '. '; - } - if(this.constraints.timerange) { - special_requests_text += this.constraints.timerange.timerange.timeranges_display + '. '; - } - if("comments" in this && this.comments.length > 0 && this.comments != "None") { - special_requests_text += this.comments; - } else { - special_requests_text += "Special requests: None"; - } - $("#special_requests").text(special_requests_text); - - this.selectit(); - - if(this.slot != undefined) { - ss = this.slot; - if(ss.timeslot_id == null){ - $("#info_location_select").val(agenda_globals.meeting_objs[ss.assignment_id]); - }else{ - $("#info_location_select").val(ss.timeslot_id); // *** - } - $("#info_location_select").val($("#info_location_select_option_"+ss.timeslot_id).val()); - } - - //console.log("ad for session",this.session_id,"is",this.responsible_ad); - if(this.responsible_ad) { - this.responsible_ad.populate_responsible_ad(); - } - $("#info_requestedby").html(this.requested_by +" ("+this.requested_time+")"); - - listeners(); -}; - -function load_all_groups() { - for(key in agenda_globals.meeting_objs) { - session = agenda_globals.meeting_objs[key]; - session.group = find_group_by_href(session.group_href, "load all"); - } -} - -var __DEBUG_THIS_SLOT; -Session.prototype.retrieve_constraints_by_session = function() { - __DEBUG_THIS_SLOT = this; - //console.log("4 retrieve loaded:", this.title, this.constraints_loaded, "loading:", this.constraints_loading); - - if(this.constraints_promise != undefined) { - return this.constraints_promise; - } - - var session_obj = this; - var href = meeting_base_url+'/session/'+session_obj.session_id+"/constraints.json"; - - this.constraints_promise = $.ajax(href); - this.constraints_loading = true; - this.constraints_promise.success(function(newobj, status, jq) { - session_obj.fill_in_constraints(newobj); - session_obj.constraints_loaded = true; - session_obj.constraints_loading = false; - find_and_populate_conflicts(session_obj); - }); - - return this.constraints_promise; -}; - -var __verbose_person_conflicts = false; -Session.prototype.calculate_bethere = function() { - var session_obj = this; - - if("bethere" in this.constraints) { - $.each(this.constraints["bethere"], function(index) { - var bethere = session_obj.constraints["bethere"][index]; - find_person_by_href(bethere.person_href).done(function(person) { - if(__verbose_person_conflicts) { - console.log("person",person.ascii,"attends session",session_obj.group.acronym); - } - person.attend_session(session_obj); - }); - }); - } -}; - -Session.prototype.fill_in_constraints = function(constraint_list) { - var session_obj = this; - $.each(constraint_list, function(key){ - thing = constraint_list[key]; - session_obj.add_constraint_obj(thing); - }); - - // here we can sort the constraints by group name. - // make a single list. this.constraints is not an array, can not use concat. - this.conflicts = []; - $.each(Object.keys(agenda_globals.group_conflict_labels), (i, conflict_name) => { - if (conflict_name in this.constraints) { - $.each(this.constraints[conflict_name], (j, conflict) => { - session_obj.conflicts.push(conflict); - }); - } - if (conflict_name in this.theirconstraints) { - $.each(this.theirconstraints[conflict_name], (j, conflict) => { - session_obj.conflicts.push(conflict); - }); - } - }); - this.calculate_bethere(); - this.conflicts = sort_conflict_list(this.conflicts) -}; - -// ++++++++++++++++++ -// Group Objects -function Group() { - this.andthen_list = []; /* should be removed, or replaced with promise */ - this.all_sessions = []; -} - -Group.prototype.loaded_andthen = function() { - me = this; - $.each(this.andthen_list, function(index, andthen) { - andthen(me); - }); - this.andthen_list = []; -}; - -Group.prototype.load_group_obj = function(andthen) { - //console.log("group ",this.href); - var group_obj = this; - - if(!this.loaded && !this.loading) { - this.loading = true; - this.andthen_list.push(andthen); - $.ajax(this.href, - { - success: function(newobj, status, jqXHR) { - if(newobj) { - $.extend(group_obj, newobj); - group_obj.loaded = true; - } - group_obj.loading = false; - group_obj.loaded_andthen(); - }, - error: function(jqXHR, textStatus, errorThrown ) { - console.log("error loading ",group_obj.href," textStatus: ", textStatus,errorThrown); - group_obj.loading = false; - group_obj.loaded = true; // white lie - group_obj.load_error = true; - } - }); - } else { - if(!this.loaded) { - // queue this continuation for later. - this.andthen_list.push(andthen); - } else { - this.loading = false; - andthen(group_obj); - } - } -} - -Group.prototype.add_session = function(session) { - if(this.all_sessions == undefined) { - this.all_sessions = []; - } - this.all_sessions.push(session); -}; - -var __DEBUG_GROUP_COLUMN_CLASSES = false; -Group.prototype.add_column_class = function(column_class) { - if(this.column_class_list == undefined) { - this.column_class_list = []; - } - if(__DEBUG_GROUP_COLUMN_CLASSES) { - console.log("group",this.acronym,"adding column_class",column_class); - } - this.column_class_list.push(column_class); -}; -Group.prototype.del_column_class = function(column_class) { - if(__DEBUG_GROUP_COLUMN_CLASSES) { - console.log("group",this.acronym,"del column_class",column_class); - } - for(n in this.column_class_list) { - if(this.column_class_list[n] == column_class) { - this.column_class_list.splice(n,1); - } - } -}; - -Group.prototype.add_column_classes = function(column_class_list) { - for(ccn in column_class_list) { - cc = column_class_list[ccn]; - this.add_column_class(cc); - } -}; -Group.prototype.del_column_classes = function(column_class_list) { - for(ccn in column_class_list) { - cc = column_class_list[ccn]; - this.del_column_class(cc); - } -}; - -var __debug_group_load = false; -function create_group_by_href(href) { - if(agenda_globals.group_objs[href] == undefined) { - agenda_globals.group_objs[href]=new Group(); - var g = agenda_globals.group_objs[href]; - if(__debug_group_load) { - console.log("creating new group for", href); - } - g.loaded = false; - g.loading= false; - } - return agenda_globals.group_objs[href]; -} - -function load_group_by_href(href) { - var g = agenda_globals.group_objs[href]; - if(!g.loaded) { - g.href = href; - if(__debug_group_load) { - console.log("loading group href", href); - } - g.load_group_obj(function() {}); - } - return g; -} - -// takes a json that has at least a "href" member, -// and finds or creates the object. Any additional -// fields are added to the group object, and the group -// is marked loaded. The resulting group object is returned. -function load_group_from_json(json) { - var g = create_group_by_href(json.href); - for(var key in json) { - if(json[key].length > 0) { - g[key]=json[key]; - } - } - g.loaded = true; - g.loading= false; - return g; -} - -var group_references = 0; -var group_demand_loads = 0; -function find_group_by_href(href, msg) { - group_references++; - var g=agenda_globals.group_objs[href]; - if(g == undefined) { - group_demand_loads++; - if(__debug_group_load) { - console.log("loading",href,"because of",msg); - } - g = create_group_by_href(href); - load_group_by_href(href); - } - //console.log("finding group by ", href, "gives: ", g); - return g; -} - -// ++++++++++++++++++ -// Constraint Objects -function Constraint() { -// fields: (see ietf.meeting.models Constraint.json_dict) -// -// -constraint_id -// -href -// -name -- really the conflict_type, which will get filled in -// -person/_href -// -source/_href -// -target/_href -// -meeting/_href -// -} - -var conflict_classes = {}; - -function clear_conflict_classes() { - // remove all conflict boxes from before - $.each(Object.keys(agenda_globals.group_conflict_labels), (i, conflict_name) => { - $(".show_" + conflict_name + "_specific_box").removeClass("show_" + conflict_name + "_specific_box"); - }); - - // reset all column headings - $(".show_conflict_view_highlight").removeClass("show_conflict_view_highlight"); -} -function find_conflict(domid) { - return conflict_classes[domid]; -} - -Constraint.prototype.column_class_list = function() { - return this.othergroup.column_class_list; -}; - -/* N.B., handling new conflict types as equivalent to the originals for prioritization. - * If this editor is not replaced by the new one, it might be worth sorting out how to - * properly prioritize. Otherwise, this maintains the behavior seen prior to the introduction - * of the new conflicts. */ -Constraint.prototype.conflict1P = function() { - return ((this.conflict_type === "conflict") || (this.conflict_type === "chair_conflict")); -}; - -Constraint.prototype.conflict2P = function() { - return ((this.conflict_type === "conflic2") || (this.conflict_type === "tech_overlap")); -}; - -Constraint.prototype.conflict3P = function() { - return ((this.conflict_type === "conflic3") || (this.conflict_type === "key_participant")); -}; - -Constraint.prototype.conflict_groupP = function() { - return this.conflict_type in agenda_globals.group_conflict_labels; -}; - -Constraint.prototype.conflict_peopleP = function() { - return (this.conflict_type === "bethere") -}; - -Constraint.prototype.conflict_compare = function(oflict) { - if(this.conflict_peopleP()) { - return oflict; - } - if(this.conflict1P()) { - /* "conflict" is highest, return it. */ - return this; - } - if(this.conflict2P() && oflict.conflict3P()) { - /* "conflic2" > "conflic3" */ - return this; - } - /* self > 2, so otype would win */ - return oflict; -}; - -// red is arbitrary here... There should be multiple shades of red for -// multiple types of conflicts. - - - -var __CONSTRAINT_DEBUG = null; -var __column_class_debug = false; - -// one used to get here by having the conflict boxes enabled/disabled, but they were -// removed from the UI. -// when a session is selected, the conflict boxes are filled in, -// and then they are all clicked in order to highlight everything. -Constraint.prototype.show_conflict_view = function() { - classes=this.column_class_list(); - if(classes == undefined) { - classes = [] - } - //console.log("show_conflict_view", this); - __CONSTRAINT_DEBUG = this; - if(__column_class_debug) { - console.log("show conflict", this.href, "classes", classes.length, this); - } - - // this highlights the column headings of the sessions that conflict. - for(ccn in classes) { - var cc = classes[ccn]; // cc is a ColumnClass now - - if(cc != undefined) { - /* this extracts the day from this structure */ - var th_tag = cc.th_tag; - if(__column_class_debug) { - console.log("add conflict for column_class", this.session.title, th_tag); - } - $(th_tag).addClass("show_conflict_view_highlight"); - } else { - console.log("cc is undefined for ccn:",ccn); - } - } - - // this highlights the conflicts themselves - //console.log("make box", this.thisgroup.href); - sessions = this.othergroup.all_sessions - // set class to like: .show_conflict_specific_box - conflict_class = "show_"+this.conflict_type+"_specific_box"; - if(sessions) { - $.each(sessions, function(key) { - //console.log("2 make box", key); - this.element().addClass(conflict_class); - }); - } - //console.log("viewed", this.thisgroup.href); -}; - -Constraint.prototype.populate_conflict_classes = function() { - // this is used for the red square highlighting. - var checkbox_id = "conflict_"+this.dom_id; - conflict_classes[checkbox_id] = this; -}; - -// Made dead by change to how the group view is built out -//Constraint.prototype.build_group_conflict_view = function() { -// -// var display = { 'conflict':'1' , 'conflic2':'2' , 'conflic3':'3' }; -// -// // this is used for the red square highlighting. -// var checkbox_id = "conflict_"+this.dom_id; -// conflict_classes[checkbox_id] = this; -// -// build = "
    "; -// if (this.direction=='theirs') { -// build += display[this.conflict_type]+"->"; -// } -// build += this.othergroup_name; -// if (this.direction=='ours') { -// build += "->"+display[this.conflict_type]; -// } -// build += "
    "; -// -// return build -// -//}; - -Constraint.prototype.build_people_conflict_view = function() { - var area_mark = ""; - if(this.person != undefined && this.person.area_mark_basic != undefined) { - area_mark = this.person.area_mark_basic; - } - return "
    "+area_mark+"
    "; -}; - -Constraint.prototype.build_othername = function() { - if(this.othergroup.load_error) { - console.log("request for unloaded group: ",this.othergroup.href); - var patt = /.*\/group\//; // ugly assumption about href structure. - var base = this.othergroup.href.replace(patt,"") - this.othergroup_name = base.replace(".json","") - } else { - this.othergroup_name = this.othergroup.acronym; - } -}; - -// subclasses would make some sense here. -Constraint.prototype.conflict_view = function() { - this.dom_id = "constraint_"+this.constraint_id; - - if(this.conflict_peopleP()) { - return this.build_people_conflict_view(); - } - else { - // This function is currently never called for this case - console.log("!! unexpected conflict_view for", this.href); - //this.build_othername(); - //return this.build_group_conflict_view(); - } -}; - -var _constraint_load_debug = false; -// SESSION CONFLICT OBJECTS -// take an object and add attributes so that it becomes a session_conflict_obj. -// note that constraints are duplicated: each session has both incoming and outgoing constraints added. -Session.prototype.add_constraint_obj = function(obj) { - // turn this into a Constraint object - // can not print or JSONify these on ff as this has cyclic references. Chrome can. - //console.log("session: ", this); - //console.log("add_constraint: ",obj.constraint_id, obj.name); - - obj2 = new Constraint(); - $.extend(obj2, obj); - - obj = obj2; - obj.session = this; - - var listname = obj.name; - obj.conflict_type = listname; - if(this.constraints[listname]==undefined) { - this.constraints[listname]={}; - } - if(this.theirconstraints[listname]==undefined) { - this.theirconstraints[listname]={}; - } - - if(listname == "bethere") { - //console.log("bethere constraint: ", obj); - var person_href = obj.person_href; - var session = this; - this.constraints[listname][person_href]=obj; - find_person_by_href(person_href).done(function(person) { - if(_constraint_load_debug) { - console.log("recorded bethere constraint: ",person.ascii,"for group",session.group.acronym); - } - obj.person = person; - }); - } else { - // must be conflic*, timerange, time_relation or wg_adjacent - var ogroupname; - if(obj.source_href == this.group_href) { - obj.thisgroup = this.group; - obj.othergroup = find_group_by_href(obj.target_href, "constraint src"+obj.href); - obj.direction = 'ours'; - if (obj.target_href) { - ogroupname = obj.target_href; - } else { - ogroupname = obj.name; - } - if(this.constraints[listname][ogroupname]) { - console.log("Found multiple instances of",this.group_href,listname,ogroupname); - } - this.constraints[listname][ogroupname] = obj - } else { - obj.thisgroup = this.group; - obj.othergroup = find_group_by_href(obj.source_href, "constraint dst"+obj.href); - obj.direction = 'theirs'; - ogroupname = obj.source_href; - if(this.theirconstraints[listname][ogroupname]) { - console.log("Found multiple instances of",ogroupname,listname,this.group_href); - } - this.theirconstraints[listname][ogroupname] = obj - } - - } - -}; - -function constraint_compare(a, b) -{ - if(a==undefined || a.othergroup == undefined) { - return -1; - } - if(b==undefined || b.othergroup == undefined) { - return 1; - } - return (a.othergroup.href > b.othergroup.href ? 1 : -1); -} - -function sort_conflict_list(things) { - var keys = Object.keys(things); - var keys1 = keys.sort(function(a,b) { - return constraint_compare(things[a],things[b]); - }); - var newlist = []; - for(i=0; i"; - this.area_mark += "AD"; - this.area_mark += ""; - this.area_mark += adnum; - this.area_mark += ""; - - this.area_mark_basic = ""; - this.area_mark_basic += adnum; - this.area_mark_basic += ""; -}; - -Person.prototype.populate_responsible_ad = function() { - var area_mark = ""; - if(this.area_mark != undefined) { - area_mark = this.area_mark_basic; - } - $("#info_responsible").html(this.name + area_mark); -}; - -var _person_bethere_debug = false; -// this marks a person as needing to attend a session -// in a particular timeslot. -Person.prototype.clear_session = function(session, old_column_classes) { - for(ccn in old_column_class_list) { - var vertical_location = session.column_class_list[ccn].column_tag; - var room_tag = session.column_class_list[ccn].room_tag; - - if(_person_bethere_debug) { - console.log("person",this.ascii,"maybe no longer attending session", - session.session_id, "in room",room_tag); - } - - // probably should make it dict, to make removal easier - if(this.sessions == undefined) { - continue; - } - - if(this.sessions[vertical_location] == undefined) { - continue; - } - - if(room_tag in this.sessions[vertical_location]) { - delete this.sessions[vertical_location][room_tag]; - if(_person_bethere_debug) { - console.log("person: ",this.ascii,"removed from room", - room_tag, "at", vertical_location); - } - } - } -}; - -Person.prototype.attend_session = function(session) { - for(ccn in session.column_class_list) { - var vertical_location = session.column_class_list[ccn].column_tag; - var room_tag = session.column_class_list[ccn].room_tag; - - if(_person_bethere_debug) { - console.log("person",this.ascii,"maybe attending session", session.session_id, "in room",room_tag); - } - - // probably should make it dict, to make removal easier - if(this.sessions == undefined) { - this.sessions = []; - } - if(this.sessions[vertical_location] == undefined) { - this.sessions[vertical_location] = []; - } - - - if(!(room_tag in this.sessions[vertical_location])) { - this.sessions[vertical_location][room_tag]=true; - if(_person_bethere_debug) { - console.log("person: ",this.ascii,"needs to be in room", - room_tag, "at", vertical_location); - } - } - } -}; - -Person.prototype.conflicted_time = function(vertical_location) { - var yesno = this.conflicted_time1(vertical_location); - //console.log("person: ",this.ascii,"examining for", vertical_location, "gives",yesno); - return yesno; -} - -Person.prototype.conflicted_time1 = function(vertical_location) { - if(this.sessions == undefined) { - return false; - } - - if(this.sessions[vertical_location] == undefined) { - return false; - } - - var placestobe = Object.keys(this.sessions[vertical_location]); - if(placestobe.length > 1) { - return true; - } else { - return false; - } -}; - - -/* - * Local Variables: - * c-basic-offset:4 - * End: - */ diff --git a/ietf/templates/meeting/edit_meeting_schedule.html b/ietf/templates/meeting/edit_meeting_schedule.html index e834215d0..5d3c60271 100644 --- a/ietf/templates/meeting/edit_meeting_schedule.html +++ b/ietf/templates/meeting/edit_meeting_schedule.html @@ -49,7 +49,7 @@ · {% endif %} - New agenda + Copy agenda · Other Agendas diff --git a/ietf/templates/meeting/landscape_edit.html b/ietf/templates/meeting/landscape_edit.html deleted file mode 100644 index 85e0ef9d6..000000000 --- a/ietf/templates/meeting/landscape_edit.html +++ /dev/null @@ -1,388 +0,0 @@ -{% extends "base.html" %} -{# Copyright The IETF Trust 2015, All Rights Reserved #} -{% load origin %} -{% load static %} -{% load ietf_filters %} -{% load humanize %} - -{% block morecss %} -{% for area in area_list %} -.{{ area.upcase_acronym}}-scheme, .meeting_event th.{{ area.upcase_acronym}}-scheme, #{{ area.upcase_acronym }}-groups, #selector-{{ area.upcase_acronym }} { color:{{ area.fg_color }}; background-color: {{ area.bg_color }} } -.director-mark-{{ area.upcase_acronym}} { - border: 2px solid {{ area.fg_color}}; - color:{{ area.fg_color }}; - background-color: {{ area.bg_color }} -} -{% endfor %} -{% endblock morecss %} - -{% block title %}IETF {{ meeting.number }} Meeting Agenda{% endblock %} -{% load agenda_custom_tags %} -{% block pagehead %} - - -{% endblock pagehead %} - -{% block js %} - - - - - - - - - - - - - - - - - - - -{% endblock js %} - - -{% block content %} - {% origin %} -
    -

    You do not have write permission to agenda: {{schedule.name}}

    - {% if schedule.is_official_record %} -

    This is the official schedule for a meeting in the past.

    - {% endif %} -

    Please save this agenda to your account first.

    -
    - -
    {% comment %} For preserving styling across the facelift {% endcomment %} - -
    - - -
    - -
    -
    hidden rooms:0/{{rooms|length}}
    -
    -
    -
    -
    hidden days:0/{{time_slices|length}}
    -
    -
    - -
    - -
    - -
    -
    -
    - < -
    - Unassigned Events: - name: {{schedule.name}} -
    -
    -
    -
    - -
    - - - - - - - - - - - - - - - - - - -{%comment%}{%endcomment%} -
    - - - {% comment %}{% endcomment %} -{% for day in time_slices %} - - -{% endfor %} - - - - - {% comment %}{% endcomment %} - {% for day in time_slices %} - {% for slot in date_slices|lookup:day %} - - - {% endfor %} - - - {% endfor %} - - - - - - {% for r in rooms %} - - -{% comment 'The secretariat is not using these features' %} - -{% endcomment %} - {% for day in time_slices %} - {% for slot in date_slices|lookup:day %} - - {% endfor %} - - {% endfor %} - - {% endfor %} -
    -
    x
    - {{day|date:'D'}} ({{day}}) - -
    -
    -
    -
    -
    {{slot.0|date:'Hi'}}-{{slot.1|date:'Hi'}}
    -
    X
    -
    {{r.name}} ({{r.capacity}})
    -
    -
    - {% for resource in r.resources.all %} - - {{resource.desc}} - - {% endfor %} -
    -
    - - - -
    - - -
    -
    Session Information:
    - -
    -
    - - - - - - - - -
    Group: -
    Name:
    Area: -
    -
    - -
    -
    - -
    -
    - -
    -
    -
    - -
    - - - - - - - - - - -
    Duration/Capacity: -
    Location:
    Responsible AD:
    Requested By:
    -
    - -
    -
    - -
    -
    - -
    - -
    - -
    -
    -
    - -
    -
    Special Requests
    - - - - - - - - - - - -
    - Group conflicts - -
      -
    -
    - be present - -
      -
    -
    -
    -
    - {% for area in area_list %} - {{area.upcase_acronym}} - {% endfor %} -
    -
    - -
    - -
    Agenda name: {{schedule.name}}
    - {% if can_edit_properties %} -
    Properties Edit
    - {% endif %} -
    -
    {% csrf_token %} - {{ saveas.as_p }} - -
    -
    -
    - -
    - -
    {% comment %} End of .content div {% endcomment %} - -{% endblock %} diff --git a/ietf/templates/meeting/schedule_list.html b/ietf/templates/meeting/schedule_list.html index a51305431..c482c0936 100644 --- a/ietf/templates/meeting/schedule_list.html +++ b/ietf/templates/meeting/schedule_list.html @@ -38,12 +38,12 @@ {% for schedule in schedules %} - {{ schedule.name }} + {{ schedule.name }} {{ schedule.owner }} {% if schedule.origin %} - {{ schedule.origin.name }} + {{ schedule.origin.name }} +{{ schedule.changes_from_origin }} {% endif %}