Refactoring. Implement iCalendar support
- Legacy-Id: 11178
This commit is contained in:
parent
3f3e6f0b14
commit
3366006653
|
@ -14,7 +14,7 @@ from ietf.group.models import Group
|
|||
from ietf.ietfauth.utils import has_role
|
||||
from ietf.meeting.models import Session, Meeting, Schedule, countries, timezones
|
||||
from ietf.meeting.helpers import get_next_interim_number, assign_interim_session
|
||||
from ietf.meeting.helpers import is_meeting_approved
|
||||
from ietf.meeting.helpers import is_meeting_approved, get_next_agenda_name
|
||||
from ietf.message.models import Message
|
||||
from ietf.person.models import Person
|
||||
from ietf.secr.utils.meeting import get_upload_root
|
||||
|
@ -159,6 +159,7 @@ class InterimMeetingModelForm(forms.ModelForm):
|
|||
self.is_edit = bool(self.instance.pk)
|
||||
self.fields['group'].widget.attrs['class'] = "select2-field"
|
||||
self.fields['time_zone'].initial = 'UTC'
|
||||
self.fields['approved'].initial = True
|
||||
self.set_group_options()
|
||||
if self.is_edit:
|
||||
self.fields['group'].initial = self.instance.session_set.first().group
|
||||
|
@ -215,7 +216,7 @@ class InterimSessionModelForm(forms.ModelForm):
|
|||
requested_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)
|
||||
remote_instructions = forms.CharField(max_length=1024, required=True)
|
||||
agenda = forms.CharField(required=False, widget=forms.Textarea)
|
||||
agenda_note = forms.CharField(max_length=255, required=False)
|
||||
|
||||
|
@ -263,10 +264,7 @@ class InterimSessionModelForm(forms.ModelForm):
|
|||
doc.rev = str(int(doc.rev) + 1).zfill(2)
|
||||
doc.save()
|
||||
else:
|
||||
filename = 'agenda-interim-{group}-{date}-{time}'.format(
|
||||
group=self.group.acronym,
|
||||
date=self.cleaned_data['date'].strftime("%Y-%m-%d-"),
|
||||
time=self.cleaned_data['time'].strftime("%H%M"))
|
||||
filename = get_next_agenda_name(meeting=self.instance.meeting)
|
||||
doc = Document.objects.create(
|
||||
type_id='agenda',
|
||||
group=self.group,
|
||||
|
|
|
@ -461,8 +461,8 @@ def is_meeting_approved(meeting):
|
|||
|
||||
|
||||
def get_next_interim_number(group, date):
|
||||
"""Returns a unique number to use for the next interim meeting for
|
||||
*group*"""
|
||||
"""Returns a unique string to use for the next interim meeting for
|
||||
*group*, used for Meeting.number field."""
|
||||
meetings = Meeting.objects.filter(
|
||||
number__startswith='interim-{year}-{group}'.format(
|
||||
year=date.year,
|
||||
|
@ -475,9 +475,23 @@ def get_next_interim_number(group, date):
|
|||
return 'interim-{year}-{group}-{sequence}'.format(
|
||||
year=date.year,
|
||||
group=group.acronym,
|
||||
sequence=last_sequence + 1)
|
||||
sequence=str(last_sequence + 1).zfill(2))
|
||||
|
||||
|
||||
def get_next_agenda_name(meeting):
|
||||
"""Returns the next name to use for an agenda document for *meeting*"""
|
||||
group = meeting.session_set.first().group
|
||||
documents = Document.objects.filter(type='agenda', session__meeting=meeting)
|
||||
if documents:
|
||||
sequences = [int(d.name.split('-')[-1]) for d in documents]
|
||||
last_sequence = sorted(sequences)[-1]
|
||||
else:
|
||||
last_sequence = 0
|
||||
return 'agenda-{meeting}-{group}-{sequence}'.format(
|
||||
meeting=meeting.number,
|
||||
group=group.acronym,
|
||||
sequence=str(last_sequence + 1).zfill(2))
|
||||
|
||||
def sessions_post_save(forms):
|
||||
"""Helper function to perform various post save operations on each form of a
|
||||
InterimSessionModelForm formset"""
|
||||
|
|
|
@ -6,14 +6,12 @@ import urlparse
|
|||
from django.core.urlresolvers import reverse as urlreverse
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import User
|
||||
from django.db import transaction
|
||||
|
||||
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.helpers import get_announcement_initial
|
||||
from ietf.meeting.models import Session, TimeSlot, Meeting
|
||||
from ietf.meeting.test_data import make_meeting_test_data
|
||||
from ietf.name.models import SessionStatusName
|
||||
|
@ -416,20 +414,31 @@ class InterimTests(TestCase):
|
|||
r = self.client.get(url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
today = datetime.date.today()
|
||||
mars_interim = Meeting.objects.filter(date__gt=today,type='interim',session__group__acronym='mars',session__status='sched').first()
|
||||
ames_interim = Meeting.objects.filter(date__gt=today,type='interim',session__group__acronym='ames',session__status='canceled').first()
|
||||
mars_interim = Meeting.objects.filter(date__gt=today, type='interim', session__group__acronym='mars', session__status='sched').first()
|
||||
ames_interim = Meeting.objects.filter(date__gt=today, type='interim', session__group__acronym='ames', session__status='canceled').first()
|
||||
self.assertTrue(mars_interim.number in r.content)
|
||||
self.assertTrue(ames_interim.number in r.content)
|
||||
self.assertTrue('IETF - 42' in r.content)
|
||||
# cancelled session
|
||||
q = PyQuery(r.content)
|
||||
self.assertTrue('CANCELLED' in q('[id*="-ames"]').text())
|
||||
self.check_interim_tabs(url)
|
||||
|
||||
def test_upcoming_ics(self):
|
||||
def test_upcoming_ical(self):
|
||||
make_meeting_test_data()
|
||||
r = self.client.get("/meeting/upcoming.ics/")
|
||||
url = urlreverse("ietf.meeting.views.upcoming_ical")
|
||||
r = self.client.get(url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertEqual(r.get('Content-Type'),"text/calendar")
|
||||
self.assertEqual(r.get('Content-Type'), "text/calendar")
|
||||
self.assertEqual(r.content.count('UID'), 5)
|
||||
# check filtered output
|
||||
url = url + '?filters=mars'
|
||||
r = self.client.get(url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertEqual(r.get('Content-Type'), "text/calendar")
|
||||
# print r.content
|
||||
self.assertEqual(r.content.count('UID'), 2)
|
||||
|
||||
|
||||
def test_interim_request_permissions(self):
|
||||
'''Ensure only authorized users see link to request interim meeting'''
|
||||
|
@ -469,45 +478,6 @@ class InterimTests(TestCase):
|
|||
self.assertEqual(Group.objects.filter(type__in=('wg','rg'),state='active').count(),
|
||||
len(q("#id_group option")) -1 ) # -1 for options placeholder
|
||||
|
||||
def test_temp(self):
|
||||
from django.forms.models import modelform_factory, inlineformset_factory
|
||||
from ietf.meeting.forms import InterimSessionModelForm
|
||||
from django.utils.functional import curry
|
||||
|
||||
make_meeting_test_data()
|
||||
group = Group.objects.get(acronym='mars')
|
||||
date = datetime.date.today() + datetime.timedelta(days=30)
|
||||
time = datetime.datetime.now().time().replace(microsecond=0,second=0)
|
||||
dt = datetime.datetime.combine(date, time)
|
||||
duration = datetime.timedelta(hours=3)
|
||||
remote_instructions = 'Use webex'
|
||||
agenda = 'Intro. Slides. Discuss.'
|
||||
agenda_note = 'On second level'
|
||||
self.client.login(username="secretary", password="secretary+password")
|
||||
data = {'group':group.pk,
|
||||
'meeting_type':'single',
|
||||
'city':'',
|
||||
'country':'',
|
||||
'time_zone':'UTC',
|
||||
'session_set-0-date':date.strftime("%Y-%m-%d"),
|
||||
'session_set-0-time':time.strftime('%H:%M'),
|
||||
'session_set-0-requested_duration':'03:00:00',
|
||||
'session_set-0-remote_instructions':remote_instructions,
|
||||
'session_set-0-agenda':agenda,
|
||||
'session_set-0-agenda_note':agenda_note,
|
||||
'session_set-TOTAL_FORMS':1,
|
||||
'session_set-INITIAL_FORMS':0,
|
||||
'session_set-MIN_NUM_FORMS':0,
|
||||
'session_set-MAX_NUM_FORMS':1000}
|
||||
|
||||
user = User.objects.get(username='secretary')
|
||||
is_approved = False
|
||||
meeting = Meeting.objects.order_by('id').last()
|
||||
SessionFormset = inlineformset_factory(Meeting, Session, form=InterimSessionModelForm, can_delete=False, extra=2)
|
||||
SessionFormset.form = staticmethod(curry(InterimSessionModelForm, user=user,group=group,is_approved=is_approved))
|
||||
formset = SessionFormset(instance=meeting, data=data)
|
||||
#assert False, (formset.management_form)
|
||||
formset.save()
|
||||
|
||||
def test_interim_request_single(self):
|
||||
make_meeting_test_data()
|
||||
|
@ -542,7 +512,7 @@ class InterimTests(TestCase):
|
|||
meeting = Meeting.objects.order_by('id').last()
|
||||
self.assertEqual(meeting.type_id,'interim')
|
||||
self.assertEqual(meeting.date,date)
|
||||
self.assertEqual(meeting.number,'interim-%s-%s-%s' % (date.year,group.acronym,1))
|
||||
self.assertEqual(meeting.number,'interim-%s-%s-%s' % (date.year,group.acronym,'01'))
|
||||
self.assertEqual(meeting.city,'')
|
||||
self.assertEqual(meeting.country,'')
|
||||
self.assertEqual(meeting.time_zone,'UTC')
|
||||
|
@ -592,7 +562,7 @@ class InterimTests(TestCase):
|
|||
meeting = Meeting.objects.order_by('id').last()
|
||||
self.assertEqual(meeting.type_id,'interim')
|
||||
self.assertEqual(meeting.date,date)
|
||||
self.assertEqual(meeting.number,'interim-%s-%s-%s' % (date.year,group.acronym,1))
|
||||
self.assertEqual(meeting.number,'interim-%s-%s-%s' % (date.year,group.acronym,'01'))
|
||||
self.assertEqual(meeting.city,city)
|
||||
self.assertEqual(meeting.country,country)
|
||||
self.assertEqual(meeting.time_zone,time_zone)
|
||||
|
@ -645,7 +615,7 @@ class InterimTests(TestCase):
|
|||
meeting = Meeting.objects.order_by('id').last()
|
||||
self.assertEqual(meeting.type_id,'interim')
|
||||
self.assertEqual(meeting.date,date)
|
||||
self.assertEqual(meeting.number,'interim-%s-%s-%s' % (date.year,group.acronym,1))
|
||||
self.assertEqual(meeting.number,'interim-%s-%s-%s' % (date.year,group.acronym,'01'))
|
||||
self.assertEqual(meeting.city,city)
|
||||
self.assertEqual(meeting.country,country)
|
||||
self.assertEqual(meeting.time_zone,time_zone)
|
||||
|
@ -712,7 +682,7 @@ class InterimTests(TestCase):
|
|||
meeting = meetings[1]
|
||||
self.assertEqual(meeting.type_id,'interim')
|
||||
self.assertEqual(meeting.date,date)
|
||||
self.assertEqual(meeting.number,'interim-%s-%s-%s' % (date.year,group.acronym,1))
|
||||
self.assertEqual(meeting.number,'interim-%s-%s-%s' % (date.year,group.acronym,'01'))
|
||||
self.assertEqual(meeting.city,city)
|
||||
self.assertEqual(meeting.country,country)
|
||||
self.assertEqual(meeting.time_zone,time_zone)
|
||||
|
@ -727,7 +697,7 @@ class InterimTests(TestCase):
|
|||
meeting = meetings[0]
|
||||
self.assertEqual(meeting.type_id,'interim')
|
||||
self.assertEqual(meeting.date,date2)
|
||||
self.assertEqual(meeting.number,'interim-%s-%s-%s' % (date.year,group.acronym,2))
|
||||
self.assertEqual(meeting.number,'interim-%s-%s-%s' % (date.year,group.acronym,'02'))
|
||||
self.assertEqual(meeting.city,city)
|
||||
self.assertEqual(meeting.country,country)
|
||||
self.assertEqual(meeting.time_zone,time_zone)
|
||||
|
|
|
@ -67,7 +67,7 @@ urlpatterns = [
|
|||
url(r'^(?:(?P<num>\d+)/)?', include(type_ietf_only_patterns_id_optional)),
|
||||
url(r'^(?P<num>\d+)/', include(type_ietf_only_patterns)),
|
||||
url(r'^upcoming/$', views.upcoming),
|
||||
url(r'^upcoming.ics/$', views.ical_upcoming),
|
||||
url(r'^upcoming.ics/$', views.upcoming_ical),
|
||||
url(r'^interim/announce/$', views.interim_announce),
|
||||
url(r'^interim/announce/(?P<number>[A-Za-z0-9._+-]+)/$', views.interim_send_announcement),
|
||||
url(r'^interim/request/$', views.interim_request),
|
||||
|
|
|
@ -21,7 +21,7 @@ from django.core.urlresolvers import reverse
|
|||
from django.db.models import Min, Max
|
||||
from django.conf import settings
|
||||
from django.forms.models import modelform_factory, inlineformset_factory
|
||||
from django.forms import ModelForm, formset_factory
|
||||
from django.forms import ModelForm
|
||||
from django.utils.functional import curry
|
||||
from django.views.decorators.csrf import ensure_csrf_cookie
|
||||
|
||||
|
@ -40,13 +40,12 @@ from ietf.meeting.helpers import preprocess_assignments_for_agenda, read_agenda_
|
|||
from ietf.meeting.helpers import convert_draft_to_pdf, get_earliest_session_date
|
||||
from ietf.meeting.helpers import can_view_interim_request, can_approve_interim_request
|
||||
from ietf.meeting.helpers import can_request_interim_meeting, get_announcement_initial
|
||||
from ietf.meeting.helpers import get_interim_initial, get_interim_session_initial
|
||||
from ietf.meeting.helpers import sessions_post_save, is_meeting_approved
|
||||
from ietf.utils.mail import send_mail_message
|
||||
from ietf.utils.pipe import pipe
|
||||
from ietf.utils.pdf import pdf_pages
|
||||
|
||||
from .forms import InterimMeetingModelForm, InterimSessionForm, InterimAnnounceForm, InterimSessionModelForm
|
||||
from .forms import InterimMeetingModelForm, InterimAnnounceForm, InterimSessionModelForm
|
||||
|
||||
|
||||
def get_menu_entries(request):
|
||||
|
@ -960,7 +959,7 @@ def interim_send_announcement(request, number):
|
|||
'RG Chair')
|
||||
def interim_pending(request):
|
||||
'''View which shows interim meeting requests pending approval'''
|
||||
meetings = Meeting.objects.filter(type='interim', session__status='apprw')
|
||||
meetings = Meeting.objects.filter(type='interim', session__status='apprw').distinct()
|
||||
menu_entries = get_menu_entries(request)
|
||||
selected_menu_entry = 'pending'
|
||||
|
||||
|
@ -1126,22 +1125,11 @@ def interim_request_edit(request, number):
|
|||
"formset": formset})
|
||||
|
||||
|
||||
def ical_upcoming(request):
|
||||
'''ICAL upcoming meetings'''
|
||||
today = datetime.datetime.today()
|
||||
meetings = Meeting.objects.filter(date__gt=today)
|
||||
|
||||
return render(request, "meeting/upcoming.ics", {
|
||||
"meetings": meetings,
|
||||
}, content_type="text/calendar")
|
||||
|
||||
|
||||
def upcoming(request):
|
||||
'''List of upcoming meetings'''
|
||||
today = datetime.datetime.today()
|
||||
meetings = Meeting.objects.filter(
|
||||
date__gte=today,
|
||||
session__status__in=('sched', 'canceled')).order_by('date')
|
||||
meetings = Meeting.objects.filter(date__gte=today).exclude(
|
||||
session__status__in=('apprw', 'schedpa')).order_by('date')
|
||||
|
||||
# extract groups hierarchy for display filter
|
||||
seen = set()
|
||||
|
@ -1170,12 +1158,40 @@ def upcoming(request):
|
|||
# add menu actions
|
||||
actions = []
|
||||
if can_request_interim_meeting(request.user):
|
||||
actions.append(("Request new interim meeting",
|
||||
reverse("ietf.meeting.views.interim_request")))
|
||||
actions.append(('Request new interim meeting',
|
||||
reverse('ietf.meeting.views.interim_request')))
|
||||
actions.append(('Download as .ics',
|
||||
reverse('ietf.meeting.views.upcoming_ical')))
|
||||
|
||||
return render(request, "meeting/upcoming.html", {
|
||||
return render(request, 'meeting/upcoming.html', {
|
||||
'meetings': meetings,
|
||||
'menu_actions': actions,
|
||||
'menu_entries': menu_entries,
|
||||
'selected_menu_entry': selected_menu_entry,
|
||||
'group_parents': group_parents})
|
||||
|
||||
|
||||
def upcoming_ical(request):
|
||||
'''Return Upcoming meetings in iCalendar file'''
|
||||
filters = request.GET.getlist('filters')
|
||||
#assert False, filters
|
||||
today = datetime.datetime.today()
|
||||
meetings = Meeting.objects.filter(date__gte=today).exclude(
|
||||
session__status__in=('apprw', 'schedpa')).order_by('date')
|
||||
|
||||
assignments = []
|
||||
for meeting in meetings:
|
||||
items = meeting.agenda.assignments.order_by(
|
||||
'session__type__slug', 'timeslot__time')
|
||||
assignments.extend(items)
|
||||
|
||||
# apply filters
|
||||
if filters:
|
||||
assignments = [a for a in assignments if
|
||||
a.session.group.acronym in filters or
|
||||
a.session.group.parent.acronym in filters]
|
||||
|
||||
|
||||
return render(request, 'meeting/upcoming.ics', {
|
||||
'assignments': assignments,
|
||||
}, content_type='text/calendar')
|
||||
|
|
|
@ -101,7 +101,6 @@ var interimRequest = {
|
|||
return false;
|
||||
},
|
||||
|
||||
|
||||
calculateEndTime : function() {
|
||||
// gets called when either start_time or duration change
|
||||
var fieldset = $(this).parents(".fieldset");
|
||||
|
|
|
@ -8,18 +8,14 @@ function toggle_visibility() {
|
|||
$(".pickviewneg").addClass("active");
|
||||
|
||||
if (h) {
|
||||
// if there are items in the hash, hide all rows that are
|
||||
// hidden by default, show all rows that are shown by default
|
||||
// if there are items in the hash, hide all rows
|
||||
$('[id^="row-"]').hide();
|
||||
//$.each($(".pickviewneg").text().trim().split(/ +/), function (i, v) {
|
||||
// v = v.trim().toLowerCase();
|
||||
// $('[id^="row-"]').filter('[id*="-' + v + '"]').show();
|
||||
//});
|
||||
|
||||
// show the customizer
|
||||
$("#customize").collapse("show");
|
||||
|
||||
// loop through the has items and change the UI element and row visibilities accordingly
|
||||
var query_array = [];
|
||||
$.each(h.split(","), function (i, v) {
|
||||
if (v.indexOf("-") == 0) {
|
||||
// this is a "negative" item: when present, hide these rows
|
||||
|
@ -32,20 +28,21 @@ function toggle_visibility() {
|
|||
$('[id^="row-"]').filter('[id*="-' + v + '"]').show();
|
||||
$(".view." + v).find("button").addClass("active disabled");
|
||||
$("button.pickview." + v).addClass("active");
|
||||
query_array.push("filters=" + v)
|
||||
}
|
||||
});
|
||||
|
||||
// show the week view
|
||||
//$("#weekview").attr("src", "week-view.html" + window.location.hash).removeClass("hidden");
|
||||
|
||||
// show the custom .ics link
|
||||
//$("#ical-link").attr("href",$("#ical-link").attr("href").split("?")[0]+"?"+h);
|
||||
//$("#ical-link").removeClass("hidden");
|
||||
|
||||
// adjust the custom .ics link
|
||||
var link = $('a[href*="upcoming.ics"]');
|
||||
var new_href = link.attr("href").split("?")[0]+"?"+query_array.join("&");
|
||||
link.attr("href",new_href);
|
||||
|
||||
} else {
|
||||
// if the hash is empty, show all and hide weekview
|
||||
// if the hash is empty, show all
|
||||
$('[id^="row-"]').show();
|
||||
//$("#ical-link, #weekview").addClass("hidden");
|
||||
// adjust the custom .ics link
|
||||
var link = $('a[href*="upcoming.ics"]');
|
||||
link.attr("href",link.attr("href").split("?")[0]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{% extends "base.html" %}
|
||||
{# Copyright The IETF Trust 2015, All Rights Reserved #}
|
||||
{% load origin %}
|
||||
{% load staticfiles bootstrap3 widget_tweaks %}
|
||||
{% load staticfiles bootstrap3 widget_tweaks ietf_filters %}
|
||||
|
||||
{% block title %}Interim Request{% endblock %}
|
||||
|
||||
|
@ -26,9 +26,11 @@
|
|||
<label class="checkbox-inline">{% render_field form.in_person %}<strong>In Person</strong></label>
|
||||
</div>
|
||||
|
||||
{% if user|has_role:"Secretariat,Area Director,IRTF Chair" %}
|
||||
<div class="col-md-2">
|
||||
<label class="checkbox-inline">{% render_field form.approved %}<strong>Preapproved by AD</strong></label>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="col-md-2 radio-inline"><strong>Meeting Type:</strong></div>
|
||||
|
||||
|
@ -98,13 +100,13 @@
|
|||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="id_session_set-{{ forloop.counter0 }}-remote_instructions" class="col-md-2 control-label">Remote Instructions</label>
|
||||
<div class="col-md-10">{% render_field form.remote_instructions class="form-control" placeholder="ie. Webex address" %}</div>
|
||||
<label for="id_session_set-{{ forloop.counter0 }}-remote_instructions" class="col-md-2 control-label required">Remote Instructions</label>
|
||||
<div class="col-md-10">{% render_field form.remote_instructions class="form-control" placeholder="Webex URL" %}<p class="help-block">"Remote participation is not supported" or "Remote participation information will be obtained at the time of approval" are acceptable values. See <a href="">here</a> for more on remote participation support.</p></div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="id_session_set-{{ forloop.counter0 }}-agenda" class="col-md-2 control-label">Agenda</label>
|
||||
<div class="col-md-10">{% render_field form.agenda class="form-control" rows="6" placeholder="paste agenda here" %}</div>
|
||||
<div class="col-md-10">{% render_field form.agenda class="form-control" rows="6" placeholder="Paste agenda here" %}</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
|
@ -127,7 +129,7 @@
|
|||
<a class="btn btn-default pull-right" href="{% url 'ietf.meeting.views.upcoming' %}">Back</a>
|
||||
{% endbuttons %}
|
||||
</div>
|
||||
|
||||
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
@ -113,7 +113,7 @@
|
|||
<tbody>
|
||||
{% for meeting in meetings %}
|
||||
{% if meeting.type.slug == 'interim' %}
|
||||
<tr id="row-{{ forloop.counter }}-{{ meeting.session_set.all.0.group.acronym }}">
|
||||
<tr id="row-{{ forloop.counter }}-{{ meeting.session_set.first.group.parent.acronym }}-{{ meeting.session_set.first.group.acronym }}">
|
||||
{% else %}
|
||||
<tr id="row-{{ forloop.counter }}-ietf">
|
||||
{% endif %}
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
VERSION:2.0
|
||||
METHOD:PUBLISH
|
||||
PRODID:-//IETF//datatracker.ietf.org ical upcoming//EN
|
||||
{% for meeting in meetings %}BEGIN:VEVENT
|
||||
UID:ietf-{{meeting.number}}-{{meeting.session_set.all.0.pk}}
|
||||
SUMMARY:{% if item.session.name %}{{item.session.name|ics_esc}}{% else %}{% if not item.session.historic_group %}{{item.timeslot.name|ics_esc}}{% else %}{{item.session.historic_group.acronym|lower}} - {{item.session.historic_group.name}}{% endif%}{%endif%}
|
||||
{% for item in assignments %}BEGIN:VEVENT
|
||||
UID:ietf-{{item.session.meeting.number}}-{{item.timeslot.pk}}
|
||||
SUMMARY:{% if item.session.name %}{{item.session.name|ics_esc}}{% else %}{{item.session.group.acronym|lower}} - {{item.session.group.name}}{%endif%}
|
||||
{% if item.timeslot.show_location %}LOCATION:{{item.timeslot.get_location}}
|
||||
{% endif %}STATUS:{{item.session.ical_status}}
|
||||
CLASS:PUBLIC
|
||||
|
|
Loading…
Reference in a new issue