Merge lp:~andrewsomething/dat-overview/new_contacted_logic into lp:dat-overview

Proposed by Andrew Starr-Bochicchio
Status: Merged
Merged at revision: 32
Proposed branch: lp:~andrewsomething/dat-overview/new_contacted_logic
Merge into: lp:dat-overview
Diff against target: 633 lines (+242/-109)
12 files modified
overview/local_settings.py.sample (+4/-2)
overview/media/css/base.css (+27/-0)
overview/settings.py (+6/-0)
overview/templates/first_timers.html (+13/-8)
overview/templates/lost_contributors.html (+3/-1)
overview/templates/person.html (+41/-9)
overview/templates/potential_devs.html (+5/-1)
overview/uploads/forms.py (+0/-3)
overview/uploads/models.py (+3/-1)
overview/uploads/templatetags/custom_tags.py (+5/-0)
overview/uploads/views.py (+133/-83)
overview/urls.py (+2/-1)
To merge this branch: bzr merge lp:~andrewsomething/dat-overview/new_contacted_logic
Reviewer Review Type Date Requested Status
Daniel Holbach Approve
Review via email: mp+170988@code.launchpad.net

Description of the change

This branch adds the comment-style system for tracking contacts. It also
provides some optimization of the queries used to generate the views making
things a bit snappier.

To post a comment you must log in.
Revision history for this message
Bhavani Shankar (bhavi) wrote :

Works great here! Awesome stuff again!

Thanks Andrew!

Regards
Bhavani

Revision history for this message
Daniel Holbach (dholbach) wrote :

That's excellent work! :)

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'overview/local_settings.py.sample'
--- overview/local_settings.py.sample 2013-04-06 18:45:17 +0000
+++ overview/local_settings.py.sample 2013-06-23 19:42:22 +0000
@@ -18,5 +18,7 @@
18 }18 }
19}19}
2020
2121# Uncomment to use django debug_toolbar. Requires: python-django-ebug-toolbar
2222#DEBUG_MIDDLEWARE_CLASSES = ('debug_toolbar.middleware.DebugToolbarMiddleware',)
23#DEBUG_APPS = ('debug_toolbar',)
24#INTERNAL_IPS = ('127.0.0.1',)
2325
=== modified file 'overview/media/css/base.css'
--- overview/media/css/base.css 2013-06-18 23:57:17 +0000
+++ overview/media/css/base.css 2013-06-23 19:42:22 +0000
@@ -364,6 +364,29 @@
364 box-shadow: 1px 1px 5px #CCC;364 box-shadow: 1px 1px 5px #CCC;
365}365}
366366
367#comment{
368 max-width: 600px;
369 border-radius: 3px;
370 border: 1px solid #CCC;
371 padding: 8px;
372 font-weight: 200;
373 font-size: 15px;
374 font-family: Ubuntu;
375 box-shadow: 1px 1px 5px #CCC;
376 position: relative;
377}
378
379#comment-details{
380 padding-left:275px;
381}
382
383#comment h3{
384 position: absolute;
385 bottom: 0;
386 right: 5px;
387 color:#A9A9A9;
388}
389
367.legend{390.legend{
368 float:right;391 float:right;
369}392}
@@ -380,3 +403,7 @@
380 min-height: 304px;403 min-height: 304px;
381 background-repeat: no-repeat, repeat;404 background-repeat: no-repeat, repeat;
382}405}
406
407#id_honeypot {
408 display: none;
409}
383410
=== modified file 'overview/settings.py'
--- overview/settings.py 2013-06-10 16:42:05 +0000
+++ overview/settings.py 2013-06-23 19:42:22 +0000
@@ -121,6 +121,7 @@
121 'django.contrib.staticfiles',121 'django.contrib.staticfiles',
122 'django_openid_auth',122 'django_openid_auth',
123 'django.contrib.admin',123 'django.contrib.admin',
124 'django.contrib.comments',
124 # Uncomment the next line to enable admin documentation:125 # Uncomment the next line to enable admin documentation:
125 # 'django.contrib.admindocs',126 # 'django.contrib.admindocs',
126 'uploads',127 'uploads',
@@ -187,7 +188,12 @@
187 }188 }
188}189}
189190
191
192DEBUG_APPS = ()
193DEBUG_MIDDLEWARE_CLASSES = ()
190try:194try:
191 from local_settings import *195 from local_settings import *
196 INSTALLED_APPS += DEBUG_APPS
197 MIDDLEWARE_CLASSES += DEBUG_MIDDLEWARE_CLASSES
192except ImportError:198except ImportError:
193 logging.warning("No local_settings.py were found. See INSTALL for instructions.")199 logging.warning("No local_settings.py were found. See INSTALL for instructions.")
194200
=== modified file 'overview/templates/first_timers.html'
--- overview/templates/first_timers.html 2013-06-19 20:56:21 +0000
+++ overview/templates/first_timers.html 2013-06-23 19:42:22 +0000
@@ -1,4 +1,5 @@
1{% extends "base.html" %}1{% extends "base.html" %}
2{% load custom_tags %}
23
3{% block content %}4{% block content %}
45
@@ -29,8 +30,9 @@
29 {% for i in output|dictsortreversed:"p.first_upload.timestamp" %}30 {% for i in output|dictsortreversed:"p.first_upload.timestamp" %}
30 {% if 'saucy' in i.p.first_upload.release %}31 {% if 'saucy' in i.p.first_upload.release %}
31 <tr class='{{ i.color }}'>32 <tr class='{{ i.color }}'>
32 <td>{% if i.p.contacted %}33 <td>{% if i.p|recent_contact %}
33 <center>✓</center>{% endif %}34 <center>✓</center>
35 {% endif %}
34 </td>36 </td>
35 <td><a href="{{ i.p.lpid }}">{{ i.p.name }}</a></td>37 <td><a href="{{ i.p.lpid }}">{{ i.p.name }}</a></td>
36 <td>38 <td>
@@ -68,8 +70,9 @@
68 {% for i in output|dictsortreversed:"p.first_upload.timestamp" %}70 {% for i in output|dictsortreversed:"p.first_upload.timestamp" %}
69 {% if 'raring' in i.p.first_upload.release %}71 {% if 'raring' in i.p.first_upload.release %}
70 <tr class='{{ i.color }}'>72 <tr class='{{ i.color }}'>
71 <td>{% if i.p.contacted %}73 <td>{% if i.p|recent_contact %}
72 <center>✓</center>{% endif %}74 <center>✓</center>
75 {% endif %}
73 </td>76 </td>
74 <td><a href="{{ i.p.lpid }}">{{ i.p.name }}</a></td>77 <td><a href="{{ i.p.lpid }}">{{ i.p.name }}</a></td>
75 <td>78 <td>
@@ -107,8 +110,9 @@
107 {% for i in output|dictsortreversed:"p.first_upload.timestamp" %}110 {% for i in output|dictsortreversed:"p.first_upload.timestamp" %}
108 {% if 'quantal' in i.p.first_upload.release %}111 {% if 'quantal' in i.p.first_upload.release %}
109 <tr class='{{ i.color }}'>112 <tr class='{{ i.color }}'>
110 <td>{% if i.p.contacted %}113 <td>{% if i.p|recent_contact %}
111 <center>✓</center>{% endif %}114 <center>✓</center>
115 {% endif %}
112 </td>116 </td>
113 <td><a href="{{ i.p.lpid }}">{{ i.p.name }}</a></td>117 <td><a href="{{ i.p.lpid }}">{{ i.p.name }}</a></td>
114 <td>118 <td>
@@ -146,8 +150,9 @@
146 {% for i in output|dictsortreversed:"p.first_upload.timestamp" %}150 {% for i in output|dictsortreversed:"p.first_upload.timestamp" %}
147 {% if 'precise' in i.p.first_upload.release %}151 {% if 'precise' in i.p.first_upload.release %}
148 <tr class='{{ i.color }}'>152 <tr class='{{ i.color }}'>
149 <td>{% if i.p.contacted %}153 <td>{% if i.p|recent_contact %}
150 <center>✓</center>{% endif %}154 <center>✓</center>
155 {% endif %}
151 </td>156 </td>
152 <td><a href="{{ i.p.lpid }}">{{ i.p.name }}</a></td>157 <td><a href="{{ i.p.lpid }}">{{ i.p.name }}</a></td>
153 <td>158 <td>
154159
=== modified file 'overview/templates/lost_contributors.html'
--- overview/templates/lost_contributors.html 2013-06-19 20:29:36 +0000
+++ overview/templates/lost_contributors.html 2013-06-23 19:42:22 +0000
@@ -18,7 +18,9 @@
18 </tr>18 </tr>
19{% for p in lost_contributors|dictsortreversed:"last_upload.timestamp" %}19{% for p in lost_contributors|dictsortreversed:"last_upload.timestamp" %}
20 <tr>20 <tr>
21 <td>{% if p.contacted %}<center>✓</center>{% endif %}</td>21 <td>{% if p|recent_contact > p.last_upload.timestamp %}
22 <center>✓</center>{% endif %}
23 </td>
22 <td>24 <td>
23 <a href="{{ p.lpid }}">{{ p.name }}</a>{% ubu_dev_img p.ubuntu_dev %}25 <a href="{{ p.lpid }}">{{ p.name }}</a>{% ubu_dev_img p.ubuntu_dev %}
24 </td>26 </td>
2527
=== modified file 'overview/templates/person.html'
--- overview/templates/person.html 2013-06-21 20:24:21 +0000
+++ overview/templates/person.html 2013-06-23 19:42:22 +0000
@@ -1,5 +1,6 @@
1{% extends "base.html" %}1{% extends "base.html" %}
2{% load custom_tags %}2{% load custom_tags %}
3{% load comments %}
34
4{% block extrahead %}5{% block extrahead %}
56
@@ -46,24 +47,18 @@
46 <li><b>Ubuntu Developer:</b> {{ person.ubuntu_dev|yesno:"Yes,No" }}</li>47 <li><b>Ubuntu Developer:</b> {{ person.ubuntu_dev|yesno:"Yes,No" }}</li>
47 <li><b>Active:</b> {{ person.is_active|yesno:"Yes,No" }}</li>48 <li><b>Active:</b> {{ person.is_active|yesno:"Yes,No" }}</li>
48 <li><b>Last Seen:</b> {{ person.last_upload.timestamp }}</li>49 <li><b>Last Seen:</b> {{ person.last_upload.timestamp }}</li>
50 <li><b>Last Contact:</b> {{ person|recent_contact }}</li>
49 <li><b>Total uploads:</b> {{ person.total_uploads }}</li>51 <li><b>Total uploads:</b> {{ person.total_uploads }}</li>
50 {% if ppu_candidates %}52 {% if ppu_candidates %}
51 <li><b>PPU Candidates:</b> 53 <li><b>PPU Candidates:</b>
52 {% for pkg in ppu_candidates %}54 {% for pkg in ppu_candidates %}
53 <a href="https://launchpad.net/ubuntu/+source/{{ pkg }}">{{ pkg }}</a>,</li>55 <a href="https://launchpad.net/ubuntu/+source/{{ pkg }}">{{ pkg }}</a>, </li>
54 {% endfor %}56 {% endfor %}
55 {% endif %}57 {% endif %}
58
56 </ul>59 </ul>
57</div>60</div>
5861
59<div id='contacted_form'>
60 <h3>Contacted?</h3>
61 <form action="#" method="post">{% csrf_token %}
62 {{ contacted.checkbox }}
63 <input type="submit" name='update_contacted' value="Save" />
64 </form>
65</div>
66
67<div id='whiteboard'>62<div id='whiteboard'>
68 <h2>Whiteboard:</h2>63 <h2>Whiteboard:</h2>
6964
@@ -73,6 +68,43 @@
73 </form>68 </form>
74</div>69</div>
7570
71<div id='contacts'>
72
73 <h2>Record contact:</h2>
74
75 <div id='comment-form'>
76 {% get_comment_form for person as form %}
77 <form action="{% comment_form_target %}" method="post">
78 {% csrf_token %}
79 {{ form.comment }}
80 {{ form.honeypot }}
81 {{ form.content_type }}
82 {{ form.object_pk }}
83 {{ form.timestamp }}
84 {{ form.security_hash }}
85 <input type="hidden" name="next" value="/contributors/{{ person.lpid }}#" />
86 <input type="submit" name="submit" value="Record contact">
87 </form>
88 </div>
89
90 <h2>Contacts:</h2>
91
92 {% get_comment_list for person as comment_list %}
93 {% for comment in comment_list reversed %}
94
95 <div id='comment'>
96 {{ comment.comment|linebreaks }}
97 <h3>#{{ forloop.revcounter }}</h3>
98 </div>
99
100 <div id='comment-details'>
101 by <a href="/users/{{ comment.user }}">{{ comment.user }}</a> on {{ comment.submit_date }}
102 </div>
103 <br>
104 {% endfor %}
105
106</div>
107
76<div id='uploads_per_release'>108<div id='uploads_per_release'>
77 <h2>Uploads per release:</h2>109 <h2>Uploads per release:</h2>
78 <div id="uploads_per_release_chart"></div>110 <div id="uploads_per_release_chart"></div>
79111
=== modified file 'overview/templates/potential_devs.html'
--- overview/templates/potential_devs.html 2013-06-19 20:29:36 +0000
+++ overview/templates/potential_devs.html 2013-06-23 19:42:22 +0000
@@ -1,4 +1,5 @@
1{% extends "base.html" %}1{% extends "base.html" %}
2{% load custom_tags %}
23
3{% block content %}4{% block content %}
45
@@ -15,9 +16,12 @@
15 <th>Uploads</th>16 <th>Uploads</th>
16 <th>Dates Active</th>17 <th>Dates Active</th>
17 </tr>18 </tr>
19
18{% for p in potential_devs|dictsortreversed:"first_upload.timestamp" %}20{% for p in potential_devs|dictsortreversed:"first_upload.timestamp" %}
19 <tr>21 <tr>
20 <td>{% if p.contacted %}<center>✓</center>{% endif %}</td>22 <td>{% if p|recent_contact > p.fortieth %}
23 <center>✓</center>{% endif %}
24 </td>
21 <td><a href="{{ p.lpid }}">{{ p.name }}</a></td>25 <td><a href="{{ p.lpid }}">{{ p.name }}</a></td>
22 <td>{{ p.total_uploads }}</td>26 <td>{{ p.total_uploads }}</td>
23 <td>{{ p.first_upload.timestamp|date:"SHORT_DATE_FORMAT" }} to {{ p.last_upload.timestamp|date:"SHORT_DATE_FORMAT" }}</td>27 <td>{{ p.first_upload.timestamp|date:"SHORT_DATE_FORMAT" }} to {{ p.last_upload.timestamp|date:"SHORT_DATE_FORMAT" }}</td>
2428
=== modified file 'overview/uploads/forms.py'
--- overview/uploads/forms.py 2013-04-23 18:32:37 +0000
+++ overview/uploads/forms.py 2013-06-23 19:42:22 +0000
@@ -3,9 +3,6 @@
3class NotesForm(forms.Form):3class NotesForm(forms.Form):
4 notes = forms.CharField(widget = forms.widgets.Textarea(), required=False)4 notes = forms.CharField(widget = forms.widgets.Textarea(), required=False)
55
6class BooleanForm(forms.Form):
7 checkbox = forms.BooleanField(required=False)
8
9class EditContrib(forms.Form):6class EditContrib(forms.Form):
10 lpid = forms.CharField(label='Launchpad ID: ', required=True)7 lpid = forms.CharField(label='Launchpad ID: ', required=True)
11 email = forms.EmailField(label='Email: ', required=True)8 email = forms.EmailField(label='Email: ', required=True)
129
=== modified file 'overview/uploads/models.py'
--- overview/uploads/models.py 2013-06-10 16:42:05 +0000
+++ overview/uploads/models.py 2013-06-23 19:42:22 +0000
@@ -1,5 +1,7 @@
1from django.db import models1from django.db import models
2from django.contrib.auth.models import User2from django.contrib.auth.models import User
3from django.contrib.contenttypes import generic
4from django.contrib.comments.models import Comment
35
4class UDD(models.Model):6class UDD(models.Model):
5 connection_name='udd'7 connection_name='udd'
@@ -41,7 +43,7 @@
41 last_upload = models.ForeignKey('Uploads', related_name='+')43 last_upload = models.ForeignKey('Uploads', related_name='+')
42 ubuntu_dev = models.BooleanField(default=False)44 ubuntu_dev = models.BooleanField(default=False)
43 notes = models.TextField(blank=True)45 notes = models.TextField(blank=True)
44 contacted = models.BooleanField(default=False)46 contacts = generic.GenericRelation(Comment, object_id_field="object_pk")
4547
46 class Meta:48 class Meta:
47 db_table = u'people'49 db_table = u'people'
4850
=== modified file 'overview/uploads/templatetags/custom_tags.py'
--- overview/uploads/templatetags/custom_tags.py 2013-04-06 22:04:28 +0000
+++ overview/uploads/templatetags/custom_tags.py 2013-06-23 19:42:22 +0000
@@ -5,3 +5,8 @@
5@register.inclusion_tag('ubu_dev_flag_img.html')5@register.inclusion_tag('ubu_dev_flag_img.html')
6def ubu_dev_img(flag):6def ubu_dev_img(flag):
7 return {'flag': flag}7 return {'flag': flag}
8
9@register.filter
10def recent_contact(person):
11 if person.contacts.all():
12 return person.contacts.all().reverse()[0].submit_date
813
=== modified file 'overview/uploads/views.py'
--- overview/uploads/views.py 2013-06-21 20:24:21 +0000
+++ overview/uploads/views.py 2013-06-23 19:42:22 +0000
@@ -13,13 +13,17 @@
13from uploads.decorators import group_perm_required13from uploads.decorators import group_perm_required
14from uploads.models import Uploads, People, UserProfile14from uploads.models import Uploads, People, UserProfile
15from django.contrib.auth.models import User15from django.contrib.auth.models import User
16from uploads.forms import NotesForm, BooleanForm, EditContrib16from uploads.forms import NotesForm, EditContrib
17
18from django.dispatch import receiver
19from django.contrib.comments.signals import comment_was_posted
1720
1821
19@group_perm_required()22@group_perm_required()
20def first_timers(request):23def first_timers(request):
21 output = []24 output = []
22 for p in People.objects.all():25 for p in People.objects.all().prefetch_related("first_upload"
26 ).prefetch_related("last_upload"):
23 if p.total_uploads < 5:27 if p.total_uploads < 5:
24 color = "lt5"28 color = "lt5"
25 if p.total_uploads >= 5 and p.total_uploads < 10:29 if p.total_uploads >= 5 and p.total_uploads < 10:
@@ -41,21 +45,8 @@
41 person = get_object_or_404(People, lpid=lpid)45 person = get_object_or_404(People, lpid=lpid)
42 uploads = Uploads.objects.filter(lpid_changer=lpid)46 uploads = Uploads.objects.filter(lpid_changer=lpid)
43 recent_uploads = uploads.order_by('timestamp').reverse()[0:10]47 recent_uploads = uploads.order_by('timestamp').reverse()[0:10]
44 uploads_per_release = OrderedDict([])48 uploads_per_release = get_uploads_per_release(lpid)
45 for d in UbuntuDistroInfo().all:49 ppu_candidates = get_ppu_candidates(uploads)
46 release_uploads = len(Uploads.objects.filter(lpid_changer=lpid).filter(release__icontains=d))
47 if uploads_per_release or release_uploads > 0:
48 uploads_per_release[d] = release_uploads
49 packages = []
50 ppu_candidates = []
51 for ul in uploads:
52 packages += [ul.package]
53 appearances = defaultdict(int)
54 for curr in packages:
55 appearances[curr] += 1
56 for pkg in appearances:
57 if appearances[pkg] > 5:
58 ppu_candidates += [pkg]
59 if request.method == 'POST':50 if request.method == 'POST':
60 if 'save_notes' in request.POST:51 if 'save_notes' in request.POST:
61 notes_form = NotesForm(request.POST)52 notes_form = NotesForm(request.POST)
@@ -67,56 +58,75 @@
67 log_action(person, change_message, request.user.pk)58 log_action(person, change_message, request.user.pk)
68 messages.success(request, 'Change successfully saved...')59 messages.success(request, 'Change successfully saved...')
69 return HttpResponseRedirect('#')60 return HttpResponseRedirect('#')
70 elif 'update_contacted' in request.POST:
71 contacted = BooleanForm(request.POST)
72 notes_form = NotesForm(initial={'notes': person.notes})
73 if contacted.is_valid():
74 person.contacted = contacted.cleaned_data['checkbox']
75 person.save()
76 change_message = "Marked %s as contacted." % person.name
77 log_action(person, change_message, request.user.pk)
78 messages.success(request, 'Change successfully saved...')
79 return HttpResponseRedirect('#')
80 else:61 else:
81 notes_form = NotesForm(initial={'notes': person.notes})62 notes_form = NotesForm(initial={'notes': person.notes})
82 contacted = BooleanForm(initial={'checkbox': person.contacted})
83 return render(request, 'person.html', {'person': person,63 return render(request, 'person.html', {'person': person,
84 'recent_uploads': recent_uploads,64 'recent_uploads': recent_uploads,
85 'ppu_candidates': ppu_candidates,65 'ppu_candidates': ppu_candidates,
86 'notes_form': notes_form,66 'notes_form': notes_form,
87 'contacted': contacted,67 'uploads_per_release':
88 'uploads_per_release': uploads_per_release})68 uploads_per_release})
69
70
71def get_ppu_candidates(uploads):
72 """
73 Takes an Uploads object filtered by lpid_changer and returns
74 a list of package that were uploaded by a contributor more than
75 five times.
76 """
77 packages = uploads.values_list('package', flat=True)
78 ppu_candidates = []
79 appearances = defaultdict(int)
80 for curr in packages:
81 appearances[curr] += 1
82 for pkg in appearances:
83 if appearances[pkg] > 5:
84 ppu_candidates += [pkg]
85 return ppu_candidates
86
87
88def get_uploads_per_release(lpid):
89 """
90 Takes an lpid and returns an ordered dict of uploads per release.
91 """
92 uploads_per_release = OrderedDict([])
93 for d in UbuntuDistroInfo().all:
94 release_uploads = len(Uploads.objects.filter(
95 lpid_changer=lpid).filter(release__icontains=d))
96 if uploads_per_release or release_uploads > 0:
97 uploads_per_release[d] = release_uploads
98 return uploads_per_release
8999
90100
91@group_perm_required()101@group_perm_required()
92def edit_person(request, lpid):102def edit_person(request, lpid):
93 person = get_object_or_404(People, lpid=lpid)103 person = get_object_or_404(People, lpid=lpid)
94 if request.method == 'POST':104 if request.method == 'POST':
95 if request.method == 'POST':105 person_form = EditContrib(request.POST)
96 person_form = EditContrib(request.POST)106 if person_form.is_valid():
97 if person_form.is_valid():107 new_lpid = person_form.cleaned_data['lpid']
98 new_lpid = person_form.cleaned_data['lpid']108 person.email = person_form.cleaned_data['email']
99 person.email = person_form.cleaned_data['email']109 person.lpid = new_lpid
100 person.lpid = new_lpid110 person.save()
101 person.save()111 if lpid is not new_lpid:
102 if lpid is not new_lpid:112 uploads = Uploads.objects.filter(lpid_changer=lpid)
103 uploads = Uploads.objects.filter(lpid_changer=lpid)113 uploads.update(lpid_changer=new_lpid)
104 uploads.update(lpid_changer = new_lpid)114 change_message = "Updated %s's details." % person.name
105 change_message = "Updated %s's details." % person.name115 log_action(person, change_message, request.user.pk)
106 log_action(person, change_message, request.user.pk)116 messages.success(request, 'Change successfully saved...')
107 messages.success(request, 'Change successfully saved...')117 return HttpResponseRedirect('/contributors/{}'.format(new_lpid))
108 return HttpResponseRedirect('/contributors/{}'.format(new_lpid))
109 else:118 else:
110 person_form = EditContrib(initial={'lpid': lpid,119 person_form = EditContrib(initial={'lpid': lpid,
111 'email': person.email})120 'email': person.email})
112 return render(request, 'edit_person.html', {'person': person,121 return render(request, 'edit_person.html', {'person': person,
113 'person_form': person_form})122 'person_form': person_form})
114123
124
115@group_perm_required()125@group_perm_required()
116def recent_contributors(request):126def recent_contributors(request):
117 recent_contributors = []127 recent_contributors = []
118 for p in People.objects.all():128 cutoff_date = timezone.now() - timedelta(days=2 * 30)
119 cutoff_date = timezone.now() - timedelta(days=2 * 30)129 for p in People.objects.all().prefetch_related("last_upload"):
120 if p.last_upload.timestamp > cutoff_date:130 if p.last_upload.timestamp > cutoff_date:
121 recent_contributors += [p]131 recent_contributors += [p]
122 return render(request, 'recent_contributors.html',132 return render(request, 'recent_contributors.html',
@@ -126,9 +136,9 @@
126@group_perm_required()136@group_perm_required()
127def lost_contributors(request):137def lost_contributors(request):
128 lost_contributors = []138 lost_contributors = []
129 for p in People.objects.all():139 one_year = timezone.now() - timedelta(days=365)
130 one_year = timezone.now() - timedelta(days=365)140 two_months = timezone.now() - timedelta(days=2 * 30)
131 two_months = timezone.now() - timedelta(days=2 * 30)141 for p in People.objects.all().prefetch_related('last_upload'):
132 ul_date = p.last_upload.timestamp142 ul_date = p.last_upload.timestamp
133 if ul_date > one_year and ul_date < two_months:143 if ul_date > one_year and ul_date < two_months:
134 lost_contributors += [p]144 lost_contributors += [p]
@@ -139,48 +149,63 @@
139@group_perm_required()149@group_perm_required()
140def potential_devs(request):150def potential_devs(request):
141 potential_devs = []151 potential_devs = []
142 for p in People.objects.all():152 cutoff_upload = {}
143 six_months = timezone.now() - timedelta(days=6 * 30)153 six_months = timezone.now() - timedelta(days=6 * 30)
144 if ((p.first_upload.timestamp < six_months154 for p in People.objects.filter(ubuntu_dev=False
145 and p.ubuntu_dev is not True155 ).filter(total_uploads__gte=40
146 and p.total_uploads >= 40156 ).filter(is_active=True
147 and p.is_active is True)):157 ).prefetch_related("first_upload"
148 potential_devs += [p]158 ).prefetch_related("last_upload"
159 ).filter(first_upload__timestamp__lte=six_months):
160 uploads = Uploads.objects.filter(lpid_changer=p.lpid)
161 p.fortieth = uploads.order_by("timestamp")[39].timestamp
162 potential_devs += [p]
149 return render(request, 'potential_devs.html',163 return render(request, 'potential_devs.html',
150 {'potential_devs': potential_devs})164 {'potential_devs': potential_devs,
165 'cutoff_upload': cutoff_upload})
166
151167
152def log_action(object, change_message, user):168def log_action(object, change_message, user):
153 LogEntry.objects.log_action(169 LogEntry.objects.log_action(
154 user_id = user, 170 user_id=user,
155 content_type_id = ContentType.objects.get_for_model(object).pk,171 content_type_id=ContentType.objects.get_for_model(object).pk,
156 object_id = object.pk,172 object_id=object.pk,
157 object_repr = object.lpid,173 object_repr=object.lpid,
158 change_message = change_message,174 change_message=change_message,
159 action_flag = ADDITION175 action_flag=ADDITION
160 )176 )
161177
178
162@group_perm_required()179@group_perm_required()
163def user_profile(request, user):180def user_profile(request, user):
164 profile = User.objects.get(username=user).profile181 profile = User.objects.get(username=user).profile
165 actions = LogEntry.objects.filter(user_id=profile.user_id)182 actions = LogEntry.objects.filter(user_id=profile.user_id)
166 edited_contribs = actions.order_by('object_repr').values_list("object_repr",183 edited_contribs = actions.order_by('object_repr'
167 flat=True).distinct()184 ).values_list("object_repr",
168 return render(request, 'user_profile.html',{'profile': profile,185 flat=True).distinct()
169 'actions': actions,186 return render(request, 'user_profile.html', {'profile': profile,
170 'edited_contribs': edited_contribs})187 'actions': actions,
188 'edited_contribs':
189 edited_contribs})
190
171191
172def site_logout(request):192def site_logout(request):
173 logout(request)193 logout(request)
174 messages.success(request, 'Logged out successfully...')194 messages.success(request, 'Logged out successfully...')
175 return HttpResponseRedirect('/')195 return HttpResponseRedirect('/')
176196
197
177def access_denied(request, redirect):198def access_denied(request, redirect):
178 messages.error(request, 'You do not have the correct permissions to view that page...')199 messages.error(request, """
200 You do not have the correct permissions to view that page...
201 """)
179 return HttpResponseRedirect(redirect)202 return HttpResponseRedirect(redirect)
180203
204
181def index(request):205def index(request):
182 return render(request, 'index.html', dashboard())206 return render(request, 'index.html', dashboard())
183207
208
184def dashboard():209def dashboard():
185 first_timers = []210 first_timers = []
186 experienced = []211 experienced = []
@@ -189,21 +214,46 @@
189 two_months = timezone.now() - timedelta(days=2 * 30)214 two_months = timezone.now() - timedelta(days=2 * 30)
190 six_months = timezone.now() - timedelta(days=6 * 30)215 six_months = timezone.now() - timedelta(days=6 * 30)
191 one_year = timezone.now() - timedelta(days=365)216 one_year = timezone.now() - timedelta(days=365)
192 for p in People.objects.all().order_by('last_upload__timestamp').reverse():217 people = People.objects.all().prefetch_related("first_upload"
193 if (len(first_timers) < 20 and p.first_upload.timestamp > three_months218 ).prefetch_related("last_upload"
194 and p.contacted is False):219 ).select_related('contacts'
220 ).order_by('last_upload__timestamp').reverse()
221 first_timers_qs = people.filter(first_upload__timestamp__gte=three_months)
222 experienced_qs = people.filter(ubuntu_dev=False
223 ).filter(total_uploads__gte=40
224 ).filter(is_active=True)
225 inactive_qs = people.filter(last_upload__timestamp__gt=one_year
226 ).filter(last_upload__timestamp__lt=two_months
227 ).filter(total_uploads__gte=5)
228 for p in first_timers_qs:
229 if (len(first_timers) < 20 and not p.contacts.all()):
195 first_timers.append(p)230 first_timers.append(p)
196 if (len(experienced) < 20 and p.first_upload.timestamp < six_months231 for p in experienced_qs:
197 and p.ubuntu_dev is not True232 if p.contacts.all():
198 and p.total_uploads >= 40233 recent_c = p.contacts.all().reverse()[0].submit_date
199 and p.is_active is True234 else:
200 and p.contacted is False):235 recent_c = None
201 experienced.append(p)236 if (len(experienced) < 20 and (recent_c is None or
202 if len(inactive) < 20:237 recent_c < Uploads.objects.filter(lpid_changer=p.lpid
203 ul_date = p.last_upload.timestamp238 ).order_by("timestamp"
204 if (ul_date > one_year and ul_date < two_months 239 )[39].timestamp)):
205 and p.contacted is False and p.total_uploads >= 5):240 experienced.append(p)
206 inactive.append(p)241 for p in inactive_qs:
242 if p.contacts.all():
243 recent_c = p.contacts.all().reverse()[0].submit_date
244 else:
245 recent_c = None
246 if len(inactive) < 20 and (recent_c is None or
247 recent_c < p.last_upload.timestamp):
248 inactive.append(p)
207 return {'first_timers': first_timers,249 return {'first_timers': first_timers,
208 'experienced': experienced,250 'experienced': experienced,
209 'inactive': inactive}251 'inactive': inactive}
252
253
254@receiver(comment_was_posted)
255def on_contact_saved(sender, comment=None, request=None, **kwargs):
256 person = People.objects.get(pk=comment.object_pk)
257 change_message = "Recorded a contact with %s." % person.name
258 log_action(person, change_message, comment.user.pk)
259 messages.success(request, 'Change successfully saved...')
210260
=== modified file 'overview/urls.py'
--- overview/urls.py 2013-06-12 18:37:58 +0000
+++ overview/urls.py 2013-06-23 19:42:22 +0000
@@ -29,7 +29,8 @@
29 name='potential_devs'),29 name='potential_devs'),
30 url(r'^contributors/(?P<lpid>.+)/edit', views.edit_person, name='edit_person'),30 url(r'^contributors/(?P<lpid>.+)/edit', views.edit_person, name='edit_person'),
31 url(r'^contributors/(?P<lpid>.+)', views.person_detail, name='person_detail'),31 url(r'^contributors/(?P<lpid>.+)', views.person_detail, name='person_detail'),
32 url(r'^users/(?P<user>.+)', views.user_profile, name='user_profile')32 url(r'^users/(?P<user>.+)', views.user_profile, name='user_profile'),
33 url(r'^comments/', include('django.contrib.comments.urls'))
33)34)
3435
35if settings.STATIC_SERVE:36if settings.STATIC_SERVE:

Subscribers

People subscribed via source and target branches