Merge lp:~cjohnston/summit/quick-links into lp:summit
- quick-links
- Merge into trunk
Proposed by
Chris Johnston
Status: | Superseded |
---|---|
Proposed branch: | lp:~cjohnston/summit/quick-links |
Merge into: | lp:summit |
Diff against target: |
744 lines (+594/-12) 13 files modified
EXTERNALS (+7/-0) summit/common/widgets.py (+111/-0) summit/media/css/style.css (+44/-0) summit/media/js/events-ui.js (+11/-0) summit/schedule/forms.py (+40/-0) summit/schedule/templates/schedule/actions.html (+8/-3) summit/schedule/templates/schedule/form.html (+47/-0) summit/schedule/templates/schedule/summit.html (+3/-7) summit/schedule/tests/__init__.py (+1/-0) summit/schedule/tests/registration.py (+272/-0) summit/schedule/views.py (+44/-1) summit/settings.py (+1/-1) summit/urls.py (+5/-0) |
To merge this branch: | bzr merge lp:~cjohnston/summit/quick-links |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Summit Hackers | Pending | ||
Review via email: mp+157550@code.launchpad.net |
This proposal has been superseded by a proposal from 2013-04-07.
Commit message
Does some work on the actions area
Description of the change
To post a comment you must log in.
lp:~cjohnston/summit/quick-links
updated
- 521. By Chris Johnston
-
Update from other branches
Unmerged revisions
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'EXTERNALS' |
2 | --- EXTERNALS 2012-04-22 20:20:10 +0000 |
3 | +++ EXTERNALS 2013-04-07 16:28:22 +0000 |
4 | @@ -19,3 +19,10 @@ |
5 | |
6 | - Twitter JS |
7 | http://twitter.com/javascripts/blogger.js |
8 | + |
9 | +- Date Picker |
10 | +provided by the JQuery UI project. See http://jqueryui.com/ |
11 | + |
12 | +- DateTimeWidget inspired by: |
13 | +http://copiesofcopies.org/webl/2010/04/26/a-better-datetime-widget-for-django/ |
14 | + |
15 | |
16 | === added file 'summit/common/widgets.py' |
17 | --- summit/common/widgets.py 1970-01-01 00:00:00 +0000 |
18 | +++ summit/common/widgets.py 2013-04-07 16:28:22 +0000 |
19 | @@ -0,0 +1,111 @@ |
20 | +# -*- coding: utf-8 -*- |
21 | +from datetime import time |
22 | +from time import strptime, strftime |
23 | + |
24 | +from django import forms |
25 | + |
26 | + |
27 | +class DateWidget(forms.DateInput): |
28 | + """ |
29 | + A more-friendly date widget with a pop-up calendar. |
30 | + """ |
31 | + def __init__(self, attrs=None): |
32 | + self.date_class = 'datepicker' |
33 | + if not attrs: |
34 | + attrs = {} |
35 | + if 'date_class' in attrs: |
36 | + self.date_class = attrs.pop('date_class') |
37 | + if 'class' not in attrs: |
38 | + attrs['class'] = 'date' |
39 | + |
40 | + super(DateWidget, self).__init__(attrs=attrs) |
41 | + |
42 | + def render(self, name, value, attrs=None): |
43 | + return '<span class="%s">%s</span>' % ( |
44 | + self.date_class, |
45 | + super(DateWidget, self).render(name, value, attrs) |
46 | + ) |
47 | + |
48 | + |
49 | +class TimeWidget(forms.MultiWidget): |
50 | + """ |
51 | + A more-friendly time widget. |
52 | + """ |
53 | + def __init__(self, attrs=None): |
54 | + self.time_class = 'timepicker' |
55 | + if not attrs: |
56 | + attrs = {} |
57 | + if 'time_class' in attrs: |
58 | + self.time_class = attrs.pop('time_class') |
59 | + if 'class' not in attrs: |
60 | + attrs['class'] = 'time' |
61 | + |
62 | + widgets = ( |
63 | + forms.Select( |
64 | + attrs=attrs, |
65 | + choices=[(i + 1, "%02d" % (i + 1)) for i in range(0, 12)], |
66 | + ), |
67 | + forms.Select( |
68 | + attrs=attrs, |
69 | + choices=[(i, "%02d" % i) for i in range(00, 60, 15)], |
70 | + ), |
71 | + forms.Select( |
72 | + attrs=attrs, |
73 | + choices=[('AM', 'AM'), ('PM', 'PM')], |
74 | + ) |
75 | + ) |
76 | + |
77 | + super(TimeWidget, self).__init__(widgets, attrs) |
78 | + |
79 | + def decompress(self, value): |
80 | + if isinstance(value, time): |
81 | + hour = int(value.strftime("%I")) |
82 | + minute = int(value.strftime("%M")) |
83 | + meridian = value.strftime("%p") |
84 | + return (hour, minute, meridian) |
85 | + return (None, None, None) |
86 | + |
87 | + def value_from_datadict(self, data, files, name): |
88 | + value = super(TimeWidget, self).value_from_datadict(data, files, name) |
89 | + t = strptime( |
90 | + "%02d:%02d %s" % ( |
91 | + int(value[0]), |
92 | + int(value[1]), |
93 | + value[2] |
94 | + ), |
95 | + "%I:%M %p", |
96 | + ) |
97 | + return strftime("%H:%M:%S", t) |
98 | + |
99 | + def format_output(self, rendered_widgets): |
100 | + return '<span class="%s">%s%s%s</span>' % ( |
101 | + self.time_class, |
102 | + rendered_widgets[0], rendered_widgets[1], rendered_widgets[2] |
103 | + ) |
104 | + |
105 | + |
106 | +class DateTimeWidget(forms.SplitDateTimeWidget): |
107 | + """ |
108 | + A more-friendly date/time widget. |
109 | + |
110 | + Inspired by: |
111 | + |
112 | + http://copiesofcopies.org/webl/2010/04/26/a-better-datetime-widget-for-django/ |
113 | + """ |
114 | + def __init__(self, attrs=None, date_format=None, time_format=None): |
115 | + super(DateTimeWidget, self).__init__(attrs, date_format, time_format) |
116 | + self.widgets = ( |
117 | + DateWidget(attrs=attrs), |
118 | + TimeWidget(attrs=attrs), |
119 | + ) |
120 | + |
121 | + def decompress(self, value): |
122 | + if value: |
123 | + d = strftime("%Y-%m-%d", value.timetuple()) |
124 | + t = strftime("%I:%M %p", value.timetuple()) |
125 | + return (d, t) |
126 | + else: |
127 | + return (None, None) |
128 | + |
129 | + def format_output(self, rendered_widgets): |
130 | + return '%s%s' % (rendered_widgets[0], rendered_widgets[1]) |
131 | |
132 | === modified file 'summit/media/css/style.css' |
133 | --- summit/media/css/style.css 2012-01-22 18:36:40 +0000 |
134 | +++ summit/media/css/style.css 2013-04-07 16:28:22 +0000 |
135 | @@ -66,3 +66,47 @@ |
136 | .summit-columns ul li h3 a:hover { text-decoration: underline; } |
137 | .summit-columns ul li img { padding: 10px 0 5px; } |
138 | .summit-columns p { margin-bottom: 5px; } |
139 | + |
140 | +#id_start_utc_0, #id_start_utc_1, |
141 | +#id_end_utc_0, #id_end_utc_1, |
142 | +#id_start_utc_1_0, #id_start_utc_1_1, #id_start_utc_1_2, |
143 | +#id_end_utc_1_0, #id_end_utc_1_1, #id_end_utc_1_2 { |
144 | + width: 100px; |
145 | + display: inline; |
146 | +} |
147 | + |
148 | +#id_start_utc_1_0, #id_start_utc_1_1, #id_start_utc_1_2, |
149 | +#id_end_utc_1_0, #id_end_utc_1_1, #id_end_utc_1_2 { |
150 | + width: 60px; |
151 | +} |
152 | + |
153 | +#id_name, #id_announce, #id_registration { |
154 | + width: 350px; |
155 | +} |
156 | + |
157 | +.link-cta { |
158 | + margin-top: 10px; |
159 | + padding: 10px; |
160 | + background: url('../img/background-cta.png') center 0 repeat-x #dd4814; |
161 | + display: block; |
162 | + float: left; |
163 | + color: #fff !important; |
164 | + -moz-border-radius: 4px; |
165 | + -webkit-border-radius: 4px; |
166 | + border-radius: 4px; |
167 | +} |
168 | + |
169 | +.link-cta + p { |
170 | + margin: 18px 0 0 10px; |
171 | + float: left; |
172 | +} |
173 | + |
174 | +.link-cta.disabled { |
175 | + background-image: none; |
176 | + background-color: #aea79f !important; |
177 | + cursor: not-allowed; |
178 | +} |
179 | + |
180 | +.link-arrow:after { |
181 | + content: " ›"; |
182 | +} |
183 | |
184 | === added file 'summit/media/js/events-ui.js' |
185 | --- summit/media/js/events-ui.js 1970-01-01 00:00:00 +0000 |
186 | +++ summit/media/js/events-ui.js 2013-04-07 16:28:22 +0000 |
187 | @@ -0,0 +1,11 @@ |
188 | +$(document).ready(function(){ |
189 | + |
190 | + $.datepicker.setDefaults({ |
191 | + showOn: 'focus', |
192 | + dateFormat: 'yy-mm-dd', |
193 | + }); |
194 | + |
195 | + $("#id_start_utc_0").datepicker(); |
196 | + $("#id_end_utc_0").datepicker(); |
197 | + |
198 | +}); |
199 | |
200 | === modified file 'summit/schedule/forms.py' |
201 | --- summit/schedule/forms.py 2013-03-09 05:14:19 +0000 |
202 | +++ summit/schedule/forms.py 2013-04-07 16:28:22 +0000 |
203 | @@ -14,6 +14,7 @@ |
204 | # You should have received a copy of the GNU Affero General Public License |
205 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
206 | |
207 | +from django.conf import settings |
208 | from django import forms |
209 | |
210 | from summit.schedule.models.meetingmodel import Meeting |
211 | @@ -21,6 +22,7 @@ |
212 | from summit.schedule.models.participantmodel import Participant |
213 | |
214 | from common.forms import RenderableMixin |
215 | +from common.widgets import DateTimeWidget |
216 | |
217 | |
218 | class MultipleAttendeeField(forms.ModelMultipleChoiceField): |
219 | @@ -226,3 +228,41 @@ |
220 | fields = ('hangout_url', 'broadcast_url') |
221 | |
222 | broadcast_url = YouTubeEmbedURL(label='Broadcast URL') |
223 | + |
224 | + |
225 | +class Registration(forms.ModelForm, RenderableMixin): |
226 | + class Media: |
227 | + css = {'all': ( |
228 | + '//code.jquery.com/ui/1.10.2/themes/smoothness/jquery-ui.css', |
229 | + )} |
230 | + js = ( |
231 | + '//code.jquery.com/ui/1.10.2/jquery-ui.js', |
232 | + settings.MEDIA_URL + 'js/events-ui.js', |
233 | + ) |
234 | + |
235 | + class Meta: |
236 | + model = Attendee |
237 | + fields = ( |
238 | + 'start_utc', |
239 | + 'end_utc', |
240 | + 'crew', |
241 | + ) |
242 | + |
243 | + def __init__(self, *args, **kargs): |
244 | + super(Registration, self).__init__(*args, **kargs) |
245 | + self.fields['start_utc'].widget = DateTimeWidget() |
246 | + self.fields['end_utc'].widget = DateTimeWidget() |
247 | + |
248 | + def clean(self): |
249 | + begin = self.cleaned_data.get('start_utc') |
250 | + end = self.cleaned_data.get('end_utc') |
251 | + if begin and end and begin > end: |
252 | + raise forms.ValidationError( |
253 | + "Availability can not end before it starts." |
254 | + ) |
255 | + return self.cleaned_data |
256 | + |
257 | + def save(self, commit=True): |
258 | + instance = super(Registration, self).save(commit) |
259 | + instance.from_launchpad=False |
260 | + instance.save() |
261 | |
262 | === modified file 'summit/schedule/templates/schedule/actions.html' |
263 | --- summit/schedule/templates/schedule/actions.html 2012-08-08 15:10:25 +0000 |
264 | +++ summit/schedule/templates/schedule/actions.html 2013-04-07 16:28:22 +0000 |
265 | @@ -1,9 +1,10 @@ |
266 | {% load schedule_perms %} |
267 | |
268 | {% ifnotequal summit.state "public" %} |
269 | -<div class="action-links"> |
270 | - <p style="font-size: 16px; font-weight: bold;">Actions</p> |
271 | - <ul> |
272 | +<div class="box box-padded"> |
273 | + <h3>Quick Links</h3> |
274 | + <div> |
275 | + <ul class="list"> |
276 | |
277 | {% ifchangeschedule summit attendee %} |
278 | {% if schedule.date %} |
279 | @@ -43,7 +44,11 @@ |
280 | {% endif %} |
281 | {% endifequal %} |
282 | |
283 | +{% if attendee %} |
284 | + <li><a href="{% url registration summit.name %}">Update registration</a></li> |
285 | +{% endif %} |
286 | </ul> |
287 | </div> |
288 | +</div> |
289 | |
290 | {% endifnotequal %} |
291 | |
292 | === added file 'summit/schedule/templates/schedule/form.html' |
293 | --- summit/schedule/templates/schedule/form.html 1970-01-01 00:00:00 +0000 |
294 | +++ summit/schedule/templates/schedule/form.html 2013-04-07 16:28:22 +0000 |
295 | @@ -0,0 +1,47 @@ |
296 | +{% extends "base.html" %} |
297 | + |
298 | +{% block page_name %}{{ form_title }} - {{ summit.title }}{%endblock %} |
299 | +{% block sub_nav %}{% endblock %} |
300 | + |
301 | +{% block extrahead %}{{ block.super }} |
302 | +{{ form.media }} |
303 | +<script type="text/javascript" src="{{MEDIA_URL}}js/colortip-1.0-jquery.js"></script> |
304 | + <link rel="stylesheet" type="text/css" href="{{MEDIA_URL}}css/colortip-1.0-jquery.css"/> |
305 | +{% endblock %} |
306 | + |
307 | +{% block closure %} |
308 | +<script type="text/javascript"><!-- |
309 | +$(document).ready(function(){ |
310 | + $('span[rel*=help]').colorTip({color:'orange'}); |
311 | +}); |
312 | +--></script> |
313 | +<style> |
314 | +form ul { |
315 | + height: 12em; |
316 | + overflow-y: scroll; |
317 | + overflow-x: hidden; |
318 | +} |
319 | +</style> |
320 | +{% endblock %} |
321 | + |
322 | + |
323 | +{% block content %} |
324 | +<div class="row"> |
325 | + <article id="form" class="span-8"> |
326 | + {% if form.errors %} |
327 | + <p style="color: red;"> |
328 | + Please correct the error{{ form.errors|pluralize }} below. |
329 | + </p> |
330 | + {% endif %} |
331 | + |
332 | + <form action="{{ request.path_info }}" method="POST">{% csrf_token %} |
333 | + <fieldset> |
334 | + <h3>{{ form_title }}</h3> |
335 | + {{ form.as_template }} |
336 | + {% if is_popup %}<input type="hidden" name="_popup" value="1">{% endif %} |
337 | + <input type="submit" name="submit" value="Create" class="submit-button" /> |
338 | + </fieldset> |
339 | + </form> |
340 | + </article> |
341 | +</div> |
342 | +{% endblock %} |
343 | |
344 | === modified file 'summit/schedule/templates/schedule/summit.html' |
345 | --- summit/schedule/templates/schedule/summit.html 2013-03-20 02:00:45 +0000 |
346 | +++ summit/schedule/templates/schedule/summit.html 2013-04-07 16:28:22 +0000 |
347 | @@ -38,19 +38,15 @@ |
348 | <div class="row"> |
349 | <section class="span-8"> |
350 | {% if attendee %} |
351 | - <p>You are attending, you can update the days and times of your attendance <ins></ins> |
352 | - <a class="launchpad" href="http://launchpad.net/sprints/{{ summit.name }}/+attend"><img src="/media/img/gem-sm.png" /> Launchpad</a>. |
353 | + <p>You are registered as attending {{ summit.title }}. <a href="{% url registration summit %}">Update registration</a>. |
354 | </p> |
355 | <p>Download your |
356 | <a href="/{{ summit.name }}/participant/my_schedule_{{ attendee.secret_key }}.ical">Participation Schedule</a> to import into your Calendar. |
357 | </p> |
358 | {% else %} |
359 | {% if request.user.is_authenticated %} |
360 | - <p><strong>You are not registered as attending.</strong></p> |
361 | - <p>You can register your attendance in |
362 | - <a class="launchpad" href="http://launchpad.net/sprints/{{ summit.name }}/+attend"><img src="/media/img/gem-sm.png" /> Launchpad</a>. |
363 | - If you have recently done so, wait a few minutes and reload this page. |
364 | - </p> |
365 | + <p><strong>You have not registered in Summit.</strong></p> |
366 | + <p><a class="link-cta" href="{% url registration summit %}">Register in Summit</a></p> |
367 | {% else %} |
368 | <p><strong>You are not logged in.</strong></p> |
369 | <p><a href="/openid/login?next={{login_next}}">Log in now</a></p> |
370 | |
371 | === modified file 'summit/schedule/tests/__init__.py' |
372 | --- summit/schedule/tests/__init__.py 2013-03-14 20:52:12 +0000 |
373 | +++ summit/schedule/tests/__init__.py 2013-04-07 16:28:22 +0000 |
374 | @@ -31,3 +31,4 @@ |
375 | from schedule import * |
376 | from summit_model import * |
377 | from propose_meeting import * |
378 | +from registration import RegistrationTestCase |
379 | |
380 | === added file 'summit/schedule/tests/registration.py' |
381 | --- summit/schedule/tests/registration.py 1970-01-01 00:00:00 +0000 |
382 | +++ summit/schedule/tests/registration.py 2013-04-07 16:28:22 +0000 |
383 | @@ -0,0 +1,272 @@ |
384 | +# The Summit Scheduler web application |
385 | +# Copyright (C) 2008 - 2013 Ubuntu Community, Canonical Ltd |
386 | +# |
387 | +# This program is free software: you can redistribute it and/or modify |
388 | +# it under the terms of the GNU Affero General Public License as |
389 | +# published by the Free Software Foundation, either version 3 of the |
390 | +# License, or (at your option) any later version. |
391 | +# |
392 | +# This program is distributed in the hope that it will be useful, |
393 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
394 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
395 | +# GNU Affero General Public License for more details. |
396 | +# |
397 | +# You should have received a copy of the GNU Affero General Public License |
398 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
399 | + |
400 | +import datetime |
401 | + |
402 | +from django import test as djangotest |
403 | +from django.core.urlresolvers import reverse |
404 | + |
405 | +from django.contrib.auth.models import User |
406 | +from django.test.client import Client |
407 | + |
408 | +from model_mommy import mommy as factory |
409 | + |
410 | +from summit.schedule.models import ( |
411 | + Summit, |
412 | + Attendee, |
413 | +) |
414 | + |
415 | + |
416 | +class RegistrationTestCase(djangotest.TestCase): |
417 | + """ |
418 | + Tests for registering to attend a Summit |
419 | + """ |
420 | + c = Client() |
421 | + |
422 | + def setUp(self): |
423 | + self.now = datetime.datetime.utcnow() |
424 | + self.one_hour = datetime.timedelta(0, 3600) |
425 | + self.one_day = datetime.timedelta(days=1) |
426 | + self.week = datetime.timedelta(days=5) |
427 | + self.end_summit = self.now + self.week |
428 | + self.summit = factory.make_one( |
429 | + Summit, |
430 | + name='test-summit', |
431 | + title='Test Summit', |
432 | + virtual_summit=True, |
433 | + date_start=self.now, |
434 | + date_end=self.now + self.week, |
435 | + timezone='UTC', |
436 | + ) |
437 | + |
438 | + self.user1 = factory.make_one( |
439 | + User, |
440 | + username='testuser', |
441 | + first_name='Test', |
442 | + last_name='User', |
443 | + is_active=True, |
444 | + is_superuser=False, |
445 | + ) |
446 | + self.user1.set_password('password') |
447 | + self.user1.save() |
448 | + |
449 | + def create_attendee(self): |
450 | + self.attendee1 = factory.make_one( |
451 | + Attendee, |
452 | + summit=self.summit, |
453 | + user=self.user1, |
454 | + start_utc=self.now, |
455 | + end_utc=self.now+self.week |
456 | + ) |
457 | + |
458 | + def tearDown(self): |
459 | + self.client.logout() |
460 | + |
461 | + def login(self): |
462 | + logged_in = self.c.login( |
463 | + username='testuser', |
464 | + password='password') |
465 | + self.assertTrue(logged_in) |
466 | + |
467 | + def get_attendee(self): |
468 | + attendee = Attendee.objects.get(user=self.user1) |
469 | + return attendee |
470 | + |
471 | + def view_summit_page(self): |
472 | + rev_args = [self.summit.name, ] |
473 | + response = self.c.get( |
474 | + reverse( |
475 | + 'summit.schedule.views.summit', |
476 | + args=rev_args |
477 | + ) |
478 | + ) |
479 | + self.assertEqual(response.status_code, 200) |
480 | + self.assertTemplateUsed(response, 'schedule/summit.html') |
481 | + |
482 | + return response |
483 | + |
484 | + def with_summit_registration_times(self): |
485 | + """ |
486 | + Using the start and end of the summit as the registration times |
487 | + """ |
488 | + self.start_date = self.now.strftime("%Y-%m-%d") |
489 | + self.start_hour = self.now.strftime("%I") |
490 | + self.start_minute = self.now.strftime("%M") |
491 | + self.start_period = self.now.strftime("%p") |
492 | + self.end_hour = self.end_summit.strftime("%I") |
493 | + self.end_minute = self.end_summit.strftime("%M") |
494 | + self.end_period = self.end_summit.strftime("%p") |
495 | + self.end_date = self.end_summit.strftime("%Y-%m-%d") |
496 | + self.edit_registration_form() |
497 | + attendee = self.get_attendee() |
498 | + self.assertEqual( |
499 | + attendee.start_utc.replace(second=0), |
500 | + self.now.replace(second=0, microsecond=0), |
501 | + ) |
502 | + self.assertEqual( |
503 | + attendee.end_utc.replace(second=0, microsecond=0), |
504 | + self.end_summit.replace(second=0, microsecond=0), |
505 | + ) |
506 | + |
507 | + def with_custom_registration_times(self): |
508 | + """ |
509 | + Using custom start and end registration times |
510 | + """ |
511 | + start = self.now + self.one_day |
512 | + self.start_date = start.strftime("%Y-%m-%d") |
513 | + self.start_hour = self.now.strftime("%I") |
514 | + self.start_minute = self.now.strftime("%M") |
515 | + self.start_period = self.now.strftime("%p") |
516 | + self.end_hour = self.end_summit.strftime("%I") |
517 | + self.end_minute = self.end_summit.strftime("%M") |
518 | + self.end_period = self.end_summit.strftime("%p") |
519 | + self.end_date = self.end_summit.strftime("%Y-%m-%d") |
520 | + self.edit_registration_form() |
521 | + attendee = self.get_attendee() |
522 | + self.assertEqual( |
523 | + attendee.start_utc.replace(second=0), |
524 | + start.replace(second=0, microsecond=0), |
525 | + ) |
526 | + self.assertEqual( |
527 | + attendee.end_utc.replace(second=0, microsecond=0), |
528 | + self.end_summit.replace(second=0, microsecond=0), |
529 | + ) |
530 | + |
531 | + def edit_registration_form(self): |
532 | + """ |
533 | + Tests that a user can register for a Summit |
534 | + """ |
535 | + # Define data to fill our the form |
536 | + |
537 | + # Post the form |
538 | + post = self.c.post( |
539 | + reverse( |
540 | + 'registration', |
541 | + args=(self.summit.name,) |
542 | + ), |
543 | + data={ |
544 | + 'end_utc_0': self.end_date, |
545 | + 'end_utc_1_0': self.end_hour, |
546 | + 'end_utc_1_1': self.end_minute, |
547 | + 'end_utc_1_2': self.end_period, |
548 | + 'start_utc_1_0': self.start_hour, |
549 | + 'start_utc_1_1': self.start_minute, |
550 | + 'start_utc_1_2': self.start_period, |
551 | + 'start_utc_0': self.start_date, |
552 | + } |
553 | + ) |
554 | + |
555 | + # A successful post should redirect to the summit page |
556 | + response = reverse( |
557 | + 'summit.schedule.views.summit', |
558 | + args=(self.summit.name,) |
559 | + ) |
560 | + self.assertEqual(post.status_code, 302) |
561 | + self.assertRedirects(post, response) |
562 | + attendee = self.get_attendee() |
563 | + self.assertEqual(attendee.user, self.user1) |
564 | + self.assertEqual(attendee.from_launchpad, False) |
565 | + |
566 | + def test_non_attendee_registers(self): |
567 | + self.login() |
568 | + self.assertRaises(Attendee.DoesNotExist, lambda: self.get_attendee()) |
569 | + rev_args = [self.summit.name, ] |
570 | + response = self.c.get(reverse('registration', args=rev_args)) |
571 | + self.assertEqual(response.status_code, 200) |
572 | + self.assertTemplateUsed(response, 'schedule/form.html') |
573 | + self.assertIn('Register for ' + self.summit.title, response.content) |
574 | + self.with_summit_registration_times() |
575 | + |
576 | + def test_attendee_updates_registration(self): |
577 | + self.create_attendee() |
578 | + self.login() |
579 | + self.assertEquals( |
580 | + self.user1.username, |
581 | + self.get_attendee().user.username, |
582 | + ) |
583 | + rev_args = [self.summit.name, ] |
584 | + response = self.c.get(reverse('registration', args=rev_args)) |
585 | + self.assertEqual(response.status_code, 200) |
586 | + self.assertTemplateUsed(response, 'schedule/form.html') |
587 | + self.assertIn( |
588 | + 'Update registration for ' + self.summit.title, |
589 | + response.content |
590 | + ) |
591 | + self.with_custom_registration_times() |
592 | + |
593 | + def test_update_registration_from_launchpad_true(self): |
594 | + """ |
595 | + update resgistration with from_launchpad=True |
596 | + to save from_launchpad=False |
597 | + """ |
598 | + self.create_attendee() |
599 | + self.attendee1.from_launchpad = True |
600 | + self.attendee1.save() |
601 | + self.login() |
602 | + self.assertEquals( |
603 | + self.user1.username, |
604 | + self.get_attendee().user.username, |
605 | + ) |
606 | + self.assertEquals( |
607 | + True, |
608 | + self.get_attendee().from_launchpad, |
609 | + ) |
610 | + rev_args = [self.summit.name, ] |
611 | + response = self.c.get(reverse('registration', args=rev_args)) |
612 | + self.assertEqual(response.status_code, 200) |
613 | + self.assertTemplateUsed(response, 'schedule/form.html') |
614 | + self.assertIn( |
615 | + 'Update registration for ' + self.summit.title, |
616 | + response.content |
617 | + ) |
618 | + self.with_custom_registration_times() |
619 | + |
620 | + def test_registered_view(self): |
621 | + """ |
622 | + Test that when a user is already registered for the summit |
623 | + that they will see a link to update their registration |
624 | + """ |
625 | + self.create_attendee() |
626 | + self.attendee1.from_launchpad = True |
627 | + self.attendee1.save() |
628 | + self.login() |
629 | + rev_args = [self.summit.name, ] |
630 | + response = self.view_summit_page() |
631 | + self.assertIn( |
632 | + reverse( |
633 | + 'registration', |
634 | + args=rev_args |
635 | + ), |
636 | + response.content |
637 | + ) |
638 | + self.assertIn('Update registration', response.content) |
639 | + |
640 | + def test_unregistered_view(self): |
641 | + """ |
642 | + Test that when a user is not registered for the summit |
643 | + that they will see a button to register |
644 | + """ |
645 | + self.login() |
646 | + rev_args = [self.summit.name, ] |
647 | + response = self.view_summit_page() |
648 | + self.assertIn( |
649 | + reverse( |
650 | + 'registration', |
651 | + args=rev_args |
652 | + ), |
653 | + response.content |
654 | + ) |
655 | + self.assertIn('Register in Summit', response.content) |
656 | |
657 | === modified file 'summit/schedule/views.py' |
658 | --- summit/schedule/views.py 2013-03-15 01:06:10 +0000 |
659 | +++ summit/schedule/views.py 2013-04-07 16:28:22 +0000 |
660 | @@ -52,7 +52,8 @@ |
661 | MeetingReview, |
662 | AttendMeeting, |
663 | OrganizerChangeAttend, |
664 | - EditMeetingHangout |
665 | + EditMeetingHangout, |
666 | + Registration, |
667 | ) |
668 | |
669 | __all__ = ( |
670 | @@ -1173,3 +1174,45 @@ |
671 | context, |
672 | RequestContext(request) |
673 | ) |
674 | + |
675 | + |
676 | +@login_required |
677 | +@summit_required |
678 | +def registration_form(request, summit, attendee): |
679 | + registration_args = dict() |
680 | + |
681 | + if attendee is None: |
682 | + attendee = Attendee( |
683 | + user=request.user, |
684 | + summit=summit, |
685 | + from_launchpad=False, |
686 | + ) |
687 | + registration_args['instance'] = attendee |
688 | + form_title="Register for %s" % summit.title |
689 | + else: |
690 | + registration_args['instance'] = attendee |
691 | + form_title="Update registration for %s" % summit.title |
692 | + |
693 | + if request.method == 'POST': |
694 | + form = Registration(data=request.POST, **registration_args) |
695 | + if form.is_valid(): |
696 | + form.save() |
697 | + return HttpResponseRedirect( |
698 | + reverse( |
699 | + 'summit.schedule.views.summit', |
700 | + args=(summit.name,) |
701 | + ) |
702 | + ) |
703 | + else: |
704 | + form = Registration(**registration_args) |
705 | + |
706 | + context = { |
707 | + 'summit': summit, |
708 | + 'form': form, |
709 | + 'form_title': form_title, |
710 | + } |
711 | + return render_to_response( |
712 | + 'schedule/form.html', |
713 | + context, |
714 | + RequestContext(request) |
715 | + ) |
716 | |
717 | === modified file 'summit/settings.py' |
718 | --- summit/settings.py 2013-04-04 02:13:03 +0000 |
719 | +++ summit/settings.py 2013-04-07 16:28:22 +0000 |
720 | @@ -167,7 +167,7 @@ |
721 | 'bzr_apps': ('http://bazaar.launchpad.net/~django-foundations-dev/ubuntu-django-foundations/bzr_apps', '7'), |
722 | |
723 | ## ubuntu-website supplied templates and styles |
724 | - 'ubuntu_website': ('http://bazaar.launchpad.net/~ubuntu-community-webthemes/ubuntu-community-webthemes/light-django-theme', '55'), |
725 | + 'ubuntu_website': ('http://bazaar.launchpad.net/~ubuntu-community-webthemes/ubuntu-community-webthemes/light-django-theme', '61'), |
726 | |
727 | ## linaro-website supplied templates and styles |
728 | 'linaro_website': ('http://bazaar.launchpad.net/~linaro-connect-theme-devs/ubuntu-community-webthemes/light-django-linaro-theme', '52'), |
729 | |
730 | === modified file 'summit/urls.py' |
731 | --- summit/urls.py 2013-02-26 19:31:21 +0000 |
732 | +++ summit/urls.py 2013-04-07 16:28:22 +0000 |
733 | @@ -68,6 +68,11 @@ |
734 | (r'^(?P<summit_name>[\w-]+)/$', 'summit'), |
735 | (r'^(?P<summit_name>[\w-]+)/mobile/$', 'mobile'), |
736 | (r'^(?P<summit_name>[\w-]+)/search/$', 'search'), |
737 | + url( |
738 | + r'^(?P<summit_name>[\w-]+)/registration/$', |
739 | + 'registration_form', |
740 | + name='registration', |
741 | + ), |
742 | (r'^(?P<summit_name>[\w-]+)/propose_meeting/$', 'propose_meeting'), |
743 | (r'^(?P<summit_name>[\w-]+)/edit_meeting/(?P<meeting_id>\d+)/(?P<meeting_slug>[%+\.\w-]+)/$', |
744 | 'edit_meeting'), |