summaryrefslogtreecommitdiff
path: root/pgcommitfest/commitfest
diff options
context:
space:
mode:
authorMagnus Hagander2019-02-05 22:01:05 +0000
committerMagnus Hagander2019-02-05 22:01:05 +0000
commita32f4007670330d05a834db1b67687ab8b8c3b34 (patch)
tree02322951c6ab86287bf26ff7634292df9357f81e /pgcommitfest/commitfest
parent83edf49e86d2db5d7a75f6c1068d3e62bda99923 (diff)
Convert all spaces to tabs
Diffstat (limited to 'pgcommitfest/commitfest')
-rw-r--r--pgcommitfest/commitfest/admin.py14
-rw-r--r--pgcommitfest/commitfest/ajax.py390
-rw-r--r--pgcommitfest/commitfest/feeds.py58
-rw-r--r--pgcommitfest/commitfest/forms.py198
-rw-r--r--pgcommitfest/commitfest/lookups.py26
-rw-r--r--pgcommitfest/commitfest/management/commands/send_notifications.py64
-rw-r--r--pgcommitfest/commitfest/migrations/0003_withdrawn_status.py4
-rw-r--r--pgcommitfest/commitfest/models.py562
-rw-r--r--pgcommitfest/commitfest/reports.py24
-rw-r--r--pgcommitfest/commitfest/templatetags/commitfest.py30
-rw-r--r--pgcommitfest/commitfest/util.py66
-rw-r--r--pgcommitfest/commitfest/views.py1270
-rw-r--r--pgcommitfest/commitfest/widgets.py10
13 files changed, 1358 insertions, 1358 deletions
diff --git a/pgcommitfest/commitfest/admin.py b/pgcommitfest/commitfest/admin.py
index 05fe3ed..232ba30 100644
--- a/pgcommitfest/commitfest/admin.py
+++ b/pgcommitfest/commitfest/admin.py
@@ -3,19 +3,19 @@ from django.contrib import admin
from models import *
class CommitterAdmin(admin.ModelAdmin):
- list_display = ('user', 'active')
+ list_display = ('user', 'active')
class PatchOnCommitFestInline(admin.TabularInline):
- model = PatchOnCommitFest
- extra=1
+ model = PatchOnCommitFest
+ extra=1
class PatchAdmin(admin.ModelAdmin):
- inlines = (PatchOnCommitFestInline,)
- list_display = ('name', )
-# list_filter = ('commitfests_set__commitfest__name',)
+ inlines = (PatchOnCommitFestInline,)
+ list_display = ('name', )
+# list_filter = ('commitfests_set__commitfest__name',)
class MailThreadAttachmentAdmin(admin.ModelAdmin):
- list_display = ('date', 'author', 'messageid', 'mailthread',)
+ list_display = ('date', 'author', 'messageid', 'mailthread',)
admin.site.register(Committer, CommitterAdmin)
admin.site.register(CommitFest)
diff --git a/pgcommitfest/commitfest/ajax.py b/pgcommitfest/commitfest/ajax.py
index 3ec76ea..b8317f5 100644
--- a/pgcommitfest/commitfest/ajax.py
+++ b/pgcommitfest/commitfest/ajax.py
@@ -12,249 +12,249 @@ import json
from pgcommitfest.auth import user_search
class HttpResponseServiceUnavailable(HttpResponse):
- status_code = 503
+ status_code = 503
class Http503(Exception):
- pass
+ pass
from models import CommitFest, Patch, MailThread, MailThreadAttachment
from models import MailThreadAnnotation, PatchHistory
def _archivesAPI(suburl, params=None):
- try:
- resp = requests.get("http{0}://{1}:{2}{3}".format(settings.ARCHIVES_PORT == 443 and 's' or '',
- settings.ARCHIVES_SERVER,
- settings.ARCHIVES_PORT,
- suburl),
- params=params,
- headers={
- 'Host': settings.ARCHIVES_HOST,
- },
- timeout=settings.ARCHIVES_TIMEOUT,
- )
- if resp.status_code != 200:
- if resp.status_code == 404:
- raise Http404()
- raise Exception("JSON call failed: %s" % resp.status_code)
-
- return resp.json()
- except Http404:
- raise
- except Exception as e:
- raise Http503("Failed to communicate with archives backend: %s" % e)
+ try:
+ resp = requests.get("http{0}://{1}:{2}{3}".format(settings.ARCHIVES_PORT == 443 and 's' or '',
+ settings.ARCHIVES_SERVER,
+ settings.ARCHIVES_PORT,
+ suburl),
+ params=params,
+ headers={
+ 'Host': settings.ARCHIVES_HOST,
+ },
+ timeout=settings.ARCHIVES_TIMEOUT,
+ )
+ if resp.status_code != 200:
+ if resp.status_code == 404:
+ raise Http404()
+ raise Exception("JSON call failed: %s" % resp.status_code)
+
+ return resp.json()
+ except Http404:
+ raise
+ except Exception as e:
+ raise Http503("Failed to communicate with archives backend: %s" % e)
def getThreads(request):
- search = request.GET.has_key('s') and request.GET['s'] or None
- if request.GET.has_key('a') and request.GET['a'] == "1":
- attachonly = 1
- else:
- attachonly = 0
+ search = request.GET.has_key('s') and request.GET['s'] or None
+ if request.GET.has_key('a') and request.GET['a'] == "1":
+ attachonly = 1
+ else:
+ attachonly = 0
- # Make a JSON api call to the archives server
- params = {'n': 100, 'a': attachonly}
- if search:
- params['s'] = search
+ # Make a JSON api call to the archives server
+ params = {'n': 100, 'a': attachonly}
+ if search:
+ params['s'] = search
- r = _archivesAPI('/list/pgsql-hackers/latest.json', params)
- return sorted(r, key=lambda x: x['date'], reverse=True)
+ r = _archivesAPI('/list/pgsql-hackers/latest.json', params)
+ return sorted(r, key=lambda x: x['date'], reverse=True)
def getMessages(request):
- threadid = request.GET['t']
+ threadid = request.GET['t']
- thread = MailThread.objects.get(pk=threadid)
+ thread = MailThread.objects.get(pk=threadid)
- # Always make a call over to the archives api
- r = _archivesAPI('/message-id.json/%s' % thread.messageid)
- return sorted(r, key=lambda x: x['date'], reverse=True)
+ # Always make a call over to the archives api
+ r = _archivesAPI('/message-id.json/%s' % thread.messageid)
+ return sorted(r, key=lambda x: x['date'], reverse=True)
def refresh_single_thread(thread):
- r = sorted(_archivesAPI('/message-id.json/%s' % thread.messageid), key=lambda x: x['date'])
- if thread.latestmsgid != r[-1]['msgid']:
- # There is now a newer mail in the thread!
- thread.latestmsgid = r[-1]['msgid']
- thread.latestmessage = r[-1]['date']
- thread.latestauthor = r[-1]['from']
- thread.latestsubject = r[-1]['subj']
- thread.save()
- parse_and_add_attachments(r, thread)
- # Potentially update the last mail date - if there wasn't already a mail on each patch
- # from a *different* thread that had an earlier date.
- for p in thread.patches.filter(lastmail__lt=thread.latestmessage):
- p.lastmail = thread.latestmessage
- p.save()
+ r = sorted(_archivesAPI('/message-id.json/%s' % thread.messageid), key=lambda x: x['date'])
+ if thread.latestmsgid != r[-1]['msgid']:
+ # There is now a newer mail in the thread!
+ thread.latestmsgid = r[-1]['msgid']
+ thread.latestmessage = r[-1]['date']
+ thread.latestauthor = r[-1]['from']
+ thread.latestsubject = r[-1]['subj']
+ thread.save()
+ parse_and_add_attachments(r, thread)
+ # Potentially update the last mail date - if there wasn't already a mail on each patch
+ # from a *different* thread that had an earlier date.
+ for p in thread.patches.filter(lastmail__lt=thread.latestmessage):
+ p.lastmail = thread.latestmessage
+ p.save()
@transaction.atomic
def annotateMessage(request):
- thread = get_object_or_404(MailThread, pk=int(request.POST['t']))
- msgid = request.POST['msgid']
- msg = request.POST['msg']
-
- # Get the subject, author and date from the archives
- # We only have an API call to get the whole thread right now, so
- # do that, and then find our entry in it.
- r = _archivesAPI('/message-id.json/%s' % thread.messageid)
- for m in r:
- if m['msgid'] == msgid:
- annotation = MailThreadAnnotation(mailthread=thread,
- user=request.user,
- msgid=msgid,
- annotationtext=msg,
- mailsubject=m['subj'],
- maildate=m['date'],
- mailauthor=m['from'])
- annotation.save()
-
- for p in thread.patches.all():
- PatchHistory(patch=p, by=request.user, what='Added annotation "%s" to %s' % (msg, msgid)).save_and_notify()
- p.set_modified()
- p.save()
-
- return 'OK'
- return 'Message not found in thread!'
+ thread = get_object_or_404(MailThread, pk=int(request.POST['t']))
+ msgid = request.POST['msgid']
+ msg = request.POST['msg']
+
+ # Get the subject, author and date from the archives
+ # We only have an API call to get the whole thread right now, so
+ # do that, and then find our entry in it.
+ r = _archivesAPI('/message-id.json/%s' % thread.messageid)
+ for m in r:
+ if m['msgid'] == msgid:
+ annotation = MailThreadAnnotation(mailthread=thread,
+ user=request.user,
+ msgid=msgid,
+ annotationtext=msg,
+ mailsubject=m['subj'],
+ maildate=m['date'],
+ mailauthor=m['from'])
+ annotation.save()
+
+ for p in thread.patches.all():
+ PatchHistory(patch=p, by=request.user, what='Added annotation "%s" to %s' % (msg, msgid)).save_and_notify()
+ p.set_modified()
+ p.save()
+
+ return 'OK'
+ return 'Message not found in thread!'
@transaction.atomic
def deleteAnnotation(request):
- annotation = get_object_or_404(MailThreadAnnotation, pk=request.POST['id'])
+ annotation = get_object_or_404(MailThreadAnnotation, pk=request.POST['id'])
- for p in annotation.mailthread.patches.all():
- PatchHistory(patch=p, by=request.user, what='Deleted annotation "%s" from %s' % (annotation.annotationtext, annotation.msgid)).save_and_notify()
- p.set_modified()
- p.save()
+ for p in annotation.mailthread.patches.all():
+ PatchHistory(patch=p, by=request.user, what='Deleted annotation "%s" from %s' % (annotation.annotationtext, annotation.msgid)).save_and_notify()
+ p.set_modified()
+ p.save()
- annotation.delete()
+ annotation.delete()
- return 'OK'
+ return 'OK'
def parse_and_add_attachments(threadinfo, mailthread):
- for t in threadinfo:
- if len(t['atts']):
- # One or more attachments. For now, we're only actually going
- # to store and process the first one, even though the API gets
- # us all of them.
- MailThreadAttachment.objects.get_or_create(mailthread=mailthread,
- messageid=t['msgid'],
- defaults={
- 'date': t['date'],
- 'author': t['from'],
- 'attachmentid': t['atts'][0]['id'],
- 'filename': t['atts'][0]['name'],
- })
- # In theory we should remove objects if they don't have an
- # attachment, but how could that ever happen? Ignore for now.
+ for t in threadinfo:
+ if len(t['atts']):
+ # One or more attachments. For now, we're only actually going
+ # to store and process the first one, even though the API gets
+ # us all of them.
+ MailThreadAttachment.objects.get_or_create(mailthread=mailthread,
+ messageid=t['msgid'],
+ defaults={
+ 'date': t['date'],
+ 'author': t['from'],
+ 'attachmentid': t['atts'][0]['id'],
+ 'filename': t['atts'][0]['name'],
+ })
+ # In theory we should remove objects if they don't have an
+ # attachment, but how could that ever happen? Ignore for now.
@transaction.atomic
def attachThread(request):
- cf = get_object_or_404(CommitFest, pk=int(request.POST['cf']))
- patch = get_object_or_404(Patch, pk=int(request.POST['p']), commitfests=cf)
- msgid = request.POST['msg']
+ cf = get_object_or_404(CommitFest, pk=int(request.POST['cf']))
+ patch = get_object_or_404(Patch, pk=int(request.POST['p']), commitfests=cf)
+ msgid = request.POST['msg']
- return doAttachThread(cf, patch, msgid, request.user)
+ return doAttachThread(cf, patch, msgid, request.user)
def doAttachThread(cf, patch, msgid, user):
- # Note! Must be called in an open transaction!
- r = sorted(_archivesAPI('/message-id.json/%s' % msgid), key=lambda x: x['date'])
- # We have the full thread metadata - using the first and last entry,
- # construct a new mailthread in our own model.
- # First, though, check if it's already there.
- threads = MailThread.objects.filter(messageid=r[0]['msgid'])
- if len(threads):
- thread = threads[0]
- if thread.patches.filter(id=patch.id).exists():
- return 'This thread is already added to this email'
-
- # We did not exist, so we'd better add ourselves.
- # While at it, we update the thread entry with the latest data from the
- # archives.
- thread.patches.add(patch)
- thread.latestmessage=r[-1]['date']
- thread.latestauthor=r[-1]['from']
- thread.latestsubject=r[-1]['subj']
- thread.latestmsgid=r[-1]['msgid']
- thread.save()
- else:
- # No existing thread existed, so create it
- # Now create a new mailthread entry
- m = MailThread(messageid=r[0]['msgid'],
- subject=r[0]['subj'],
- firstmessage=r[0]['date'],
- firstauthor=r[0]['from'],
- latestmessage=r[-1]['date'],
- latestauthor=r[-1]['from'],
- latestsubject=r[-1]['subj'],
- latestmsgid=r[-1]['msgid'],
- )
- m.save()
- m.patches.add(patch)
- m.save()
- parse_and_add_attachments(r, m)
-
- PatchHistory(patch=patch, by=user, what='Attached mail thread %s' % r[0]['msgid']).save_and_notify()
- patch.update_lastmail()
- patch.set_modified()
- patch.save()
-
- return 'OK'
+ # Note! Must be called in an open transaction!
+ r = sorted(_archivesAPI('/message-id.json/%s' % msgid), key=lambda x: x['date'])
+ # We have the full thread metadata - using the first and last entry,
+ # construct a new mailthread in our own model.
+ # First, though, check if it's already there.
+ threads = MailThread.objects.filter(messageid=r[0]['msgid'])
+ if len(threads):
+ thread = threads[0]
+ if thread.patches.filter(id=patch.id).exists():
+ return 'This thread is already added to this email'
+
+ # We did not exist, so we'd better add ourselves.
+ # While at it, we update the thread entry with the latest data from the
+ # archives.
+ thread.patches.add(patch)
+ thread.latestmessage=r[-1]['date']
+ thread.latestauthor=r[-1]['from']
+ thread.latestsubject=r[-1]['subj']
+ thread.latestmsgid=r[-1]['msgid']
+ thread.save()
+ else:
+ # No existing thread existed, so create it
+ # Now create a new mailthread entry
+ m = MailThread(messageid=r[0]['msgid'],
+ subject=r[0]['subj'],
+ firstmessage=r[0]['date'],
+ firstauthor=r[0]['from'],
+ latestmessage=r[-1]['date'],
+ latestauthor=r[-1]['from'],
+ latestsubject=r[-1]['subj'],
+ latestmsgid=r[-1]['msgid'],
+ )
+ m.save()
+ m.patches.add(patch)
+ m.save()
+ parse_and_add_attachments(r, m)
+
+ PatchHistory(patch=patch, by=user, what='Attached mail thread %s' % r[0]['msgid']).save_and_notify()
+ patch.update_lastmail()
+ patch.set_modified()
+ patch.save()
+
+ return 'OK'
@transaction.atomic
def detachThread(request):
- cf = get_object_or_404(CommitFest, pk=int(request.POST['cf']))
- patch = get_object_or_404(Patch, pk=int(request.POST['p']), commitfests=cf)
- thread = get_object_or_404(MailThread, messageid=request.POST['msg'])
+ cf = get_object_or_404(CommitFest, pk=int(request.POST['cf']))
+ patch = get_object_or_404(Patch, pk=int(request.POST['p']), commitfests=cf)
+ thread = get_object_or_404(MailThread, messageid=request.POST['msg'])
- patch.mailthread_set.remove(thread)
- PatchHistory(patch=patch, by=request.user, what='Detached mail thread %s' % request.POST['msg']).save_and_notify()
- patch.update_lastmail()
- patch.set_modified()
- patch.save()
+ patch.mailthread_set.remove(thread)
+ PatchHistory(patch=patch, by=request.user, what='Detached mail thread %s' % request.POST['msg']).save_and_notify()
+ patch.update_lastmail()
+ patch.set_modified()
+ patch.save()
- return 'OK'
+ return 'OK'
def searchUsers(request):
- if request.GET.has_key('s') and request.GET['s']:
- return user_search(request.GET['s'])
- else:
- return []
+ if request.GET.has_key('s') and request.GET['s']:
+ return user_search(request.GET['s'])
+ else:
+ return []
def importUser(request):
- if request.GET.has_key('u') and request.GET['u']:
- u = user_search(userid=request.GET['u'])
- if len (u) != 1:
- return "Internal error, duplicate user found"
-
- u = u[0]
-
- if User.objects.filter(username=u['u']).exists():
- return "User already exists"
- User(username=u['u'],
- first_name=u['f'],
- last_name=u['l'],
- email=u['e'],
- password='setbypluginnotsha1',
- ).save()
- return 'OK'
- else:
- raise Http404()
+ if request.GET.has_key('u') and request.GET['u']:
+ u = user_search(userid=request.GET['u'])
+ if len (u) != 1:
+ return "Internal error, duplicate user found"
+
+ u = u[0]
+
+ if User.objects.filter(username=u['u']).exists():
+ return "User already exists"
+ User(username=u['u'],
+ first_name=u['f'],
+ last_name=u['l'],
+ email=u['e'],
+ password='setbypluginnotsha1',
+ ).save()
+ return 'OK'
+ else:
+ raise Http404()
_ajax_map={
- 'getThreads': getThreads,
- 'getMessages': getMessages,
- 'attachThread': attachThread,
- 'detachThread': detachThread,
- 'annotateMessage': annotateMessage,
- 'deleteAnnotation': deleteAnnotation,
- 'searchUsers': searchUsers,
- 'importUser': importUser,
+ 'getThreads': getThreads,
+ 'getMessages': getMessages,
+ 'attachThread': attachThread,
+ 'detachThread': detachThread,
+ 'annotateMessage': annotateMessage,
+ 'deleteAnnotation': deleteAnnotation,
+ 'searchUsers': searchUsers,
+ 'importUser': importUser,
}
# Main entrypoint for /ajax/<command>/
@csrf_exempt
@login_required
def main(request, command):
- if not _ajax_map.has_key(command):
- raise Http404
- try:
- resp = HttpResponse(content_type='application/json')
- json.dump(_ajax_map[command](request), resp)
- return resp
- except Http503, e:
- return HttpResponseServiceUnavailable(e, content_type='text/plain')
+ if not _ajax_map.has_key(command):
+ raise Http404
+ try:
+ resp = HttpResponse(content_type='application/json')
+ json.dump(_ajax_map[command](request), resp)
+ return resp
+ except Http503, e:
+ return HttpResponseServiceUnavailable(e, content_type='text/plain')
diff --git a/pgcommitfest/commitfest/feeds.py b/pgcommitfest/commitfest/feeds.py
index 025ddef..d858703 100644
--- a/pgcommitfest/commitfest/feeds.py
+++ b/pgcommitfest/commitfest/feeds.py
@@ -1,38 +1,38 @@
from django.contrib.syndication.views import Feed
class ActivityFeed(Feed):
- title = description = 'Commitfest Activity Log'
- link = 'https://commitfest.postgresql.org/'
+ title = description = 'Commitfest Activity Log'
+ link = 'https://commitfest.postgresql.org/'
- def __init__(self, activity, cf, *args, **kwargs):
- super(ActivityFeed, self).__init__(*args, **kwargs)
- self.activity = activity
- if cf:
- self.cfid = cf.id
- self.title = self.description = 'PostgreSQL Commitfest {0} Activity Log'.format(cf.name)
- else:
- self.cfid = None
+ def __init__(self, activity, cf, *args, **kwargs):
+ super(ActivityFeed, self).__init__(*args, **kwargs)
+ self.activity = activity
+ if cf:
+ self.cfid = cf.id
+ self.title = self.description = 'PostgreSQL Commitfest {0} Activity Log'.format(cf.name)
+ else:
+ self.cfid = None
- def items(self):
- return self.activity
+ def items(self):
+ return self.activity
- def item_title(self, item):
- if self.cfid:
- return item['name']
- else:
- return u'{cfname}: {name}'.format(**item)
+ def item_title(self, item):
+ if self.cfid:
+ return item['name']
+ else:
+ return u'{cfname}: {name}'.format(**item)
- def item_description(self, item):
- if self.cfid:
- return u"<div>Patch: {name}</div><div>User: {by}</div>\n<div>{what}</div>".format(**item)
- else:
- return u"<div>Commitfest: {cfname}</div><div>Patch: {name}</div><div>User: {by}</div><div>{what}</div>".format(**item)
+ def item_description(self, item):
+ if self.cfid:
+ return u"<div>Patch: {name}</div><div>User: {by}</div>\n<div>{what}</div>".format(**item)
+ else:
+ return u"<div>Commitfest: {cfname}</div><div>Patch: {name}</div><div>User: {by}</div><div>{what}</div>".format(**item)
- def item_link(self, item):
- if self.cfid:
- return 'https://commitfest.postgresql.org/{cfid}/{patchid}/'.format(cfid=self.cfid,**item)
- else:
- return 'https://commitfest.postgresql.org/{cfid}/{patchid}/'.format(**item)
+ def item_link(self, item):
+ if self.cfid:
+ return 'https://commitfest.postgresql.org/{cfid}/{patchid}/'.format(cfid=self.cfid,**item)
+ else:
+ return 'https://commitfest.postgresql.org/{cfid}/{patchid}/'.format(**item)
- def item_pubdate(self, item):
- return item['date']
+ def item_pubdate(self, item):
+ return item['date']
diff --git a/pgcommitfest/commitfest/forms.py b/pgcommitfest/commitfest/forms.py
index 568af64..a341f99 100644
--- a/pgcommitfest/commitfest/forms.py
+++ b/pgcommitfest/commitfest/forms.py
@@ -13,125 +13,125 @@ from widgets import ThreadPickWidget
from ajax import _archivesAPI
class CommitFestFilterForm(forms.Form):
- text = forms.CharField(max_length=50, required=False)
- status = forms.ChoiceField(required=False)
- author = forms.ChoiceField(required=False)
- reviewer = forms.ChoiceField(required=False)
- sortkey = forms.IntegerField(required=False)
+ text = forms.CharField(max_length=50, required=False)
+ status = forms.ChoiceField(required=False)
+ author = forms.ChoiceField(required=False)
+ reviewer = forms.ChoiceField(required=False)
+ sortkey = forms.IntegerField(required=False)
- def __init__(self, cf, *args, **kwargs):
- super(CommitFestFilterForm, self).__init__(*args, **kwargs)
+ def __init__(self, cf, *args, **kwargs):
+ super(CommitFestFilterForm, self).__init__(*args, **kwargs)
- self.fields['sortkey'].widget = forms.HiddenInput()
+ self.fields['sortkey'].widget = forms.HiddenInput()
- c = [(-1, '* All')] + list(PatchOnCommitFest._STATUS_CHOICES)
- self.fields['status'] = forms.ChoiceField(choices=c, required=False)
+ c = [(-1, '* All')] + list(PatchOnCommitFest._STATUS_CHOICES)
+ self.fields['status'] = forms.ChoiceField(choices=c, required=False)
- q = Q(patch_author__commitfests=cf) | Q(patch_reviewer__commitfests=cf)
- userchoices = [(-1, '* All'), (-2, '* None'), (-3, '* Yourself') ] + [(u.id, '%s %s (%s)' % (u.first_name, u.last_name, u.username)) for u in User.objects.filter(q).distinct().order_by('first_name', 'last_name')]
- self.fields['author'] = forms.ChoiceField(choices=userchoices, required=False)
- self.fields['reviewer'] = forms.ChoiceField(choices=userchoices, required=False)
+ q = Q(patch_author__commitfests=cf) | Q(patch_reviewer__commitfests=cf)
+ userchoices = [(-1, '* All'), (-2, '* None'), (-3, '* Yourself') ] + [(u.id, '%s %s (%s)' % (u.first_name, u.last_name, u.username)) for u in User.objects.filter(q).distinct().order_by('first_name', 'last_name')]
+ self.fields['author'] = forms.ChoiceField(choices=userchoices, required=False)
+ self.fields['reviewer'] = forms.ChoiceField(choices=userchoices, required=False)
- for f in ('status', 'author', 'reviewer',):
- self.fields[f].widget.attrs = {'class': 'input-medium'}
+ for f in ('status', 'author', 'reviewer',):
+ self.fields[f].widget.attrs = {'class': 'input-medium'}
class PatchForm(forms.ModelForm):
- class Meta:
- model = Patch
- exclude = ('commitfests', 'mailthreads', 'modified', 'lastmail', 'subscribers', )
- widgets = {
- 'authors': AutoCompleteSelectMultipleWidget(lookup_class=UserLookup, position='top'),
- 'reviewers': AutoCompleteSelectMultipleWidget(lookup_class=UserLookup, position='top'),
- }
+ class Meta:
+ model = Patch
+ exclude = ('commitfests', 'mailthreads', 'modified', 'lastmail', 'subscribers', )
+ widgets = {
+ 'authors': AutoCompleteSelectMultipleWidget(lookup_class=UserLookup, position='top'),
+ 'reviewers': AutoCompleteSelectMultipleWidget(lookup_class=UserLookup, position='top'),
+ }
- def __init__(self, *args, **kwargs):
- super(PatchForm, self).__init__(*args, **kwargs)
- self.fields['authors'].help_text = 'Enter part of name to see list'
- self.fields['reviewers'].help_text = 'Enter part of name to see list'
- self.fields['committer'].label_from_instance = lambda x: '%s %s (%s)' % (x.user.first_name, x.user.last_name, x.user.username)
+ def __init__(self, *args, **kwargs):
+ super(PatchForm, self).__init__(*args, **kwargs)
+ self.fields['authors'].help_text = 'Enter part of name to see list'
+ self.fields['reviewers'].help_text = 'Enter part of name to see list'
+ self.fields['committer'].label_from_instance = lambda x: '%s %s (%s)' % (x.user.first_name, x.user.last_name, x.user.username)
class NewPatchForm(forms.ModelForm):
- threadmsgid = forms.CharField(max_length=200, required=True, label='Specify thread msgid', widget=ThreadPickWidget)
-# patchfile = forms.FileField(allow_empty_file=False, max_length=50000, label='or upload patch file', required=False, help_text='This may be supported sometime in the future, and would then autogenerate a mail to the hackers list. At such a time, the threadmsgid would no longer be required.')
-
- class Meta:
- model = Patch
- exclude = ('commitfests', 'mailthreads', 'modified', 'authors', 'reviewers', 'committer', 'wikilink', 'gitlink', 'lastmail', 'subscribers', )
-
- def clean_threadmsgid(self):
- try:
- _archivesAPI('/message-id.json/%s' % self.cleaned_data['threadmsgid'])
- except Http404:
- raise ValidationError("Message not found in archives")
- except:
- raise ValidationError("Error in API call to validate thread")
- return self.cleaned_data['threadmsgid']
+ threadmsgid = forms.CharField(max_length=200, required=True, label='Specify thread msgid', widget=ThreadPickWidget)
+# patchfile = forms.FileField(allow_empty_file=False, max_length=50000, label='or upload patch file', required=False, help_text='This may be supported sometime in the future, and would then autogenerate a mail to the hackers list. At such a time, the threadmsgid would no longer be required.')
+
+ class Meta:
+ model = Patch
+ exclude = ('commitfests', 'mailthreads', 'modified', 'authors', 'reviewers', 'committer', 'wikilink', 'gitlink', 'lastmail', 'subscribers', )
+
+ def clean_threadmsgid(self):
+ try:
+ _archivesAPI('/message-id.json/%s' % self.cleaned_data['threadmsgid'])
+ except Http404:
+ raise ValidationError("Message not found in archives")
+ except:
+ raise ValidationError("Error in API call to validate thread")
+ return self.cleaned_data['threadmsgid']
def _fetch_thread_choices(patch):
- for mt in patch.mailthread_set.order_by('-latestmessage'):
- ti = sorted(_archivesAPI('/message-id.json/%s' % mt.messageid), key=lambda x: x['date'], reverse=True)
- yield [mt.subject,
- [('%s,%s' % (mt.messageid, t['msgid']),'From %s at %s' % (t['from'], t['date'])) for t in ti]]
+ for mt in patch.mailthread_set.order_by('-latestmessage'):
+ ti = sorted(_archivesAPI('/message-id.json/%s' % mt.messageid), key=lambda x: x['date'], reverse=True)
+ yield [mt.subject,
+ [('%s,%s' % (mt.messageid, t['msgid']),'From %s at %s' % (t['from'], t['date'])) for t in ti]]
review_state_choices = (
- (0, 'Tested'),
- (1, 'Passed'),
+ (0, 'Tested'),
+ (1, 'Passed'),
)
def reviewfield(label):
- return forms.MultipleChoiceField(choices=review_state_choices, label=label, widget=forms.CheckboxSelectMultiple, required=False)
+ return forms.MultipleChoiceField(choices=review_state_choices, label=label, widget=forms.CheckboxSelectMultiple, required=False)
class CommentForm(forms.Form):
- responseto = forms.ChoiceField(choices=[], required=True, label='In response to')
-
- # Specific checkbox fields for reviews
- review_installcheck = reviewfield('make installcheck-world')
- review_implements = reviewfield('Implements feature')
- review_spec = reviewfield('Spec compliant')
- review_doc = reviewfield('Documentation')
-
- message = forms.CharField(required=True, widget=forms.Textarea)
- newstatus = forms.ChoiceField(choices=PatchOnCommitFest.OPEN_STATUS_CHOICES, label='New status')
-
- def __init__(self, patch, poc, is_review, *args, **kwargs):
- super(CommentForm, self).__init__(*args, **kwargs)
- self.is_review = is_review
-
- self.fields['responseto'].choices = _fetch_thread_choices(patch)
- self.fields['newstatus'].initial = poc.status
- if not is_review:
- del self.fields['review_installcheck']
- del self.fields['review_implements']
- del self.fields['review_spec']
- del self.fields['review_doc']
-
- def clean_responseto(self):
- try:
- (threadid, respid) = self.cleaned_data['responseto'].split(',')
- self.thread = MailThread.objects.get(messageid=threadid)
- self.respid = respid
- except MailThread.DoesNotExist:
- raise ValidationError('Selected thread appears to no longer exist')
- except:
- raise ValidationError('Invalid message selected')
- return self.cleaned_data['responseto']
-
- def clean(self):
- if self.is_review:
- for fn,f in self.fields.items():
- if fn.startswith('review_') and self.cleaned_data.has_key(fn):
- if '1' in self.cleaned_data[fn] and not '0' in self.cleaned_data[fn]:
- self.errors[fn] = (('Cannot pass a test without performing it!'),)
- return self.cleaned_data
+ responseto = forms.ChoiceField(choices=[], required=True, label='In response to')
+
+ # Specific checkbox fields for reviews
+ review_installcheck = reviewfield('make installcheck-world')
+ review_implements = reviewfield('Implements feature')
+ review_spec = reviewfield('Spec compliant')
+ review_doc = reviewfield('Documentation')
+
+ message = forms.CharField(required=True, widget=forms.Textarea)
+ newstatus = forms.ChoiceField(choices=PatchOnCommitFest.OPEN_STATUS_CHOICES, label='New status')
+
+ def __init__(self, patch, poc, is_review, *args, **kwargs):
+ super(CommentForm, self).__init__(*args, **kwargs)
+ self.is_review = is_review
+
+ self.fields['responseto'].choices = _fetch_thread_choices(patch)
+ self.fields['newstatus'].initial = poc.status
+ if not is_review:
+ del self.fields['review_installcheck']
+ del self.fields['review_implements']
+ del self.fields['review_spec']
+ del self.fields['review_doc']
+
+ def clean_responseto(self):
+ try:
+ (threadid, respid) = self.cleaned_data['responseto'].split(',')
+ self.thread = MailThread.objects.get(messageid=threadid)
+ self.respid = respid
+ except MailThread.DoesNotExist:
+ raise ValidationError('Selected thread appears to no longer exist')
+ except:
+ raise ValidationError('Invalid message selected')
+ return self.cleaned_data['responseto']
+
+ def clean(self):
+ if self.is_review:
+ for fn,f in self.fields.items():
+ if fn.startswith('review_') and self.cleaned_data.has_key(fn):
+ if '1' in self.cleaned_data[fn] and not '0' in self.cleaned_data[fn]:
+ self.errors[fn] = (('Cannot pass a test without performing it!'),)
+ return self.cleaned_data
class BulkEmailForm(forms.Form):
- reviewers = forms.CharField(required=False, widget=HiddenInput())
- authors = forms.CharField(required=False, widget=HiddenInput())
- subject = forms.CharField(required=True)
- body = forms.CharField(required=True, widget=forms.Textarea)
- confirm = forms.BooleanField(required=True, label='Check to confirm sending')
-
- def __init__(self, *args, **kwargs):
- super(BulkEmailForm, self).__init__(*args, **kwargs)
+ reviewers = forms.CharField(required=False, widget=HiddenInput())
+ authors = forms.CharField(required=False, widget=HiddenInput())
+ subject = forms.CharField(required=True)
+ body = forms.CharField(required=True, widget=forms.Textarea)
+ confirm = forms.BooleanField(required=True, label='Check to confirm sending')
+
+ def __init__(self, *args, **kwargs):
+ super(BulkEmailForm, self).__init__(*args, **kwargs)
diff --git a/pgcommitfest/commitfest/lookups.py b/pgcommitfest/commitfest/lookups.py
index 74c08cc..1cb567f 100644
--- a/pgcommitfest/commitfest/lookups.py
+++ b/pgcommitfest/commitfest/lookups.py
@@ -6,20 +6,20 @@ from selectable.decorators import login_required
@login_required
class UserLookup(ModelLookup):
- model = User
- search_fields = (
- 'username__icontains',
- 'first_name__icontains',
- 'last_name__icontains',
- )
- filters = {'is_active': True, }
+ model = User
+ search_fields = (
+ 'username__icontains',
+ 'first_name__icontains',
+ 'last_name__icontains',
+ )
+ filters = {'is_active': True, }
- def get_item_value(self, item):
- # Display for currently selected item
- return u"%s (%s)" % (item.username, item.get_full_name())
+ def get_item_value(self, item):
+ # Display for currently selected item
+ return u"%s (%s)" % (item.username, item.get_full_name())
- def get_item_label(self, item):
- # Display for choice listings
- return u"%s (%s)" % (item.username, item.get_full_name())
+ def get_item_label(self, item):
+ # Display for choice listings
+ return u"%s (%s)" % (item.username, item.get_full_name())
registry.register(UserLookup)
diff --git a/pgcommitfest/commitfest/management/commands/send_notifications.py b/pgcommitfest/commitfest/management/commands/send_notifications.py
index 6a8fe42..be8cd90 100644
--- a/pgcommitfest/commitfest/management/commands/send_notifications.py
+++ b/pgcommitfest/commitfest/management/commands/send_notifications.py
@@ -9,38 +9,38 @@ from pgcommitfest.userprofile.models import UserProfile
from pgcommitfest.mailqueue.util import send_template_mail
class Command(BaseCommand):
- help = "Send queued notifications"
+ help = "Send queued notifications"
- def handle(self, *args, **options):
- with transaction.atomic():
- # Django doesn't do proper group by in the ORM, so we have to
- # build our own.
- matches = {}
- for n in PendingNotification.objects.all().order_by('user', 'history__patch__id', 'history__id'):
- if not matches.has_key(n.user.id):
- matches[n.user.id] = {'user': n.user, 'patches': {}}
- if not matches[n.user.id]['patches'].has_key(n.history.patch.id):
- matches[n.user.id]['patches'][n.history.patch.id] = {'patch': n.history.patch, 'entries': []}
- matches[n.user.id]['patches'][n.history.patch.id]['entries'].append(n.history)
- n.delete()
+ def handle(self, *args, **options):
+ with transaction.atomic():
+ # Django doesn't do proper group by in the ORM, so we have to
+ # build our own.
+ matches = {}
+ for n in PendingNotification.objects.all().order_by('user', 'history__patch__id', 'history__id'):
+ if not matches.has_key(n.user.id):
+ matches[n.user.id] = {'user': n.user, 'patches': {}}
+ if not matches[n.user.id]['patches'].has_key(n.history.patch.id):
+ matches[n.user.id]['patches'][n.history.patch.id] = {'patch': n.history.patch, 'entries': []}
+ matches[n.user.id]['patches'][n.history.patch.id]['entries'].append(n.history)
+ n.delete()
- # Ok, now let's build emails from this
- for v in matches.values():
- user = v['user']
- email = user.email
- try:
- if user.userprofile and user.userprofile.notifyemail:
- email = user.userprofile.notifyemail.email
- except UserProfile.DoesNotExist:
- pass
+ # Ok, now let's build emails from this
+ for v in matches.values():
+ user = v['user']
+ email = user.email
+ try:
+ if user.userprofile and user.userprofile.notifyemail:
+ email = user.userprofile.notifyemail.email
+ except UserProfile.DoesNotExist:
+ pass
- send_template_mail(settings.NOTIFICATION_FROM,
- None,
- email,
- "PostgreSQL commitfest updates",
- 'mail/patch_notify.txt',
- {
- 'user': user,
- 'patches': v['patches'],
- },
- )
+ send_template_mail(settings.NOTIFICATION_FROM,
+ None,
+ email,
+ "PostgreSQL commitfest updates",
+ 'mail/patch_notify.txt',
+ {
+ 'user': user,
+ 'patches': v['patches'],
+ },
+ )
diff --git a/pgcommitfest/commitfest/migrations/0003_withdrawn_status.py b/pgcommitfest/commitfest/migrations/0003_withdrawn_status.py
index 346adf9..fa61fe3 100644
--- a/pgcommitfest/commitfest/migrations/0003_withdrawn_status.py
+++ b/pgcommitfest/commitfest/migrations/0003_withdrawn_status.py
@@ -16,7 +16,7 @@ class Migration(migrations.Migration):
name='status',
field=models.IntegerField(default=1, choices=[(1, b'Needs review'), (2, b'Waiting on Author'), (3, b'Ready for Committer'), (4, b'Committed'), (5, b'Moved to next CF'), (6, b'Rejected'), (7, b'Returned with feedback'), (8, b'Withdrawn')]),
),
- migrations.RunSQL("""
+ migrations.RunSQL("""
INSERT INTO commitfest_patchstatus (status, statusstring, sortkey) VALUES
(1,'Needs review',10),
(2,'Waiting on Author',15),
@@ -28,5 +28,5 @@ INSERT INTO commitfest_patchstatus (status, statusstring, sortkey) VALUES
(8,'Withdrawn', 50)
ON CONFLICT (status) DO UPDATE SET statusstring=excluded.statusstring, sortkey=excluded.sortkey;
"""),
- migrations.RunSQL("DELETE FROM commitfest_patchstatus WHERE status < 1 OR status > 8"),
+ migrations.RunSQL("DELETE FROM commitfest_patchstatus WHERE status < 1 OR status > 8"),
]
diff --git a/pgcommitfest/commitfest/models.py b/pgcommitfest/commitfest/models.py
index 60dcaf4..1cc694e 100644
--- a/pgcommitfest/commitfest/models.py
+++ b/pgcommitfest/commitfest/models.py
@@ -11,310 +11,310 @@ from pgcommitfest.userprofile.models import UserProfile
# need to extend from the user model, so just create a separate
# class.
class Committer(models.Model):
- user = models.OneToOneField(User, null=False, blank=False, primary_key=True)
- active = models.BooleanField(null=False, blank=False, default=True)
+ user = models.OneToOneField(User, null=False, blank=False, primary_key=True)
+ active = models.BooleanField(null=False, blank=False, default=True)
- def __unicode__(self):
- return unicode(self.user)
+ def __unicode__(self):
+ return unicode(self.user)
- @property
- def fullname(self):
- return "%s %s (%s)" % (self.user.first_name, self.user.last_name, self.user.username)
+ @property
+ def fullname(self):
+ return "%s %s (%s)" % (self.user.first_name, self.user.last_name, self.user.username)
- class Meta:
- ordering = ('user__last_name', 'user__first_name')
+ class Meta:
+ ordering = ('user__last_name', 'user__first_name')
class CommitFest(models.Model):
- STATUS_FUTURE=1
- STATUS_OPEN=2
- STATUS_INPROGRESS=3
- STATUS_CLOSED=4
- _STATUS_CHOICES = (
- (STATUS_FUTURE, 'Future'),
- (STATUS_OPEN, 'Open'),
- (STATUS_INPROGRESS, 'In Progress'),
- (STATUS_CLOSED, 'Closed'),
- )
- name = models.CharField(max_length=100, blank=False, null=False, unique=True)
- status = models.IntegerField(null=False, blank=False, default=1, choices=_STATUS_CHOICES)
- startdate = models.DateField(blank=True, null=True)
- enddate = models.DateField(blank=True, null=True)
-
- @property
- def statusstring(self):
- return [v for k,v in self._STATUS_CHOICES if k==self.status][0]
-
- @property
- def periodstring(self):
- if self.startdate and self.enddate:
- return "{0} - {1}".format(self.startdate, self.enddate)
- return ""
-
- @property
- def title(self):
- return "Commitfest %s" % self.name
-
- @property
- def isopen(self):
- return self.status == self.STATUS_OPEN
-
- def __unicode__(self):
- return self.name
-
- class Meta:
- verbose_name_plural='Commitfests'
- ordering = ('-startdate',)
+ STATUS_FUTURE=1
+ STATUS_OPEN=2
+ STATUS_INPROGRESS=3
+ STATUS_CLOSED=4
+ _STATUS_CHOICES = (
+ (STATUS_FUTURE, 'Future'),
+ (STATUS_OPEN, 'Open'),
+ (STATUS_INPROGRESS, 'In Progress'),
+ (STATUS_CLOSED, 'Closed'),
+ )
+ name = models.CharField(max_length=100, blank=False, null=False, unique=True)
+ status = models.IntegerField(null=False, blank=False, default=1, choices=_STATUS_CHOICES)
+ startdate = models.DateField(blank=True, null=True)
+ enddate = models.DateField(blank=True, null=True)
+
+ @property
+ def statusstring(self):
+ return [v for k,v in self._STATUS_CHOICES if k==self.status][0]
+
+ @property
+ def periodstring(self):
+ if self.startdate and self.enddate:
+ return "{0} - {1}".format(self.startdate, self.enddate)
+ return ""
+
+ @property
+ def title(self):
+ return "Commitfest %s" % self.name
+
+ @property
+ def isopen(self):
+ return self.status == self.STATUS_OPEN
+
+ def __unicode__(self):
+ return self.name
+
+ class Meta:
+ verbose_name_plural='Commitfests'
+ ordering = ('-startdate',)
class Topic(models.Model):
- topic = models.CharField(max_length=100, blank=False, null=False)
+ topic = models.CharField(max_length=100, blank=False, null=False)
- def __unicode__(self):
- return self.topic
+ def __unicode__(self):
+ return self.topic
class Patch(models.Model, DiffableModel):
- name = models.CharField(max_length=500, blank=False, null=False, verbose_name='Description')
- topic = models.ForeignKey(Topic, blank=False, null=False)
-
- # One patch can be in multiple commitfests, if it has history
- commitfests = models.ManyToManyField(CommitFest, through='PatchOnCommitFest')
-
- # If there is a wiki page discussing this patch
- wikilink = models.URLField(blank=True, null=False, default='')
-
- # If there is a git repo about this patch
- gitlink = models.URLField(blank=True, null=False, default='')
-
- # Mailthreads are ManyToMany in the other direction
- #mailthreads_set = ...
-
- authors = models.ManyToManyField(User, related_name='patch_author', blank=True)
- reviewers = models.ManyToManyField(User, related_name='patch_reviewer', blank=True)
-
- committer = models.ForeignKey(Committer, blank=True, null=True)
-
- # Users to be notified when something happens
- subscribers = models.ManyToManyField(User, related_name='patch_subscriber', blank=True)
-
- # Datestamps for tracking activity
- created = models.DateTimeField(blank=False, null=False, auto_now_add=True)
- modified = models.DateTimeField(blank=False, null=False)
-
- # Materialize the last time an email was sent on any of the threads
- # that's attached to this message.
- lastmail = models.DateTimeField(blank=True, null=True)
-
- map_manytomany_for_diff = {
- 'authors': 'authors_string',
- 'reviewers': 'reviewers_string',
- }
- # Some accessors
- @property
- def authors_string(self):
- return ", ".join(["%s %s (%s)" % (a.first_name, a.last_name, a.username) for a in self.authors.all()])
-
- @property
- def reviewers_string(self):
- return ", ".join(["%s %s (%s)" % (a.first_name, a.last_name, a.username) for a in self.reviewers.all()])
-
- @property
- def history(self):
- # Need to wrap this in a function to make sure it calls
- # select_related() and doesn't generate a bazillion queries
- return self.patchhistory_set.select_related('by').all()
-
- def set_modified(self, newmod=None):
- # Set the modified date to newmod, but only if that's newer than
- # what's currently set. If newmod is not specified, use the
- # current timestamp.
- if not newmod:
- newmod = datetime.now()
- if not self.modified or newmod > self.modified:
- self.modified = newmod
-
- def update_lastmail(self):
- # Update the lastmail field, based on the newest email in any of
- # the threads attached to it.
- threads = list(self.mailthread_set.all())
- if len(threads) == 0:
- self.lastmail = None
- else:
- self.lastmail = max(threads, key=lambda t:t.latestmessage).latestmessage
-
- def __unicode__(self):
- return self.name
-
- class Meta:
- verbose_name_plural = 'patches'
+ name = models.CharField(max_length=500, blank=False, null=False, verbose_name='Description')
+ topic = models.ForeignKey(Topic, blank=False, null=False)
+
+ # One patch can be in multiple commitfests, if it has history
+ commitfests = models.ManyToManyField(CommitFest, through='PatchOnCommitFest')
+
+ # If there is a wiki page discussing this patch
+ wikilink = models.URLField(blank=True, null=False, default='')
+
+ # If there is a git repo about this patch
+ gitlink = models.URLField(blank=True, null=False, default='')
+
+ # Mailthreads are ManyToMany in the other direction
+ #mailthreads_set = ...
+
+ authors = models.ManyToManyField(User, related_name='patch_author', blank=True)
+ reviewers = models.ManyToManyField(User, related_name='patch_reviewer', blank=True)
+
+ committer = models.ForeignKey(Committer, blank=True, null=True)
+
+ # Users to be notified when something happens
+ subscribers = models.ManyToManyField(User, related_name='patch_subscriber', blank=True)
+
+ # Datestamps for tracking activity
+ created = models.DateTimeField(blank=False, null=False, auto_now_add=True)
+ modified = models.DateTimeField(blank=False, null=False)
+
+ # Materialize the last time an email was sent on any of the threads
+ # that's attached to this message.
+ lastmail = models.DateTimeField(blank=True, null=True)
+
+ map_manytomany_for_diff = {
+ 'authors': 'authors_string',
+ 'reviewers': 'reviewers_string',
+ }
+ # Some accessors
+ @property
+ def authors_string(self):
+ return ", ".join(["%s %s (%s)" % (a.first_name, a.last_name, a.username) for a in self.authors.all()])
+
+ @property
+ def reviewers_string(self):
+ return ", ".join(["%s %s (%s)" % (a.first_name, a.last_name, a.username) for a in self.reviewers.all()])
+
+ @property
+ def history(self):
+ # Need to wrap this in a function to make sure it calls
+ # select_related() and doesn't generate a bazillion queries
+ return self.patchhistory_set.select_related('by').all()
+
+ def set_modified(self, newmod=None):
+ # Set the modified date to newmod, but only if that's newer than
+ # what's currently set. If newmod is not specified, use the
+ # current timestamp.
+ if not newmod:
+ newmod = datetime.now()
+ if not self.modified or newmod > self.modified:
+ self.modified = newmod
+
+ def update_lastmail(self):
+ # Update the lastmail field, based on the newest email in any of
+ # the threads attached to it.
+ threads = list(self.mailthread_set.all())
+ if len(threads) == 0:
+ self.lastmail = None
+ else:
+ self.lastmail = max(threads, key=lambda t:t.latestmessage).latestmessage
+
+ def __unicode__(self):
+ return self.name
+
+ class Meta:
+ verbose_name_plural = 'patches'
class PatchOnCommitFest(models.Model):
- # NOTE! This is also matched by the commitfest_patchstatus table,
- # but we hardcoded it in here simply for performance reasons since
- # the data should be entirely static. (Yes, that's something we
- # might re-evaluate in the future)
- STATUS_REVIEW=1
- STATUS_AUTHOR=2
- STATUS_COMMITTER=3
- STATUS_COMMITTED=4
- STATUS_NEXT=5
- STATUS_REJECTED=6
- STATUS_RETURNED=7
- STATUS_WITHDRAWN=8
- _STATUS_CHOICES=(
- (STATUS_REVIEW, 'Needs review'),
- (STATUS_AUTHOR, 'Waiting on Author'),
- (STATUS_COMMITTER, 'Ready for Committer'),
- (STATUS_COMMITTED, 'Committed'),
- (STATUS_NEXT, 'Moved to next CF'),
- (STATUS_REJECTED, 'Rejected'),
- (STATUS_RETURNED, 'Returned with feedback'),
- (STATUS_WITHDRAWN, 'Withdrawn'),
- )
- _STATUS_LABELS=(
- (STATUS_REVIEW, 'default'),
- (STATUS_AUTHOR, 'primary'),
- (STATUS_COMMITTER, 'info'),
- (STATUS_COMMITTED, 'success'),
- (STATUS_NEXT, 'warning'),
- (STATUS_REJECTED, 'danger'),
- (STATUS_RETURNED, 'danger'),
- (STATUS_WITHDRAWN, 'danger'),
- )
- OPEN_STATUSES=[STATUS_REVIEW, STATUS_AUTHOR, STATUS_COMMITTER]
- OPEN_STATUS_CHOICES=[x for x in _STATUS_CHOICES if x[0] in OPEN_STATUSES]
-
- patch = models.ForeignKey(Patch, blank=False, null=False)
- commitfest = models.ForeignKey(CommitFest, blank=False, null=False)
- enterdate = models.DateTimeField(blank=False, null=False)
- leavedate = models.DateTimeField(blank=True, null=True)
-
- status = models.IntegerField(blank=False, null=False, default=STATUS_REVIEW, choices=_STATUS_CHOICES)
-
- @property
- def is_closed(self):
- return self.status not in self.OPEN_STATUSES
-
- @property
- def statusstring(self):
- return [v for k,v in self._STATUS_CHOICES if k==self.status][0]
-
- class Meta:
- unique_together = (('patch', 'commitfest',),)
- ordering = ('-commitfest__startdate', )
+ # NOTE! This is also matched by the commitfest_patchstatus table,
+ # but we hardcoded it in here simply for performance reasons since
+ # the data should be entirely static. (Yes, that's something we
+ # might re-evaluate in the future)
+ STATUS_REVIEW=1
+ STATUS_AUTHOR=2
+ STATUS_COMMITTER=3
+ STATUS_COMMITTED=4
+ STATUS_NEXT=5
+ STATUS_REJECTED=6
+ STATUS_RETURNED=7
+ STATUS_WITHDRAWN=8
+ _STATUS_CHOICES=(
+ (STATUS_REVIEW, 'Needs review'),
+ (STATUS_AUTHOR, 'Waiting on Author'),
+ (STATUS_COMMITTER, 'Ready for Committer'),
+ (STATUS_COMMITTED, 'Committed'),
+ (STATUS_NEXT, 'Moved to next CF'),
+ (STATUS_REJECTED, 'Rejected'),
+ (STATUS_RETURNED, 'Returned with feedback'),
+ (STATUS_WITHDRAWN, 'Withdrawn'),
+ )
+ _STATUS_LABELS=(
+ (STATUS_REVIEW, 'default'),
+ (STATUS_AUTHOR, 'primary'),
+ (STATUS_COMMITTER, 'info'),
+ (STATUS_COMMITTED, 'success'),
+ (STATUS_NEXT, 'warning'),
+ (STATUS_REJECTED, 'danger'),
+ (STATUS_RETURNED, 'danger'),
+ (STATUS_WITHDRAWN, 'danger'),
+ )
+ OPEN_STATUSES=[STATUS_REVIEW, STATUS_AUTHOR, STATUS_COMMITTER]
+ OPEN_STATUS_CHOICES=[x for x in _STATUS_CHOICES if x[0] in OPEN_STATUSES]
+
+ patch = models.ForeignKey(Patch, blank=False, null=False)
+ commitfest = models.ForeignKey(CommitFest, blank=False, null=False)
+ enterdate = models.DateTimeField(blank=False, null=False)
+ leavedate = models.DateTimeField(blank=True, null=True)
+
+ status = models.IntegerField(blank=False, null=False, default=STATUS_REVIEW, choices=_STATUS_CHOICES)
+
+ @property
+ def is_closed(self):
+ return self.status not in self.OPEN_STATUSES
+
+ @property
+ def statusstring(self):
+ return [v for k,v in self._STATUS_CHOICES if k==self.status][0]
+
+ class Meta:
+ unique_together = (('patch', 'commitfest',),)
+ ordering = ('-commitfest__startdate', )
class PatchHistory(models.Model):
- patch = models.ForeignKey(Patch, blank=False, null=False)
- date = models.DateTimeField(blank=False, null=False, auto_now_add=True)
- by = models.ForeignKey(User, blank=False, null=False)
- what = models.CharField(max_length=500, null=False, blank=False)
-
- @property
- def by_string(self):
- return "%s %s (%s)" % (self.by.first_name, self.by.last_name, self.by.username)
-
- def __unicode__(self):
- return "%s - %s" % (self.patch.name, self.date)
-
- class Meta:
- ordering = ('-date', )
-
- def save_and_notify(self, prevcommitter=None,
- prevreviewers=None, prevauthors=None):
- # Save this model, and then trigger notifications if there are any. There are
- # many different things that can trigger notifications, so try them all.
- self.save()
-
- recipients = []
- recipients.extend(self.patch.subscribers.all())
-
- # Current or previous committer wants all notifications
- try:
- if self.patch.committer and self.patch.committer.user.userprofile.notify_all_committer:
- recipients.append(self.patch.committer.user)
- except UserProfile.DoesNotExist:
- pass
-
- try:
- if prevcommitter and prevcommitter.user.userprofile.notify_all_committer:
- recipients.append(prevcommitter.user)
- except UserProfile.DoesNotExist:
- pass
-
- # Current or previous reviewers wants all notifications
- recipients.extend(self.patch.reviewers.filter(userprofile__notify_all_reviewer=True))
- if prevreviewers:
- # prevreviewers is a list
- recipients.extend(User.objects.filter(id__in=[p.id for p in prevreviewers], userprofile__notify_all_reviewer=True))
-
- # Current or previous authors wants all notifications
- recipients.extend(self.patch.authors.filter(userprofile__notify_all_author=True))
-
- for u in set(recipients):
- if u != self.by: # Don't notify for changes we make ourselves
- PendingNotification(history=self, user=u).save()
+ patch = models.ForeignKey(Patch, blank=False, null=False)
+ date = models.DateTimeField(blank=False, null=False, auto_now_add=True)
+ by = models.ForeignKey(User, blank=False, null=False)
+ what = models.CharField(max_length=500, null=False, blank=False)
+
+ @property
+ def by_string(self):
+ return "%s %s (%s)" % (self.by.first_name, self.by.last_name, self.by.username)
+
+ def __unicode__(self):
+ return "%s - %s" % (self.patch.name, self.date)
+
+ class Meta:
+ ordering = ('-date', )
+
+ def save_and_notify(self, prevcommitter=None,
+ prevreviewers=None, prevauthors=None):
+ # Save this model, and then trigger notifications if there are any. There are
+ # many different things that can trigger notifications, so try them all.
+ self.save()
+
+ recipients = []
+ recipients.extend(self.patch.subscribers.all())
+
+ # Current or previous committer wants all notifications
+ try:
+ if self.patch.committer and self.patch.committer.user.userprofile.notify_all_committer:
+ recipients.append(self.patch.committer.user)
+ except UserProfile.DoesNotExist:
+ pass
+
+ try:
+ if prevcommitter and prevcommitter.user.userprofile.notify_all_committer:
+ recipients.append(prevcommitter.user)
+ except UserProfile.DoesNotExist:
+ pass
+
+ # Current or previous reviewers wants all notifications
+ recipients.extend(self.patch.reviewers.filter(userprofile__notify_all_reviewer=True))
+ if prevreviewers:
+ # prevreviewers is a list
+ recipients.extend(User.objects.filter(id__in=[p.id for p in prevreviewers], userprofile__notify_all_reviewer=True))
+
+ # Current or previous authors wants all notifications
+ recipients.extend(self.patch.authors.filter(userprofile__notify_all_author=True))
+
+ for u in set(recipients):
+ if u != self.by: # Don't notify for changes we make ourselves
+ PendingNotification(history=self, user=u).save()
class MailThread(models.Model):
- # This class tracks mail threads from the main postgresql.org
- # mailinglist archives. For each thread, we store *one* messageid.
- # Using this messageid we can always query the archives for more
- # detailed information, which is done dynamically as the page
- # is loaded.
- # For threads in an active or future commitfest, we also poll
- # the archives to fetch "updated entries" at (ir)regular intervals
- # so we can keep track of when there was last a change on the
- # thread in question.
- messageid = models.CharField(max_length=1000, null=False, blank=False, unique=True)
- patches = models.ManyToManyField(Patch, blank=False)
- subject = models.CharField(max_length=500, null=False, blank=False)
- firstmessage = models.DateTimeField(null=False, blank=False)
- firstauthor = models.CharField(max_length=500, null=False, blank=False)
- latestmessage = models.DateTimeField(null=False, blank=False)
- latestauthor = models.CharField(max_length=500, null=False, blank=False)
- latestsubject = models.CharField(max_length=500, null=False, blank=False)
- latestmsgid = models.CharField(max_length=1000, null=False, blank=False)
-
- def __unicode__(self):
- return self.subject
-
- class Meta:
- ordering = ('firstmessage', )
+ # This class tracks mail threads from the main postgresql.org
+ # mailinglist archives. For each thread, we store *one* messageid.
+ # Using this messageid we can always query the archives for more
+ # detailed information, which is done dynamically as the page
+ # is loaded.
+ # For threads in an active or future commitfest, we also poll
+ # the archives to fetch "updated entries" at (ir)regular intervals
+ # so we can keep track of when there was last a change on the
+ # thread in question.
+ messageid = models.CharField(max_length=1000, null=False, blank=False, unique=True)
+ patches = models.ManyToManyField(Patch, blank=False)
+ subject = models.CharField(max_length=500, null=False, blank=False)
+ firstmessage = models.DateTimeField(null=False, blank=False)
+ firstauthor = models.CharField(max_length=500, null=False, blank=False)
+ latestmessage = models.DateTimeField(null=False, blank=False)
+ latestauthor = models.CharField(max_length=500, null=False, blank=False)
+ latestsubject = models.CharField(max_length=500, null=False, blank=False)
+ latestmsgid = models.CharField(max_length=1000, null=False, blank=False)
+
+ def __unicode__(self):
+ return self.subject
+
+ class Meta:
+ ordering = ('firstmessage', )
class MailThreadAttachment(models.Model):
- mailthread = models.ForeignKey(MailThread, null=False, blank=False)
- messageid = models.CharField(max_length=1000, null=False, blank=False)
- attachmentid = models.IntegerField(null=False, blank=False)
- filename = models.CharField(max_length=1000, null=False, blank=True)
- date = models.DateTimeField(null=False, blank=False)
- author = models.CharField(max_length=500, null=False, blank=False)
- ispatch = models.NullBooleanField()
-
- class Meta:
- ordering = ('-date',)
- unique_together = (('mailthread', 'messageid',), )
+ mailthread = models.ForeignKey(MailThread, null=False, blank=False)
+ messageid = models.CharField(max_length=1000, null=False, blank=False)
+ attachmentid = models.IntegerField(null=False, blank=False)
+ filename = models.CharField(max_length=1000, null=False, blank=True)
+ date = models.DateTimeField(null=False, blank=False)
+ author = models.CharField(max_length=500, null=False, blank=False)
+ ispatch = models.NullBooleanField()
+
+ class Meta:
+ ordering = ('-date',)
+ unique_together = (('mailthread', 'messageid',), )
class MailThreadAnnotation(models.Model):
- mailthread = models.ForeignKey(MailThread, null=False, blank=False)
- date = models.DateTimeField(null=False, blank=False, auto_now_add=True)
- user = models.ForeignKey(User, null=False, blank=False)
- msgid = models.CharField(max_length=1000, null=False, blank=False)
- annotationtext = models.TextField(null=False, blank=False, max_length=2000)
- mailsubject = models.CharField(max_length=500, null=False, blank=False)
- maildate = models.DateTimeField(null=False, blank=False)
- mailauthor = models.CharField(max_length=500, null=False, blank=False)
-
- @property
- def user_string(self):
- return "%s %s (%s)" % (self.user.first_name, self.user.last_name, self.user.username)
-
- class Meta:
- ordering = ('date', )
+ mailthread = models.ForeignKey(MailThread, null=False, blank=False)
+ date = models.DateTimeField(null=False, blank=False, auto_now_add=True)
+ user = models.ForeignKey(User, null=False, blank=False)
+ msgid = models.CharField(max_length=1000, null=False, blank=False)
+ annotationtext = models.TextField(null=False, blank=False, max_length=2000)
+ mailsubject = models.CharField(max_length=500, null=False, blank=False)
+ maildate = models.DateTimeField(null=False, blank=False)
+ mailauthor = models.CharField(max_length=500, null=False, blank=False)
+
+ @property
+ def user_string(self):
+ return "%s %s (%s)" % (self.user.first_name, self.user.last_name, self.user.username)
+
+ class Meta:
+ ordering = ('date', )
class PatchStatus(models.Model):
- status = models.IntegerField(null=False, blank=False, primary_key=True)
- statusstring = models.TextField(max_length=50, null=False, blank=False)
- sortkey = models.IntegerField(null=False, blank=False, default=10)
+ status = models.IntegerField(null=False, blank=False, primary_key=True)
+ statusstring = models.TextField(max_length=50, null=False, blank=False)
+ sortkey = models.IntegerField(null=False, blank=False, default=10)
class PendingNotification(models.Model):
- history = models.ForeignKey(PatchHistory, blank=False, null=False)
- user = models.ForeignKey(User, blank=False, null=False)
+ history = models.ForeignKey(PatchHistory, blank=False, null=False)
+ user = models.ForeignKey(User, blank=False, null=False)
diff --git a/pgcommitfest/commitfest/reports.py b/pgcommitfest/commitfest/reports.py
index f0d352e..4645856 100644
--- a/pgcommitfest/commitfest/reports.py
+++ b/pgcommitfest/commitfest/reports.py
@@ -8,12 +8,12 @@ from models import CommitFest
@login_required
def authorstats(request, cfid):
- cf = get_object_or_404(CommitFest, pk=cfid)
- if not request.user.is_staff:
- raise Http404("Only CF Managers can do that.")
+ cf = get_object_or_404(CommitFest, pk=cfid)
+ if not request.user.is_staff:
+ raise Http404("Only CF Managers can do that.")
- cursor = connection.cursor()
- cursor.execute("""WITH patches(id,name) AS (
+ cursor = connection.cursor()
+ cursor.execute("""WITH patches(id,name) AS (
SELECT p.id, name
FROM commitfest_patch p
INNER JOIN commitfest_patchoncommitfest poc ON poc.patch_id=p.id AND poc.commitfest_id=%(cid)s
@@ -35,12 +35,12 @@ FROM (authors FULL OUTER JOIN reviewers ON authors.userid=reviewers.userid)
INNER JOIN auth_user u ON u.id=COALESCE(authors.userid, reviewers.userid)
ORDER BY last_name, first_name
""", {
- 'cid': cf.id,
+ 'cid': cf.id,
})
- return render(request, 'report_authors.html', {
- 'cf': cf,
- 'report': cursor.fetchall(),
- 'title': 'Author stats',
- 'breadcrumbs': [{'title': cf.title, 'href': '/%s/' % cf.pk},],
- })
+ return render(request, 'report_authors.html', {
+ 'cf': cf,
+ 'report': cursor.fetchall(),
+ 'title': 'Author stats',
+ 'breadcrumbs': [{'title': cf.title, 'href': '/%s/' % cf.pk},],
+ })
diff --git a/pgcommitfest/commitfest/templatetags/commitfest.py b/pgcommitfest/commitfest/templatetags/commitfest.py
index b8f68e4..bde7d07 100644
--- a/pgcommitfest/commitfest/templatetags/commitfest.py
+++ b/pgcommitfest/commitfest/templatetags/commitfest.py
@@ -8,36 +8,36 @@ register = template.Library()
@register.filter(name='patchstatusstring')
@stringfilter
def patchstatusstring(value):
- i = int(value)
- return [v for k,v in PatchOnCommitFest._STATUS_CHOICES if k==i][0]
+ i = int(value)
+ return [v for k,v in PatchOnCommitFest._STATUS_CHOICES if k==i][0]
@register.filter(name='patchstatuslabel')
@stringfilter
def patchstatuslabel(value):
- i = int(value)
- return [v for k,v in PatchOnCommitFest._STATUS_LABELS if k==i][0]
+ i = int(value)
+ return [v for k,v in PatchOnCommitFest._STATUS_LABELS if k==i][0]
@register.filter(is_safe=True)
def label_class(value, arg):
- return value.label_tag(attrs={'class': arg})
+ return value.label_tag(attrs={'class': arg})
@register.filter(is_safe=True)
def field_class(value, arg):
- return value.as_widget(attrs={"class": arg})
+ return value.as_widget(attrs={"class": arg})
@register.filter(name='alertmap')
@stringfilter
def alertmap(value):
- if value == 'error':
- return 'alert-danger'
- elif value == 'warning':
- return 'alert-warning'
- elif value == 'success':
- return 'alert-success'
- else:
- return 'alert-info'
+ if value == 'error':
+ return 'alert-danger'
+ elif value == 'warning':
+ return 'alert-warning'
+ elif value == 'success':
+ return 'alert-success'
+ else:
+ return 'alert-info'
@register.filter(name='hidemail')
@stringfilter
def hidemail(value):
- return value.replace('@', ' at ')
+ return value.replace('@', ' at ')
diff --git a/pgcommitfest/commitfest/util.py b/pgcommitfest/commitfest/util.py
index ba09433..4698879 100644
--- a/pgcommitfest/commitfest/util.py
+++ b/pgcommitfest/commitfest/util.py
@@ -3,42 +3,42 @@ import django.db.models.fields.related
class DiffableModel(object):
- """
- Make it possible to diff a model.
+ """
+ Make it possible to diff a model.
"""
- def __init__(self, *args, **kwargs):
- super(DiffableModel, self).__init__(*args, **kwargs)
- self.__initial = self._dict
+ def __init__(self, *args, **kwargs):
+ super(DiffableModel, self).__init__(*args, **kwargs)
+ self.__initial = self._dict
- @property
- def diff(self):
- manytomanyfieldnames = [f.name for f in self._meta.many_to_many]
- d1 = self.__initial
- d2 = self._dict
- diffs = dict([(k, (v, d2[k])) for k, v in d1.items() if v != d2[k]])
- # Foreign key lookups
- for k,v in diffs.items():
- if type(self._meta.get_field(k)) is django.db.models.fields.related.ForeignKey:
- # If it's a foreign key, look up the name again on ourselves.
- # Since we only care about the *new* value, it's easy enough.
- diffs[k] = (v[0], getattr(self, k))
- # Many to many lookups
- if hasattr(self, 'map_manytomany_for_diff'):
- for k,v in diffs.items():
- if k in manytomanyfieldnames and self.map_manytomany_for_diff.has_key(k):
- # Try to show the display name instead here
- newvalue = getattr(self, self.map_manytomany_for_diff[k])
- diffs[k] = (v[0], newvalue)
- return diffs
+ @property
+ def diff(self):
+ manytomanyfieldnames = [f.name for f in self._meta.many_to_many]
+ d1 = self.__initial
+ d2 = self._dict
+ diffs = dict([(k, (v, d2[k])) for k, v in d1.items() if v != d2[k]])
+ # Foreign key lookups
+ for k,v in diffs.items():
+ if type(self._meta.get_field(k)) is django.db.models.fields.related.ForeignKey:
+ # If it's a foreign key, look up the name again on ourselves.
+ # Since we only care about the *new* value, it's easy enough.
+ diffs[k] = (v[0], getattr(self, k))
+ # Many to many lookups
+ if hasattr(self, 'map_manytomany_for_diff'):
+ for k,v in diffs.items():
+ if k in manytomanyfieldnames and self.map_manytomany_for_diff.has_key(k):
+ # Try to show the display name instead here
+ newvalue = getattr(self, self.map_manytomany_for_diff[k])
+ diffs[k] = (v[0], newvalue)
+ return diffs
- def save(self, *args, **kwargs):
- super(DiffableModel, self).save(*args, **kwargs)
- self.__initial = self._dict
+ def save(self, *args, **kwargs):
+ super(DiffableModel, self).save(*args, **kwargs)
+ self.__initial = self._dict
- @property
- def _dict(self):
- fields = [field.name for field in self._meta.fields]
- fields.extend([field.name for field in self._meta.many_to_many])
- return model_to_dict(self, fields=fields)
+ @property
+ def _dict(self):
+ fields = [field.name for field in self._meta.fields]
+ fields.extend([field.name for field in self._meta.many_to_many])
+ return model_to_dict(self, fields=fields)
diff --git a/pgcommitfest/commitfest/views.py b/pgcommitfest/commitfest/views.py
index 5f68895..705b172 100644
--- a/pgcommitfest/commitfest/views.py
+++ b/pgcommitfest/commitfest/views.py
@@ -27,170 +27,170 @@ from ajax import doAttachThread, refresh_single_thread
from feeds import ActivityFeed
def home(request):
- commitfests = list(CommitFest.objects.all())
- opencf = next((c for c in commitfests if c.status == CommitFest.STATUS_OPEN), None)
- inprogresscf = next((c for c in commitfests if c.status == CommitFest.STATUS_INPROGRESS), None)
+ commitfests = list(CommitFest.objects.all())
+ opencf = next((c for c in commitfests if c.status == CommitFest.STATUS_OPEN), None)
+ inprogresscf = next((c for c in commitfests if c.status == CommitFest.STATUS_INPROGRESS), None)
- return render(request, 'home.html', {
- 'commitfests': commitfests,
- 'opencf': opencf,
- 'inprogresscf': inprogresscf,
- 'title': 'Commitfests',
- 'header_activity': 'Activity log',
- 'header_activity_link': '/activity/',
- })
+ return render(request, 'home.html', {
+ 'commitfests': commitfests,
+ 'opencf': opencf,
+ 'inprogresscf': inprogresscf,
+ 'title': 'Commitfests',
+ 'header_activity': 'Activity log',
+ 'header_activity_link': '/activity/',
+ })
def activity(request, cfid=None, rss=None):
- # Number of notes to fetch
- if rss:
- num = 50
- else:
- num = 100
-
- if cfid:
- cf = get_object_or_404(CommitFest, pk=cfid)
-
- # Yes, we do string concatenation of the were clause. Because
- # we're evil. And also because the number has been verified
- # when looking up the cf itself, so nothing can be injected
- # there.
- extrafields = ''
- where = 'WHERE poc.commitfest_id={0}'.format(cf.id)
- else:
- cf = None
- extrafields = ',poc.commitfest_id AS cfid,cf.name AS cfname'
- where = ' INNER JOIN commitfest_commitfest cf ON cf.id=poc.commitfest_id'
-
- sql = "SELECT ph.date, auth_user.username AS by, ph.what, p.id AS patchid, p.name{0} FROM commitfest_patchhistory ph INNER JOIN commitfest_patch p ON ph.patch_id=p.id INNER JOIN auth_user on auth_user.id=ph.by_id INNER JOIN commitfest_patchoncommitfest poc ON poc.patch_id=p.id {1} ORDER BY ph.date DESC LIMIT {2}".format(extrafields,where, num)
-
- curs = connection.cursor()
- curs.execute(sql)
- activity = [dict(zip([c[0] for c in curs.description],r)) for r in curs.fetchall()]
-
- if rss:
- # Return RSS feed with these objects
- return ActivityFeed(activity, cf)(request)
- else:
- # Return regular webpage
- return render(request, 'activity.html', {
- 'commitfest': cf,
- 'activity': activity,
- 'title': cf and 'Commitfest activity' or 'Global Commitfest activity',
- 'rss_alternate': cf and '/{0}/activity.rss/'.format(cf.id) or '/activity.rss/',
- 'rss_alternate_title': 'PostgreSQL Commitfest Activity Log',
- 'breadcrumbs': cf and [{'title': cf.title, 'href': '/%s/' % cf.pk},] or None,
- })
+ # Number of notes to fetch
+ if rss:
+ num = 50
+ else:
+ num = 100
+
+ if cfid:
+ cf = get_object_or_404(CommitFest, pk=cfid)
+
+ # Yes, we do string concatenation of the were clause. Because
+ # we're evil. And also because the number has been verified
+ # when looking up the cf itself, so nothing can be injected
+ # there.
+ extrafields = ''
+ where = 'WHERE poc.commitfest_id={0}'.format(cf.id)
+ else:
+ cf = None
+ extrafields = ',poc.commitfest_id AS cfid,cf.name AS cfname'
+ where = ' INNER JOIN commitfest_commitfest cf ON cf.id=poc.commitfest_id'
+
+ sql = "SELECT ph.date, auth_user.username AS by, ph.what, p.id AS patchid, p.name{0} FROM commitfest_patchhistory ph INNER JOIN commitfest_patch p ON ph.patch_id=p.id INNER JOIN auth_user on auth_user.id=ph.by_id INNER JOIN commitfest_patchoncommitfest poc ON poc.patch_id=p.id {1} ORDER BY ph.date DESC LIMIT {2}".format(extrafields,where, num)
+
+ curs = connection.cursor()
+ curs.execute(sql)
+ activity = [dict(zip([c[0] for c in curs.description],r)) for r in curs.fetchall()]
+
+ if rss:
+ # Return RSS feed with these objects
+ return ActivityFeed(activity, cf)(request)
+ else:
+ # Return regular webpage
+ return render(request, 'activity.html', {
+ 'commitfest': cf,
+ 'activity': activity,
+ 'title': cf and 'Commitfest activity' or 'Global Commitfest activity',
+ 'rss_alternate': cf and '/{0}/activity.rss/'.format(cf.id) or '/activity.rss/',
+ 'rss_alternate_title': 'PostgreSQL Commitfest Activity Log',
+ 'breadcrumbs': cf and [{'title': cf.title, 'href': '/%s/' % cf.pk},] or None,
+ })
def redir(request, what):
- if what == 'open':
- cfs = list(CommitFest.objects.filter(status=CommitFest.STATUS_OPEN))
- elif what == 'inprogress':
- cfs = list(CommitFest.objects.filter(status=CommitFest.STATUS_INPROGRESS))
- else:
- raise Http404()
-
- if len(cfs) == 0:
- messages.warning(request, "No {0} commitfests exist, redirecting to startpage.".format(what))
- return HttpResponseRedirect("/")
- if len(cfs) != 1:
- messages.warning(request, "More than one {0} commitfest exists, redirecting to startpage instead.".format(what))
- return HttpResponseRedirect("/")
-
- return HttpResponseRedirect("/%s/" % cfs[0].id)
+ if what == 'open':
+ cfs = list(CommitFest.objects.filter(status=CommitFest.STATUS_OPEN))
+ elif what == 'inprogress':
+ cfs = list(CommitFest.objects.filter(status=CommitFest.STATUS_INPROGRESS))
+ else:
+ raise Http404()
+
+ if len(cfs) == 0:
+ messages.warning(request, "No {0} commitfests exist, redirecting to startpage.".format(what))
+ return HttpResponseRedirect("/")
+ if len(cfs) != 1:
+ messages.warning(request, "More than one {0} commitfest exists, redirecting to startpage instead.".format(what))
+ return HttpResponseRedirect("/")
+
+ return HttpResponseRedirect("/%s/" % cfs[0].id)
def commitfest(request, cfid):
- # Find ourselves
- cf = get_object_or_404(CommitFest, pk=cfid)
-
- # Build a dynamic filter based on the filtering options entered
- whereclauses = []
- whereparams = {}
- if request.GET.has_key('status') and request.GET['status'] != "-1":
- try:
- whereparams['status'] = int(request.GET['status'])
- whereclauses.append("poc.status=%(status)s")
- except ValueError:
- # int() failed -- so just ignore this filter
- pass
-
- if request.GET.has_key('author') and request.GET['author'] != "-1":
- if request.GET['author'] == '-2':
- whereclauses.append("NOT EXISTS (SELECT 1 FROM commitfest_patch_authors cpa WHERE cpa.patch_id=p.id)")
- elif request.GET['author'] == '-3':
- # Checking for "yourself" requires the user to be logged in!
- if not request.user.is_authenticated():
- return HttpResponseRedirect('%s?next=%s' % (settings.LOGIN_URL, request.path))
- whereclauses.append("EXISTS (SELECT 1 FROM commitfest_patch_authors cpa WHERE cpa.patch_id=p.id AND cpa.user_id=%(self)s)")
- whereparams['self'] = request.user.id
- else:
- try:
- whereparams['author'] = int(request.GET['author'])
- whereclauses.append("EXISTS (SELECT 1 FROM commitfest_patch_authors cpa WHERE cpa.patch_id=p.id AND cpa.user_id=%(author)s)")
- except ValueError:
- # int() failed -- so just ignore this filter
- pass
-
- if request.GET.has_key('reviewer') and request.GET['reviewer'] != "-1":
- if request.GET['reviewer'] == '-2':
- whereclauses.append("NOT EXISTS (SELECT 1 FROM commitfest_patch_reviewers cpr WHERE cpr.patch_id=p.id)")
- elif request.GET['reviewer'] == '-3':
- # Checking for "yourself" requires the user to be logged in!
- if not request.user.is_authenticated():
- return HttpResponseRedirect('%s?next=%s' % (settings.LOGIN_URL, request.path))
- whereclauses.append("EXISTS (SELECT 1 FROM commitfest_patch_reviewers cpr WHERE cpr.patch_id=p.id AND cpr.user_id=%(self)s)")
- whereparams['self'] = request.user.id
- else:
- try:
- whereparams['reviewer'] = int(request.GET['reviewer'])
- whereclauses.append("EXISTS (SELECT 1 FROM commitfest_patch_reviewers cpr WHERE cpr.patch_id=p.id AND cpr.user_id=%(reviewer)s)")
- except ValueError:
- # int() failed -- so just ignore this filter
- pass
-
- if request.GET.has_key('text') and request.GET['text'] != '':
- whereclauses.append("p.name ILIKE '%%' || %(txt)s || '%%'")
- whereparams['txt'] = request.GET['text']
-
- has_filter = len(whereclauses) > 0
-
- # Figure out custom ordering
- if request.GET.has_key('sortkey') and request.GET['sortkey']!='':
- try:
- sortkey=int(request.GET['sortkey'])
- except ValueError:
- sortkey=0
-
- if sortkey==1:
- orderby_str = 'modified, created'
- elif sortkey==2:
- orderby_str = 'lastmail, created'
- elif sortkey==3:
- orderby_str = 'num_cfs DESC, modified, created'
- else:
- orderby_str = 'p.id'
- sortkey=0
- else:
- orderby_str = 'topic, created'
- sortkey = 0
-
- if not has_filter and sortkey==0 and request.GET:
- # Redirect to get rid of the ugly url
- return HttpResponseRedirect('/%s/' % cf.id)
-
- if whereclauses:
- where_str = 'AND ({0})'.format(' AND '.join(whereclauses))
- else:
- where_str = ''
- params = {
- 'cid': cf.id,
- 'openstatuses': PatchOnCommitFest.OPEN_STATUSES,
- }
- params.update(whereparams)
-
- # Let's not overload the poor django ORM
- curs = connection.cursor()
- curs.execute("""SELECT p.id, p.name, poc.status, p.created, p.modified, p.lastmail, committer.username AS committer, t.topic,
+ # Find ourselves
+ cf = get_object_or_404(CommitFest, pk=cfid)
+
+ # Build a dynamic filter based on the filtering options entered
+ whereclauses = []
+ whereparams = {}
+ if request.GET.has_key('status') and request.GET['status'] != "-1":
+ try:
+ whereparams['status'] = int(request.GET['status'])
+ whereclauses.append("poc.status=%(status)s")
+ except ValueError:
+ # int() failed -- so just ignore this filter
+ pass
+
+ if request.GET.has_key('author') and request.GET['author'] != "-1":
+ if request.GET['author'] == '-2':
+ whereclauses.append("NOT EXISTS (SELECT 1 FROM commitfest_patch_authors cpa WHERE cpa.patch_id=p.id)")
+ elif request.GET['author'] == '-3':
+ # Checking for "yourself" requires the user to be logged in!
+ if not request.user.is_authenticated():
+ return HttpResponseRedirect('%s?next=%s' % (settings.LOGIN_URL, request.path))
+ whereclauses.append("EXISTS (SELECT 1 FROM commitfest_patch_authors cpa WHERE cpa.patch_id=p.id AND cpa.user_id=%(self)s)")
+ whereparams['self'] = request.user.id
+ else:
+ try:
+ whereparams['author'] = int(request.GET['author'])
+ whereclauses.append("EXISTS (SELECT 1 FROM commitfest_patch_authors cpa WHERE cpa.patch_id=p.id AND cpa.user_id=%(author)s)")
+ except ValueError:
+ # int() failed -- so just ignore this filter
+ pass
+
+ if request.GET.has_key('reviewer') and request.GET['reviewer'] != "-1":
+ if request.GET['reviewer'] == '-2':
+ whereclauses.append("NOT EXISTS (SELECT 1 FROM commitfest_patch_reviewers cpr WHERE cpr.patch_id=p.id)")
+ elif request.GET['reviewer'] == '-3':
+ # Checking for "yourself" requires the user to be logged in!
+ if not request.user.is_authenticated():
+ return HttpResponseRedirect('%s?next=%s' % (settings.LOGIN_URL, request.path))
+ whereclauses.append("EXISTS (SELECT 1 FROM commitfest_patch_reviewers cpr WHERE cpr.patch_id=p.id AND cpr.user_id=%(self)s)")
+ whereparams['self'] = request.user.id
+ else:
+ try:
+ whereparams['reviewer'] = int(request.GET['reviewer'])
+ whereclauses.append("EXISTS (SELECT 1 FROM commitfest_patch_reviewers cpr WHERE cpr.patch_id=p.id AND cpr.user_id=%(reviewer)s)")
+ except ValueError:
+ # int() failed -- so just ignore this filter
+ pass
+
+ if request.GET.has_key('text') and request.GET['text'] != '':
+ whereclauses.append("p.name ILIKE '%%' || %(txt)s || '%%'")
+ whereparams['txt'] = request.GET['text']
+
+ has_filter = len(whereclauses) > 0
+
+ # Figure out custom ordering
+ if request.GET.has_key('sortkey') and request.GET['sortkey']!='':
+ try:
+ sortkey=int(request.GET['sortkey'])
+ except ValueError:
+ sortkey=0
+
+ if sortkey==1:
+ orderby_str = 'modified, created'
+ elif sortkey==2:
+ orderby_str = 'lastmail, created'
+ elif sortkey==3:
+ orderby_str = 'num_cfs DESC, modified, created'
+ else:
+ orderby_str = 'p.id'
+ sortkey=0
+ else:
+ orderby_str = 'topic, created'
+ sortkey = 0
+
+ if not has_filter and sortkey==0 and request.GET:
+ # Redirect to get rid of the ugly url
+ return HttpResponseRedirect('/%s/' % cf.id)
+
+ if whereclauses:
+ where_str = 'AND ({0})'.format(' AND '.join(whereclauses))
+ else:
+ where_str = ''
+ params = {
+ 'cid': cf.id,
+ 'openstatuses': PatchOnCommitFest.OPEN_STATUSES,
+ }
+ params.update(whereparams)
+
+ # Let's not overload the poor django ORM
+ curs = connection.cursor()
+ curs.execute("""SELECT p.id, p.name, poc.status, p.created, p.modified, p.lastmail, committer.username AS committer, t.topic,
(poc.status=ANY(%(openstatuses)s)) AS is_open,
(SELECT string_agg(first_name || ' ' || last_name || ' (' || username || ')', ', ') FROM auth_user INNER JOIN commitfest_patch_authors cpa ON cpa.user_id=auth_user.id WHERE cpa.patch_id=p.id) AS author_names,
(SELECT string_agg(first_name || ' ' || last_name || ' (' || username || ')', ', ') FROM auth_user INNER JOIN commitfest_patch_reviewers cpr ON cpr.user_id=auth_user.id WHERE cpr.patch_id=p.id) AS reviewer_names,
@@ -202,533 +202,533 @@ LEFT JOIN auth_user committer ON committer.id=p.committer_id
WHERE poc.commitfest_id=%(cid)s {0}
GROUP BY p.id, poc.id, committer.id, t.id
ORDER BY is_open DESC, {1}""".format(where_str, orderby_str), params)
- patches = [dict(zip([col[0] for col in curs.description], row)) for row in curs.fetchall()]
-
- # Generate patch status summary.
- curs = connection.cursor()
- curs.execute("SELECT ps.status, ps.statusstring, count(*) FROM commitfest_patchoncommitfest poc INNER JOIN commitfest_patchstatus ps ON ps.status=poc.status WHERE commitfest_id=%(id)s GROUP BY ps.status ORDER BY ps.sortkey", {
- 'id': cf.id,
- })
- statussummary = curs.fetchall()
- statussummary.append([-1, 'Total', sum((r[2] for r in statussummary))])
-
- # Generates a fairly expensive query, which we shouldn't do unless
- # the user is logged in. XXX: Figure out how to avoid doing that..
- form = CommitFestFilterForm(cf, request.GET)
-
- return render(request, 'commitfest.html', {
- 'cf': cf,
- 'form': form,
- 'patches': patches,
- 'statussummary': statussummary,
- 'has_filter': has_filter,
- 'title': cf.title,
- 'grouping': sortkey==0,
- 'sortkey': sortkey,
- 'openpatchids': [p['id'] for p in patches if p['is_open']],
- 'header_activity': 'Activity log',
- 'header_activity_link': 'activity/',
- })
+ patches = [dict(zip([col[0] for col in curs.description], row)) for row in curs.fetchall()]
+
+ # Generate patch status summary.
+ curs = connection.cursor()
+ curs.execute("SELECT ps.status, ps.statusstring, count(*) FROM commitfest_patchoncommitfest poc INNER JOIN commitfest_patchstatus ps ON ps.status=poc.status WHERE commitfest_id=%(id)s GROUP BY ps.status ORDER BY ps.sortkey", {
+ 'id': cf.id,
+ })
+ statussummary = curs.fetchall()
+ statussummary.append([-1, 'Total', sum((r[2] for r in statussummary))])
+
+ # Generates a fairly expensive query, which we shouldn't do unless
+ # the user is logged in. XXX: Figure out how to avoid doing that..
+ form = CommitFestFilterForm(cf, request.GET)
+
+ return render(request, 'commitfest.html', {
+ 'cf': cf,
+ 'form': form,
+ 'patches': patches,
+ 'statussummary': statussummary,
+ 'has_filter': has_filter,
+ 'title': cf.title,
+ 'grouping': sortkey==0,
+ 'sortkey': sortkey,
+ 'openpatchids': [p['id'] for p in patches if p['is_open']],
+ 'header_activity': 'Activity log',
+ 'header_activity_link': 'activity/',
+ })
def global_search(request):
- if not request.GET.has_key('searchterm'):
- return HttpResponseRedirect('/')
- searchterm = request.GET['searchterm']
+ if not request.GET.has_key('searchterm'):
+ return HttpResponseRedirect('/')
+ searchterm = request.GET['searchterm']
- patches = Patch.objects.select_related().filter(name__icontains=searchterm).order_by('created',)
+ patches = Patch.objects.select_related().filter(name__icontains=searchterm).order_by('created',)
- return render(request, 'patchsearch.html', {
- 'patches': patches,
- 'title': 'Patch search results',
- })
+ return render(request, 'patchsearch.html', {
+ 'patches': patches,
+ 'title': 'Patch search results',
+ })
def patch(request, cfid, patchid):
- cf = get_object_or_404(CommitFest, pk=cfid)
- patch = get_object_or_404(Patch.objects.select_related(), pk=patchid, commitfests=cf)
- patch_commitfests = PatchOnCommitFest.objects.select_related('commitfest').filter(patch=patch).order_by('-commitfest__startdate')
- committers = Committer.objects.filter(active=True).order_by('user__last_name', 'user__first_name')
-
- #XXX: this creates a session, so find a smarter way. Probably handle
- #it in the callback and just ask the user then?
- if request.user.is_authenticated():
- committer = [c for c in committers if c.user==request.user]
- if len(committer) > 0:
- is_committer= True
- is_this_committer = committer[0] == patch.committer
- else:
- is_committer = is_this_committer = False
-
- is_reviewer = request.user in patch.reviewers.all()
- is_subscribed = patch.subscribers.filter(id=request.user.id).exists()
- else:
- is_committer = False
- is_this_committer = False
- is_reviewer = False
- is_subscribed = False
-
- return render(request, 'patch.html', {
- 'cf': cf,
- 'patch': patch,
- 'patch_commitfests': patch_commitfests,
- 'is_committer': is_committer,
- 'is_this_committer': is_this_committer,
- 'is_reviewer': is_reviewer,
- 'is_subscribed': is_subscribed,
- 'committers': committers,
- 'attachnow': request.GET.has_key('attachthreadnow'),
- 'title': patch.name,
- 'breadcrumbs': [{'title': cf.title, 'href': '/%s/' % cf.pk},],
- })
+ cf = get_object_or_404(CommitFest, pk=cfid)
+ patch = get_object_or_404(Patch.objects.select_related(), pk=patchid, commitfests=cf)
+ patch_commitfests = PatchOnCommitFest.objects.select_related('commitfest').filter(patch=patch).order_by('-commitfest__startdate')
+ committers = Committer.objects.filter(active=True).order_by('user__last_name', 'user__first_name')
+
+ #XXX: this creates a session, so find a smarter way. Probably handle
+ #it in the callback and just ask the user then?
+ if request.user.is_authenticated():
+ committer = [c for c in committers if c.user==request.user]
+ if len(committer) > 0:
+ is_committer= True
+ is_this_committer = committer[0] == patch.committer
+ else:
+ is_committer = is_this_committer = False
+
+ is_reviewer = request.user in patch.reviewers.all()
+ is_subscribed = patch.subscribers.filter(id=request.user.id).exists()
+ else:
+ is_committer = False
+ is_this_committer = False
+ is_reviewer = False
+ is_subscribed = False
+
+ return render(request, 'patch.html', {
+ 'cf': cf,
+ 'patch': patch,
+ 'patch_commitfests': patch_commitfests,
+ 'is_committer': is_committer,
+ 'is_this_committer': is_this_committer,
+ 'is_reviewer': is_reviewer,
+ 'is_subscribed': is_subscribed,
+ 'committers': committers,
+ 'attachnow': request.GET.has_key('attachthreadnow'),
+ 'title': patch.name,
+ 'breadcrumbs': [{'title': cf.title, 'href': '/%s/' % cf.pk},],
+ })
@login_required
@transaction.atomic
def patchform(request, cfid, patchid):
- cf = get_object_or_404(CommitFest, pk=cfid)
- patch = get_object_or_404(Patch, pk=patchid, commitfests=cf)
-
- prevreviewers = list(patch.reviewers.all())
- prevauthors = list(patch.authors.all())
- prevcommitter = patch.committer
-
- if request.method == 'POST':
- form = PatchForm(data=request.POST, instance=patch)
- if form.is_valid():
- # Some fields need to be set when creating a new one
- r = form.save(commit=False)
- # Fill out any locked fields here
-
- form.save_m2m()
-
- # Track all changes
- for field, values in r.diff.items():
- PatchHistory(patch=patch, by=request.user, what='Changed %s to %s' % (field, values[1])).save_and_notify(prevcommitter=prevcommitter, prevreviewers=prevreviewers, prevauthors=prevauthors)
- r.set_modified()
- r.save()
- return HttpResponseRedirect('../../%s/' % r.pk)
- # Else fall through and render the page again
- else:
- form = PatchForm(instance=patch)
-
- return render(request, 'base_form.html', {
- 'cf': cf,
- 'form': form,
- 'patch': patch,
- 'title': 'Edit patch',
- 'breadcrumbs': [{'title': cf.title, 'href': '/%s/' % cf.pk},
- {'title': 'View patch', 'href': '/%s/%s/' % (cf.pk, patch.pk)}],
- })
+ cf = get_object_or_404(CommitFest, pk=cfid)
+ patch = get_object_or_404(Patch, pk=patchid, commitfests=cf)
+
+ prevreviewers = list(patch.reviewers.all())
+ prevauthors = list(patch.authors.all())
+ prevcommitter = patch.committer
+
+ if request.method == 'POST':
+ form = PatchForm(data=request.POST, instance=patch)
+ if form.is_valid():
+ # Some fields need to be set when creating a new one
+ r = form.save(commit=False)
+ # Fill out any locked fields here
+
+ form.save_m2m()
+
+ # Track all changes
+ for field, values in r.diff.items():
+ PatchHistory(patch=patch, by=request.user, what='Changed %s to %s' % (field, values[1])).save_and_notify(prevcommitter=prevcommitter, prevreviewers=prevreviewers, prevauthors=prevauthors)
+ r.set_modified()
+ r.save()
+ return HttpResponseRedirect('../../%s/' % r.pk)
+ # Else fall through and render the page again
+ else:
+ form = PatchForm(instance=patch)
+
+ return render(request, 'base_form.html', {
+ 'cf': cf,
+ 'form': form,
+ 'patch': patch,
+ 'title': 'Edit patch',
+ 'breadcrumbs': [{'title': cf.title, 'href': '/%s/' % cf.pk},
+ {'title': 'View patch', 'href': '/%s/%s/' % (cf.pk, patch.pk)}],
+ })
@login_required
@transaction.atomic
def newpatch(request, cfid):
- cf = get_object_or_404(CommitFest, pk=cfid)
- if not cf.status == CommitFest.STATUS_OPEN and not request.user.is_staff:
- raise Http404("This commitfest is not open!")
-
- if request.method == 'POST':
- form = NewPatchForm(data=request.POST)
- if form.is_valid():
- patch = Patch(name=form.cleaned_data['name'],
- topic=form.cleaned_data['topic'])
- patch.set_modified()
- patch.save()
- poc = PatchOnCommitFest(patch=patch, commitfest=cf, enterdate=datetime.now())
- poc.save()
- PatchHistory(patch=patch, by=request.user, what='Created patch record').save()
- # Now add the thread
- try:
- doAttachThread(cf, patch, form.cleaned_data['threadmsgid'], request.user)
- return HttpResponseRedirect("/%s/%s/edit/" % (cf.id, patch.id))
- except Http404:
- # Thread not found!
- # This is a horrible breakage of API layers
- form._errors['threadmsgid'] = form.error_class(('Selected thread did not exist in the archives',))
- except Exception:
- form._errors['threadmsgid'] = form.error_class(('An error occurred looking up the thread in the archives.',))
- # In this case, we have created a patch - delete it. This causes a agp in id's, but it should
- # not happen very often. If we successfully attached to it, we will have already returned.
- patch.delete()
- else:
- form = NewPatchForm()
-
- return render(request, 'base_form.html', {
- 'form': form,
- 'title': 'New patch',
- 'breadcrumbs': [{'title': cf.title, 'href': '/%s/' % cf.pk},],
- 'savebutton': 'Create patch',
- 'threadbrowse': True,
- })
+ cf = get_object_or_404(CommitFest, pk=cfid)
+ if not cf.status == CommitFest.STATUS_OPEN and not request.user.is_staff:
+ raise Http404("This commitfest is not open!")
+
+ if request.method == 'POST':
+ form = NewPatchForm(data=request.POST)
+ if form.is_valid():
+ patch = Patch(name=form.cleaned_data['name'],
+ topic=form.cleaned_data['topic'])
+ patch.set_modified()
+ patch.save()
+ poc = PatchOnCommitFest(patch=patch, commitfest=cf, enterdate=datetime.now())
+ poc.save()
+ PatchHistory(patch=patch, by=request.user, what='Created patch record').save()
+ # Now add the thread
+ try:
+ doAttachThread(cf, patch, form.cleaned_data['threadmsgid'], request.user)
+ return HttpResponseRedirect("/%s/%s/edit/" % (cf.id, patch.id))
+ except Http404:
+ # Thread not found!
+ # This is a horrible breakage of API layers
+ form._errors['threadmsgid'] = form.error_class(('Selected thread did not exist in the archives',))
+ except Exception:
+ form._errors['threadmsgid'] = form.error_class(('An error occurred looking up the thread in the archives.',))
+ # In this case, we have created a patch - delete it. This causes a agp in id's, but it should
+ # not happen very often. If we successfully attached to it, we will have already returned.
+ patch.delete()
+ else:
+ form = NewPatchForm()
+
+ return render(request, 'base_form.html', {
+ 'form': form,
+ 'title': 'New patch',
+ 'breadcrumbs': [{'title': cf.title, 'href': '/%s/' % cf.pk},],
+ 'savebutton': 'Create patch',
+ 'threadbrowse': True,
+ })
def _review_status_string(reviewstatus):
- if '0' in reviewstatus:
- if '1' in reviewstatus:
- return "tested, passed"
- else:
- return "tested, failed"
- else:
- return "not tested"
+ if '0' in reviewstatus:
+ if '1' in reviewstatus:
+ return "tested, passed"
+ else:
+ return "tested, failed"
+ else:
+ return "not tested"
@login_required
@transaction.atomic
def comment(request, cfid, patchid, what):
- cf = get_object_or_404(CommitFest, pk=cfid)
- patch = get_object_or_404(Patch, pk=patchid)
- poc = get_object_or_404(PatchOnCommitFest, patch=patch, commitfest=cf)
- is_review = (what=='review')
-
- if poc.is_closed:
- # We allow modification of patches in closed CFs *only* if it's the
- # last CF that the patch is part of. If it's part of another CF, that
- # is later than this one, tell the user to go there instead.
- lastcf = PatchOnCommitFest.objects.filter(patch=patch).order_by('-commitfest__startdate')[0]
- if poc != lastcf:
- messages.add_message(request, messages.INFO, "The status of this patch cannot be changed in this commitfest. You must modify it in the one where it's open!")
- return HttpResponseRedirect('..')
-
- if request.method == 'POST':
- try:
- form = CommentForm(patch, poc, is_review, data=request.POST)
- except Exception, e:
- messages.add_message(request, messages.ERROR, "Failed to build list of response options from the archives: %s" % e)
- return HttpResponseRedirect('/%s/%s/' % (cf.id, patch.id))
-
- if form.is_valid():
- if is_review:
- txt = "The following review has been posted through the commitfest application:\n%s\n\n%s" % (
- "\n".join(["%-25s %s" % (f.label + ':', _review_status_string(form.cleaned_data[fn])) for (fn, f) in form.fields.items() if fn.startswith('review_')]),
- form.cleaned_data['message']
- )
- else:
- txt = form.cleaned_data['message']
-
- if int(form.cleaned_data['newstatus']) != poc.status:
- poc.status = int(form.cleaned_data['newstatus'])
- poc.save()
- PatchHistory(patch=poc.patch, by=request.user, what='New status: %s' % poc.statusstring).save_and_notify()
- txt += "\n\nThe new status of this patch is: %s\n" % poc.statusstring
-
- msg = MIMEText(txt, _charset='utf-8')
-
- if form.thread.subject.startswith('Re:'):
- msg['Subject'] = form.thread.subject
- else:
- msg['Subject'] = 'Re: %s' % form.thread.subject
-
- msg['To'] = settings.HACKERS_EMAIL
- msg['From'] = UserWrapper(request.user).encoded_email_header
-
- # CC the authors of a patch, if there are any
- authors = list(patch.authors.all())
- if len(authors):
- msg['Cc'] = ", ".join([UserWrapper(a).encoded_email_header for a in authors])
-
- msg['Date'] = formatdate(localtime=True)
- msg['User-Agent'] = 'pgcommitfest'
- msg['X-cfsender'] = request.user.username
- msg['In-Reply-To'] = '<%s>' % form.respid
- # We just add the "top" messageid and the one we're responding to.
- # This along with in-reply-to should indicate clearly enough where
- # in the thread the message belongs.
- msg['References'] = '<%s> <%s>' % (form.thread.messageid, form.respid)
- msg['Message-ID'] = make_msgid('pgcf')
-
- uw = UserWrapper(request.user)
- msgstring = msg.as_string()
- send_mail(uw.email, settings.HACKERS_EMAIL, msgstring)
- for a in authors:
- # Actually send a copy directly to the author. Just setting the Cc field doesn't
- # make it deliver the email...
- send_mail(uw.email, UserWrapper(a).email, msgstring)
-
- PatchHistory(patch=patch, by=request.user, what='Posted %s with messageid %s' % (what, msg['Message-ID'])).save()
-
- messages.add_message(request, messages.INFO, "Your email has been queued for %s, and will be sent within a few minutes." % (settings.HACKERS_EMAIL))
-
- return HttpResponseRedirect('/%s/%s/' % (cf.id, patch.id))
- else:
- try:
- form = CommentForm(patch, poc, is_review)
- except Exception, e:
- messages.add_message(request, messages.ERROR, "Failed to build list of response options from the archives: %s" % e)
- return HttpResponseRedirect('/%s/%s/' % (cf.id, patch.id))
-
- return render(request, 'base_form.html', {
- 'cf': cf,
- 'form': form,
- 'patch': patch,
- 'extraformclass': 'patchcommentform',
- 'breadcrumbs': [{'title': cf.title, 'href': '/%s/' % cf.pk},
- {'title': 'View patch', 'href': '/%s/%s/' % (cf.pk, patch.pk)}],
- 'title': "Add %s" % what,
- 'note': '<b>Note!</b> This form will generate an email to the public mailinglist <i>%s</i>, with sender set to <i>%s</i>!<br/>Please ensure that the email settings for your domain (<a href="https://en.wikipedia.org/wiki/DomainKeys_Identified_Mail" target="_blank">DKIM</a>, <a href="https://en.wikipedia.org/wiki/SPF" target="_blank">SPF</a>) allow emails from external sources.' % (settings.HACKERS_EMAIL, UserWrapper(request.user).email),
- 'savebutton': 'Send %s' % what,
- })
+ cf = get_object_or_404(CommitFest, pk=cfid)
+ patch = get_object_or_404(Patch, pk=patchid)
+ poc = get_object_or_404(PatchOnCommitFest, patch=patch, commitfest=cf)
+ is_review = (what=='review')
+
+ if poc.is_closed:
+ # We allow modification of patches in closed CFs *only* if it's the
+ # last CF that the patch is part of. If it's part of another CF, that
+ # is later than this one, tell the user to go there instead.
+ lastcf = PatchOnCommitFest.objects.filter(patch=patch).order_by('-commitfest__startdate')[0]
+ if poc != lastcf:
+ messages.add_message(request, messages.INFO, "The status of this patch cannot be changed in this commitfest. You must modify it in the one where it's open!")
+ return HttpResponseRedirect('..')
+
+ if request.method == 'POST':
+ try:
+ form = CommentForm(patch, poc, is_review, data=request.POST)
+ except Exception, e:
+ messages.add_message(request, messages.ERROR, "Failed to build list of response options from the archives: %s" % e)
+ return HttpResponseRedirect('/%s/%s/' % (cf.id, patch.id))
+
+ if form.is_valid():
+ if is_review:
+ txt = "The following review has been posted through the commitfest application:\n%s\n\n%s" % (
+ "\n".join(["%-25s %s" % (f.label + ':', _review_status_string(form.cleaned_data[fn])) for (fn, f) in form.fields.items() if fn.startswith('review_')]),
+ form.cleaned_data['message']
+ )
+ else:
+ txt = form.cleaned_data['message']
+
+ if int(form.cleaned_data['newstatus']) != poc.status:
+ poc.status = int(form.cleaned_data['newstatus'])
+ poc.save()
+ PatchHistory(patch=poc.patch, by=request.user, what='New status: %s' % poc.statusstring).save_and_notify()
+ txt += "\n\nThe new status of this patch is: %s\n" % poc.statusstring
+
+ msg = MIMEText(txt, _charset='utf-8')
+
+ if form.thread.subject.startswith('Re:'):
+ msg['Subject'] = form.thread.subject
+ else:
+ msg['Subject'] = 'Re: %s' % form.thread.subject
+
+ msg['To'] = settings.HACKERS_EMAIL
+ msg['From'] = UserWrapper(request.user).encoded_email_header
+
+ # CC the authors of a patch, if there are any
+ authors = list(patch.authors.all())
+ if len(authors):
+ msg['Cc'] = ", ".join([UserWrapper(a).encoded_email_header for a in authors])
+
+ msg['Date'] = formatdate(localtime=True)
+ msg['User-Agent'] = 'pgcommitfest'
+ msg['X-cfsender'] = request.user.username
+ msg['In-Reply-To'] = '<%s>' % form.respid
+ # We just add the "top" messageid and the one we're responding to.
+ # This along with in-reply-to should indicate clearly enough where
+ # in the thread the message belongs.
+ msg['References'] = '<%s> <%s>' % (form.thread.messageid, form.respid)
+ msg['Message-ID'] = make_msgid('pgcf')
+
+ uw = UserWrapper(request.user)
+ msgstring = msg.as_string()
+ send_mail(uw.email, settings.HACKERS_EMAIL, msgstring)
+ for a in authors:
+ # Actually send a copy directly to the author. Just setting the Cc field doesn't
+ # make it deliver the email...
+ send_mail(uw.email, UserWrapper(a).email, msgstring)
+
+ PatchHistory(patch=patch, by=request.user, what='Posted %s with messageid %s' % (what, msg['Message-ID'])).save()
+
+ messages.add_message(request, messages.INFO, "Your email has been queued for %s, and will be sent within a few minutes." % (settings.HACKERS_EMAIL))
+
+ return HttpResponseRedirect('/%s/%s/' % (cf.id, patch.id))
+ else:
+ try:
+ form = CommentForm(patch, poc, is_review)
+ except Exception, e:
+ messages.add_message(request, messages.ERROR, "Failed to build list of response options from the archives: %s" % e)
+ return HttpResponseRedirect('/%s/%s/' % (cf.id, patch.id))
+
+ return render(request, 'base_form.html', {
+ 'cf': cf,
+ 'form': form,
+ 'patch': patch,
+ 'extraformclass': 'patchcommentform',
+ 'breadcrumbs': [{'title': cf.title, 'href': '/%s/' % cf.pk},
+ {'title': 'View patch', 'href': '/%s/%s/' % (cf.pk, patch.pk)}],
+ 'title': "Add %s" % what,
+ 'note': '<b>Note!</b> This form will generate an email to the public mailinglist <i>%s</i>, with sender set to <i>%s</i>!<br/>Please ensure that the email settings for your domain (<a href="https://en.wikipedia.org/wiki/DomainKeys_Identified_Mail" target="_blank">DKIM</a>, <a href="https://en.wikipedia.org/wiki/SPF" target="_blank">SPF</a>) allow emails from external sources.' % (settings.HACKERS_EMAIL, UserWrapper(request.user).email),
+ 'savebutton': 'Send %s' % what,
+ })
@login_required
@transaction.atomic
def status(request, cfid, patchid, status):
- poc = get_object_or_404(PatchOnCommitFest.objects.select_related(), commitfest__id=cfid, patch__id=patchid)
-
- if poc.is_closed:
- # We allow modification of patches in closed CFs *only* if it's the
- # last CF that the patch is part of. If it's part of another CF, that
- # is later than this one, tell the user to go there instead.
- lastcf = PatchOnCommitFest.objects.filter(patch__id=patchid).order_by('-commitfest__startdate')[0]
- if poc != lastcf:
- messages.add_message(request, messages.INFO, "The status of this patch cannot be changed in this commitfest. You must modify it in the one where it's open!")
- return HttpResponseRedirect('/%s/%s/' % (poc.commitfest.id, poc.patch.id))
-
- if status == 'review':
- newstatus = PatchOnCommitFest.STATUS_REVIEW
- elif status == 'author':
- newstatus = PatchOnCommitFest.STATUS_AUTHOR
- elif status == 'committer':
- newstatus = PatchOnCommitFest.STATUS_COMMITTER
- else:
- raise Exception("Can't happen")
-
- if newstatus != poc.status:
- # Only save it if something actually changed
- poc.status = newstatus
- poc.patch.set_modified()
- poc.patch.save()
- poc.save()
-
- PatchHistory(patch=poc.patch, by=request.user, what='New status: %s' % poc.statusstring).save_and_notify()
-
- return HttpResponseRedirect('/%s/%s/' % (poc.commitfest.id, poc.patch.id))
+ poc = get_object_or_404(PatchOnCommitFest.objects.select_related(), commitfest__id=cfid, patch__id=patchid)
+
+ if poc.is_closed:
+ # We allow modification of patches in closed CFs *only* if it's the
+ # last CF that the patch is part of. If it's part of another CF, that
+ # is later than this one, tell the user to go there instead.
+ lastcf = PatchOnCommitFest.objects.filter(patch__id=patchid).order_by('-commitfest__startdate')[0]
+ if poc != lastcf:
+ messages.add_message(request, messages.INFO, "The status of this patch cannot be changed in this commitfest. You must modify it in the one where it's open!")
+ return HttpResponseRedirect('/%s/%s/' % (poc.commitfest.id, poc.patch.id))
+
+ if status == 'review':
+ newstatus = PatchOnCommitFest.STATUS_REVIEW
+ elif status == 'author':
+ newstatus = PatchOnCommitFest.STATUS_AUTHOR
+ elif status == 'committer':
+ newstatus = PatchOnCommitFest.STATUS_COMMITTER
+ else:
+ raise Exception("Can't happen")
+
+ if newstatus != poc.status:
+ # Only save it if something actually changed
+ poc.status = newstatus
+ poc.patch.set_modified()
+ poc.patch.save()
+ poc.save()
+
+ PatchHistory(patch=poc.patch, by=request.user, what='New status: %s' % poc.statusstring).save_and_notify()
+
+ return HttpResponseRedirect('/%s/%s/' % (poc.commitfest.id, poc.patch.id))
@login_required
@transaction.atomic
def close(request, cfid, patchid, status):
- poc = get_object_or_404(PatchOnCommitFest.objects.select_related(), commitfest__id=cfid, patch__id=patchid)
-
- if poc.is_closed:
- # We allow modification of patches in closed CFs *only* if it's the
- # last CF that the patch is part of. If it's part of another CF, that
- # is later than this one, tell the user to go there instead.
- lastcf = PatchOnCommitFest.objects.filter(patch__id=patchid).order_by('-commitfest__startdate')[0]
- if poc != lastcf:
- messages.add_message(request, messages.INFO, "The status of this patch cannot be changed in this commitfest. You must modify it in the one where it's open!")
- return HttpResponseRedirect('/%s/%s/' % (poc.commitfest.id, poc.patch.id))
-
- poc.leavedate = datetime.now()
-
- # We know the status can't be one of the ones below, since we
- # have checked that we're not closed yet. Therefor, we don't
- # need to check if the individual status has changed.
- if status == 'reject':
- poc.status = PatchOnCommitFest.STATUS_REJECTED
- elif status == 'withdrawn':
- poc.status = PatchOnCommitFest.STATUS_WITHDRAWN
- elif status == 'feedback':
- poc.status = PatchOnCommitFest.STATUS_RETURNED
- elif status == 'next':
- # Only some patch statuses can actually be moved.
- if poc.status in (PatchOnCommitFest.STATUS_AUTHOR,
- PatchOnCommitFest.STATUS_COMMITTED,
- PatchOnCommitFest.STATUS_NEXT,
- PatchOnCommitFest.STATUS_RETURNED,
- PatchOnCommitFest.STATUS_REJECTED):
- # Can't be moved!
- messages.error(request, "A patch in status {0} cannot be moved to next commitfest.".format(poc.statusstring))
- return HttpResponseRedirect('/%s/%s/' % (poc.commitfest.id, poc.patch.id))
- elif poc.status in (PatchOnCommitFest.STATUS_REVIEW,
- PatchOnCommitFest.STATUS_COMMITTER):
- # This one can be moved
- pass
- else:
- messages.error(request, "Invalid existing patch status")
-
- oldstatus = poc.status
-
- poc.status = PatchOnCommitFest.STATUS_NEXT
- # Figure out the commitfest to actually put it on
- newcf = CommitFest.objects.filter(status=CommitFest.STATUS_OPEN)
- if len(newcf) == 0:
- # Ok, there is no open CF at all. Let's see if there is a
- # future one.
- newcf = CommitFest.objects.filter(status=CommitFest.STATUS_FUTURE)
- if len(newcf) == 0:
- messages.error(request,"No open and no future commitfest exists!")
- return HttpResponseRedirect('/%s/%s/' % (poc.commitfest.id, poc.patch.id))
- elif len(newcf) != 1:
- messages.error(request, "No open and multiple future commitfests exist!")
- return HttpResponseRedirect('/%s/%s/' % (poc.commitfest.id, poc.patch.id))
- elif len(newcf) != 1:
- messages.error(request, "Multiple open commitfests exists!")
- return HttpResponseRedirect('/%s/%s/' % (poc.commitfest.id, poc.patch.id))
- elif newcf[0] == poc.commitfest:
- # The current open CF is the same one that we are already on.
- # In this case, try to see if there is a future CF we can
- # move it to.
- newcf = CommitFest.objects.filter(status=CommitFest.STATUS_FUTURE)
- if len(newcf) == 0:
- messages.error(request, "Cannot move patch to the same commitfest, and no future commitfests exist!")
- return HttpResponseRedirect('/%s/%s/' % (poc.commitfest.id, poc.patch.id))
- elif len(newcf) != 1:
- messages.error(request, "Cannot move patch to the same commitfest, and multiple future commitfests exist!")
- return HttpResponseRedirect('/%s/%s/' % (poc.commitfest.id, poc.patch.id))
- # Create a mapping to the new commitfest that we are bouncing
- # this patch to.
- newpoc = PatchOnCommitFest(patch=poc.patch,
- commitfest=newcf[0],
- status=oldstatus,
- enterdate=datetime.now())
- newpoc.save()
- elif status == 'committed':
- committer = get_object_or_404(Committer, user__username=request.GET['c'])
- if committer != poc.patch.committer:
- # Committer changed!
- prevcommitter = poc.patch.committer
- poc.patch.committer = committer
- PatchHistory(patch=poc.patch, by=request.user, what='Changed committer to %s' % committer).save_and_notify(prevcommitter=prevcommitter)
- poc.status = PatchOnCommitFest.STATUS_COMMITTED
- else:
- raise Exception("Can't happen")
-
- poc.patch.set_modified()
- poc.patch.save()
- poc.save()
-
- PatchHistory(patch=poc.patch, by=request.user, what='Closed in commitfest %s with status: %s' % (poc.commitfest, poc.statusstring)).save_and_notify()
-
- return HttpResponseRedirect('/%s/%s/' % (poc.commitfest.id, poc.patch.id))
+ poc = get_object_or_404(PatchOnCommitFest.objects.select_related(), commitfest__id=cfid, patch__id=patchid)
+
+ if poc.is_closed:
+ # We allow modification of patches in closed CFs *only* if it's the
+ # last CF that the patch is part of. If it's part of another CF, that
+ # is later than this one, tell the user to go there instead.
+ lastcf = PatchOnCommitFest.objects.filter(patch__id=patchid).order_by('-commitfest__startdate')[0]
+ if poc != lastcf:
+ messages.add_message(request, messages.INFO, "The status of this patch cannot be changed in this commitfest. You must modify it in the one where it's open!")
+ return HttpResponseRedirect('/%s/%s/' % (poc.commitfest.id, poc.patch.id))
+
+ poc.leavedate = datetime.now()
+
+ # We know the status can't be one of the ones below, since we
+ # have checked that we're not closed yet. Therefor, we don't
+ # need to check if the individual status has changed.
+ if status == 'reject':
+ poc.status = PatchOnCommitFest.STATUS_REJECTED
+ elif status == 'withdrawn':
+ poc.status = PatchOnCommitFest.STATUS_WITHDRAWN
+ elif status == 'feedback':
+ poc.status = PatchOnCommitFest.STATUS_RETURNED
+ elif status == 'next':
+ # Only some patch statuses can actually be moved.
+ if poc.status in (PatchOnCommitFest.STATUS_AUTHOR,
+ PatchOnCommitFest.STATUS_COMMITTED,
+ PatchOnCommitFest.STATUS_NEXT,
+ PatchOnCommitFest.STATUS_RETURNED,
+ PatchOnCommitFest.STATUS_REJECTED):
+ # Can't be moved!
+ messages.error(request, "A patch in status {0} cannot be moved to next commitfest.".format(poc.statusstring))
+ return HttpResponseRedirect('/%s/%s/' % (poc.commitfest.id, poc.patch.id))
+ elif poc.status in (PatchOnCommitFest.STATUS_REVIEW,
+ PatchOnCommitFest.STATUS_COMMITTER):
+ # This one can be moved
+ pass
+ else:
+ messages.error(request, "Invalid existing patch status")
+
+ oldstatus = poc.status
+
+ poc.status = PatchOnCommitFest.STATUS_NEXT
+ # Figure out the commitfest to actually put it on
+ newcf = CommitFest.objects.filter(status=CommitFest.STATUS_OPEN)
+ if len(newcf) == 0:
+ # Ok, there is no open CF at all. Let's see if there is a
+ # future one.
+ newcf = CommitFest.objects.filter(status=CommitFest.STATUS_FUTURE)
+ if len(newcf) == 0:
+ messages.error(request,"No open and no future commitfest exists!")
+ return HttpResponseRedirect('/%s/%s/' % (poc.commitfest.id, poc.patch.id))
+ elif len(newcf) != 1:
+ messages.error(request, "No open and multiple future commitfests exist!")
+ return HttpResponseRedirect('/%s/%s/' % (poc.commitfest.id, poc.patch.id))
+ elif len(newcf) != 1:
+ messages.error(request, "Multiple open commitfests exists!")
+ return HttpResponseRedirect('/%s/%s/' % (poc.commitfest.id, poc.patch.id))
+ elif newcf[0] == poc.commitfest:
+ # The current open CF is the same one that we are already on.
+ # In this case, try to see if there is a future CF we can
+ # move it to.
+ newcf = CommitFest.objects.filter(status=CommitFest.STATUS_FUTURE)
+ if len(newcf) == 0:
+ messages.error(request, "Cannot move patch to the same commitfest, and no future commitfests exist!")
+ return HttpResponseRedirect('/%s/%s/' % (poc.commitfest.id, poc.patch.id))
+ elif len(newcf) != 1:
+ messages.error(request, "Cannot move patch to the same commitfest, and multiple future commitfests exist!")
+ return HttpResponseRedirect('/%s/%s/' % (poc.commitfest.id, poc.patch.id))
+ # Create a mapping to the new commitfest that we are bouncing
+ # this patch to.
+ newpoc = PatchOnCommitFest(patch=poc.patch,
+ commitfest=newcf[0],
+ status=oldstatus,
+ enterdate=datetime.now())
+ newpoc.save()
+ elif status == 'committed':
+ committer = get_object_or_404(Committer, user__username=request.GET['c'])
+ if committer != poc.patch.committer:
+ # Committer changed!
+ prevcommitter = poc.patch.committer
+ poc.patch.committer = committer
+ PatchHistory(patch=poc.patch, by=request.user, what='Changed committer to %s' % committer).save_and_notify(prevcommitter=prevcommitter)
+ poc.status = PatchOnCommitFest.STATUS_COMMITTED
+ else:
+ raise Exception("Can't happen")
+
+ poc.patch.set_modified()
+ poc.patch.save()
+ poc.save()
+
+ PatchHistory(patch=poc.patch, by=request.user, what='Closed in commitfest %s with status: %s' % (poc.commitfest, poc.statusstring)).save_and_notify()
+
+ return HttpResponseRedirect('/%s/%s/' % (poc.commitfest.id, poc.patch.id))
@login_required
@transaction.atomic
def reviewer(request, cfid, patchid, status):
- get_object_or_404(CommitFest, pk=cfid)
- patch = get_object_or_404(Patch, pk=patchid)
+ get_object_or_404(CommitFest, pk=cfid)
+ patch = get_object_or_404(Patch, pk=patchid)
- is_reviewer = request.user in patch.reviewers.all()
+ is_reviewer = request.user in patch.reviewers.all()
- if status=='become' and not is_reviewer:
- patch.reviewers.add(request.user)
- patch.set_modified()
- PatchHistory(patch=patch, by=request.user, what='Added %s as reviewer' % request.user.username).save_and_notify()
- elif status=='remove' and is_reviewer:
- patch.reviewers.remove(request.user)
- patch.set_modified()
- PatchHistory(patch=patch, by=request.user, what='Removed %s from reviewers' % request.user.username).save_and_notify()
- return HttpResponseRedirect('../../')
+ if status=='become' and not is_reviewer:
+ patch.reviewers.add(request.user)
+ patch.set_modified()
+ PatchHistory(patch=patch, by=request.user, what='Added %s as reviewer' % request.user.username).save_and_notify()
+ elif status=='remove' and is_reviewer:
+ patch.reviewers.remove(request.user)
+ patch.set_modified()
+ PatchHistory(patch=patch, by=request.user, what='Removed %s from reviewers' % request.user.username).save_and_notify()
+ return HttpResponseRedirect('../../')
@login_required
@transaction.atomic
def committer(request, cfid, patchid, status):
- get_object_or_404(CommitFest, pk=cfid)
- patch = get_object_or_404(Patch, pk=patchid)
-
- committer = list(Committer.objects.filter(user=request.user, active=True))
- if len(committer) == 0:
- return HttpResponseForbidden('Only committers can do that!')
- committer = committer[0]
-
- is_committer = committer == patch.committer
-
- prevcommitter = patch.committer
- if status=='become' and not is_committer:
- patch.committer = committer
- patch.set_modified()
- PatchHistory(patch=patch, by=request.user, what='Added %s as committer' % request.user.username).save_and_notify(prevcommitter=prevcommitter)
- elif status=='remove' and is_committer:
- patch.committer = None
- patch.set_modified()
- PatchHistory(patch=patch, by=request.user, what='Removed %s from committers' % request.user.username).save_and_notify(prevcommitter=prevcommitter)
- patch.save()
- return HttpResponseRedirect('../../')
+ get_object_or_404(CommitFest, pk=cfid)
+ patch = get_object_or_404(Patch, pk=patchid)
+
+ committer = list(Committer.objects.filter(user=request.user, active=True))
+ if len(committer) == 0:
+ return HttpResponseForbidden('Only committers can do that!')
+ committer = committer[0]
+
+ is_committer = committer == patch.committer
+
+ prevcommitter = patch.committer
+ if status=='become' and not is_committer:
+ patch.committer = committer
+ patch.set_modified()
+ PatchHistory(patch=patch, by=request.user, what='Added %s as committer' % request.user.username).save_and_notify(prevcommitter=prevcommitter)
+ elif status=='remove' and is_committer:
+ patch.committer = None
+ patch.set_modified()
+ PatchHistory(patch=patch, by=request.user, what='Removed %s from committers' % request.user.username).save_and_notify(prevcommitter=prevcommitter)
+ patch.save()
+ return HttpResponseRedirect('../../')
@login_required
@transaction.atomic
def subscribe(request, cfid, patchid, sub):
- get_object_or_404(CommitFest, pk=cfid)
- patch = get_object_or_404(Patch, pk=patchid)
-
- if sub == 'un':
- patch.subscribers.remove(request.user)
- messages.info(request, "You have been unsubscribed from updates on this patch")
- else:
- patch.subscribers.add(request.user)
- messages.info(request, "You have been subscribed to updates on this patch")
- patch.save()
- return HttpResponseRedirect("../")
+ get_object_or_404(CommitFest, pk=cfid)
+ patch = get_object_or_404(Patch, pk=patchid)
+
+ if sub == 'un':
+ patch.subscribers.remove(request.user)
+ messages.info(request, "You have been unsubscribed from updates on this patch")
+ else:
+ patch.subscribers.add(request.user)
+ messages.info(request, "You have been subscribed to updates on this patch")
+ patch.save()
+ return HttpResponseRedirect("../")
@login_required
@transaction.atomic
def send_email(request, cfid):
- cf = get_object_or_404(CommitFest, pk=cfid)
- if not request.user.is_staff:
- raise Http404("Only CF managers can do that.")
-
- if request.method == 'POST':
- authoridstring = request.POST['authors']
- revieweridstring = request.POST['reviewers']
- form = BulkEmailForm(data=request.POST)
- if form.is_valid():
- q = Q()
- if authoridstring:
- q = q | Q(patch_author__in=[int(x) for x in authoridstring.split(',')])
- if revieweridstring:
- q = q | Q(patch_reviewer__in=[int(x) for x in revieweridstring.split(',')])
-
- recipients = User.objects.filter(q).distinct()
-
- for r in recipients:
- send_simple_mail(UserWrapper(request.user).email, r.email, form.cleaned_data['subject'], form.cleaned_data['body'], request.user.username)
- messages.add_message(request, messages.INFO, "Sent email to %s" % r.email)
- return HttpResponseRedirect('..')
- else:
- authoridstring = request.GET.get('authors', None)
- revieweridstring = request.GET.get('reviewers', None)
- form = BulkEmailForm(initial={'authors': authoridstring, 'reviewers': revieweridstring})
-
- if authoridstring:
- authors = list(User.objects.filter(patch_author__in=[int(x) for x in authoridstring.split(',')]).distinct())
- else:
- authors = []
- if revieweridstring:
- reviewers = list(User.objects.filter(patch_reviewer__in=[int(x) for x in revieweridstring.split(',')]).distinct())
- else:
- reviewers = []
-
- if len(authors)==0 and len(reviewers)==0:
- messages.add_message(request, messages.WARNING, "No recipients specified, cannot send email")
- return HttpResponseRedirect('..')
-
- messages.add_message(request, messages.INFO, "Email will be sent from: %s" % UserWrapper(request.user).email)
- def _user_and_mail(u):
- return "%s %s (%s)" % (u.first_name, u.last_name, u.email)
-
- if len(authors):
- messages.add_message(request, messages.INFO, "The email will be sent to the following authors: %s" % ", ".join([_user_and_mail(u) for u in authors]))
- if len(reviewers):
- messages.add_message(request, messages.INFO, "The email will be sent to the following reviewers: %s" % ", ".join([_user_and_mail(u) for u in reviewers]))
-
- return render(request, 'base_form.html', {
- 'cf': cf,
- 'form': form,
- 'title': 'Send email',
- 'breadcrumbs': [{'title': cf.title, 'href': '/%s/' % cf.pk},],
- 'savebutton': 'Send email',
- })
+ cf = get_object_or_404(CommitFest, pk=cfid)
+ if not request.user.is_staff:
+ raise Http404("Only CF managers can do that.")
+
+ if request.method == 'POST':
+ authoridstring = request.POST['authors']
+ revieweridstring = request.POST['reviewers']
+ form = BulkEmailForm(data=request.POST)
+ if form.is_valid():
+ q = Q()
+ if authoridstring:
+ q = q | Q(patch_author__in=[int(x) for x in authoridstring.split(',')])
+ if revieweridstring:
+ q = q | Q(patch_reviewer__in=[int(x) for x in revieweridstring.split(',')])
+
+ recipients = User.objects.filter(q).distinct()
+
+ for r in recipients:
+ send_simple_mail(UserWrapper(request.user).email, r.email, form.cleaned_data['subject'], form.cleaned_data['body'], request.user.username)
+ messages.add_message(request, messages.INFO, "Sent email to %s" % r.email)
+ return HttpResponseRedirect('..')
+ else:
+ authoridstring = request.GET.get('authors', None)
+ revieweridstring = request.GET.get('reviewers', None)
+ form = BulkEmailForm(initial={'authors': authoridstring, 'reviewers': revieweridstring})
+
+ if authoridstring:
+ authors = list(User.objects.filter(patch_author__in=[int(x) for x in authoridstring.split(',')]).distinct())
+ else:
+ authors = []
+ if revieweridstring:
+ reviewers = list(User.objects.filter(patch_reviewer__in=[int(x) for x in revieweridstring.split(',')]).distinct())
+ else:
+ reviewers = []
+
+ if len(authors)==0 and len(reviewers)==0:
+ messages.add_message(request, messages.WARNING, "No recipients specified, cannot send email")
+ return HttpResponseRedirect('..')
+
+ messages.add_message(request, messages.INFO, "Email will be sent from: %s" % UserWrapper(request.user).email)
+ def _user_and_mail(u):
+ return "%s %s (%s)" % (u.first_name, u.last_name, u.email)
+
+ if len(authors):
+ messages.add_message(request, messages.INFO, "The email will be sent to the following authors: %s" % ", ".join([_user_and_mail(u) for u in authors]))
+ if len(reviewers):
+ messages.add_message(request, messages.INFO, "The email will be sent to the following reviewers: %s" % ", ".join([_user_and_mail(u) for u in reviewers]))
+
+ return render(request, 'base_form.html', {
+ 'cf': cf,
+ 'form': form,
+ 'title': 'Send email',
+ 'breadcrumbs': [{'title': cf.title, 'href': '/%s/' % cf.pk},],
+ 'savebutton': 'Send email',
+ })
@csrf_exempt
def thread_notify(request):
- if request.method != 'POST':
- return HttpResponseForbidden("Invalid method")
-
- j = json.loads(request.body)
- if j['apikey'] != settings.ARCHIVES_APIKEY:
- return HttpResponseForbidden("Invalid API key")
-
- for m in j['messageids']:
- try:
- t = MailThread.objects.get(messageid=m)
- refresh_single_thread(t)
- except Exception, e:
- # Just ignore it, we'll check again later
- pass
-
- return HttpResponse(status=200)
+ if request.method != 'POST':
+ return HttpResponseForbidden("Invalid method")
+
+ j = json.loads(request.body)
+ if j['apikey'] != settings.ARCHIVES_APIKEY:
+ return HttpResponseForbidden("Invalid API key")
+
+ for m in j['messageids']:
+ try:
+ t = MailThread.objects.get(messageid=m)
+ refresh_single_thread(t)
+ except Exception, e:
+ # Just ignore it, we'll check again later
+ pass
+
+ return HttpResponse(status=200)
diff --git a/pgcommitfest/commitfest/widgets.py b/pgcommitfest/commitfest/widgets.py
index 0475001..ee2df3d 100644
--- a/pgcommitfest/commitfest/widgets.py
+++ b/pgcommitfest/commitfest/widgets.py
@@ -2,8 +2,8 @@ from django.forms import TextInput
from django.utils.safestring import mark_safe
class ThreadPickWidget(TextInput):
- def render(self, name, value, attrs=None):
- attrs['class'] += ' threadpick-input'
- html = super(ThreadPickWidget, self).render(name, value, attrs)
- html = html + '&nbsp;<button class="btn btn-default attachThreadButton" id="btn_%s">Find thread</button>' % name
- return mark_safe(html)
+ def render(self, name, value, attrs=None):
+ attrs['class'] += ' threadpick-input'
+ html = super(ThreadPickWidget, self).render(name, value, attrs)
+ html = html + '&nbsp;<button class="btn btn-default attachThreadButton" id="btn_%s">Find thread</button>' % name
+ return mark_safe(html)