Merge lp:~daker/loco-team-portal/fix.profiles into lp:loco-team-portal

Proposed by Adnane Belmadiaf
Status: Merged
Approved by: Adnane Belmadiaf
Approved revision: 561
Merged at revision: 563
Proposed branch: lp:~daker/loco-team-portal/fix.profiles
Merge into: lp:loco-team-portal
Diff against target: 916 lines (+447/-220)
14 files modified
loco_directory/common/launchpad.py (+4/-9)
loco_directory/media/css/styles.css (+193/-36)
loco_directory/teams/models.py (+9/-9)
loco_directory/teams/urls.py (+1/-1)
loco_directory/teams/views.py (+0/-10)
loco_directory/templates/base.html (+0/-2)
loco_directory/templates/profiles/details.html (+98/-0)
loco_directory/templates/teams/my_teams.html (+0/-103)
loco_directory/urls.py (+1/-0)
loco_directory/userprofiles/management/commands/update-profiles.py (+26/-32)
loco_directory/userprofiles/models.py (+45/-18)
loco_directory/userprofiles/tests.py (+30/-0)
loco_directory/userprofiles/urls.py (+10/-0)
loco_directory/userprofiles/views.py (+30/-0)
To merge this branch: bzr merge lp:~daker/loco-team-portal/fix.profiles
Reviewer Review Type Date Requested Status
LoCo Team Portal Developers Pending
Review via email: mp+135014@code.launchpad.net

Commit message

Added Profile page

To post a comment you must log in.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'loco_directory/common/launchpad.py'
2--- loco_directory/common/launchpad.py 2012-09-13 00:55:39 +0000
3+++ loco_directory/common/launchpad.py 2012-11-19 23:09:20 +0000
4@@ -1,9 +1,6 @@
5 # -*- coding: utf-8 -*-
6-
7 from launchpadlib.launchpad import Launchpad
8 from launchpadlib.errors import HTTPError
9-
10-from teams.models import *
11 from django.conf import settings
12
13 import os
14@@ -75,18 +72,17 @@
15 timezone = 'UTC'
16 if username is None:
17 return timezone
18-
19- if not lp:
20+ if not lp:
21 lp = lp_login()
22 if not lp:
23 return timezone
24- try:
25+ try:
26 lp_user = lp.people[username]
27 timezone = lp_user.timezone
28 except:
29 pass
30 return timezone
31-
32+
33
34 def get_permanent_openid_from_username(username):
35 import urllib
36@@ -118,10 +114,9 @@
37 claimed_by.delete()
38 else:
39 return False
40-
41+
42 openid_assoc.claimed_id = openid.local_id
43 openid_assoc.display_id = openid.local_id
44 openid_assoc.save()
45 return True
46 return False
47-
48
49=== modified file 'loco_directory/media/css/styles.css'
50--- loco_directory/media/css/styles.css 2012-11-12 20:25:25 +0000
51+++ loco_directory/media/css/styles.css 2012-11-19 23:09:20 +0000
52@@ -816,6 +816,198 @@
53 box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.3);
54 }
55
56+
57+.profil-teams-wrapper {
58+ display: block;
59+ overflow: auto;
60+}
61+
62+.profil-teams-wrapper h2.title
63+{
64+ padding: 20px;
65+ padding-top: 10px;
66+ padding-bottom: 10px;
67+ margin-bottom: 0;
68+ border-bottom: 1px solid #DFDFDF;
69+ -webkit-box-shadow: 0 1px 0 #F8F8F8;
70+ -moz-box-shadow: 0 1px 0 #f8f8f8;
71+ box-shadow: 0 1px 0 #F8F8F8;
72+ -webkit-box-shadow: 0px 1px 2px 0 1px 0 #F8F8F8;
73+ -moz-box-shadow: 0px 1px 2px 0 1px 0 #f8f8f8;
74+ box-shadow: 0px 1px 2px 0 1px 0 #F8F8F8;
75+}
76+
77+.profil-team-wrapper
78+{
79+ overflow: auto;
80+ padding-left: 20px;
81+ padding-bottom: 10px;
82+ padding-top: 10px;
83+ border-bottom: 1px solid #D8D8D8;
84+ -webkit-box-shadow: 0 1px 0 #F8F8F8;
85+ -moz-box-shadow: 0 1px 0 #f8f8f8;
86+ box-shadow: 0 1px 0 #F8F8F8;
87+ -webkit-box-shadow: 0px 1px 2px 0 1px 0 #F8F8F8;
88+ -moz-box-shadow: 0px 1px 2px 0 1px 0 #f8f8f8;
89+ box-shadow: 0px 1px 2px 0 1px 0 #F8F8F8;
90+}
91+
92+.profil-team-wrapper .mugshot-wrapper::after {
93+ content: "";
94+ display: block;
95+ height: 140px;
96+ width: 1px;
97+ background-color: white;
98+ background-repeat: repeat-x;
99+ background-image: -khtml-gradient(linear, left top, left bottom, from(#DDD), to(white));
100+ background-image: -moz-linear-gradient(top, #DDD, white);
101+ background-image: -ms-linear-gradient(top, #DDD, white);
102+ background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #DDD), color-stop(100%, white));
103+ background-image: -webkit-linear-gradient(top, #DDD, white);
104+ background-image: -o-linear-gradient(top, #DDD, white);
105+ background-image: linear-gradient(top, #DDD, white);
106+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#dddddd', endColorstr='#ffffff', GradientType=0);
107+ position: relative;
108+ left: 110px;
109+ top: -105px;
110+}
111+
112+.profil-team-wrapper .mugshot-wrapper {
113+ float: left;
114+ height: 140px;
115+}
116+.profil-team-wrapper img.mugshot
117+{
118+ width: 96px;
119+ height: 96px;
120+ margin-right: 10px;
121+ -webkit-border-radius: 3px;
122+ -moz-border-radius: 3px;
123+ border-radius: 3px;
124+ display: block;
125+}
126+
127+.profil-team-wrapper a.link2
128+{
129+ font-size: 1.4em;
130+ font-weight: normal;
131+ line-height: 1.8;
132+ overflow: auto;
133+}
134+
135+.profil-team-meeting-wrapper {
136+ float: left;
137+ display: block;
138+ overflow: auto;
139+ width: 535px;
140+ margin-left: 20px;
141+}
142+
143+.profil-team-meeting-wrapper h4.title {
144+ font-size: 16px;
145+ font-style: italic;
146+}
147+
148+ul.indent {
149+ list-style: none;
150+ padding: 0;
151+ margin: 0;
152+ border: 1px solid #D2D3D3;
153+ border-bottom-color: #B9BABC;
154+ border-top-color: #DFDFDF;
155+ border-bottom: 1px solid #D8D8D8;
156+ -webkit-box-shadow: 0px 1px 2px 0 1px 0 #F8F8F8;
157+ -moz-box-shadow: 0px 1px 2px 0 1px 0 #f8f8f8;
158+ box-shadow: 0px 1px 2px 0 1px 0 #F8F8F8;
159+ background: white;
160+ border-radius: 3px;
161+ -moz-border-radius: 3px;
162+ -webkit-border-radius: 3px;
163+ margin-bottom: 5px;
164+}
165+
166+ul.indent li {
167+ margin: 0;
168+ line-height: 18px;
169+ color: #59636D;
170+ border-bottom: 1px dotted #DDD;
171+}
172+
173+ul.indent li.nothing {
174+ font-style: italic;
175+ padding: 10px;
176+}
177+
178+ul.indent li:first-child {
179+ border-top: 0;
180+}
181+
182+ul.indent li:last-child {
183+ border-bottom: 0;
184+}
185+
186+ul.indent li:first-child a{
187+ border-radius: 3px 3px 0 0;
188+ -moz-border-radius: 3px 3px 0 0;
189+ -webkit-border-radius: 3px 3px 0 0;
190+}
191+
192+ul.indent li:last-child a{
193+ border-radius: 0 0 3px 3px;
194+ -moz-border-radius: 0 0 3px 3px;
195+ -webkit-border-radius: 0 0 3px 3px;
196+}
197+
198+ul.indent li a {
199+ display: block;
200+ padding: 10px;
201+ color: #333;
202+}
203+
204+ul.indent li a:hover {
205+ background-color: #FBFBFB;
206+}
207+
208+div.profil-inner img.mugshot {
209+ display: block;
210+ width: 200px;
211+ height: 200px;
212+}
213+
214+div.profil-inner h2 {
215+ display: block;
216+ margin: 0;
217+ margin-top: 5px;
218+}
219+
220+.new {
221+ background: transparent url(../images/bg_btn.png) repeat-x top left;
222+ -webkit-border-radius: 4px;
223+ -moz-border-radius: 4px;
224+ border-radius: 4px;
225+ font-size: 11px;
226+ font-weight: bold;
227+ text-align: center;
228+ white-space: nowrap;
229+ margin: 0;
230+ margin-left: 16px;
231+ height: 27px;
232+ line-height: 27px;
233+ outline: 0;
234+ padding: 0 10px;
235+ color: white!important;
236+ text-shadow: 0 1px rgba(0, 0, 0, 0.1);
237+ position: relative;
238+ display: inline-block;
239+ text-transform: uppercase;
240+ top: -4px;
241+}
242+
243+.new:active {
244+ -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.3);
245+ -moz-box-shadow: inset 0 1px 2px rgba(0,0,0,0.3);
246+ box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.3);
247+}
248 /* My Teams style */
249 h2.dynamic-width {
250 display: inline-block;
251@@ -841,32 +1033,6 @@
252 width: 75px;
253 }
254
255-.main-content-split {
256- width:450px;
257- margin-top: 15px;
258-}
259-
260-.main-content-split.left {
261- float: left;
262-}
263-
264-.main-content-split.right {
265- float: right;
266-}
267-
268-.main-content-split.left .title {
269- font-size: 0.875em;
270-}
271-
272-.main-content-split.left .new {
273- float: right;
274- font-size: 0.75em;
275-}
276-
277-.event-summary {
278- margin-top:15px;
279-}
280-
281 hr.no-top {
282 margin: 0 0;
283 }
284@@ -875,16 +1041,6 @@
285 vertical-align: bottom;
286 }
287
288-ul.indent {
289- list-style: none;
290- background-color: #F7F7F7;
291- padding: 2px 2px 2px 15px;
292-}
293-
294-ul.indent li {
295- line-height:1.2em;
296- margin: 0.5em 0;
297-}
298
299 .teams-legend {
300 width:200px;
301@@ -1335,6 +1491,7 @@
302 .ml:after { content: "%";}
303 .forums:after { content: "9";}
304 .ical:before { content: "P"; }
305+.website:after { content: "5"; margin-top: 10px;}
306
307 .action {
308 top: 3px!important;
309
310=== modified file 'loco_directory/teams/models.py'
311--- loco_directory/teams/models.py 2011-10-27 14:00:26 +0000
312+++ loco_directory/teams/models.py 2012-11-19 23:09:20 +0000
313@@ -47,14 +47,14 @@
314
315 class Meta:
316 ordering = ('name',)
317-
318+
319 def __unicode__(self):
320 return u'%s' % (self.name)
321
322 @property
323 def related_teams(self):
324 return self.team_set.all()
325-
326+
327 @property
328 def related_venues(self):
329 return self.venue_set.all()
330@@ -65,7 +65,7 @@
331 return self.name.replace(',', '').replace(' ', '_')
332 else:
333 return 'no_country'
334-
335+
336 def countries_without_continent():
337 return Country.objects.filter(continents__isnull=True)
338
339@@ -116,16 +116,16 @@
340 microbloghashtag = models.CharField(max_length=50, help_text=_('Hash Tag used for microbloggers to mention this team'), verbose_name=_('Microblogging Hash Tag'), null=True, blank=True)
341 active = models.BooleanField(_("Active Team"), default=True)
342 tz = models.CharField(max_length=32, verbose_name=_('Default Timezone'), default='UTC', choices=[(tz, tz) for tz in pytz.all_timezones], blank=False, null=False, help_text=_('The most commonly used timezone for this Team.'))
343-
344+
345 objects = TeamManager()
346-
347+
348 class Meta:
349 db_table = 'teams'
350 ordering = ('approved', 'name',)
351-
352+
353 def __unicode__(self):
354 return self.lp_name
355-
356+
357 @property
358 def display_name(self):
359 return self.override_name or self.name
360@@ -133,7 +133,7 @@
361 @models.permalink
362 def get_absolute_url(self):
363 return ('team-detail', [str(self.lp_name)])
364-
365+
366 def next_5_events_and_meetings(self):
367 """ a list with all upcoming team meetings """
368 events_and_meetings = []
369@@ -149,6 +149,6 @@
370 if len(events_and_meetings) > 5:
371 events_and_meetings = events_and_meetings[:5]
372 return events_and_meetings
373-
374+
375 def teams_without_country():
376 return Team.objects.filter(countries__isnull=True)
377
378=== modified file 'loco_directory/teams/urls.py'
379--- loco_directory/teams/urls.py 2011-02-26 02:09:43 +0000
380+++ loco_directory/teams/urls.py 2012-11-19 23:09:20 +0000
381@@ -2,7 +2,7 @@
382
383 urlpatterns = patterns('',
384 url(r'^$', 'teams.views.team_list', name='team-list'),
385- url(r'^me$', 'teams.views.my_teams', name='my-teams'),
386+ #url(r'^me$', 'teams.views.my_teams', name='my-teams'),
387 url(r'(?P<team_slug>[a-zA-Z0-9\-\.\+?]+)/edit$', 'teams.views.team_edit', name='team-edit'),
388 url(r'(?P<team_slug>[a-zA-Z0-9\-\.\+?]+)/merge/(?P<other_team_slug>[a-zA-Z0-9\-\.\+?]+)/$', 'teams.views.team_merge', name='team-merge'),
389 url(r'(?P<team_slug>[a-zA-Z0-9\-\.\+?]+)/merge', 'teams.views.select_other_team', name='select-other-team'),
390
391=== modified file 'loco_directory/teams/views.py'
392--- loco_directory/teams/views.py 2012-11-12 20:47:10 +0000
393+++ loco_directory/teams/views.py 2012-11-19 23:09:20 +0000
394@@ -83,16 +83,6 @@
395 return render_to_response('teams/team_list.html', context,
396 RequestContext(request))
397
398-@login_required
399-def my_teams(request):
400- teams = Team.objects.filter(lp_name__in=[group.name for group in request.user.groups.all()])
401- context = {
402- 'teams': teams,
403- }
404- return render_to_response('teams/my_teams.html', context,
405- RequestContext(request))
406-
407-
408 def team_ical(request, team_slug):
409 """
410 Return a ical list with the events and meetings in ical format.
411
412=== modified file 'loco_directory/templates/base.html'
413--- loco_directory/templates/base.html 2012-11-16 21:13:18 +0000
414+++ loco_directory/templates/base.html 2012-11-19 23:09:20 +0000
415@@ -28,8 +28,6 @@
416 {% trans "Welcome" %}&nbps;
417 <a class="top-login-item" href="http://launchpad.net/~{{ user.username }}">{{ user.username }}</a>
418 <span class="top-login-separator">|</span>
419- <a class="top-login-item" href="{% url my-teams %}">{% trans "My Teams" %}</a>
420- <span class="top-login-separator">|</span>
421 {% if user.is_staff %}
422 <a class="top-login-item" href="/admin" title="Admin">{% trans "Admin" %}</a>
423 <span class="top-login-separator">|</span>
424
425=== added directory 'loco_directory/templates/profiles'
426=== added file 'loco_directory/templates/profiles/details.html'
427--- loco_directory/templates/profiles/details.html 1970-01-01 00:00:00 +0000
428+++ loco_directory/templates/profiles/details.html 2012-11-19 23:09:20 +0000
429@@ -0,0 +1,98 @@
430+{% extends "base.html" %}
431+{% load i18n smart_if %}
432+
433+{% block title %}Profile for {{ profile.realname }}{% endblock %}
434+
435+{% block content %}
436+<div class="row">
437+ <section class="span-9">
438+ <div class="box_content">
439+ <div class="profil-teams-wrapper">
440+ <h2 class="title">{% if user == profile.user %}{% trans "Your Teams" %}{% else %}{% trans "Teams" %}{% endif %}</h2>
441+ {% for team in teams %}
442+ <div class="profil-team-wrapper">
443+ <div class="mugshot-wrapper">
444+ <img src="{{ team.mugshot_url }}" alt="{{ team.name }}" class="mugshot" />
445+ </div>
446+ <div class="profil-team-meeting-wrapper">
447+ <a href="{% url team-detail team_slug=team.lp_name %}" title="{{ team.name }}" class="link2">
448+ {{ team.name }}
449+ </a>
450+ <a href="{% url team-meeting-new team_slug=team.lp_name %}" title="{% trans "Schedule a meeting" %}" class="new">{% trans "Schedule a meeting" %}</a>
451+ <a href="{% url team-event-new team_slug=team.lp_name %}" title="{% trans "Create an event" %}" class="new">{% trans "Create an event" %}</a>
452+ <h4 class="title">{% trans "Meetings" %}</h4>
453+ <ul class="indent">
454+ {% if team.teammeeting_set.next_3_meetings %}
455+ {% for team_event in team.teammeeting_set.next_3_meetings %}
456+ <li>
457+ <a title="{{team_event.date_begin|date:"M d"}}" href="{% url team-meeting-detail team_event.first_team.lp_name team_event.id %}">{{ team_event.name }} {% trans "on" %} {{ team_event.local_date_begin|date:"l, d N Y" }} {% trans "at " %}{{ team_event.local_date_begin|date:"H:i T" }}</a>
458+ </li>
459+ {% endfor %}
460+ {% else %}
461+ <li class="nothing">{% trans "There are currently no upcoming meeting :(" %}</li>
462+ {% endif %}
463+ </ul>
464+ <h4 class="title">{% trans "Events" %}</h4>
465+
466+ <ul class="indent">
467+ {% if team.teamevent_set.next_3_events %}
468+ {% for team_event in team.teamevent_set.next_3_events %}
469+ <li>
470+ <a title="{% if team_event.venue %}{% else %}{{team_event.date_begin|date:"M d"}}{% endif %}" href="{% url team-event-detail team_event.first_team.lp_name team_event.id %}">{{ team_event.name }} {% trans "on" %} {{ team_event.local_date_begin|date:"l, d N Y" }} {% trans "at " %}{{ team_event.local_date_begin|date:"H:i T" }}</a>
471+ </li>
472+ {% endfor %}
473+ {% else %}
474+ <li class="nothing">{% trans "There are currently no upcoming events :(" %}</li>
475+ {% endif %}
476+ </ul>
477+ </div>
478+ </div>
479+ {% endfor %}
480+
481+ {% if user == profile.user %}
482+ <h2 class="title">{% trans "Events you will be attending" %}</h2>
483+ {% for event in my_events %}
484+ <a href="{% url team-event-detail event.first_team.lp_name event.id %}" title="{% if event.venue %}{% else %}{{event.date_begin|date:"M d"}}{% endif %}" class="event-meetings">
485+ <span class="title">{{ event.name }}</span>
486+ <small>{{ event.local_date_begin|date:"l, d N Y" }} {% trans "at " %}{{ event.local_date_begin|date:"H:i T" }}</small>
487+ <p>{{ event.description }}</p>
488+ </a>
489+ {% endfor %}
490+ {% endif %}
491+ </div>
492+ </div>
493+ </section>
494+ <section class="span-3 box_content last">
495+ <div class="sidebar-inner">
496+ <div class="profil-inner">
497+ <img src="{{ profile.mugshot }}" class="mugshot"alt="{{ profile.realname }}" title="{{ profile.realname }}" />
498+ <h2>{{ profile.realname }}</a></h2>
499+ <div style="margin-bottom: 10px;">
500+ <a style="font-style: italic;" title="Launchpad" href="https://launchpad.net/~{{ profile.user.username }}">{{ profile.user.username }}</a>
501+ </div>
502+
503+ <div>
504+ <span class="pictogram calendar" title="{% trans "Timezone" %}"></span>
505+ <span class="pictogram-l">{{ profile.tz }}</span>
506+ </div>
507+ {% if profile.irc %}
508+ <div>
509+ <span class="pictogram forums" title="{% trans "IRC" %}"></span>
510+ <span class="pictogram-l">{{ profile.irc }}</span>
511+ </div>
512+ {% endif %}
513+ {% if profile.blog or profile.twitter %}
514+ <div>
515+ <span class="pictogram website" title="{% trans "Social Media" %}"></span>
516+ {% if profile.blog %}
517+ <a title="{{ profile.blog }}" href="{{ profile.blog }}">{% trans "Blog" %}</a>
518+ {% endif %}{% if profile.blog and profile.twitter %}/{% endif %}{% if profile.twitter %}
519+ <a title="{{ profile.twitter }}" href="https://twitter.com/{{ profile.twitter }}">{% trans "Twitter" %}</a>
520+ {% endif %}
521+ </div>
522+ {% endif %}
523+ </div>
524+ </div>
525+ </section>
526+</div>
527+{% endblock %}
528\ No newline at end of file
529
530=== removed file 'loco_directory/templates/teams/my_teams.html'
531--- loco_directory/templates/teams/my_teams.html 2012-06-02 11:50:27 +0000
532+++ loco_directory/templates/teams/my_teams.html 1970-01-01 00:00:00 +0000
533@@ -1,103 +0,0 @@
534-{% extends "base.html" %}
535-{% load i18n %}
536-
537-{% block page_name %}{% trans "My Teams" %}{% endblock %}
538-
539-{% block extrahead %}{{ block.super }}
540- <link rel="stylesheet" type="text/css" href="{{MEDIA_URL}}/css/twidenash.css" />
541- <script type="text/javascript" src="{{MEDIA_URL}}jquery/jquery.js"></script>
542- <script type="text/javascript" src="{{MEDIA_URL}}js/jquery.lightbox-0.5.min.js"></script>
543- <link rel="stylesheet" href="{{MEDIA_URL}}css/jquery.lightbox-0.5.css" type="text/css" media="screen" />
544- <script type="text/javascript" src="{{MEDIA_URL}}js/jquery-flickr.js"></script>
545- <script src="{{ MEDIA_URL }}js/twidenash/jquery-twidenash.js" type="text/javascript"></script>
546- <script type="text/javascript">
547- //<![CDATA[
548- $(function () {
549- $('.twidenash').twidenash({ amount: 3 });
550- {% for team in teams %}
551- $('#flickr-{{ team.lp_name }}').flickr({ key: "{{ flickr_api_key }}", id: "{{ team.flickr_id }}", amount: 10 });
552- {% endfor %}
553- });
554- //]]>
555- </script>
556- {# <!-- TODO: this shpuld be rss of events AND meetings --> #}
557- {% for team in teams %}
558- <link type="application/rss+xml" rel="alternate" title="{% trans "Team Events (RSS)" %}" href="{% url team-events-rss team_slug=team.lp_name %}" />
559- {% endfor %}
560-{% endblock %}
561-
562-{% block sub_nav_links %}
563- <li><a class="sub-nav-item" href="{% url my-teams %}" title="{% trans 'My Teams' %}">{% trans "My Teams" %}</a></li>
564- <li><a class="sub-nav-item" href="{% url team-list %}" title="{% trans 'All Teams' %}">{% trans "All Teams" %}</a></li>
565- {% comment %}
566- <!-- TODO: Need to create those pages -->
567- <li><a class="sub-nav-item" href="" title="{% trans 'Join a Team' %}">{% trans "Join a Team" %}</a></li>
568- <li><a class="sub-nav-item" href="" title="{% trans 'Create a Team' %}">{% trans "Create a Team" %}</a></li>
569- {% endcomment %}
570-{% endblock %}
571-
572-{% block content %}
573- {% for team in teams %}
574-<div class="row">
575-<section class="span-12">
576- <h2 class="dynamic-width">
577- <a href="{% url team-detail team_slug=team.lp_name %}" class="team-title" title="{{ team.name }}">{{ team.name }}</a>
578- {% comment %}
579- <a href="{% url team-calendar team_slug=team.lp_name %}" title="{% trans "Team Calendar (ICAL)" %}"><img class="bottom" src="{{ MEDIA_URL }}img/ical.png" /></a>
580- {# <!-- TODO: this shpuld be rss of events AND meetings --> #}
581- {# <!-- TODO: Change icon to RSS icon --> #}
582- <a href="{% url team-events-rss team_slug=team.lp_name %}" title="{% trans "Team Feed (RSS)" %}"><img class="bottom" src="{{ MEDIA_URL }}img/rss.png" /></a>
583- {% endcomment %}
584- </h2>
585- <aside class="resources">
586- <span>{% trans "Resources: " %}</span>
587- {% if team.web_url %}<a href="{{ team.web_url }}">{% trans "Website" %}</a>{% endif %}
588- {% if team.wiki_url %}<a href="{{ team.wiki_url }}">{% trans "Wiki" %}</a>{% endif %}
589- {% if team.forum_url %}<a href="{{ team.forum_url }}">{% trans "Forums" %}</a>{% endif %}
590- {% if team.ml_url %}<a href="{{ team.ml_url }}">{% trans "Mailing List" %}</a>{% endif %}
591- {% if team.irc_chan %}<a href="{% url team-detail team_slug=team.lp_name %}#chat">{% trans "IRC Channel" %}</a>{% endif %}
592- </aside>
593- <hr class="no-top" />
594- <section class="main-content-split left">
595- <section class="meeting-summary">
596- <a href="{% url team-meeting-list team_slug=team.lp_name %}" class="title">{% trans "Meetings" %}</a>
597- <a href="{% url team-meeting-new team_slug=team.lp_name %}" class="new">{% trans "+ new meeting" %}</a>
598- <hr class="no-top" />
599- <ul class="indent">
600- {% if team.teammeeting_set.next_3_meetings %}
601- {% for team_event in team.teammeeting_set.next_3_meetings %}
602- {% include 'meetings/team_meeting_li.inc.html' %}
603- {% endfor %}
604- {% else %}
605- <li>{% trans "There are no schedules meetings for this team" %}</li>
606- {% endif %}
607- </ul>
608- </section>
609- <section class="event-summary">
610- <a href="{% url team-event-list team_slug=team.lp_name %}" class="title">{% trans "Events" %}</a>
611- <a href="{% url team-event-new team_slug=team.lp_name %}" class="new">{% trans "+ new event" %}</a>
612- <hr class="no-top" />
613- <ul class="indent">
614- {% if team.teamevent_set.next_3_events %}
615- {% for team_event in team.teamevent_set.next_3_events %}
616- {% include 'events/team_event_li.inc.html' %}
617- {% endfor %}
618- {% else %}
619- <li>{% trans "There are no schedules events for this team" %}</li>
620- {% endif %}
621- </ul>
622- </section>
623- </section>
624- <section class="main-content-split right">
625- {% if team.microbloghashtag %}
626- <div class="twidenash" id="{{ team.microbloghashtag }}"></div>
627- {% else %}
628- {% if team.flickr_id %} {# or team.picasa_id or team.pixie_id %}#}
629- <div class="photo" id="flickr-{{ team.lp_name }}"></div>
630- {% endif %}
631- {% endif %}
632- </section>
633- </section>
634- </div>
635- {% endfor %}
636-{% endblock %}
637
638=== modified file 'loco_directory/urls.py'
639--- loco_directory/urls.py 2011-11-13 14:49:25 +0000
640+++ loco_directory/urls.py 2012-11-19 23:09:20 +0000
641@@ -27,6 +27,7 @@
642 url(r'^jsi18n', 'django.views.i18n.javascript_catalog', name='jsi18n'),
643 url(r'^services/', include('services.urls')),
644 url(r'^search/', 'common.views.site_search', name='site_search'),
645+ url(r'', include('userprofiles.urls')),
646 )
647
648 urlpatterns += patterns('django_openid_auth.views',
649
650=== modified file 'loco_directory/userprofiles/management/commands/update-profiles.py'
651--- loco_directory/userprofiles/management/commands/update-profiles.py 2011-07-24 16:30:57 +0000
652+++ loco_directory/userprofiles/management/commands/update-profiles.py 2012-11-19 23:09:20 +0000
653@@ -1,37 +1,31 @@
654+# -*- coding: utf-8 -*-
655 #!/usr/bin/python
656-# -*- coding: utf-8 -*-
657-
658-from django.core.management.base import NoArgsCommand
659-
660-from common import launchpad
661-from django.contrib.auth.models import User, Group
662-from userprofiles.models import UserProfile, create_profile
663-
664+
665+from django.core.management.base import BaseCommand
666+from django.contrib.auth.models import User
667+
668+from userprofiles.models import create_userprofile
669+
670+from optparse import make_option
671 from datetime import datetime
672 import sys
673
674-class Command(NoArgsCommand):
675+class Command(BaseCommand):
676 help = "Updates user profiles with information from Launchpad."
677-
678- def handle_noargs(self, **options):
679- lp = launchpad.lp_login()
680- if not lp:
681- sys.exit(1)
682- USER_BLACKLIST = (u"root", u"admin")
683-
684- ld_users = User.objects.all()
685-
686- for ld_user in ld_users:
687- if not ld_user.username in USER_BLACKLIST:
688- profile, created = UserProfile.objects.get_or_create(user=ld_user)
689- try:
690- print "updating %s" % ld_user
691- lp_user = lp.people[ld_user.username]
692- profile.tz = lp_user.time_zone or "UTC"
693- profile.realname = lp_user.display_name or user.username
694- profile.mugshot = launchpad.get_mugshot_url(lp_user.logo_link)
695- profile.save()
696- except Exception, e:
697- print "Error updating %s" % ld_user
698- print e
699- pass
700+ option_list = BaseCommand.option_list + (
701+ make_option("-u", "--user", dest='username',
702+ help="Supply a user to setup the openid identity url from launchpad."),
703+ )
704+
705+ def handle(self, *args, **options):
706+ username = options['username']
707+ if not username:
708+ ld_users = User.objects.all()
709+ for ld_user in ld_users:
710+ create_userprofile(sender=User, instance=ld_user, need_update=True)
711+ else:
712+ try:
713+ ld_user = User.objects.get(username=username)
714+ except User.DoesNotExist:
715+ sys.exit('Seems that this user does not exist')
716+ create_userprofile(sender=User, instance=ld_user, need_update=True)
717
718=== modified file 'loco_directory/userprofiles/models.py'
719--- loco_directory/userprofiles/models.py 2011-03-04 18:16:03 +0000
720+++ loco_directory/userprofiles/models.py 2012-11-19 23:09:20 +0000
721@@ -1,10 +1,22 @@
722+# -*- coding: utf-8 -*-
723 from django.db import models
724 from django.utils.translation import ugettext_lazy as _
725 from django.contrib.auth import models as auth_models
726-# Create your models here.
727+from django.db.models.signals import post_save
728+from common.launchpad import (get_user_timezone, lp_login, get_mugshot_url, set_user_openid)
729
730 import pytz
731
732+import logging
733+import sys
734+st_handler = logging.StreamHandler(sys.stderr)
735+formatter = logging.Formatter("[%(asctime)s] - %(levelname)s - %(module)s - %(message)s")
736+st_handler.setFormatter(formatter)
737+
738+log = logging.getLogger(__name__)
739+log.addHandler(st_handler)
740+log.setLevel(logging.DEBUG)
741+
742 class UserProfile(models.Model):
743 " Store profile information about a user "
744
745@@ -25,7 +37,7 @@
746
747 class Meta:
748 ordering = ('user__username',)
749-
750+
751 def __unicode__(self):
752 try:
753 if self.realname:
754@@ -40,7 +52,7 @@
755 except:
756 return pytz.utc
757 timezone = property(get_timezone)
758-
759+
760 def tolocaltime(self, dt):
761 as_utc = pytz.utc.localize(dt)
762 return as_utc.astimezone(self.timezone)
763@@ -48,36 +60,51 @@
764 def fromlocaltime(self, dt):
765 local = self.timezone.localize(dt)
766 return local.astimezone(pytz.utc)
767-
768+
769 def _getUserProfile(self):
770 if not self.is_authenticated():
771 return UserProfile()
772-
773+
774 profile, created = UserProfile.objects.get_or_create(user=self)
775-
776+
777 if created:
778- from common.launchpad import get_user_timezone
779 profile.tz = get_user_timezone(self.username)
780 profile.save()
781-
782+
783 return profile
784
785 def _getAnonProfile(self):
786 return UserProfile()
787-
788+
789 auth_models.User.profile = property(_getUserProfile)
790 auth_models.AnonymousUser.profile = property(_getAnonProfile)
791
792+def create_userprofile(sender, instance, lp=None, USER_BLACKLIST=(u"root", u"admin"), need_update=False, **kwargs):
793+ profile, created = UserProfile.objects.get_or_create(user=instance)
794+ if instance.username in USER_BLACKLIST:
795+ log.error('%s tried to create a userprofile, but exists in USER_BLACKLIST' % instance)
796+ return profile
797+
798+ if need_update or created:
799+ if not lp:
800+ lp = lp_login()
801+ if lp:
802+ try:
803+ log.debug("Updating %s" % instance)
804+ lp_user = lp.people[instance.username]
805+ profile.tz = lp_user.time_zone or "UTC"
806+ profile.realname = lp_user.display_name or user.username
807+ profile.mugshot = get_mugshot_url(lp_user.mugshot_link)
808+ profile.save()
809+ except Exception, e:
810+ log.error("Error updating %s" % instance)
811+ return profile
812+
813+post_save.connect(create_userprofile, sender=auth_models.User, dispatch_uid=__name__)
814+
815 def create_profile(username):
816 user, created = auth_models.User.objects.get_or_create(username=username)
817 if created:
818 user.save()
819- from common import launchpad
820- launchpad.set_user_openid(user, force=True)
821- profile, created = UserProfile.objects.get_or_create(user=user)
822- if created:
823- # set real name as username for now,
824- # we get the name later on via cronjob
825- profile.realname = username
826- profile.save()
827- return profile
828+ set_user_openid(user, force=True)
829+ return user.get_profile()
830
831=== added file 'loco_directory/userprofiles/tests.py'
832--- loco_directory/userprofiles/tests.py 1970-01-01 00:00:00 +0000
833+++ loco_directory/userprofiles/tests.py 2012-11-19 23:09:20 +0000
834@@ -0,0 +1,30 @@
835+# -*- coding: utf-8 -*-
836+from django.test import TestCase
837+from django.contrib.auth.models import User
838+from django.db.models import signals
839+
840+from userprofiles.models import UserProfile
841+
842+class SignalTest(TestCase):
843+ '''
844+ Tests that signals fire properly
845+ '''
846+
847+ def test_signals(self):
848+ global received
849+ received = False
850+
851+ def handle_post_save(**kwargs):
852+ global received
853+ received = True
854+
855+ signals.post_save.connect(handle_post_save, sender=User)
856+ self.user = User.objects.create(username='bob')
857+ self.user.set_password('bob')
858+ self.user.save()
859+ self.assertTrue(received)
860+
861+ def test_profile_creation(self):
862+ self.user = User.objects.create(username='bob', password='bob')
863+ self.user.save()
864+ self.assertEqual(UserProfile.objects.all().count(), 1)
865\ No newline at end of file
866
867=== added file 'loco_directory/userprofiles/urls.py'
868--- loco_directory/userprofiles/urls.py 1970-01-01 00:00:00 +0000
869+++ loco_directory/userprofiles/urls.py 2012-11-19 23:09:20 +0000
870@@ -0,0 +1,10 @@
871+# -*- coding: utf-8 -*-
872+from django.conf.urls.defaults import *
873+
874+urlpatterns = patterns('userprofiles.views',
875+ url(r'^(?P<username>[-.\w]+)/$',
876+ view='profile_detail',
877+ name='profile_detail',
878+ ),
879+
880+)
881\ No newline at end of file
882
883=== added file 'loco_directory/userprofiles/views.py'
884--- loco_directory/userprofiles/views.py 1970-01-01 00:00:00 +0000
885+++ loco_directory/userprofiles/views.py 2012-11-19 23:09:20 +0000
886@@ -0,0 +1,30 @@
887+# -*- coding: utf-8 -*-
888+from django.shortcuts import render_to_response, get_object_or_404
889+from django.template import RequestContext
890+from django.http import Http404
891+from django.contrib.auth.models import User
892+
893+from django.db.models import Count
894+
895+from userprofiles.models import UserProfile
896+from teams.models import Team
897+from events.models import Attendee, TeamEvent
898+
899+def profile_detail(request, username):
900+ user = get_object_or_404(User, username__iexact=username)
901+ teams = Team.objects.filter(lp_name__in=[group.name for group in user.groups.all()])
902+
903+ if request.user == user:
904+ profile = request.user.get_profile()
905+ my_events = TeamEvent.objects.next_events().annotate(attendees=Count('attendee')).filter(attendee__attendee_profile=profile)
906+ else:
907+ profile = UserProfile.objects.get(user=user)
908+ my_events = {}
909+
910+ context = {
911+ 'profile': profile,
912+ 'teams': teams,
913+ 'my_events': my_events,
914+ }
915+
916+ return render_to_response('profiles/details.html', context, context_instance=RequestContext(request))

Subscribers

People subscribed via source and target branches