Merged in ^/branch/proceedings/6.33.1.dev0@12141 from rcross@amsl.com:

Added meeting proceedings introduction pages: Progress Report and Attendees.  Added a data migration to provide proceedings introduction information for IETF 95 and 96.
 - Legacy-Id: 12150
This commit is contained in:
Henrik Levkowetz 2016-10-14 15:48:10 +00:00
commit 1a92efb77e
16 changed files with 298 additions and 132 deletions

View file

@ -1,6 +1,6 @@
# -*- conf-mode -*- # -*- conf-mode -*-
/branch/proceedings/6.33.1.dev0@12111 # Missing data migration to add dbtemplates ?
/branch/proceedings/6.29.1.dev0@11850 # Merged into /branch/proceedings/6.30.1.dev0, will be merged from there /branch/proceedings/6.29.1.dev0@11850 # Merged into /branch/proceedings/6.30.1.dev0, will be merged from there
/branch/proceedings/6.29.1.dev0@11856 # Merged into /branch/proceedings/6.30.1.dev0, will be merged from there /branch/proceedings/6.29.1.dev0@11856 # Merged into /branch/proceedings/6.30.1.dev0, will be merged from there
/personal/rcross/6.29.1.dev0@11765 # Merged into ^/personal/rjs/6.29.1.dev0@11770, will be merged from there /personal/rcross/6.29.1.dev0@11765 # Merged into ^/personal/rjs/6.29.1.dev0@11770, will be merged from there

View file

@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations
from ietf.meeting.utils import create_proceedings_templates
def create_attendee_templates(apps, schema_editor):
"""Create attendee templates for supported meetings"""
Meeting = apps.get_model("meeting", "Meeting")
create_proceedings_templates(Meeting.objects.get(number=95))
create_proceedings_templates(Meeting.objects.get(number=96))
class Migration(migrations.Migration):
dependencies = [
('meeting', '0037_change_meta_options_on_sessionpresentation'),
]
operations = [
migrations.RunPython(create_attendee_templates),
]

View file

@ -11,6 +11,7 @@ from django.core.urlresolvers import reverse as urlreverse
from django.conf import settings from django.conf import settings
from django.contrib.auth.models import User from django.contrib.auth.models import User
from mock import patch
from pyquery import PyQuery from pyquery import PyQuery
from StringIO import StringIO from StringIO import StringIO
@ -22,6 +23,7 @@ from ietf.meeting.helpers import send_interim_cancellation_notice
from ietf.meeting.helpers import send_interim_minutes_reminder from ietf.meeting.helpers import send_interim_minutes_reminder
from ietf.meeting.models import Session, TimeSlot, Meeting from ietf.meeting.models import Session, TimeSlot, Meeting
from ietf.meeting.test_data import make_meeting_test_data, make_interim_meeting from ietf.meeting.test_data import make_meeting_test_data, make_interim_meeting
from ietf.meeting.utils import finalize
from ietf.name.models import SessionStatusName from ietf.name.models import SessionStatusName
from ietf.utils.test_utils import TestCase, login_testing_unauthorized, unicontent from ietf.utils.test_utils import TestCase, login_testing_unauthorized, unicontent
from ietf.utils.mail import outbox from ietf.utils.mail import outbox
@ -33,6 +35,7 @@ from ietf.meeting.factories import ( SessionFactory, SessionPresentationFactory,
MeetingFactory, FloorPlanFactory ) MeetingFactory, FloorPlanFactory )
from ietf.doc.factories import DocumentFactory from ietf.doc.factories import DocumentFactory
class MeetingTests(TestCase): class MeetingTests(TestCase):
def setUp(self): def setUp(self):
self.materials_dir = os.path.abspath(settings.TEST_MATERIALS_DIR) self.materials_dir = os.path.abspath(settings.TEST_MATERIALS_DIR)
@ -272,41 +275,50 @@ class MeetingTests(TestCase):
self.assertEqual(r.status_code, 200) self.assertEqual(r.status_code, 200)
def test_proceedings_acknowledgements(self): def test_proceedings_acknowledgements(self):
meeting = make_meeting_test_data() make_meeting_test_data()
meeting = MeetingFactory(type_id='ietf', date=datetime.date(2016,7,14), number="96")
meeting.acknowledgements = 'test acknowledgements'
meeting.save()
url = urlreverse('ietf.meeting.views.proceedings_acknowledgements',kwargs={'num':meeting.number}) url = urlreverse('ietf.meeting.views.proceedings_acknowledgements',kwargs={'num':meeting.number})
login_testing_unauthorized(self,"secretary",url) response = self.client.get(url)
r = self.client.get(url) self.assertEqual(response.status_code, 200)
self.assertEqual(r.status_code, 200) self.assertTrue('test acknowledgements' in response.content)
@patch('urllib2.urlopen')
def test_proceedings_attendees(self, mock_urlopen):
mock_urlopen.return_value = StringIO('[{"LastName":"Smith","FirstName":"John","Company":"ABC","Country":"US"}]')
make_meeting_test_data()
meeting = MeetingFactory(type_id='ietf', date=datetime.date(2016,7,14), number="96")
finalize(meeting)
url = urlreverse('ietf.meeting.views.proceedings_attendees',kwargs={'num':96})
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertTrue('Attendee List' in response.content)
q = PyQuery(response.content)
self.assertEqual(1,len(q("#id_attendees tbody tr")))
def test_proceedings_overview(self): def test_proceedings_overview(self):
'''Test proceedings IETF Overview page. '''Test proceedings IETF Overview page.
Note: old meetings aren't supported so need to add a new meeting then test. Note: old meetings aren't supported so need to add a new meeting then test.
''' '''
make_meeting_test_data() make_meeting_test_data()
# add meeting requires a previous meeting to work meeting = MeetingFactory(type_id='ietf', date=datetime.date(2016,7,14), number="96")
date = datetime.date(2016,7,14) finalize(meeting)
Meeting.objects.create(type_id='ietf',date=date,number=96) url = urlreverse('ietf.meeting.views.proceedings_overview',kwargs={'num':96})
url = urlreverse('ietf.secr.meetings.views.add')
post_data = dict(number='97',city='Seoul',date='2016-11-13',country='KR',
time_zone='Asia/Seoul',venue_name='Conrad Seoul',
venue_addr='10 Gukjegeumyung-ro',
idsubmit_cutoff_day_offset_00=13,
idsubmit_cutoff_day_offset_01=20,
idsubmit_cutoff_time_utc =datetime.timedelta(hours=23, minutes=59, seconds=59),
idsubmit_cutoff_warning_days =datetime.timedelta(days=21),
submission_start_day_offset=90,
submission_cutoff_day_offset=26,
submission_correction_day_offset=50,
)
self.client.login(username='secretary', password='secretary+password')
response = self.client.post(url, post_data)
self.assertRedirects(response,urlreverse('ietf.secr.meetings.views.main'))
url = urlreverse('ietf.meeting.views.proceedings_overview',kwargs={'num':97})
response = self.client.get(url) response = self.client.get(url)
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertTrue('The Internet Engineering Task Force' in response.content) self.assertTrue('The Internet Engineering Task Force' in response.content)
def test_proceedings_progress_report(self):
make_meeting_test_data()
MeetingFactory(type_id='ietf', date=datetime.date(2016,4,3), number="95")
MeetingFactory(type_id='ietf', date=datetime.date(2016,7,14), number="96")
url = urlreverse('ietf.meeting.views.proceedings_progress_report',kwargs={'num':96})
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertTrue('Progress Report' in response.content)
def test_feed(self): def test_feed(self):
meeting = make_meeting_test_data() meeting = make_meeting_test_data()
session = Session.objects.filter(meeting=meeting, group__acronym="mars").first() session = Session.objects.filter(meeting=meeting, group__acronym="mars").first()
@ -1279,7 +1291,9 @@ class IphoneAppJsonTests(TestCase):
self.assertEqual(r.status_code,200) self.assertEqual(r.status_code,200)
class FinalizeProceedingsTests(TestCase): class FinalizeProceedingsTests(TestCase):
def test_finalize_proceedings(self): @patch('urllib2.urlopen')
def test_finalize_proceedings(self, mock_urlopen):
mock_urlopen.return_value = StringIO('[{"LastName":"Smith","FirstName":"John","Company":"ABC","Country":"US"}]')
make_meeting_test_data() make_meeting_test_data()
meeting = Meeting.objects.filter(type_id='ietf').order_by('id').last() meeting = Meeting.objects.filter(type_id='ietf').order_by('id').last()
meeting.session_set.filter(group__acronym='mars').first().sessionpresentation_set.create(document=Document.objects.filter(type='draft').first(),rev=None) meeting.session_set.filter(group__acronym='mars').first().sessionpresentation_set.create(document=Document.objects.filter(type='draft').first(),rev=None)

View file

@ -78,7 +78,9 @@ type_ietf_only_patterns_id_optional = [
url(r'^proceedings(?:.html)?/?$', views.proceedings), url(r'^proceedings(?:.html)?/?$', views.proceedings),
url(r'^proceedings(?:.html)?/finalize/?$', views.finalize_proceedings), url(r'^proceedings(?:.html)?/finalize/?$', views.finalize_proceedings),
url(r'^proceedings/acknowledgements/$', views.proceedings_acknowledgements), url(r'^proceedings/acknowledgements/$', views.proceedings_acknowledgements),
url(r'^proceedings/attendees/$', views.proceedings_attendees),
url(r'^proceedings/overview/$', views.proceedings_overview), url(r'^proceedings/overview/$', views.proceedings_overview),
url(r'^proceedings/progress-report/$', views.proceedings_progress_report),
] ]
urlpatterns = [ urlpatterns = [

View file

@ -1,5 +1,12 @@
import datetime import datetime
import json
import urllib2
import urlparse
from django.conf import settings
from django.template.loader import render_to_string
from ietf.dbtemplate.models import DBTemplate
from ietf.meeting.models import Session from ietf.meeting.models import Session
from ietf.group.utils import can_manage_materials from ietf.group.utils import can_manage_materials
@ -72,6 +79,35 @@ def sort_sessions(sessions):
return meeting_sorted return meeting_sorted
def create_proceedings_templates(meeting):
'''Create DBTemplates for meeting proceedings'''
# Get meeting attendees from registration system
url = urlparse.urljoin(settings.REGISTRATION_ATTENDEES_BASE_URL,meeting.number)
try:
attendees = json.load(urllib2.urlopen(url))
except (ValueError, urllib2.HTTPError):
attendees = []
if attendees:
attendees = sorted(attendees, key = lambda a: a['LastName'])
content = render_to_string('meeting/proceedings_attendees_table.html', {
'attendees':attendees})
DBTemplate.objects.create(
path='/meeting/proceedings/%s/attendees.html' % meeting.number,
title='IETF %s Attendee List' % meeting.number,
type_id='django',
content=content)
# Make copy of default IETF Overview template
if not meeting.overview:
template = DBTemplate.objects.get(path='/meeting/proceedings/defaults/overview.rst')
template.id = None
template.path = '/meeting/proceedings/%s/overview.rst' % (meeting.number)
template.title = 'IETF %s Proceedings Overview' % (meeting.number)
template.save()
meeting.overview = template
meeting.save()
def finalize(meeting): def finalize(meeting):
end_date = meeting.end_date() end_date = meeting.end_date()
end_time = datetime.datetime.combine(end_date, datetime.datetime.min.time())+datetime.timedelta(days=1) end_time = datetime.datetime.combine(end_date, datetime.datetime.min.time())+datetime.timedelta(days=1)
@ -81,8 +117,10 @@ def finalize(meeting):
if rev_before_end: if rev_before_end:
sp.rev = rev_before_end[-1].newrevisiondocevent.rev sp.rev = rev_before_end[-1].newrevisiondocevent.rev
else: else:
sp.rev = '00' sp.rev = '00'
sp.save() sp.save()
create_proceedings_templates(meeting)
meeting.proceedings_final = True meeting.proceedings_final = True
meeting.save() meeting.save()
return return

View file

@ -25,6 +25,7 @@ from django.db.models import Min, Max
from django.conf import settings from django.conf import settings
from django.forms.models import modelform_factory, inlineformset_factory from django.forms.models import modelform_factory, inlineformset_factory
from django.forms import ModelForm from django.forms import ModelForm
from django.template import TemplateDoesNotExist
from django.template.loader import render_to_string from django.template.loader import render_to_string
from django.utils.functional import curry from django.utils.functional import curry
from django.views.decorators.cache import cache_page from django.views.decorators.cache import cache_page
@ -56,6 +57,7 @@ from ietf.meeting.helpers import send_interim_approval_request
from ietf.meeting.helpers import send_interim_announcement_request from ietf.meeting.helpers import send_interim_announcement_request
from ietf.meeting.utils import finalize from ietf.meeting.utils import finalize
from ietf.secr.proceedings.utils import handle_upload_file from ietf.secr.proceedings.utils import handle_upload_file
from ietf.secr.proceedings.proc_utils import get_progress_stats
from ietf.utils.mail import send_mail_message from ietf.utils.mail import send_mail_message
from ietf.utils.pipe import pipe from ietf.utils.pipe import pipe
from ietf.utils.pdf import pdf_pages from ietf.utils.pdf import pdf_pages
@ -2009,30 +2011,65 @@ def finalize_proceedings(request, num=None):
return render(request, "meeting/finalize.html", {'meeting':meeting,}) return render(request, "meeting/finalize.html", {'meeting':meeting,})
@role_required('Secretariat')
def proceedings_acknowledgements(request, num=None): def proceedings_acknowledgements(request, num=None):
'''Display Acknowledgements for meeting'''
meeting = get_meeting(num) meeting = get_meeting(num)
if meeting.number < 95: if not num.isdigit():
raise Http404
if int(meeting.number) < settings.NEW_PROCEEDINGS_START:
return HttpResponseRedirect( 'https://www.ietf.org/proceedings/%s/acknowledgement.html' % num ) return HttpResponseRedirect( 'https://www.ietf.org/proceedings/%s/acknowledgement.html' % num )
return render(request, "meeting/proceedings_acknowledgements.html", { return render(request, "meeting/proceedings_acknowledgements.html", {
'meeting': meeting, 'meeting': meeting,
}) })
@role_required('Secretariat') def proceedings_attendees(request, num=None):
'''Display list of meeting attendees'''
meeting = get_meeting(num)
if not num.isdigit():
raise Http404
if int(meeting.number) < settings.NEW_PROCEEDINGS_START:
return HttpResponseRedirect( 'https://www.ietf.org/proceedings/%s/attendees.html' % num )
overview_template = '/meeting/proceedings/%s/attendees.html' % meeting.number
try:
template = render_to_string(overview_template, {})
except TemplateDoesNotExist:
raise Http404
return render(request, "meeting/proceedings_attendees.html", {
'meeting': meeting,
'template': template,
})
def proceedings_overview(request, num=None): def proceedings_overview(request, num=None):
'''Display Overview for given meeting''' '''Display Overview for given meeting'''
meeting = get_meeting(num) meeting = get_meeting(num)
if meeting.number < 95: if not num.isdigit():
raise Http404
if int(meeting.number) < settings.NEW_PROCEEDINGS_START:
return HttpResponseRedirect( 'https://www.ietf.org/proceedings/%s/overview.html' % num ) return HttpResponseRedirect( 'https://www.ietf.org/proceedings/%s/overview.html' % num )
overview_template = '/meeting/proceedings/%s/overview.rst' % meeting.number overview_template = '/meeting/proceedings/%s/overview.rst' % meeting.number
template = render_to_string(overview_template, {}) try:
template = render_to_string(overview_template, {})
except TemplateDoesNotExist:
raise Http404
return render(request, "meeting/proceedings_overview.html", { return render(request, "meeting/proceedings_overview.html", {
'meeting': meeting, 'meeting': meeting,
'template': template, 'template': template,
}) })
@cache_page( 60 * 60 )
def proceedings_progress_report(request, num=None):
'''Display Progress Report (stats since last meeting)'''
meeting = get_meeting(num)
if not num.isdigit():
raise Http404
if int(meeting.number) < settings.NEW_PROCEEDINGS_START:
return HttpResponseRedirect( 'https://www.ietf.org/proceedings/%s/progress-report.html' % num )
sdate = meeting.previous_meeting().date
edate = meeting.date
context = get_progress_stats(sdate,edate)
context['meeting'] = meeting
return render(request, "meeting/proceedings_progress_report.html", context)
class OldUploadRedirect(RedirectView): class OldUploadRedirect(RedirectView):
def get_redirect_url(self, **kwargs): def get_redirect_url(self, **kwargs):
return reverse_lazy('ietf.meeting.views.session_details',kwargs=self.kwargs) return reverse_lazy('ietf.meeting.views.session_details',kwargs=self.kwargs)

View file

@ -85,8 +85,6 @@ class SecrMeetingTestCase(TestCase):
response = self.client.post(url, post_data, follow=True) response = self.client.post(url, post_data, follow=True)
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertEqual(Meeting.objects.count(),count + 1) self.assertEqual(Meeting.objects.count(),count + 1)
meeting = Meeting.objects.order_by('id').last()
self.assertEqual(meeting.overview.path,'/meeting/proceedings/%s/overview.rst' % meeting.number)
def test_edit_meeting(self): def test_edit_meeting(self):
"Edit Meeting" "Edit Meeting"

View file

@ -14,7 +14,6 @@ from django.shortcuts import render_to_response, get_object_or_404, redirect
from django.template import RequestContext from django.template import RequestContext
from django.utils.functional import curry from django.utils.functional import curry
from ietf.dbtemplate.models import DBTemplate
from ietf.ietfauth.utils import role_required from ietf.ietfauth.utils import role_required
from ietf.utils.mail import send_mail from ietf.utils.mail import send_mail
from ietf.meeting.helpers import get_meeting, make_materials_directories from ietf.meeting.helpers import get_meeting, make_materials_directories
@ -317,16 +316,7 @@ def add(request):
# Create Physical new meeting directory and subdirectories # Create Physical new meeting directory and subdirectories
make_materials_directories(meeting) make_materials_directories(meeting)
# Make copy of IETF Overview template
template = DBTemplate.objects.get(path='/meeting/proceedings/defaults/overview.rst')
template.id = None
template.path = '/meeting/proceedings/%s/overview.rst' % (meeting.number)
template.title = 'IETF %s Proceedings Overview' % (meeting.number)
template.save()
meeting.overview = template
meeting.save()
messages.success(request, 'The Meeting was created successfully!') messages.success(request, 'The Meeting was created successfully!')
return redirect('meetings') return redirect('meetings')
else: else:

View file

@ -106,97 +106,67 @@ def mycomp(timeslot):
def get_progress_stats(sdate,edate): def get_progress_stats(sdate,edate):
''' '''
This function takes a date range and produces a dictionary of statistics / objects for use This function takes a date range and produces a dictionary of statistics / objects for
in a progress report. Generally the end date will be the date of the last meeting use in a progress report. Generally the end date will be the date of the last meeting
and the start date will be the date of the meeting before that. and the start date will be the date of the meeting before that.
''' '''
data = {} data = {}
data['sdate'] = sdate data['sdate'] = sdate
data['edate'] = edate data['edate'] = edate
# Activty Report Section
new_docs = Document.objects.filter(type='draft').filter(docevent__type='new_revision',
docevent__newrevisiondocevent__rev='00',
docevent__time__gte=sdate,
docevent__time__lt=edate)
data['new'] = new_docs.count()
data['updated'] = 0
data['updated_more'] = 0
for d in new_docs:
updates = d.docevent_set.filter(type='new_revision',time__gte=sdate,time__lt=edate).count()
if updates > 1:
data['updated'] += 1
if updates > 2:
data['updated_more'] +=1
# calculate total documents updated, not counting new, rev=00
result = set()
events = DocEvent.objects.filter(doc__type='draft',time__gte=sdate,time__lt=edate) events = DocEvent.objects.filter(doc__type='draft',time__gte=sdate,time__lt=edate)
for e in events.filter(type='new_revision').exclude(newrevisiondocevent__rev='00'):
result.add(e.doc) data['actions_count'] = events.filter(type='iesg_approved').count()
data['total_updated'] = len(result) data['last_calls_count'] = events.filter(type='sent_last_call').count()
new_draft_events = events.filter(newrevisiondocevent__rev='00')
new_drafts = list(set([ e.doc_id for e in new_draft_events ]))
data['new_drafts_count'] = len(new_drafts)
data['new_drafts_updated_count'] = events.filter(doc__in=new_drafts,newrevisiondocevent__rev='01').count()
data['new_drafts_updated_more_count'] = events.filter(doc__in=new_drafts,newrevisiondocevent__rev='02').count()
update_events = events.filter(type='new_revision').exclude(doc__in=new_drafts)
data['updated_drafts_count'] = len(set([ e.doc_id for e in update_events ]))
# Calculate Final Four Weeks stats (ffw)
ffwdate = edate - datetime.timedelta(days=28)
ffw_new_count = events.filter(time__gte=ffwdate,newrevisiondocevent__rev='00').count()
try:
ffw_new_percent = format(ffw_new_count / float(data['new_drafts_count']),'.0%')
except ZeroDivisionError:
ffw_new_percent = 0
data['ffw_new_count'] = ffw_new_count
data['ffw_new_percent'] = ffw_new_percent
ffw_update_events = events.filter(time__gte=ffwdate,type='new_revision').exclude(doc__in=new_drafts)
ffw_update_count = len(set([ e.doc_id for e in ffw_update_events ]))
try:
ffw_update_percent = format(ffw_update_count / float(data['updated_drafts_count']),'.0%')
except ZeroDivisionError:
ffw_update_percent = 0
data['ffw_update_count'] = ffw_update_count
data['ffw_update_percent'] = ffw_update_percent
# calculate sent last call rfcs = events.filter(type='published_rfc')
data['last_call'] = events.filter(type='sent_last_call').count() data['rfcs'] = rfcs.select_related('doc').select_related('doc__group').select_related('doc__intended_std_level')
# calculate approved data['counts'] = {'std':rfcs.filter(doc__intended_std_level__in=('ps','ds','std')).count(),
data['approved'] = events.filter(type='iesg_approved').count() 'bcp':rfcs.filter(doc__intended_std_level='bcp').count(),
'exp':rfcs.filter(doc__intended_std_level='exp').count(),
'inf':rfcs.filter(doc__intended_std_level='inf').count()}
# get 4 weeks data['new_groups'] = Group.objects.filter(
ff1_date = edate - datetime.timedelta(days=28) type='wg',
ff_docs = Document.objects.filter(type='draft').filter(docevent__type='new_revision', groupevent__changestategroupevent__state='active',
docevent__newrevisiondocevent__rev='00', groupevent__time__gte=sdate,
docevent__time__gte=ff1_date, groupevent__time__lt=edate)
docevent__time__lt=edate)
ff_new_count = ff_docs.count() data['concluded_groups'] = Group.objects.filter(
ff_new_percent = format(ff_new_count / float(data['new']),'.0%') type='wg',
groupevent__changestategroupevent__state='conclude',
# calculate total documents updated in final four weeks, not counting new, rev=00 groupevent__time__gte=sdate,
result = set() groupevent__time__lt=edate)
events = DocEvent.objects.filter(doc__type='draft',time__gte=ff1_date,time__lt=edate)
for e in events.filter(type='new_revision').exclude(newrevisiondocevent__rev='00'):
result.add(e.doc)
ff_update_count = len(result)
ff_update_percent = format(ff_update_count / float(data['total_updated']),'.0%')
data['ff_new_count'] = ff_new_count
data['ff_new_percent'] = ff_new_percent
data['ff_update_count'] = ff_update_count
data['ff_update_percent'] = ff_update_percent
# Progress Report Section
data['docevents'] = DocEvent.objects.filter(doc__type='draft',time__gte=sdate,time__lt=edate)
data['action_events'] = data['docevents'].filter(type='iesg_approved')
data['lc_events'] = data['docevents'].filter(type='sent_last_call')
data['new_groups'] = Group.objects.filter(type='wg',
groupevent__changestategroupevent__state='active',
groupevent__time__gte=sdate,
groupevent__time__lt=edate)
data['concluded_groups'] = Group.objects.filter(type='wg',
groupevent__changestategroupevent__state='conclude',
groupevent__time__gte=sdate,
groupevent__time__lt=edate)
data['new_docs'] = Document.objects.filter(type='draft').filter(docevent__type='new_revision',
docevent__time__gte=sdate,
docevent__time__lt=edate).distinct()
data['rfcs'] = DocEvent.objects.filter(type='published_rfc',
doc__type='draft',
time__gte=sdate,
time__lt=edate)
# attach the ftp URL for use in the template
for event in data['rfcs']:
num = get_rfc_num(event.doc)
event.ftp_url = 'ftp://ftp.ietf.org/rfc/rfc%s.txt' % num
data['counts'] = {'std':data['rfcs'].filter(doc__intended_std_level__in=('ps','ds','std')).count(),
'bcp':data['rfcs'].filter(doc__intended_std_level='bcp').count(),
'exp':data['rfcs'].filter(doc__intended_std_level='exp').count(),
'inf':data['rfcs'].filter(doc__intended_std_level='inf').count()}
return data return data

View file

@ -599,7 +599,8 @@ SECR_INTERIM_LISTING_DIR = '/a/www/www6/meeting/interim'
SECR_MAX_UPLOAD_SIZE = 40960000 SECR_MAX_UPLOAD_SIZE = 40960000
SECR_PROCEEDINGS_DIR = '/a/www/www6s/proceedings/' SECR_PROCEEDINGS_DIR = '/a/www/www6s/proceedings/'
SECR_PPT2PDF_COMMAND = ['/usr/bin/soffice','--headless','--convert-to','pdf','--outdir'] SECR_PPT2PDF_COMMAND = ['/usr/bin/soffice','--headless','--convert-to','pdf','--outdir']
REGISTRATION_ATTENDEES_BASE_URL = 'https://ietf.org/registration/attendees/'
NEW_PROCEEDINGS_START = 95
USE_ETAGS=True USE_ETAGS=True
PRODUCTION_TIMEZONE = "America/Los_Angeles" PRODUCTION_TIMEZONE = "America/Los_Angeles"

View file

@ -544,6 +544,12 @@ ul.errorlist {
color: #555; color: #555;
} }
/* === Proceedings ===================================================== */
ul.progress-section {
list-style-type: none;
margin-bottom: 2em;
}
/* === Font-Awesome ========================================================= */ /* === Font-Awesome ========================================================= */
.btn .fa-stack { width: 1em; height: 1em; } .btn .fa-stack { width: 1em; height: 1em; }

View file

@ -33,13 +33,15 @@
{% load cache %} {% load cache %}
{% cache 900 ietf_meeting_proceedings meeting.number cache_version %} {% cache 900 ietf_meeting_proceedings meeting.number cache_version %}
{% if meeting.proceedings_final %}
<h2 class="anchor-target" id="introduction">Introduction</h2> <h2 class="anchor-target" id="introduction">Introduction</h2>
<div> <div>
<a href="{% url 'ietf.meeting.views.proceedings_acknowledgements' num=meeting.number %}">Acknowledgements</a><br> <a href="{% url 'ietf.meeting.views.proceedings_acknowledgements' num=meeting.number %}">Acknowledgements</a><br>
<a href="{% url 'ietf.meeting.views.proceedings_overview' num=meeting.number %}">IETF Overview</a><br> <a href="{% url 'ietf.meeting.views.proceedings_overview' num=meeting.number %}">IETF Overview</a><br>
<a href="#">Progress Report</a><br> <a href="{% url 'ietf.meeting.views.proceedings_progress_report' num=meeting.number %}">Progress Report</a><br>
<a href="#">Attendees</a><br> <a href="{% url 'ietf.meeting.views.proceedings_attendees' num=meeting.number %}">Attendees</a><br>
</div> </div>
{% endif %}
{% with "True" as show_agenda %} {% with "True" as show_agenda %}
<!-- Plenaries --> <!-- Plenaries -->

View file

@ -0,0 +1,14 @@
{% extends "base.html" %}
{# Copyright The IETF Trust 2015, All Rights Reserved #}
{% load origin markup_tags %}
{% block title %}IETF {{ meeting.number }} Proceedings {% endblock %}
{% block content %}
{% origin %}
<h1><a href="{% url 'ietf.meeting.views.proceedings' num=meeting.number %}">IETF {{ meeting.number }} Proceedings</a></h1>
<h2>Attendee List of IETF {{ meeting.number }} Meeting</h2>
{{ template|safe }}
{% endblock %}

View file

@ -0,0 +1,18 @@
<table id="id_attendees" class="table">
<thead>
<tr>
<th>Last Name</th>
<th>First Name</th>
<th>Organization</th>
</tr>
</thead>
<tbody>
{% for attendee in attendees %}
<tr>
<td>{{ attendee.LastName }}</td>
<td>{{ attendee.FirstName }}</td>
<td>{{ attendee.Company }}</td>
</tr>
{% endfor %}
</tbody>
</table>

View file

@ -0,0 +1,53 @@
{% extends "base.html" %}
{% load ams_filters %}
{% block title %}IETF {{ meeting.number }} Proceedings - Progress Report{% endblock %}
{% block content %}
<h1><a href="{% url 'ietf.meeting.views.proceedings' num=meeting.number %}">IETF {{ meeting.number }} Proceedings</a></h1>
<h2>IETF Progress Report</h2>
<h4>{{ sdate|date:"d-F-y" }} to {{ edate|date:"d-F-y" }}</h4>
<ul class="progress-section">
<li>{{ actions_count }} IESG Protocol and Document Actions this period</li>
<li>{{ last_calls_count }} IESG Last Calls issued to the IETF this period</li>
<li></li>
<li>{{ new_drafts_count|stringformat:"3s" }} New I-Ds ({{ new_drafts_updated_count }} of which were updated, some ({{ new_drafts_updated_more_count }}) more than once)</li>
<li>{{ updated_drafts_count|stringformat:"3s" }} I-Ds were updated (Some more than once)</li>
<li></li>
<li><h4>In the final 4 weeks before meeting</h4></li>
<li>{{ ffw_new_count|stringformat:"3s" }} New I-Ds were received - {{ ffw_new_percent }} of total newbies since last meeting</li>
<li>{{ ffw_update_count|stringformat:"3s" }} I-Ds were updated - {{ ffw_update_percent }} of total updated since last meeting</li>
</ul>
<h4>{{ new_groups.count }} New Working Group(s) formed this period</h4>
<ul class="progress-section">
{% for group in new_groups %}
<li>{{ group.name }} ({{ group.acronym }})</li>
{% endfor %}
</ul>
<h4>{{ concluded_groups.count }} Working Group(s) concluded this period</h4>
<ul class="progress-section">
{% for group in concluded_groups %}
<li>{{ group.name }} ({{ group.acronym }})</li>
{% endfor %}
</ul>
<h4>{{ rfcs.count }} RFCs published this period</h4>
<p>{{ counts.std }} Standards Track; {{ counts.bcp }} BCP; {{ counts.exp }} Experimental; {{ counts.inf }} Informational</p>
<table class="table">
{% for rfc in rfcs %}
<tr>
<td><a href="{{ rfc.doc.get_absolute_url }}">{{ rfc.doc.canonical_name|upper }}</a></td>
<td>{{ rfc.doc.intended_std_level.name|abbr_status }}</td>
<td>({{ rfc.doc.group.acronym }})</td>
<td>{{ rfc.time|date:"F Y" }}</td>
<td>{{ rfc.doc.title }}</td>
</tr>
{% endfor %}
</table>
{% endblock %}

View file

@ -18,6 +18,7 @@ html5lib>=0.90,<0.99999999 # ietf.utils.html needs a rewrite for html5lib 1.x --
jsonfield>=1.0.3 # for SubmissionCheck. This is https://github.com/bradjasper/django-jsonfield/. jsonfield>=1.0.3 # for SubmissionCheck. This is https://github.com/bradjasper/django-jsonfield/.
#lxml>=3.4.0 # from PyQuery; #lxml>=3.4.0 # from PyQuery;
mimeparse>=0.1.3 # from TastyPie mimeparse>=0.1.3 # from TastyPie
mock>=2.0.0
MySQL-python>=1.2.5 MySQL-python>=1.2.5
pathlib>=1.0 pathlib>=1.0
Pillow>=3.0 Pillow>=3.0