Merge lp:~danilo/summit/private-attendees into lp:summit

Proposed by Данило Шеган
Status: Merged
Approved by: Chris Johnston
Approved revision: 271
Merged at revision: 271
Proposed branch: lp:~danilo/summit/private-attendees
Merge into: lp:summit
Diff against target: 155 lines (+89/-14)
5 files modified
summit/schedule/forms.py (+73/-10)
summit/schedule/templates/schedule/create_private.html (+7/-0)
summit/schedule/templates/schedule/edit_private.html (+7/-0)
summit/schedule/tests.py (+1/-2)
summit/schedule/views.py (+1/-2)
To merge this branch: bzr merge lp:~danilo/summit/private-attendees
Reviewer Review Type Date Requested Status
Chris Johnston Approve
Review via email: mp+90183@code.launchpad.net

Commit message

Makes it possible for the creator of a private meeting to add required participants.

Description of the change

WARNING: no tests due to time being limited!

Add ability to private meetings page to add attendees directly.

It turns out formsets do not really support dealing with many-to-many relations, so I turned back to the approach I planned from the get-go: override save() method to take special consideration for the 'participants' field.

For better usability, I am also using multiple-checkboxes widget instead of multi-select box, because it allows at least easy searching using one's browser's built-in search in a long list of ~200 registered people.

We should definitely display meeting attendants on the private meeting page as well, but I'd rather do that as a separate branch.

To post a comment you must log in.
Revision history for this message
Chris Johnston (cjohnston) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'summit/schedule/forms.py'
2--- summit/schedule/forms.py 2012-01-23 01:18:55 +0000
3+++ summit/schedule/forms.py 2012-01-25 18:44:24 +0000
4@@ -27,13 +27,76 @@
5
6 from summit.schedule.models.meetingmodel import Meeting
7 from summit.schedule.models.attendeemodel import Attendee
8-
9-class CreatePrivateMeeting(forms.ModelForm):
10- class Meta:
11- model = Meeting
12- fields = ('name', 'title', 'description', 'spec_url', 'wiki_url', 'pad_url', 'requires_dial_in')
13-
14-class EditPrivateMeeting(forms.ModelForm):
15- class Meta:
16- model = Meeting
17- fields = ('title', 'description', 'spec_url', 'wiki_url', 'pad_url', 'requires_dial_in')
18+from summit.schedule.models.participantmodel import Participant
19+
20+
21+class MultipleAttendeeField(forms.ModelMultipleChoiceField):
22+ def label_from_instance(self, obj):
23+ return u"%s %s (%s)" % (
24+ obj.user.first_name, obj.user.last_name, obj.user.username)
25+
26+
27+class PrivateMeetingFormBase(forms.ModelForm):
28+ participants = MultipleAttendeeField(
29+ queryset=Attendee.objects.all,
30+ widget=forms.CheckboxSelectMultiple)
31+
32+ def __init__(self, *args, **kwargs):
33+ if 'instance' in kwargs:
34+ if kwargs['instance'].pk is not None:
35+ # We get the 'initial' keyword argument or initialize it
36+ # as a dict if it didn't exist.
37+ initial = kwargs.setdefault('initial', {})
38+ # The widget for a ModelMultipleChoiceField expects
39+ # a list of primary key for the selected data.
40+ initial['participants'] = [
41+ attendee.pk
42+ for attendee in kwargs['instance'].participants.all()]
43+
44+ super(PrivateMeetingFormBase, self).__init__(*args, **kwargs)
45+
46+ summit = self.instance.summit
47+ self.fields['participants'].queryset = Attendee.objects.filter(
48+ summit=summit).order_by('user__first_name',
49+ 'user__last_name',
50+ 'user__username')
51+
52+ def save(self, commit=True):
53+ instance = super(PrivateMeetingFormBase, self).save(commit)
54+
55+ # Prepare a 'save_m2m' method for the form,
56+ if 'save_m2m' in dir(self):
57+ old_save_m2m = self.save_m2m
58+ else:
59+ old_save_m2m = None
60+ def save_m2m():
61+ if old_save_m2m is not None:
62+ old_save_m2m()
63+ # This is where we actually link the pizza with toppings
64+ instance.participants.clear()
65+ for participant in self.cleaned_data['participants']:
66+ record = Participant(meeting=instance, attendee=participant,
67+ required=True)
68+ record.save()
69+ self.save_m2m = save_m2m
70+
71+ # Do we need to save all changes now?
72+ if commit:
73+ instance.save()
74+ self.save_m2m()
75+
76+ return instance
77+
78+
79+class CreatePrivateMeeting(PrivateMeetingFormBase):
80+ class Meta:
81+ model = Meeting
82+ fields = ('name', 'title', 'description', 'spec_url', 'wiki_url',
83+ 'pad_url', 'requires_dial_in')
84+
85+
86+class EditPrivateMeeting(PrivateMeetingFormBase):
87+ class Meta:
88+ model = Meeting
89+ fields = ('title', 'description', 'spec_url', 'wiki_url', 'pad_url',
90+ 'requires_dial_in')
91
92=== modified file 'summit/schedule/templates/schedule/create_private.html'
93--- summit/schedule/templates/schedule/create_private.html 2012-01-23 01:18:55 +0000
94+++ summit/schedule/templates/schedule/create_private.html 2012-01-25 18:44:24 +0000
95@@ -8,6 +8,13 @@
96 $('span[rel*=help]').colorTip({color:'orange'});
97 });
98 --></script>
99+<style>
100+ul {
101+ height: 12em;
102+ overflow-y: scroll;
103+ overflow-x: hidden;
104+}
105+</style>
106 {% endblock %}
107
108
109
110=== modified file 'summit/schedule/templates/schedule/edit_private.html'
111--- summit/schedule/templates/schedule/edit_private.html 2012-01-23 01:18:55 +0000
112+++ summit/schedule/templates/schedule/edit_private.html 2012-01-25 18:44:24 +0000
113@@ -8,6 +8,13 @@
114 $('span[rel*=help]').colorTip({color:'orange'});
115 });
116 --></script>
117+<style>
118+ul {
119+ height: 12em;
120+ overflow-y: scroll;
121+ overflow-x: hidden;
122+}
123+</style>
124 {% endblock %}
125
126
127
128=== modified file 'summit/schedule/tests.py'
129--- summit/schedule/tests.py 2012-01-25 02:53:03 +0000
130+++ summit/schedule/tests.py 2012-01-25 18:44:24 +0000
131@@ -1516,8 +1516,7 @@
132 # To avoid test being fragile and arbitrarily dependent on the
133 # room order, we check the returned schedule.meetings bit-by-bit.
134 self.assertEqual([slot], schedule.meetings.keys())
135- self.assertEqual(set([(room1, None)]),
136- set(schedule.meetings[slot]))
137+ self.assertEqual(1, len(set(schedule.meetings[slot])))
138
139
140 class LaunchpadExportNode(dict):
141
142=== modified file 'summit/schedule/views.py'
143--- summit/schedule/views.py 2012-01-24 05:06:04 +0000
144+++ summit/schedule/views.py 2012-01-25 18:44:24 +0000
145@@ -21,10 +21,9 @@
146 from django.contrib.auth import logout
147 from django.contrib.auth.decorators import login_required, permission_required
148 from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned
149-from django.http import HttpResponse, HttpResponseRedirect
150+from django.http import Http404, HttpResponse, HttpResponseRedirect
151 from django.shortcuts import render_to_response, get_object_or_404
152 from django.template import RequestContext
153-from django.http import Http404
154 from django.core.urlresolvers import reverse
155 from django.utils.datastructures import SortedDict
156

Subscribers

People subscribed via source and target branches