add pending and detail views and tests.
- Legacy-Id: 11125
This commit is contained in:
parent
8f8e4df650
commit
be6e536769
|
@ -3,18 +3,19 @@ import re
|
|||
|
||||
from django import forms
|
||||
from django.core.validators import ValidationError
|
||||
from django.forms import BaseFormSet
|
||||
from django.forms.fields import Field
|
||||
from django.utils.encoding import force_text
|
||||
from django.utils import six
|
||||
|
||||
from ietf.group.models import Group
|
||||
from ietf.ietfauth.utils import has_role
|
||||
from ietf.meeting.models import Meeting, Schedule, TimeSlot, Session, SchedTimeSessAssignment, countries, timezones
|
||||
from ietf.meeting.models import Session, countries, timezones
|
||||
from ietf.meeting.helpers import assign_interim_session
|
||||
from ietf.utils.fields import DatepickerDateField
|
||||
|
||||
# need to insert empty option for use in ChoiceField
|
||||
countries.insert(0, ('', '-'*9 ))
|
||||
#countries.insert(0, ('', '-'*9 ))
|
||||
countries.insert(0, ('', ''))
|
||||
timezones.insert(0, ('', '-'*9 ))
|
||||
|
||||
# -------------------------------------------------
|
||||
|
@ -138,7 +139,7 @@ class BaseSessionFormSet(BaseFormSet):
|
|||
|
||||
class InterimRequestForm(forms.Form):
|
||||
group = GroupModelChoiceField(queryset = Group.objects.filter(type__in=('wg','rg'),state='active').order_by('acronym'))
|
||||
face_to_face = forms.BooleanField(required=False)
|
||||
in_person = forms.BooleanField(required=False)
|
||||
meeting_type = forms.ChoiceField(choices=(("single", "Single"), ("multi-day", "Multi-Day"), ('series','Series')), required=False, initial='single', widget=forms.RadioSelect)
|
||||
approved = forms.BooleanField(required=False)
|
||||
|
||||
|
@ -160,7 +161,8 @@ class InterimRequestForm(forms.Form):
|
|||
queryset = Group.objects.filter(type="rg", state="active").order_by('acronym')
|
||||
elif has_role(self.user, "WG Chair"):
|
||||
queryset = Group.objects.filter(type="wg", state="active", role__person=self.person, role__name="chair").distinct().order_by('acronym')
|
||||
|
||||
elif has_role(self.user, "RG Chair"):
|
||||
queryset = Group.objects.filter(type="rg", state="active", role__person=self.person, role__name="chair").distinct().order_by('acronym')
|
||||
self.fields['group'].queryset = queryset
|
||||
|
||||
# if there's only one possibility make it the default
|
||||
|
@ -168,10 +170,11 @@ class InterimRequestForm(forms.Form):
|
|||
self.fields['group'].initial = queryset[0]
|
||||
|
||||
class InterimSessionForm(forms.Form):
|
||||
date = DatepickerDateField(date_format="yyyy-mm-dd", picker_settings={"autoclose": "1" }, label='Date', required=True)
|
||||
time = forms.TimeField()
|
||||
# unset: date,time,duration
|
||||
date = DatepickerDateField(date_format="yyyy-mm-dd", picker_settings={"autoclose": "1" }, label='Date', required=False)
|
||||
time = forms.TimeField(required=False)
|
||||
time_utc = forms.TimeField(required=False)
|
||||
duration = DurationField()
|
||||
duration = DurationField(required=False)
|
||||
end_time = forms.TimeField(required=False)
|
||||
end_time_utc = forms.TimeField(required=False)
|
||||
remote_instructions = forms.CharField(max_length=1024,required=False)
|
||||
|
@ -196,17 +199,16 @@ class InterimSessionForm(forms.Form):
|
|||
time = self.cleaned_data.get('time')
|
||||
duration = self.cleaned_data.get('duration')
|
||||
remote_instructions = self.cleaned_data.get('remote_instructions')
|
||||
|
||||
slot = TimeSlot.objects.create(meeting=meeting, type_id="session", duration=duration,
|
||||
time=datetime.datetime.combine(date, time))
|
||||
time=datetime.datetime.combine(date, time)
|
||||
session = Session.objects.create(meeting=meeting,
|
||||
group=group,
|
||||
requested_by=person,
|
||||
requested_duration=duration,
|
||||
status_id='apprw',
|
||||
type_id='session',
|
||||
remote_instructions=remote_instructions,
|
||||
agenda_note=agenda_note,)
|
||||
SchedTimeSessAssignment.objects.create(timeslot=slot, session=session, schedule=meeting.agenda)
|
||||
|
||||
assign_interim_session(session,time)
|
||||
|
||||
if agenda:
|
||||
self._save_agenda(agenda)
|
||||
|
|
|
@ -17,8 +17,9 @@ import debug # pyflakes:ignore
|
|||
from ietf.doc.models import Document
|
||||
from ietf.group.models import Group
|
||||
from ietf.ietfauth.utils import has_role, user_is_person
|
||||
from ietf.liaisons.utils import get_person_for_user
|
||||
from ietf.person.models import Person
|
||||
from ietf.meeting.models import Meeting, Schedule
|
||||
from ietf.meeting.models import Meeting, Schedule, TimeSlot, SchedTimeSessAssignment
|
||||
from ietf.utils.history import find_history_active_at, find_history_replacements_active_at
|
||||
from ietf.utils.pipe import pipe
|
||||
|
||||
|
@ -285,6 +286,10 @@ def session_constraint_expire(request,session):
|
|||
if key is not None and cache.has_key(key):
|
||||
cache.delete(key)
|
||||
|
||||
# -------------------------------------------------
|
||||
# Interim Meeting Helpers
|
||||
# -------------------------------------------------
|
||||
|
||||
def get_earliest_session(session_formset):
|
||||
'''Return earliest InterimSessionForm from formset'''
|
||||
earliest = session_formset[0]
|
||||
|
@ -300,20 +305,79 @@ def get_next_interim_number(group,date):
|
|||
sequence = Meeting.objects.filter(number__startswith='interim-%s-%s' % (date.year,group.acronym)).count() + 1
|
||||
return 'interim-%s-%s-%s' % (date.year,group.acronym,sequence)
|
||||
|
||||
def create_interim_meeting(request_form,session_form):
|
||||
'''Create an Interim meeting, given an InterimRequestForm and InterimSessionForm'''
|
||||
group = request_form.cleaned_data.get('group')
|
||||
date = session_form.cleaned_data.get('date')
|
||||
def create_interim_meeting(group,date,city='',country='',timezone='UTC',person=None):
|
||||
'''Helper function to create interim meeting and associated schedule'''
|
||||
if not person:
|
||||
person = Person.objects.get(name="(System)")
|
||||
number = get_next_interim_number(group,date)
|
||||
city = session_form.cleaned_data.get('city')
|
||||
country = session_form.cleaned_data.get('country')
|
||||
timezone = session_form.cleaned_data.get('timezone')
|
||||
if not request_form.cleaned_data.get('face_to_face'):
|
||||
timezone = 'UTC'
|
||||
meeting = Meeting.objects.create(number=number,type_id='interim',date=date,city=city,
|
||||
country=country,time_zone=timezone)
|
||||
schedule = Schedule.objects.create(meeting=meeting, owner=request_form.person, visible=True, public=True)
|
||||
schedule = Schedule.objects.create(meeting=meeting, owner=person, visible=True, public=True)
|
||||
meeting.agenda = schedule
|
||||
meeting.save()
|
||||
return meeting
|
||||
|
||||
def create_interim_meeting_from_forms(request_form,session_form):
|
||||
'''Create an Interim meeting, given an InterimRequestForm and InterimSessionForm'''
|
||||
group = request_form.cleaned_data.get('group')
|
||||
date = session_form.cleaned_data.get('date')
|
||||
city = session_form.cleaned_data.get('city')
|
||||
country = session_form.cleaned_data.get('country')
|
||||
timezone = session_form.cleaned_data.get('timezone')
|
||||
person = request_form.person
|
||||
return create_interim_meeting(group=group,date=date,city=city,country=country,timezone=timezone,person=person)
|
||||
|
||||
def assign_interim_session(session,time):
|
||||
'''Helper function to create a timeslot and assign the interim session'''
|
||||
slot = TimeSlot.objects.create(meeting=session.meeting, type_id="session",
|
||||
duration=session.requested_duration, time=time)
|
||||
SchedTimeSessAssignment.objects.create(timeslot=slot, session=session, schedule=session.meeting.agenda)
|
||||
|
||||
def can_approve_interim_request(meeting,user):
|
||||
'''Returns True if the user has permissions to approve an interim meeting request'''
|
||||
if meeting.type.slug != 'interim':
|
||||
return False
|
||||
if has_role(user, 'Secretariat'):
|
||||
return True
|
||||
person = get_person_for_user(user)
|
||||
session = meeting.session_set.first()
|
||||
if not session:
|
||||
return False
|
||||
group = session.group
|
||||
if group.type.slug == 'wg' and group.parent.role_set.filter(name='ad',person=person):
|
||||
return True
|
||||
if group.type.slug == 'rg' and group.parent.role_set.filter(name='chair',person=person):
|
||||
return True
|
||||
return False
|
||||
|
||||
def can_edit_interim_request(meeting,user):
|
||||
'''Returns True if the user can edit the interim meeting request'''
|
||||
|
||||
if can_approve_interim_request(meeting,user):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def can_request_interim_meeting(user):
|
||||
if has_role(user, ('Secretariat','Area Director','WG Chair','IRTF Chair', 'RG Chair')):
|
||||
return True
|
||||
return False
|
||||
|
||||
def can_view_interim_request(meeting,user):
|
||||
'''Returns True if the user can see the pending interim request in the pending interim view'''
|
||||
if meeting.type.slug != 'interim':
|
||||
return False
|
||||
if has_role(user, 'Secretariat'):
|
||||
return True
|
||||
person = get_person_for_user(user)
|
||||
session = meeting.session_set.first()
|
||||
if not session:
|
||||
return False
|
||||
group = session.group
|
||||
if has_role(user, 'Area Director') and group.type.slug == 'wg':
|
||||
return True
|
||||
if has_role(user, 'IRTF Chair') and group.type.slug == 'rg':
|
||||
return True
|
||||
if group.role_set.filter(name='chair',person=person):
|
||||
return True
|
||||
return False
|
||||
|
|
|
@ -3,10 +3,21 @@ import datetime
|
|||
from ietf.doc.models import Document, State
|
||||
from ietf.group.models import Group
|
||||
from ietf.meeting.models import Meeting, Room, TimeSlot, Session, Schedule, SchedTimeSessAssignment, ResourceAssociation, SessionPresentation
|
||||
from ietf.meeting.helpers import create_interim_meeting, assign_interim_session
|
||||
from ietf.name.models import RoomResourceName
|
||||
from ietf.person.models import Person
|
||||
from ietf.utils.test_data import make_test_data
|
||||
|
||||
def make_interim_meeting(group,date,status='sched'):
|
||||
system_person = Person.objects.get(name="(System)")
|
||||
time = datetime.datetime.combine(date, datetime.time(9))
|
||||
meeting = create_interim_meeting(group=group,date=date)
|
||||
session = Session.objects.create(meeting=meeting, group=group,
|
||||
attendees=10, requested_by=system_person,
|
||||
requested_duration=20, status_id=status,
|
||||
scheduled=datetime.datetime.now(),type_id="session")
|
||||
assign_interim_session(session,time)
|
||||
return meeting
|
||||
|
||||
def make_meeting_test_data():
|
||||
if not Group.objects.filter(acronym='mars'):
|
||||
|
@ -79,29 +90,13 @@ def make_meeting_test_data():
|
|||
|
||||
# Future Interim Meetings
|
||||
date = datetime.date.today() + datetime.timedelta(days=365)
|
||||
mars_meeting = Meeting.objects.create(
|
||||
number="interim-%s-mars-1" % date.year,
|
||||
type_id='interim',
|
||||
date=date,
|
||||
city="New York",
|
||||
country="US",
|
||||
)
|
||||
mars_session = Session.objects.create(meeting=mars_meeting, group=mars,
|
||||
attendees=10, requested_by=system_person,
|
||||
requested_duration=20, status_id="sched",
|
||||
scheduled=datetime.datetime.now(),type_id="session")
|
||||
date2 = datetime.date.today() + datetime.timedelta(days=1000)
|
||||
ames = Group.objects.get(acronym="ames")
|
||||
ames_meeting = Meeting.objects.create(
|
||||
number="interim-%s-ames-1" % date.year,
|
||||
type_id='interim',
|
||||
date=date,
|
||||
city="New York",
|
||||
country="US",
|
||||
)
|
||||
ames_session = Session.objects.create(meeting=ames_meeting, group=ames,
|
||||
attendees=10, requested_by=system_person,
|
||||
requested_duration=20, status_id="canceled",
|
||||
scheduled=datetime.datetime.now(),type_id="session")
|
||||
|
||||
make_interim_meeting(group=mars,date=date,status='sched')
|
||||
make_interim_meeting(group=mars,date=date2,status='apprw')
|
||||
make_interim_meeting(group=ames,date=date,status='canceled')
|
||||
make_interim_meeting(group=ames,date=date2,status='apprw')
|
||||
|
||||
return meeting
|
||||
|
||||
|
|
|
@ -5,11 +5,13 @@ import urlparse
|
|||
|
||||
from django.core.urlresolvers import reverse as urlreverse
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
from pyquery import PyQuery
|
||||
|
||||
from ietf.doc.models import Document
|
||||
from ietf.group.models import Group
|
||||
from ietf.meeting.helpers import can_approve_interim_request, can_view_interim_request
|
||||
from ietf.meeting.models import Session, TimeSlot, Meeting
|
||||
from ietf.meeting.test_data import make_meeting_test_data
|
||||
from ietf.utils.test_utils import TestCase, login_testing_unauthorized, unicontent
|
||||
|
@ -332,6 +334,10 @@ class EditTests(TestCase):
|
|||
self.assertTrue(mars_slot.slot_to_the_right)
|
||||
self.assertTrue(mars_scheduled.slot_to_the_right)
|
||||
|
||||
# -------------------------------------------------
|
||||
# Interim Meeting Tests
|
||||
# -------------------------------------------------
|
||||
|
||||
class InterimTests(TestCase):
|
||||
def test_upcoming(self):
|
||||
make_meeting_test_data()
|
||||
|
@ -354,20 +360,30 @@ class InterimTests(TestCase):
|
|||
|
||||
def test_interim_request_permissions(self):
|
||||
'''Ensure only authorized users see link to request interim meeting'''
|
||||
# test unauthorized
|
||||
make_meeting_test_data()
|
||||
|
||||
# test unauthorized not logged in
|
||||
upcoming_url = urlreverse("ietf.meeting.views.upcoming")
|
||||
request_url = urlreverse("ietf.meeting.views.interim_request")
|
||||
r = self.client.get(upcoming_url)
|
||||
self.assertNotContains(r,'Request new interim meeting')
|
||||
|
||||
# test unauthorized user
|
||||
login_testing_unauthorized(self,"plain",request_url)
|
||||
r = self.client.get(upcoming_url)
|
||||
self.assertNotContains(r,'Request new interim meeting')
|
||||
r = self.client.get(request_url)
|
||||
self.assertRedirects(r, '/accounts/login/?next=/meeting/interim/request/')
|
||||
self.assertEqual(r.status_code, 403)
|
||||
self.client.logout()
|
||||
|
||||
# test authorized
|
||||
self.client.login(username="secretary", password="secretary+password")
|
||||
r = self.client.get(upcoming_url)
|
||||
self.assertContains(r,'Request new interim meeting')
|
||||
r = self.client.get(request_url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
for username in ('secretary','ad','marschairman','irtf-chair','irgchairman'):
|
||||
self.client.login(username=username, password= username + "+password")
|
||||
r = self.client.get(upcoming_url)
|
||||
self.assertContains(r,'Request new interim meeting')
|
||||
r = self.client.get(request_url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.client.logout()
|
||||
|
||||
def test_interim_request_options(self):
|
||||
make_meeting_test_data()
|
||||
|
@ -398,7 +414,7 @@ class InterimTests(TestCase):
|
|||
'form-0-duration':'03:00:00',
|
||||
'form-0-city':'',
|
||||
'form-0-country':'',
|
||||
'form-0-timezone':'',
|
||||
'form-0-timezone':'UTC',
|
||||
'form-0-remote_instructions':remote_instructions,
|
||||
'form-0-agenda':agenda,
|
||||
'form-0-agenda_note':agenda_note,
|
||||
|
@ -415,9 +431,9 @@ class InterimTests(TestCase):
|
|||
self.assertEqual(meeting.city,'')
|
||||
self.assertEqual(meeting.country,'')
|
||||
self.assertEqual(meeting.time_zone,'UTC')
|
||||
self.assertEqual(meeting.agenda_note,agenda_note)
|
||||
session = meeting.session_set.first()
|
||||
self.assertEqual(session.remote_instructions,remote_instructions)
|
||||
self.assertEqual(session.agenda_note,agenda_note)
|
||||
timeslot = session.official_timeslotassignment().timeslot
|
||||
self.assertEqual(timeslot.time,dt)
|
||||
self.assertEqual(timeslot.duration,duration)
|
||||
|
@ -460,9 +476,9 @@ class InterimTests(TestCase):
|
|||
self.assertEqual(meeting.city,city)
|
||||
self.assertEqual(meeting.country,country)
|
||||
self.assertEqual(meeting.time_zone,timezone)
|
||||
self.assertEqual(meeting.agenda_note,agenda_note)
|
||||
session = meeting.session_set.first()
|
||||
self.assertEqual(session.remote_instructions,remote_instructions)
|
||||
self.assertEqual(session.agenda_note,agenda_note)
|
||||
timeslot = session.official_timeslotassignment().timeslot
|
||||
self.assertEqual(timeslot.time,dt)
|
||||
self.assertEqual(timeslot.duration,duration)
|
||||
|
@ -516,11 +532,97 @@ class InterimTests(TestCase):
|
|||
self.assertEqual(meeting.city,city)
|
||||
self.assertEqual(meeting.country,country)
|
||||
self.assertEqual(meeting.time_zone,timezone)
|
||||
self.assertEqual(meeting.agenda_note,agenda_note)
|
||||
self.assertEqual(meeting.session_set.count(),2)
|
||||
for session in meeting.session_set.all():
|
||||
self.assertEqual(session.remote_instructions,remote_instructions)
|
||||
timeslot = session.official_timeslotassignment().timeslot
|
||||
self.assertEqual(timeslot.time,dt2)
|
||||
self.assertEqual(timeslot.duration,duration)
|
||||
# first sesstion
|
||||
session = meeting.session_set.all()[0]
|
||||
self.assertEqual(session.remote_instructions,remote_instructions)
|
||||
timeslot = session.official_timeslotassignment().timeslot
|
||||
self.assertEqual(timeslot.time,dt)
|
||||
self.assertEqual(timeslot.duration,duration)
|
||||
self.assertEqual(session.agenda_note,agenda_note)
|
||||
# second sesstion
|
||||
session = meeting.session_set.all()[1]
|
||||
self.assertEqual(session.remote_instructions,remote_instructions)
|
||||
timeslot = session.official_timeslotassignment().timeslot
|
||||
self.assertEqual(timeslot.time,dt2)
|
||||
self.assertEqual(timeslot.duration,duration)
|
||||
self.assertEqual(session.agenda_note,agenda_note)
|
||||
|
||||
def test_interim_pending(self):
|
||||
make_meeting_test_data()
|
||||
url = urlreverse('ietf.meeting.views.interim_pending')
|
||||
count = Meeting.objects.filter(type='interim',session__status='apprw').distinct().count()
|
||||
|
||||
# unpriviledged user
|
||||
login_testing_unauthorized(self,"plain",url)
|
||||
r = self.client.get(url)
|
||||
self.assertEqual(r.status_code, 403)
|
||||
|
||||
# secretariat
|
||||
login_testing_unauthorized(self,"secretary",url)
|
||||
r = self.client.get(url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertEqual(len(q("#pending-interim-meetings-table tr"))-1, count)
|
||||
self.client.logout()
|
||||
|
||||
def test_can_approve_interim_request(self):
|
||||
make_meeting_test_data()
|
||||
# unprivileged user
|
||||
user = User.objects.get(username='plain')
|
||||
group = Group.objects.get(acronym='mars')
|
||||
meeting = Meeting.objects.filter(type='interim',session__status='apprw',session__group=group).first()
|
||||
self.assertFalse(can_approve_interim_request(meeting=meeting,user=user))
|
||||
# Secretariat
|
||||
user = User.objects.get(username='secretary')
|
||||
self.assertTrue(can_approve_interim_request(meeting=meeting,user=user))
|
||||
# related AD
|
||||
user = User.objects.get(username='ad')
|
||||
self.assertTrue(can_approve_interim_request(meeting=meeting,user=user))
|
||||
# other AD
|
||||
user = User.objects.get(username='ops-ad')
|
||||
self.assertFalse(can_approve_interim_request(meeting=meeting,user=user))
|
||||
# WG Chair
|
||||
user = User.objects.get(username='marschairman')
|
||||
self.assertFalse(can_approve_interim_request(meeting=meeting,user=user))
|
||||
|
||||
def test_can_view_interim_request(self):
|
||||
make_meeting_test_data()
|
||||
# unprivileged user
|
||||
user = User.objects.get(username='plain')
|
||||
group = Group.objects.get(acronym='mars')
|
||||
meeting = Meeting.objects.filter(type='interim',session__status='apprw',session__group=group).first()
|
||||
self.assertFalse(can_view_interim_request(meeting=meeting,user=user))
|
||||
# Secretariat
|
||||
user = User.objects.get(username='secretary')
|
||||
self.assertTrue(can_view_interim_request(meeting=meeting,user=user))
|
||||
# related AD
|
||||
user = User.objects.get(username='ad')
|
||||
self.assertTrue(can_view_interim_request(meeting=meeting,user=user))
|
||||
# other AD
|
||||
user = User.objects.get(username='ops-ad')
|
||||
self.assertTrue(can_view_interim_request(meeting=meeting,user=user))
|
||||
# WG Chair
|
||||
user = User.objects.get(username='marschairman')
|
||||
self.assertTrue(can_view_interim_request(meeting=meeting,user=user))
|
||||
# Other WG Chair
|
||||
user = User.objects.get(username='ameschairman')
|
||||
self.assertFalse(can_view_interim_request(meeting=meeting,user=user))
|
||||
|
||||
def test_interim_request_details(self):
|
||||
make_meeting_test_data()
|
||||
meeting = Meeting.objects.filter(type='interim',session__status='apprw',session__group__acronym='mars').first()
|
||||
url = urlreverse('ietf.meeting.views.interim_request_details',kwargs={'number':meeting.number})
|
||||
login_testing_unauthorized(self,"secretary",url)
|
||||
r = self.client.get(url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
|
||||
def test_interim_request_details_permissions(self):
|
||||
make_meeting_test_data()
|
||||
meeting = Meeting.objects.filter(type='interim',session__status='apprw',session__group__acronym='mars').first()
|
||||
url = urlreverse('ietf.meeting.views.interim_request_details',kwargs={'number':meeting.number})
|
||||
|
||||
# unprivileged user
|
||||
login_testing_unauthorized(self,"plain",url)
|
||||
r = self.client.get(url)
|
||||
self.assertEqual(r.status_code, 403)
|
||||
|
|
|
@ -68,7 +68,9 @@ urlpatterns = [
|
|||
url(r'^(?P<num>\d+)/', include(type_ietf_only_patterns)),
|
||||
url(r'^upcoming/$', views.upcoming),
|
||||
url(r'^upcoming.ics/$', views.ical_upcoming),
|
||||
url(r'^interim/request/(?P<number>[A-Za-z0-9._+-]+)/$', views.interim_request_details),
|
||||
url(r'^interim/request/$', views.interim_request),
|
||||
url(r'^interim/pending/$', views.interim_pending),
|
||||
url(r'^$', views.current_materials),
|
||||
]
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ import pytz
|
|||
import debug # pyflakes:ignore
|
||||
|
||||
from django import forms
|
||||
from django.shortcuts import render, redirect
|
||||
from django.shortcuts import render, redirect, get_object_or_404
|
||||
from django.http import HttpResponse, HttpResponseRedirect, HttpResponseForbidden, Http404
|
||||
from django.contrib import messages
|
||||
from django.core.urlresolvers import reverse
|
||||
|
@ -37,19 +37,14 @@ from ietf.meeting.helpers import get_wg_list, find_ads_for_meeting
|
|||
from ietf.meeting.helpers import get_meeting, get_schedule, agenda_permissions, get_meetings
|
||||
from ietf.meeting.helpers import preprocess_assignments_for_agenda, read_agenda_file
|
||||
from ietf.meeting.helpers import convert_draft_to_pdf, get_earliest_session
|
||||
from ietf.meeting.helpers import create_interim_meeting
|
||||
from ietf.meeting.helpers import create_interim_meeting_from_forms
|
||||
from ietf.meeting.helpers import can_view_interim_request, can_approve_interim_request
|
||||
from ietf.meeting.helpers import can_request_interim_meeting
|
||||
from ietf.utils.pipe import pipe
|
||||
from ietf.utils.pdf import pdf_pages
|
||||
|
||||
from .forms import InterimRequestForm, InterimSessionForm
|
||||
|
||||
# -------------------------------------------------
|
||||
# Helper Functions
|
||||
# -------------------------------------------------
|
||||
def can_request_interim_meeting(user):
|
||||
if has_role(user, ('Secretariat','Area Director','WG Chair','IRTF Chair')):
|
||||
return True
|
||||
return False
|
||||
|
||||
# -------------------------------------------------
|
||||
# View Functions
|
||||
|
@ -907,8 +902,20 @@ def ajax_get_utc(request):
|
|||
utc = utc_dt.strftime('%H:%M')
|
||||
context_data = {'timezone':timezone,'time':time,'utc':utc}
|
||||
return HttpResponse(json.dumps(context_data),content_type='application/json')
|
||||
|
||||
@role_required('Area Director','Secretariat','IRTF Chair','WG Chair','RG Chair')
|
||||
def interim_pending(request):
|
||||
'''View which shows interim meeting requests pending approval'''
|
||||
meetings = Meeting.objects.filter(type='interim',session__status='apprw')
|
||||
|
||||
meetings = [ m for m in meetings if can_view_interim_request(m,request.user)]
|
||||
for meeting in meetings:
|
||||
if can_approve_interim_request(meeting,request.user):
|
||||
meeting.can_approve = True
|
||||
|
||||
@role_required('Area Director','Secretariat','IRTF Chair','WG Chair')
|
||||
return render(request, "meeting/interim_pending.html", {"meetings":meetings})
|
||||
|
||||
@role_required('Area Director','Secretariat','IRTF Chair','WG Chair', 'RG Chair')
|
||||
def interim_request(request):
|
||||
'''View for requesting an interim meeting'''
|
||||
SessionFormset = formset_factory(InterimSessionForm, extra=2)
|
||||
|
@ -916,20 +923,22 @@ def interim_request(request):
|
|||
if request.method == 'POST':
|
||||
form = InterimRequestForm(request, data=request.POST)
|
||||
formset = SessionFormset(data=request.POST)
|
||||
person = request.user.person
|
||||
#person = request.user.person
|
||||
if form.is_valid() and formset.is_valid():
|
||||
group = form.cleaned_data.get('group')
|
||||
meeting_type = form.cleaned_data.get('meeting_type')
|
||||
|
||||
# pre create meeting
|
||||
if meeting_type in ('single','multi-day'):
|
||||
meeting = create_interim_meeting(request_form=form,session_form=get_earliest_session(formset))
|
||||
meeting = create_interim_meeting_from_forms(
|
||||
request_form=form,
|
||||
session_form=get_earliest_session(formset))
|
||||
|
||||
for f in formset.forms:
|
||||
if not f.has_changed():
|
||||
continue
|
||||
if meeting_type == 'series':
|
||||
meeting = create_interim_meeting(form,f)
|
||||
meeting = create_interim_meeting_from_forms(form,f)
|
||||
f.save(request,group,meeting)
|
||||
return redirect(upcoming)
|
||||
else:
|
||||
|
@ -940,6 +949,21 @@ def interim_request(request):
|
|||
|
||||
return render(request, "meeting/interim_request.html", {"form":form, "formset":formset})
|
||||
|
||||
@role_required('Area Director','Secretariat','IRTF Chair','WG Chair', 'RG Chair')
|
||||
def interim_request_details(request, number):
|
||||
'''View details of an interim meeting reqeust'''
|
||||
meeting = get_object_or_404(Meeting,number=number)
|
||||
sessions = meeting.session_set.all()
|
||||
can_edit = can_view_interim_request(meeting,request.user)
|
||||
can_approve = can_approve_interim_request(meeting,request.user)
|
||||
|
||||
return render(request, "meeting/interim_request_details.html",{
|
||||
"meeting":meeting,
|
||||
"sessions":sessions,
|
||||
"can_edit":can_edit,
|
||||
"can_approve":can_approve,
|
||||
})
|
||||
|
||||
def ical_upcoming(request):
|
||||
'''ICAL upcoming meetings'''
|
||||
today = datetime.datetime.today()
|
||||
|
|
|
@ -5,22 +5,26 @@ var interimRequest = {
|
|||
// get elements
|
||||
interimRequest.form = $(this);
|
||||
interimRequest.addButton = $('#add_session');
|
||||
interimRequest.faceToFace = $('#id_face_to_face');
|
||||
interimRequest.inPerson = $('#id_in_person');
|
||||
// bind functions
|
||||
$('.select2-field').select2();
|
||||
$('#add_session').click(interimRequest.addSession);
|
||||
interimRequest.addButton.click(interimRequest.addSession);
|
||||
$('.btn-delete').click(interimRequest.deleteSession);
|
||||
$('#id_face_to_face').change(interimRequest.toggleLocation);
|
||||
$('input[name="meeting_type"]').change(interimRequest.checkAddButton);
|
||||
interimRequest.inPerson.change(interimRequest.toggleLocation);
|
||||
//$('input[name="meeting_type"]').change(interimRequest.checkAddButton);
|
||||
$('input[name="meeting_type"]').change(interimRequest.meetingTypeChanged);
|
||||
$('input[name$="-duration"]').blur(interimRequest.calculateEndTime);
|
||||
$('input[name$="-time"]').blur(interimRequest.calculateEndTime);
|
||||
//$('input[name$="-time"]').blur(interimRequest.setUTC);
|
||||
$('input[name$="-time"]').blur(interimRequest.updateInfo);
|
||||
$('input[name$="-end_time"]').change(interimRequest.updateInfo);
|
||||
$('select[name$="-timezone"]').change(interimRequest.timezoneChange);
|
||||
//interimRequest.form.submit(interimRequest.onSubmit);
|
||||
// init
|
||||
interimRequest.faceToFace.each(interimRequest.toggleLocation);
|
||||
interimRequest.inPerson.each(interimRequest.toggleLocation);
|
||||
interimRequest.checkAddButton();
|
||||
interimRequest.checkHelpText();
|
||||
//interimRequest.showRequired();
|
||||
},
|
||||
|
||||
addSession : function() {
|
||||
|
@ -53,13 +57,13 @@ var interimRequest = {
|
|||
setupSelect2Field($(this));
|
||||
});
|
||||
|
||||
//if(interimRequest.faceToFace.prop('checked')){
|
||||
var first_session = $(".fieldset:first");
|
||||
el.find("input[name$='city']").val(first_session.find("input[name$='city']").val());
|
||||
el.find("select[name$='country']").val(first_session.find("select[name$='country']").val());
|
||||
el.find("select[name$='timezone']").val(first_session.find("select[name$='timezone']").val());
|
||||
//}
|
||||
|
||||
// copy field contents
|
||||
var first_session = $(".fieldset:first");
|
||||
el.find("input[name$='city']").val(first_session.find("input[name$='city']").val());
|
||||
el.find("select[name$='country']").val(first_session.find("select[name$='country']").val());
|
||||
el.find("select[name$='timezone']").val(first_session.find("select[name$='timezone']").val());
|
||||
el.find("input[name$='remote_instructions']").val(first_session.find("input[name$='remote_instructions']").val());
|
||||
|
||||
if(meeting_type == 'multi-day'){
|
||||
el.find(".location").prop('disabled', true);
|
||||
}
|
||||
|
@ -67,6 +71,15 @@ var interimRequest = {
|
|||
$('.btn-delete').removeClass("hidden");
|
||||
},
|
||||
|
||||
onSubmit : function() {
|
||||
// must remove required attribute from non visible fields
|
||||
var template = $(this).find(".template");
|
||||
//template.find('input,textarea,select').filter('[required]').prop('required',false);
|
||||
var x = template.find('input,textarea,select')
|
||||
alert(x.length);
|
||||
return true;
|
||||
},
|
||||
|
||||
updateInfo : function() {
|
||||
//var url = liaisonForm.form.data("ajaxInfoUrl");
|
||||
//alert('called update');
|
||||
|
@ -124,6 +137,30 @@ var interimRequest = {
|
|||
interimRequest.addButton.show();
|
||||
}
|
||||
},
|
||||
|
||||
checkHelpText : function() {
|
||||
var meeting_type = $('input[name="meeting_type"]:checked').val();
|
||||
if(meeting_type == 'single'){
|
||||
$('.meeting-type-help').hide();
|
||||
} else if(meeting_type == 'multi-day') {
|
||||
$('.meeting-type-help').hide();
|
||||
$('.mth-multi').show();
|
||||
} else if(meeting_type == 'series') {
|
||||
$('.meeting-type-help').hide();
|
||||
$('.mth-series').show();
|
||||
}
|
||||
},
|
||||
|
||||
checkInPerson : function() {
|
||||
var meeting_type = $('input[name="meeting_type"]:checked').val();
|
||||
if(meeting_type == 'series'){
|
||||
interimRequest.inPerson.prop('disabled', true);
|
||||
interimRequest.inPerson.prop('checked', false);
|
||||
interimRequest.toggleLocation();
|
||||
} else {
|
||||
interimRequest.inPerson.prop('disabled', false);
|
||||
}
|
||||
},
|
||||
|
||||
get_formatted_time : function (d) {
|
||||
// returns time from Date object as HH:MM
|
||||
|
@ -151,6 +188,12 @@ var interimRequest = {
|
|||
return interimRequest.pad(hours) + ":" + interimRequest.pad(minutes);
|
||||
},
|
||||
|
||||
meetingTypeChanged : function () {
|
||||
interimRequest.checkAddButton();
|
||||
interimRequest.checkInPerson();
|
||||
interimRequest.checkHelpText();
|
||||
},
|
||||
|
||||
pad : function(str) {
|
||||
// zero pads string 00
|
||||
if(str.length == 1){
|
||||
|
@ -169,6 +212,12 @@ var interimRequest = {
|
|||
//alert(utc.attr("id"));
|
||||
},
|
||||
|
||||
showRequired : function() {
|
||||
// add a required class on labels on forms that should have
|
||||
// explicit requirement asterisks
|
||||
//$("form.show-required").find("input[required],select[required],textarea[required]").closest(".form-group").find("label").first().addClass("required");
|
||||
},
|
||||
|
||||
timezoneChange : function() {
|
||||
//alert("tz change");
|
||||
var fieldset = $(this).parents(".fieldset");
|
||||
|
|
62
ietf/templates/meeting/interim_pending.html
Normal file
62
ietf/templates/meeting/interim_pending.html
Normal file
|
@ -0,0 +1,62 @@
|
|||
{% extends "base.html" %}
|
||||
{# Copyright The IETF Trust 2015, All Rights Reserved #}
|
||||
{% load origin %}
|
||||
{% load staticfiles bootstrap3 widget_tweaks %}
|
||||
|
||||
{% block title %}Interim Pending{% endblock %}
|
||||
|
||||
{% block pagehead %}
|
||||
<link rel="stylesheet" href="{% static 'select2/select2.css' %}">
|
||||
<link rel="stylesheet" href="{% static 'select2-bootstrap-css/select2-bootstrap.min.css' %}">
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% origin %}
|
||||
<h1>Pending Interim Meetings</h1>
|
||||
|
||||
|
||||
{% if meetings %}
|
||||
<table id="pending-interim-meetings-table" class="table table-condensed table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Date</th>
|
||||
<th>Group</th>
|
||||
<th>Name</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for meeting in meetings %}
|
||||
{% if meeting.type.slug == 'interim' %}
|
||||
<tr id="row-{{ forloop.counter }}-{{ meeting.session_set.all.0.group.acronym }}">
|
||||
{% else %}
|
||||
<tr id="row-{{ forloop.counter }}-ietf">
|
||||
{% endif %}
|
||||
<td>{{ meeting.date }}</td>
|
||||
{% if meeting.type.slug == 'interim' %}
|
||||
<td>{{ meeting.session_set.all.0.group.acronym }}</td>
|
||||
{% else %}
|
||||
<td>ietf</td>
|
||||
{% endif %}
|
||||
<td>
|
||||
{% if meeting.type.slug == "interim" %}
|
||||
<a href="{% url 'ietf.meeting.views.interim_request_details' number=meeting.number %}">{{ meeting.number }}{% if meeting.session_set.all.0.status.slug == "canceled" %} -- CANCELLED --{% endif %}</a>
|
||||
{% else %}
|
||||
<a href="{% url 'ietf.meeting.views.interim_request_details' number=meeting.number %}">IETF - {{ meeting.number }}</a>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{% if meeting.can_approve %}<span class="label label-warning">can approve</span>{% endif %}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
<h3>No pending interim meetings</h3>
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block js %}
|
||||
<script src="{% static 'select2/select2.min.js' %}"></script>
|
||||
<script src="{% static 'ietf/js/meeting-interim-request.js' %}"></script>
|
||||
{% endblock %}
|
|
@ -22,61 +22,69 @@
|
|||
|
||||
<div class="form-group form-inline">
|
||||
<div class="col-md-offset-2">
|
||||
|
||||
<div class="col-md-2">
|
||||
<!-- <div class="checkbox"> -->
|
||||
<label class="checkbox-inline">{% render_field form.face_to_face %}<strong>Face to Face</strong></label>
|
||||
<!-- </div> -->
|
||||
<div class="col-md-2">
|
||||
<label class="checkbox-inline">{% render_field form.in_person %}<strong>In Person</strong></label>
|
||||
</div>
|
||||
|
||||
<div class="col-md-2">
|
||||
<label class="checkbox-inline">{% render_field form.approved %}<strong>Preapproved by AD</strong></label>
|
||||
</div>
|
||||
|
||||
<div class="col-md-2 radio-inline"><strong>Meeting Type:</strong></div>
|
||||
|
||||
<label class="radio-inline">
|
||||
<input type="radio" value="single" checked="checked" name="meeting_type">Single
|
||||
</label>
|
||||
<label class="radio-inline">
|
||||
<input type="radio" value="multi-day" name="meeting_type">Multi-Day
|
||||
</label>
|
||||
<label class="radio-inline">
|
||||
<input type="radio" value="series" name="meeting_type">Series
|
||||
</label>
|
||||
</div> <!-- col-md-offset-2 -->
|
||||
</div> <!-- form-group form-inline -->
|
||||
|
||||
|
||||
<div class="form-group meeting-type-help mth-multi">
|
||||
<div class="col-md-offset-2">
|
||||
<div class="col-md-10">
|
||||
<p class="help-block">Use Multi-Day to request one meeting with sessions over multiple days.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-2">
|
||||
<!-- <div class="checkbox"> -->
|
||||
<label class="checkbox-inline">{% render_field form.approved %}<strong>Approved</strong></label>
|
||||
<!-- </div> -->
|
||||
</div>
|
||||
|
||||
<!-- <div id="meeting-type-options"> -->
|
||||
<div class="col-md-2 radio-inline"><strong>Meeting Type:</strong></div>
|
||||
<label class="radio-inline">
|
||||
<input type="radio" value="single" checked="checked" name="meeting_type">Single
|
||||
</label>
|
||||
<label class="radio-inline">
|
||||
<input type="radio" value="multi-day" name="meeting_type">Multi-Day
|
||||
</label>
|
||||
<label class="radio-inline">
|
||||
<input type="radio" value="series" name="meeting_type">Series
|
||||
</label>
|
||||
<!-- </div> -->
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-group meeting-type-help mth-series">
|
||||
<div class="col-md-offset-2">
|
||||
<div class="col-md-10">
|
||||
<p class="help-block">Use Series to request a number of separate meetings. In Person series is not supported.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{ formset.management_form }}
|
||||
{% for form in formset %}
|
||||
<div class="fieldset{% if forloop.last %} template{% endif %}" >
|
||||
<div class="form-group">
|
||||
<label for="face-to_face" class="col-md-2 control-label">Location</label>
|
||||
<label for="id_form-{{ forloop.counter0 }}-city" class="col-md-2 control-label">Location</label>
|
||||
<div class="col-md-10 form-inline">
|
||||
{% render_field form.city class="form-control location" placeholder="City" %}
|
||||
{% render_field form.country class="form-control location" %}
|
||||
{% render_field form.country class="form-control location" placeholder="Country" %}
|
||||
{% render_field form.timezone class="form-control" %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="id_date" class="col-md-2 control-label">Date</label>
|
||||
<label for="id_form-{{ forloop.counter0 }}-date" class="col-md-2 control-label required">Date</label>
|
||||
<div class="col-md-2">{% render_field form.date class="form-control" %}</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="id_time" class="col-md-2 control-label">Start Time</label>
|
||||
<label for="id_form-{{ forloop.counter0 }}-time" class="col-md-2 control-label required">Start Time</label>
|
||||
<div class="col-md-3 form-inline">
|
||||
{% render_field form.time class="form-control time-field" placeholder="HH:MM" %}
|
||||
{% render_field form.time_utc class="form-control time-field computed" disabled="disabled" placeholder="UTC" %}
|
||||
</div>
|
||||
<label for="id_end_time" class="col-md-2 control-label">End Time</label>
|
||||
<label for="id_form-{{ forloop.counter0 }}-end_time" class="col-md-2 control-label">End Time</label>
|
||||
<div class="col-md-4 form-inline">
|
||||
{% render_field form.end_time class="form-control time-field computed" placeholder="HH:MM" disabled="disabled" %}
|
||||
{% render_field form.end_time_utc class="form-control time-field computed" disabled="disabled" placeholder="UTC" %}
|
||||
|
@ -84,7 +92,7 @@
|
|||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="id_duration" class="col-md-2 control-label">Duration</label>
|
||||
<label for="id_duration" class="col-md-2 control-label required">Duration</label>
|
||||
<div class="col-md-2">{% render_field form.duration class="form-control time-field" placeholder="HH:MM" %}</div>
|
||||
</div>
|
||||
|
||||
|
|
59
ietf/templates/meeting/interim_request_details.html
Normal file
59
ietf/templates/meeting/interim_request_details.html
Normal file
|
@ -0,0 +1,59 @@
|
|||
{% extends "base.html" %}
|
||||
{# Copyright The IETF Trust 2015, All Rights Reserved #}
|
||||
{% load origin %}
|
||||
{% load staticfiles bootstrap3 widget_tweaks %}
|
||||
|
||||
{% block title %}Interim Request Details{% endblock %}
|
||||
|
||||
{% block pagehead %}
|
||||
<link rel="stylesheet" href="{% static 'select2/select2.css' %}">
|
||||
<link rel="stylesheet" href="{% static 'select2-bootstrap-css/select2-bootstrap.min.css' %}">
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% origin %}
|
||||
<h1>Interim Meeting Request Details</h1>
|
||||
<dl class="dl-horizontal">
|
||||
<dt>Group</dt>
|
||||
<dd>{{ sessions.0.group.acronym }}
|
||||
<dt>Requested By</dt>
|
||||
<dd>{{ sessions.0.requested_by }}
|
||||
<dt>City</dt>
|
||||
<dd>{{ meeting.city }}</dd>
|
||||
<dt>Country</dt>
|
||||
<dd>{{ meeting.country }}</dd>
|
||||
<dt>Timezone</dt>
|
||||
<dd>{{ meeting.time_zone }}</dd>
|
||||
{% for session in sessions %}
|
||||
<dt>Date</dt>
|
||||
<dd>{{ session.official_timeslotassignment.timeslot.time|date:"Y-m-d" }}
|
||||
<dt>Start Time</dt>
|
||||
<dd>{{ session.official_timeslotassignment.timeslot.time|date:"H:i" }}
|
||||
<dt>Duration</dt>
|
||||
<dd>{{ session.requested_duration }}
|
||||
<dt>Remote Instructions</dt>
|
||||
<dd>{{ session.remote_instructions }}
|
||||
<dt>Agenda Note</dt>
|
||||
<dd>{{ session.agenda_note }}</dd>
|
||||
{% endfor %}
|
||||
</dl>
|
||||
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
{% if can_edit %}
|
||||
<a class="btn btn-default" href="">Edit</a>
|
||||
{% endif %}
|
||||
{% if can_approve %}
|
||||
<input class="btn btn-default" type="submit" value="Approve" name='approved' />
|
||||
{% endif %}
|
||||
{% if can_edit %}
|
||||
<input class="btn btn-default" type="submit" value="Cancel" name='cancel' />
|
||||
{% endif %}
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block js %}
|
||||
<script src="{% static 'select2/select2.min.js' %}"></script>
|
||||
<script src="{% static 'ietf/js/meeting-interim-request.js' %}"></script>
|
||||
{% endblock %}
|
|
@ -89,6 +89,12 @@ def make_immutable_base_data():
|
|||
area = create_group(name="Far Future", acronym="farfut", type_id="area", parent=ietf)
|
||||
create_person(area, "ad", name="Areað Irector", username="ad", email_address="aread@ietf.org")
|
||||
|
||||
# second area
|
||||
opsarea = create_group(name="Operations", acronym="ops", type_id="area", parent=ietf)
|
||||
create_person(opsarea, "ad")
|
||||
sops = create_group(name="Server Operations", acronym="sops", type_id="wg", parent=opsarea)
|
||||
create_person(sops, "chair", name="Sops Chairman", username="sopschairman")
|
||||
|
||||
# create a bunch of ads for swarm tests
|
||||
for i in range(1, 10):
|
||||
u = User.objects.create(username="ad%s" % i)
|
||||
|
@ -116,6 +122,7 @@ def make_immutable_base_data():
|
|||
def make_test_data():
|
||||
area = Group.objects.get(acronym="farfut")
|
||||
ad = Person.objects.get(user__username="ad")
|
||||
irtf = Group.objects.get(acronym='irtf')
|
||||
|
||||
# mars WG
|
||||
group = Group.objects.create(
|
||||
|
@ -166,6 +173,30 @@ def make_test_data():
|
|||
group.charter = charter
|
||||
group.save()
|
||||
|
||||
# irg RG
|
||||
irg_rg = Group.objects.create(
|
||||
name="Internet Research Group",
|
||||
acronym="irg",
|
||||
state_id="active",
|
||||
type_id="rg",
|
||||
parent=irtf,
|
||||
list_email="irg-rg@ietf.org",
|
||||
)
|
||||
#charter = Document.objects.create(
|
||||
# name="charter-ietf-" + group.acronym,
|
||||
# type_id="charter",
|
||||
# title=group.name,
|
||||
# group=group,
|
||||
# rev="00",
|
||||
# )
|
||||
#charter.set_state(State.objects.get(used=True, slug="infrev", type="charter"))
|
||||
#DocAlias.objects.create(
|
||||
# name=charter.name,
|
||||
# document=charter
|
||||
# )
|
||||
#group.charter = charter
|
||||
#group.save()
|
||||
|
||||
# plain IETF'er
|
||||
u = User.objects.create(username="plain")
|
||||
u.set_password("plain+password")
|
||||
|
@ -187,6 +218,8 @@ def make_test_data():
|
|||
ames_wg.role_set.get_or_create(name_id='ad',person=ad,email=ad.role_email('ad'))
|
||||
ames_wg.save()
|
||||
|
||||
create_person(irg_rg, "chair", name="Irg Chair Man", username="irgchairman")
|
||||
|
||||
# old draft
|
||||
old_draft = Document.objects.create(
|
||||
name="draft-foo-mars-test",
|
||||
|
|
Loading…
Reference in a new issue