summaryrefslogtreecommitdiffstats
path: root/apps/patchwork/views
diff options
context:
space:
mode:
authorJeremy Kerr <jk@ozlabs.org>2015-05-24 16:57:33 +0800
committerJeremy Kerr <jk@ozlabs.org>2015-05-27 10:26:41 +0800
commitad2762cf775a8dde508de47164d6429f3fd724f1 (patch)
treee63015a468cfe32c961908f0338d423227799815 /apps/patchwork/views
parentf09e982f58384946111d4157fd2b7c2b31b78612 (diff)
downloadpatchwork-ad2762cf775a8dde508de47164d6429f3fd724f1.tar.bz2
patchwork-ad2762cf775a8dde508de47164d6429f3fd724f1.tar.xz
Move to a more recent django project structure
This change updates patchwor to the newer project struture: we've moved the actual application out of the apps/ directory, and the patchwork-specific templates to under the patchwork application. This gives us the manage.py script in the top-level now. Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
Diffstat (limited to 'apps/patchwork/views')
-rw-r--r--apps/patchwork/views/__init__.py220
-rw-r--r--apps/patchwork/views/base.py122
-rw-r--r--apps/patchwork/views/bundle.py221
-rw-r--r--apps/patchwork/views/mail.py119
-rw-r--r--apps/patchwork/views/patch.py107
-rw-r--r--apps/patchwork/views/project.py38
-rw-r--r--apps/patchwork/views/user.py216
-rw-r--r--apps/patchwork/views/xmlrpc.py450
8 files changed, 0 insertions, 1493 deletions
diff --git a/apps/patchwork/views/__init__.py b/apps/patchwork/views/__init__.py
deleted file mode 100644
index dfca56d..0000000
--- a/apps/patchwork/views/__init__.py
+++ /dev/null
@@ -1,220 +0,0 @@
-# Patchwork - automated patch tracking system
-# Copyright (C) 2008 Jeremy Kerr <jk@ozlabs.org>
-#
-# This file is part of the Patchwork package.
-#
-# Patchwork is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# Patchwork is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with Patchwork; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
-
-from base import *
-from patchwork.utils import Order, get_patch_ids, bundle_actions, set_bundle
-from patchwork.paginator import Paginator
-from patchwork.forms import MultiplePatchForm
-from patchwork.models import Comment
-import re
-import datetime
-
-try:
- from email.mime.nonmultipart import MIMENonMultipart
- from email.encoders import encode_7or8bit
- from email.parser import HeaderParser
- from email.header import Header
- import email.utils
-except ImportError:
- # Python 2.4 compatibility
- from email.MIMENonMultipart import MIMENonMultipart
- from email.Encoders import encode_7or8bit
- from email.Parser import HeaderParser
- from email.Header import Header
- import email.Utils
- email.utils = email.Utils
-
-def generic_list(request, project, view,
- view_args = {}, filter_settings = [], patches = None,
- editable_order = False):
-
- context = PatchworkRequestContext(request,
- list_view = view,
- list_view_params = view_args)
-
- context.project = project
- order = Order(request.REQUEST.get('order'), editable = editable_order)
-
- # Explicitly set data to None because request.POST will be an empty dict
- # when the form is not submitted, but passing a non-None data argument to
- # a forms.Form will make it bound and we don't want that to happen unless
- # there's been a form submission.
- data = None
- if request.method == 'POST':
- data = request.POST
- user = request.user
- properties_form = None
- if project.is_editable(user):
-
- # we only pass the post data to the MultiplePatchForm if that was
- # the actual form submitted
- data_tmp = None
- if data and data.get('form', '') == 'patchlistform':
- data_tmp = data
-
- properties_form = MultiplePatchForm(project, data = data_tmp)
-
- if request.method == 'POST' and data.get('form') == 'patchlistform':
- action = data.get('action', '').lower()
-
- # special case: the user may have hit enter in the 'create bundle'
- # text field, so if non-empty, assume the create action:
- if data.get('bundle_name', False):
- action = 'create'
-
- ps = Patch.objects.filter(id__in = get_patch_ids(data))
-
- if action in bundle_actions:
- errors = set_bundle(user, project, action, data, ps, context)
-
- elif properties_form and action == properties_form.action:
- errors = process_multiplepatch_form(properties_form, user,
- action, ps, context)
- else:
- errors = []
-
- if errors:
- context['errors'] = errors
-
- for (filterclass, setting) in filter_settings:
- if isinstance(setting, dict):
- context.filters.set_status(filterclass, **setting)
- elif isinstance(setting, list):
- context.filters.set_status(filterclass, *setting)
- else:
- context.filters.set_status(filterclass, setting)
-
- if patches is None:
- patches = Patch.objects.filter(project=project)
-
- patches = context.filters.apply(patches)
- if not editable_order:
- patches = order.apply(patches)
-
- # we don't need the content or headers for a list; they're text fields
- # that can potentially contain a lot of data
- patches = patches.defer('content', 'headers')
-
- # but we will need to follow the state and submitter relations for
- # rendering the list template
- patches = patches.select_related('state', 'submitter')
-
- paginator = Paginator(request, patches)
-
- context.update({
- 'page': paginator.current_page,
- 'patchform': properties_form,
- 'project': project,
- 'order': order,
- })
-
- return context
-
-
-def process_multiplepatch_form(form, user, action, patches, context):
- errors = []
- if not form.is_valid() or action != form.action:
- return ['The submitted form data was invalid']
-
- if len(patches) == 0:
- context.add_message("No patches selected; nothing updated")
- return errors
-
- changed_patches = 0
- for patch in patches:
- if not patch.is_editable(user):
- errors.append("You don't have permissions to edit patch '%s'"
- % patch.name)
- continue
-
- changed_patches += 1
- form.save(patch)
-
- if changed_patches == 1:
- context.add_message("1 patch updated")
- elif changed_patches > 1:
- context.add_message("%d patches updated" % changed_patches)
- else:
- context.add_message("No patches updated")
-
- return errors
-
-class PatchMbox(MIMENonMultipart):
- patch_charset = 'utf-8'
- def __init__(self, _text):
- MIMENonMultipart.__init__(self, 'text', 'plain',
- **{'charset': self.patch_charset})
- self.set_payload(_text.encode(self.patch_charset))
- encode_7or8bit(self)
-
-def patch_to_mbox(patch):
- postscript_re = re.compile('\n-{2,3} ?\n')
-
- comment = None
- try:
- comment = Comment.objects.get(patch = patch, msgid = patch.msgid)
- except Exception:
- pass
-
- body = ''
- if comment:
- body = comment.content.strip() + "\n"
-
- parts = postscript_re.split(body, 1)
- if len(parts) == 2:
- (body, postscript) = parts
- body = body.strip() + "\n"
- postscript = postscript.rstrip()
- else:
- postscript = ''
-
- for comment in Comment.objects.filter(patch = patch) \
- .exclude(msgid = patch.msgid):
- body += comment.patch_responses()
-
- if postscript:
- body += '---\n' + postscript + '\n'
-
- if patch.content:
- body += '\n' + patch.content
-
- delta = patch.date - datetime.datetime.utcfromtimestamp(0)
- utc_timestamp = delta.seconds + delta.days*24*3600
-
- mail = PatchMbox(body)
- mail['Subject'] = patch.name
- mail['From'] = email.utils.formataddr((
- str(Header(patch.submitter.name, mail.patch_charset)),
- patch.submitter.email))
- mail['X-Patchwork-Id'] = str(patch.id)
- mail['Message-Id'] = patch.msgid
- mail.set_unixfrom('From patchwork ' + patch.date.ctime())
-
-
- copied_headers = ['To', 'Cc', 'Date']
- orig_headers = HeaderParser().parsestr(str(patch.headers))
- for header in copied_headers:
- if header in orig_headers:
- mail[header] = orig_headers[header]
-
- if 'Date' not in mail:
- mail['Date'] = email.utils.formatdate(utc_timestamp)
-
- return mail
diff --git a/apps/patchwork/views/base.py b/apps/patchwork/views/base.py
deleted file mode 100644
index 6d7dd13..0000000
--- a/apps/patchwork/views/base.py
+++ /dev/null
@@ -1,122 +0,0 @@
-# Patchwork - automated patch tracking system
-# Copyright (C) 2008 Jeremy Kerr <jk@ozlabs.org>
-#
-# This file is part of the Patchwork package.
-#
-# Patchwork is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# Patchwork is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with Patchwork; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
-
-from patchwork.models import Patch, Project, Person, EmailConfirmation
-from django.shortcuts import render_to_response, get_object_or_404
-from django.http import HttpResponse, HttpResponseRedirect, Http404
-from patchwork.requestcontext import PatchworkRequestContext
-from django.core import serializers, urlresolvers
-from django.template.loader import render_to_string
-from django.conf import settings
-from django.db.models import Q
-
-def projects(request):
- context = PatchworkRequestContext(request)
- projects = Project.objects.all()
-
- if projects.count() == 1:
- return HttpResponseRedirect(
- urlresolvers.reverse('patchwork.views.patch.list',
- kwargs = {'project_id': projects[0].linkname}))
-
- context['projects'] = projects
- return render_to_response('patchwork/projects.html', context)
-
-def pwclientrc(request, project_id):
- project = get_object_or_404(Project, linkname = project_id)
- context = PatchworkRequestContext(request)
- context.project = project
- if settings.FORCE_HTTPS_LINKS or request.is_secure():
- context['scheme'] = 'https'
- else:
- context['scheme'] = 'http'
- response = HttpResponse(content_type = "text/plain")
- response['Content-Disposition'] = 'attachment; filename=.pwclientrc'
- response.write(render_to_string('patchwork/pwclientrc', context))
- return response
-
-def pwclient(request):
- context = PatchworkRequestContext(request)
- response = HttpResponse(content_type = "text/x-python")
- response['Content-Disposition'] = 'attachment; filename=pwclient'
- response.write(render_to_string('patchwork/pwclient', context))
- return response
-
-def confirm(request, key):
- import patchwork.views.user, patchwork.views.mail
- views = {
- 'userperson': patchwork.views.user.link_confirm,
- 'registration': patchwork.views.user.register_confirm,
- 'optout': patchwork.views.mail.optout_confirm,
- 'optin': patchwork.views.mail.optin_confirm,
- }
-
- conf = get_object_or_404(EmailConfirmation, key = key)
- if conf.type not in views:
- raise Http404
-
- if conf.active and conf.is_valid():
- return views[conf.type](request, conf)
-
- context = PatchworkRequestContext(request)
- context['conf'] = conf
- if not conf.active:
- context['error'] = 'inactive'
- elif not conf.is_valid():
- context['error'] = 'expired'
-
- return render_to_response('patchwork/confirm-error.html', context)
-
-def submitter_complete(request):
- search = request.GET.get('q', '')
- limit = request.GET.get('l', None)
- response = HttpResponse(content_type = "text/plain")
-
- if len(search) <= 3:
- return response
-
- queryset = Person.objects.filter(Q(name__icontains = search) |
- Q(email__icontains = search))
- if limit is not None:
- try:
- limit = int(limit)
- except ValueError:
- limit = None
-
- if limit is not None and limit > 0:
- queryset = queryset[:limit]
-
- json_serializer = serializers.get_serializer("json")()
- json_serializer.serialize(queryset, ensure_ascii=False, stream=response)
- return response
-
-help_pages = {'': 'index.html',
- 'about/': 'about.html',
- }
-
-if settings.ENABLE_XMLRPC:
- help_pages['pwclient/'] = 'pwclient.html'
-
-def help(request, path):
- context = PatchworkRequestContext(request)
- if path in help_pages:
- return render_to_response('patchwork/help/' + help_pages[path], context)
- raise Http404
-
diff --git a/apps/patchwork/views/bundle.py b/apps/patchwork/views/bundle.py
deleted file mode 100644
index 3fb47e2..0000000
--- a/apps/patchwork/views/bundle.py
+++ /dev/null
@@ -1,221 +0,0 @@
-# Patchwork - automated patch tracking system
-# Copyright (C) 2008 Jeremy Kerr <jk@ozlabs.org>
-#
-# This file is part of the Patchwork package.
-#
-# Patchwork is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# Patchwork is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with Patchwork; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
-from django.contrib.auth.decorators import login_required
-from django.contrib.auth.models import User
-from django.shortcuts import render_to_response, get_object_or_404
-from patchwork.requestcontext import PatchworkRequestContext
-from django.http import HttpResponse, HttpResponseRedirect, HttpResponseNotFound
-import django.core.urlresolvers
-from patchwork.models import Patch, Bundle, BundlePatch, Project
-from patchwork.utils import get_patch_ids
-from patchwork.forms import BundleForm, DeleteBundleForm
-from patchwork.views import generic_list, patch_to_mbox
-from patchwork.filters import DelegateFilter
-
-@login_required
-def setbundle(request):
- context = PatchworkRequestContext(request)
-
- bundle = None
-
- if request.method == 'POST':
- action = request.POST.get('action', None)
- if action is None:
- pass
- elif action == 'create':
- project = get_object_or_404(Project,
- id = request.POST.get('project'))
- bundle = Bundle(owner = request.user, project = project,
- name = request.POST['name'])
- bundle.save()
- patch_id = request.POST.get('patch_id', None)
- if patch_id:
- patch = get_object_or_404(Patch, id = patch_id)
- try:
- bundle.append_patch(patch)
- except Exception:
- pass
- bundle.save()
- elif action == 'add':
- bundle = get_object_or_404(Bundle,
- owner = request.user, id = request.POST['id'])
- bundle.save()
-
- patch_id = request.get('patch_id', None)
- if patch_id:
- patch_ids = patch_id
- else:
- patch_ids = get_patch_ids(request.POST)
-
- for id in patch_ids:
- try:
- patch = Patch.objects.get(id = id)
- bundle.append_patch(patch)
- except:
- pass
-
- bundle.save()
- elif action == 'delete':
- try:
- bundle = Bundle.objects.get(owner = request.user,
- id = request.POST['id'])
- bundle.delete()
- except Exception:
- pass
-
- bundle = None
-
- else:
- bundle = get_object_or_404(Bundle, owner = request.user,
- id = request.POST['bundle_id'])
-
- if 'error' in context:
- pass
-
- if bundle:
- return HttpResponseRedirect(
- django.core.urlresolvers.reverse(
- 'patchwork.views.bundle.bundle',
- kwargs = {'bundle_id': bundle.id}
- )
- )
- else:
- return HttpResponseRedirect(
- django.core.urlresolvers.reverse(
- 'patchwork.views.bundle.list')
- )
-
-@login_required
-def bundles(request):
- context = PatchworkRequestContext(request)
-
- if request.method == 'POST':
- form_name = request.POST.get('form_name', '')
-
- if form_name == DeleteBundleForm.name:
- form = DeleteBundleForm(request.POST)
- if form.is_valid():
- bundle = get_object_or_404(Bundle,
- id = form.cleaned_data['bundle_id'])
- bundle.delete()
-
- bundles = Bundle.objects.filter(owner = request.user)
- for bundle in bundles:
- bundle.delete_form = DeleteBundleForm(auto_id = False,
- initial = {'bundle_id': bundle.id})
-
- context['bundles'] = bundles
-
- return render_to_response('patchwork/bundles.html', context)
-
-def bundle(request, username, bundlename):
- bundle = get_object_or_404(Bundle, owner__username = username,
- name = bundlename)
- filter_settings = [(DelegateFilter, DelegateFilter.AnyDelegate)]
-
- is_owner = request.user == bundle.owner
-
- if not (is_owner or bundle.public):
- return HttpResponseNotFound()
-
- if is_owner:
- if request.method == 'POST' and request.POST.get('form') == 'bundle':
- action = request.POST.get('action', '').lower()
- if action == 'delete':
- bundle.delete()
- return HttpResponseRedirect(
- django.core.urlresolvers.reverse(
- 'patchwork.views.user.profile')
- )
- elif action == 'update':
- form = BundleForm(request.POST, instance = bundle)
- if form.is_valid():
- form.save()
-
- # if we've changed the bundle name, redirect to new URL
- bundle = Bundle.objects.get(pk = bundle.pk)
- if bundle.name != bundlename:
- return HttpResponseRedirect(bundle.get_absolute_url())
-
- else:
- form = BundleForm(instance = bundle)
- else:
- form = BundleForm(instance = bundle)
-
- if request.method == 'POST' and \
- request.POST.get('form') == 'reorderform':
- order = get_object_or_404(BundlePatch, bundle = bundle,
- patch__id = request.POST.get('order_start')).order
-
- for patch_id in request.POST.getlist('neworder'):
- bundlepatch = get_object_or_404(BundlePatch,
- bundle = bundle, patch__id = patch_id)
- bundlepatch.order = order
- bundlepatch.save()
- order += 1
- else:
- form = None
-
- context = generic_list(request, bundle.project,
- 'patchwork.views.bundle.bundle',
- view_args = {'username': bundle.owner.username,
- 'bundlename': bundle.name},
- filter_settings = filter_settings,
- patches = bundle.ordered_patches(),
- editable_order = is_owner)
-
- context['bundle'] = bundle
- context['bundleform'] = form
-
- return render_to_response('patchwork/bundle.html', context)
-
-def mbox(request, username, bundlename):
- bundle = get_object_or_404(Bundle, owner__username = username,
- name = bundlename)
-
- if not (request.user == bundle.owner or bundle.public):
- return HttpResponseNotFound()
-
- mbox = '\n'.join([patch_to_mbox(p).as_string(True)
- for p in bundle.ordered_patches()])
-
- response = HttpResponse(content_type='text/plain')
- response['Content-Disposition'] = \
- 'attachment; filename=bundle-%d-%s.mbox' % (bundle.id, bundle.name)
-
- response.write(mbox)
- return response
-
-@login_required
-def bundle_redir(request, bundle_id):
- bundle = get_object_or_404(Bundle, id = bundle_id, owner = request.user)
- return HttpResponseRedirect(bundle.get_absolute_url())
-
-@login_required
-def mbox_redir(request, bundle_id):
- bundle = get_object_or_404(Bundle, id = bundle_id, owner = request.user)
- return HttpResponseRedirect(django.core.urlresolvers.reverse(
- 'patchwork.views.bundle.mbox', kwargs = {
- 'username': request.user.username,
- 'bundlename': bundle.name,
- }))
-
-
-
diff --git a/apps/patchwork/views/mail.py b/apps/patchwork/views/mail.py
deleted file mode 100644
index aebba34..0000000
--- a/apps/patchwork/views/mail.py
+++ /dev/null
@@ -1,119 +0,0 @@
-# Patchwork - automated patch tracking system
-# Copyright (C) 2010 Jeremy Kerr <jk@ozlabs.org>
-#
-# This file is part of the Patchwork package.
-#
-# Patchwork is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# Patchwork is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with Patchwork; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
-from patchwork.requestcontext import PatchworkRequestContext
-from patchwork.models import EmailOptout, EmailConfirmation
-from patchwork.forms import OptinoutRequestForm, EmailForm
-from django.shortcuts import render_to_response
-from django.template.loader import render_to_string
-from django.conf import settings as conf_settings
-from django.core.mail import send_mail
-from django.core.urlresolvers import reverse
-from django.http import HttpResponseRedirect
-
-def settings(request):
- context = PatchworkRequestContext(request)
- if request.method == 'POST':
- form = EmailForm(data = request.POST)
- if form.is_valid():
- email = form.cleaned_data['email']
- is_optout = EmailOptout.objects.filter(email = email).count() > 0
- context.update({
- 'email': email,
- 'is_optout': is_optout,
- })
- return render_to_response('patchwork/mail-settings.html', context)
-
- else:
- form = EmailForm()
- context['form'] = form
- return render_to_response('patchwork/mail-form.html', context)
-
-def optout_confirm(request, conf):
- context = PatchworkRequestContext(request)
-
- email = conf.email.strip().lower()
- # silently ignore duplicated optouts
- if EmailOptout.objects.filter(email = email).count() == 0:
- optout = EmailOptout(email = email)
- optout.save()
-
- conf.deactivate()
- context['email'] = conf.email
-
- return render_to_response('patchwork/optout.html', context)
-
-def optin_confirm(request, conf):
- context = PatchworkRequestContext(request)
-
- email = conf.email.strip().lower()
- EmailOptout.objects.filter(email = email).delete()
-
- conf.deactivate()
- context['email'] = conf.email
-
- return render_to_response('patchwork/optin.html', context)
-
-def optinout(request, action, description):
- context = PatchworkRequestContext(request)
-
- mail_template = 'patchwork/%s-request.mail' % action
- html_template = 'patchwork/%s-request.html' % action
-
- if request.method != 'POST':
- return HttpResponseRedirect(reverse(settings))
-
- form = OptinoutRequestForm(data = request.POST)
- if not form.is_valid():
- context['error'] = ('There was an error in the %s form. ' +
- 'Please review the form and re-submit.') % \
- description
- context['form'] = form
- return render_to_response(html_template, context)
-
- email = form.cleaned_data['email']
- if action == 'optin' and \
- EmailOptout.objects.filter(email = email).count() == 0:
- context['error'] = ('The email address %s is not on the ' +
- 'patchwork opt-out list, so you don\'t ' +
- 'need to opt back in') % email
- context['form'] = form
- return render_to_response(html_template, context)
-
- conf = EmailConfirmation(type = action, email = email)
- conf.save()
- context['confirmation'] = conf
- mail = render_to_string(mail_template, context)
- try:
- send_mail('Patchwork %s confirmation' % description, mail,
- conf_settings.DEFAULT_FROM_EMAIL, [email])
- context['email'] = mail
- context['email_sent'] = True
- except Exception, ex:
- context['error'] = 'An error occurred during confirmation . ' + \
- 'Please try again later.'
- context['admins'] = conf_settings.ADMINS
-
- return render_to_response(html_template, context)
-
-def optout(request):
- return optinout(request, 'optout', 'opt-out')
-
-def optin(request):
- return optinout(request, 'optin', 'opt-in')
diff --git a/apps/patchwork/views/patch.py b/apps/patchwork/views/patch.py
deleted file mode 100644
index 62ff853..0000000
--- a/apps/patchwork/views/patch.py
+++ /dev/null
@@ -1,107 +0,0 @@
-# Patchwork - automated patch tracking system
-# Copyright (C) 2008 Jeremy Kerr <jk@ozlabs.org>
-#
-# This file is part of the Patchwork package.
-#
-# Patchwork is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# Patchwork is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with Patchwork; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
-
-from patchwork.models import Patch, Project, Bundle
-from patchwork.forms import PatchForm, CreateBundleForm
-from patchwork.requestcontext import PatchworkRequestContext
-from django.shortcuts import render_to_response, get_object_or_404
-from django.http import HttpResponse, HttpResponseForbidden
-from patchwork.views import generic_list, patch_to_mbox
-
-def patch(request, patch_id):
- context = PatchworkRequestContext(request)
- patch = get_object_or_404(Patch, id=patch_id)
- context.project = patch.project
- editable = patch.is_editable(request.user)
-
- form = None
- createbundleform = None
-
- if editable:
- form = PatchForm(instance = patch)
- if request.user.is_authenticated():
- createbundleform = CreateBundleForm()
-
- if request.method == 'POST':
- action = request.POST.get('action', None)
- if action:
- action = action.lower()
-
- if action == 'createbundle':
- bundle = Bundle(owner = request.user, project = patch.project)
- createbundleform = CreateBundleForm(instance = bundle,
- data = request.POST)
- if createbundleform.is_valid():
- createbundleform.save()
- bundle.append_patch(patch)
- bundle.save()
- createbundleform = CreateBundleForm()
- context.add_message('Bundle %s created' % bundle.name)
-
- elif action == 'addtobundle':
- bundle = get_object_or_404(Bundle, id = \
- request.POST.get('bundle_id'))
- try:
- bundle.append_patch(patch)
- bundle.save()
- context.add_message('Patch added to bundle "%s"' % bundle.name)
- except Exception, ex:
- context.add_message("Couldn't add patch '%s' to bundle %s: %s" \
- % (patch.name, bundle.name, ex.message))
-
- # all other actions require edit privs
- elif not editable:
- return HttpResponseForbidden()
-
- elif action is None:
- form = PatchForm(data = request.POST, instance = patch)
- if form.is_valid():
- form.save()
- context.add_message('Patch updated')
-
- context['patch'] = patch
- context['patchform'] = form
- context['createbundleform'] = createbundleform
- context['project'] = patch.project
-
- return render_to_response('patchwork/patch.html', context)
-
-def content(request, patch_id):
- patch = get_object_or_404(Patch, id=patch_id)
- response = HttpResponse(content_type="text/x-patch")
- response.write(patch.content)
- response['Content-Disposition'] = 'attachment; filename=' + \
- patch.filename().replace(';', '').replace('\n', '')
- return response
-
-def mbox(request, patch_id):
- patch = get_object_or_404(Patch, id=patch_id)
- response = HttpResponse(content_type="text/plain")
- response.write(patch_to_mbox(patch).as_string(True))
- response['Content-Disposition'] = 'attachment; filename=' + \
- patch.filename().replace(';', '').replace('\n', '')
- return response
-
-
-def list(request, project_id):
- project = get_object_or_404(Project, linkname=project_id)
- context = generic_list(request, project, 'patchwork.views.patch.list',
- view_args = {'project_id': project.linkname})
- return render_to_response('patchwork/list.html', context)
diff --git a/apps/patchwork/views/project.py b/apps/patchwork/views/project.py
deleted file mode 100644
index 114dbe0..0000000
--- a/apps/patchwork/views/project.py
+++ /dev/null
@@ -1,38 +0,0 @@
-# Patchwork - automated patch tracking system
-# Copyright (C) 2009 Jeremy Kerr <jk@ozlabs.org>
-#
-# This file is part of the Patchwork package.
-#
-# Patchwork is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# Patchwork is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with Patchwork; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
-
-from patchwork.models import Patch, Project
-from django.shortcuts import render_to_response, get_object_or_404
-from django.contrib.auth.models import User
-from patchwork.requestcontext import PatchworkRequestContext
-
-def project(request, project_id):
- context = PatchworkRequestContext(request)
- project = get_object_or_404(Project, linkname = project_id)
- context.project = project
-
- context['maintainers'] = User.objects.filter( \
- profile__maintainer_projects = project)
- context['n_patches'] = Patch.objects.filter(project = project,
- archived = False).count()
- context['n_archived_patches'] = Patch.objects.filter(project = project,
- archived = True).count()
-
- return render_to_response('patchwork/project.html', context)
diff --git a/apps/patchwork/views/user.py b/apps/patchwork/views/user.py
deleted file mode 100644
index 126ecc9..0000000
--- a/apps/patchwork/views/user.py
+++ /dev/null
@@ -1,216 +0,0 @@
-# Patchwork - automated patch tracking system
-# Copyright (C) 2008 Jeremy Kerr <jk@ozlabs.org>
-#
-# This file is part of the Patchwork package.
-#
-# Patchwork is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# Patchwork is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with Patchwork; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
-
-from django.contrib.auth.decorators import login_required
-from patchwork.requestcontext import PatchworkRequestContext
-from django.shortcuts import render_to_response, get_object_or_404
-from django.contrib import auth
-from django.contrib.sites.models import Site
-from django.http import HttpResponseRedirect
-from patchwork.models import Project, Bundle, Person, EmailConfirmation, \
- State, EmailOptout
-from patchwork.forms import UserProfileForm, UserPersonLinkForm, \
- RegistrationForm
-from patchwork.filters import DelegateFilter
-from patchwork.views import generic_list
-from django.template.loader import render_to_string
-from django.conf import settings
-from django.core.mail import send_mail
-import django.core.urlresolvers
-
-def register(request):
- context = PatchworkRequestContext(request)
- if request.method == 'POST':
- form = RegistrationForm(request.POST)
- if form.is_valid():
- data = form.cleaned_data
- # create inactive user
- user = auth.models.User.objects.create_user(data['username'],
- data['email'],
- data['password'])
- user.is_active = False;
- user.first_name = data.get('first_name', '')
- user.last_name = data.get('last_name', '')
- user.save()
-
- # create confirmation
- conf = EmailConfirmation(type = 'registration', user = user,
- email = user.email)
- conf.save()
-
- # send email
- mail_ctx = {'site': Site.objects.get_current(),
- 'confirmation': conf}
-
- subject = render_to_string('patchwork/activation_email_subject.txt',
- mail_ctx).replace('\n', ' ').strip()
-
- message = render_to_string('patchwork/activation_email.txt',
- mail_ctx)
-
- send_mail(subject, message, settings.DEFAULT_FROM_EMAIL,
- [conf.email])
-
- # setting 'confirmation' in the template indicates success
- context['confirmation'] = conf
-
- else:
- form = RegistrationForm()
-
- return render_to_response('patchwork/registration_form.html',
- { 'form': form },
- context_instance=context)
-
-def register_confirm(request, conf):
- conf.user.is_active = True
- conf.user.save()
- conf.deactivate()
- try:
- person = Person.objects.get(email__iexact = conf.user.email)
- except Person.DoesNotExist:
- person = Person(email = conf.user.email,
- name = conf.user.profile.name())
- person.user = conf.user
- person.save()
-
- return render_to_response('patchwork/registration-confirm.html')
-
-@login_required
-def profile(request):
- context = PatchworkRequestContext(request)
-
- if request.method == 'POST':
- form = UserProfileForm(instance = request.user.profile,
- data = request.POST)
- if form.is_valid():
- form.save()
- else:
- form = UserProfileForm(instance = request.user.profile)
-
- context.project = request.user.profile.primary_project
- context['bundles'] = Bundle.objects.filter(owner = request.user)
- context['profileform'] = form
-
- optout_query = '%s.%s IN (SELECT %s FROM %s)' % (
- Person._meta.db_table,
- Person._meta.get_field('email').column,
- EmailOptout._meta.get_field('email').column,
- EmailOptout._meta.db_table)
- people = Person.objects.filter(user = request.user) \
- .extra(select = {'is_optout': optout_query})
- context['linked_emails'] = people
- context['linkform'] = UserPersonLinkForm()
-
- return render_to_response('patchwork/profile.html', context)
-
-@login_required
-def link(request):
- context = PatchworkRequestContext(request)
-
- if request.method == 'POST':
- form = UserPersonLinkForm(request.POST)
- if form.is_valid():
- conf = EmailConfirmation(type = 'userperson',
- user = request.user,
- email = form.cleaned_data['email'])
- conf.save()
- context['confirmation'] = conf
-
- try:
- send_mail('Patchwork email address confirmation',
- render_to_string('patchwork/user-link.mail',
- context),
- settings.DEFAULT_FROM_EMAIL,
- [form.cleaned_data['email']])
- except Exception:
- context['confirmation'] = None
- context['error'] = 'An error occurred during confirmation. ' + \
- 'Please try again later'
- else:
- form = UserPersonLinkForm()
- context['linkform'] = form
-
- return render_to_response('patchwork/user-link.html', context)
-
-@login_required
-def link_confirm(request, conf):
- context = PatchworkRequestContext(request)
-
- try:
- person = Person.objects.get(email__iexact = conf.email)
- except Person.DoesNotExist:
- person = Person(email = conf.email)
-
- person.link_to_user(conf.user)
- person.save()
- conf.deactivate()
-
- context['person'] = person
-
- return render_to_response('patchwork/user-link-confirm.html', context)
-
-@login_required
-def unlink(request, person_id):
- person = get_object_or_404(Person, id = person_id)
-
- if request.method == 'POST':
- if person.email != request.user.email:
- person.user = None
- person.save()
-
- url = django.core.urlresolvers.reverse('patchwork.views.user.profile')
- return HttpResponseRedirect(url)
-
-
-@login_required
-def todo_lists(request):
- todo_lists = []
-
- for project in Project.objects.all():
- patches = request.user.profile.todo_patches(project = project)
- if not patches.count():
- continue
-
- todo_lists.append({'project': project, 'n_patches': patches.count()})
-
- if len(todo_lists) == 1:
- return todo_list(request, todo_lists[0]['project'].linkname)
-
- context = PatchworkRequestContext(request)
- context['todo_lists'] = todo_lists
- context.project = request.user.profile.primary_project
- return render_to_response('patchwork/todo-lists.html', context)
-
-@login_required
-def todo_list(request, project_id):
- project = get_object_or_404(Project, linkname = project_id)
- patches = request.user.profile.todo_patches(project = project)
- filter_settings = [(DelegateFilter,
- {'delegate': request.user})]
-
- context = generic_list(request, project,
- 'patchwork.views.user.todo_list',
- view_args = {'project_id': project.linkname},
- filter_settings = filter_settings,
- patches = patches)
-
- context['action_required_states'] = \
- State.objects.filter(action_required = True).all()
- return render_to_response('patchwork/todo-list.html', context)
diff --git a/apps/patchwork/views/xmlrpc.py b/apps/patchwork/views/xmlrpc.py
deleted file mode 100644
index 84ed408..0000000
--- a/apps/patchwork/views/xmlrpc.py
+++ /dev/null
@@ -1,450 +0,0 @@
-# Patchwork - automated patch tracking system
-# Copyright (C) 2008 Jeremy Kerr <jk@ozlabs.org>
-#
-# This file is part of the Patchwork package.
-#
-# Patchwork is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# Patchwork is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with Patchwork; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-#
-# Patchwork XMLRPC interface
-#
-
-from SimpleXMLRPCServer import SimpleXMLRPCDispatcher
-from django.http import HttpResponse, HttpResponseRedirect, \
- HttpResponseServerError
-from django.core import urlresolvers
-from django.contrib.auth import authenticate
-from patchwork.models import Patch, Project, Person, State
-from patchwork.views import patch_to_mbox
-from django.views.decorators.csrf import csrf_exempt
-
-import sys
-import base64
-import xmlrpclib
-
-class PatchworkXMLRPCDispatcher(SimpleXMLRPCDispatcher):
- def __init__(self):
- if sys.version_info[:3] >= (2,5,):
- SimpleXMLRPCDispatcher.__init__(self, allow_none=False,
- encoding=None)
- def _dumps(obj, *args, **kwargs):
- kwargs['allow_none'] = self.allow_none
- kwargs['encoding'] = self.encoding
- return xmlrpclib.dumps(obj, *args, **kwargs)
- else:
- def _dumps(obj, *args, **kwargs):
- return xmlrpclib.dumps(obj, *args, **kwargs)
- SimpleXMLRPCDispatcher.__init__(self)
-
- self.dumps = _dumps
-
- # map of name => (auth, func)
- self.func_map = {}
-
- def register_function(self, fn, auth_required):
- self.func_map[fn.__name__] = (auth_required, fn)
-
-
- def _user_for_request(self, request):
- auth_header = None
-
- if 'HTTP_AUTHORIZATION' in request.META:
- auth_header = request.META.get('HTTP_AUTHORIZATION')
- elif 'Authorization' in request.META:
- auth_header = request.META.get('Authorization')
-
- if auth_header is None or auth_header == '':
- raise Exception("No authentication credentials given")
-
- str = auth_header.strip()
-
- if not str.startswith('Basic '):
- raise Exception("Authentication scheme not supported")
-
- str = str[len('Basic '):].strip()
-
- try:
- decoded = base64.decodestring(str)
- username, password = decoded.split(':', 1)
- except:
- raise Exception("Invalid authentication credentials")
-
- return authenticate(username = username, password = password)
-
-
- def _dispatch(self, request, method, params):
- if method not in self.func_map.keys():
- raise Exception('method "%s" is not supported' % method)
-
- auth_required, fn = self.func_map[method]
-
- if auth_required:
- user = self._user_for_request(request)
- if not user:
- raise Exception("Invalid username/password")
-
- params = (user,) + params
-
- return fn(*params)
-
- def _marshaled_dispatch(self, request):
- try:
- params, method = xmlrpclib.loads(request.body)
-
- response = self._dispatch(request, method, params)
- # wrap response in a singleton tuple
- response = (response,)
- response = self.dumps(response, methodresponse=1)
- except xmlrpclib.Fault, fault:
- response = self.dumps(fault)
- except:
- # report exception back to server
- response = self.dumps(
- xmlrpclib.Fault(1, "%s:%s" % (sys.exc_type, sys.exc_value)),
- )
-
- return response
-
-dispatcher = PatchworkXMLRPCDispatcher()
-
-# XMLRPC view function
-@csrf_exempt
-def xmlrpc(request):
- if request.method != 'POST':
- return HttpResponseRedirect(
- urlresolvers.reverse('patchwork.views.help',
- kwargs = {'path': 'pwclient/'}))
-
- response = HttpResponse()
- try:
- ret = dispatcher._marshaled_dispatch(request)
- response.write(ret)
- except Exception:
- return HttpResponseServerError()
-
- return response
-
-# decorator for XMLRPC methods. Setting login_required to true will call
-# the decorated function with a non-optional user as the first argument.
-def xmlrpc_method(login_required = False):
- def wrap(f):
- dispatcher.register_function(f, login_required)
- return f
-
- return wrap
-
-
-
-# We allow most of the Django field lookup types for remote queries
-LOOKUP_TYPES = ["iexact", "contains", "icontains", "gt", "gte", "lt",
- "in", "startswith", "istartswith", "endswith",
- "iendswith", "range", "year", "month", "day", "isnull" ]
-
-#######################################################################
-# Helper functions
-#######################################################################
-
-def project_to_dict(obj):
- """Return a trimmed down dictionary representation of a Project
- object which is OK to send to the client."""
- return \
- {
- 'id' : obj.id,
- 'linkname' : obj.linkname,
- 'name' : obj.name,
- }
-
-def person_to_dict(obj):
- """Return a trimmed down dictionary representation of a Person
- object which is OK to send to the client."""
-
- # Make sure we don't return None even if the user submitted a patch
- # with no real name. XMLRPC can't marshall None.
- if obj.name is not None:
- name = obj.name
- else:
- name = obj.email
-
- return \
- {
- 'id' : obj.id,
- 'email' : obj.email,
- 'name' : name,
- 'user' : unicode(obj.user).encode("utf-8"),
- }
-
-def patch_to_dict(obj):
- """Return a trimmed down dictionary representation of a Patch
- object which is OK to send to the client."""
- return \
- {
- 'id' : obj.id,
- 'date' : unicode(obj.date).encode("utf-8"),
- 'filename' : obj.filename(),
- 'msgid' : obj.msgid,
- 'name' : obj.name,
- 'project' : unicode(obj.project).encode("utf-8"),
- 'project_id' : obj.project_id,
- 'state' : unicode(obj.state).encode("utf-8"),
- 'state_id' : obj.state_id,
- 'archived' : obj.archived,
- 'submitter' : unicode(obj.submitter).encode("utf-8"),
- 'submitter_id' : obj.submitter_id,
- 'delegate' : unicode(obj.delegate).encode("utf-8"),
- 'delegate_id' : max(obj.delegate_id, 0),
- 'commit_ref' : max(obj.commit_ref, ''),
- }
-
-def bundle_to_dict(obj):
- """Return a trimmed down dictionary representation of a Bundle
- object which is OK to send to the client."""
- return \
- {
- 'id' : obj.id,
- 'name' : obj.name,
- 'n_patches' : obj.n_patches(),
- 'public_url' : obj.public_url(),
- }
-
-def state_to_dict(obj):
- """Return a trimmed down dictionary representation of a State
- object which is OK to send to the client."""
- return \
- {
- 'id' : obj.id,
- 'name' : obj.name,
- }
-
-#######################################################################
-# Public XML-RPC methods
-#######################################################################
-
-@xmlrpc_method(False)
-def pw_rpc_version():
- """Return Patchwork XML-RPC interface version."""
- return 1
-
-@xmlrpc_method(False)
-def project_list(search_str="", max_count=0):
- """Get a list of projects matching the given filters."""
- try:
- if len(search_str) > 0:
- projects = Project.objects.filter(linkname__icontains = search_str)
- else:
- projects = Project.objects.all()
-
- if max_count > 0:
- return map(project_to_dict, projects)[:max_count]
- else:
- return map(project_to_dict, projects)
- except:
- return []
-
-@xmlrpc_method(False)
-def project_get(project_id):
- """Return structure for the given project ID."""
- try:
- project = Project.objects.filter(id = project_id)[0]
- return project_to_dict(project)
- except:
- return {}
-
-@xmlrpc_method(False)
-def person_list(search_str="", max_count=0):
- """Get a list of Person objects matching the given filters."""
- try:
- if len(search_str) > 0:
- people = (Person.objects.filter(name__icontains = search_str) |
- Person.objects.filter(email__icontains = search_str))
- else:
- people = Person.objects.all()
-
- if max_count > 0:
- return map(person_to_dict, people)[:max_count]
- else:
- return map(person_to_dict, people)
-
- except:
- return []
-
-@xmlrpc_method(False)
-def person_get(person_id):
- """Return structure for the given person ID."""
- try:
- person = Person.objects.filter(id = person_id)[0]
- return person_to_dict(person)
- except:
- return {}
-
-@xmlrpc_method(False)
-def patch_list(filter={}):
- """Get a list of patches matching the given filters."""
- try:
- # We allow access to many of the fields. But, some fields are
- # filtered by raw object so we must lookup by ID instead over
- # XML-RPC.
- ok_fields = [
- "id",
- "name",
- "project_id",
- "submitter_id",
- "delegate_id",
- "archived",
- "state_id",
- "date",
- "commit_ref",
- "hash",
- "msgid",
- "max_count",
- ]
-
- dfilter = {}
- max_count = 0
-
- for key in filter:
- parts = key.split("__")
- if parts[0] not in ok_fields:
- # Invalid field given
- return []
- if len(parts) > 1:
- if LOOKUP_TYPES.count(parts[1]) == 0:
- # Invalid lookup type given
- return []
-
- if parts[0] == 'project_id':
- dfilter['project'] = Project.objects.filter(id =
- filter[key])[0]
- elif parts[0] == 'submitter_id':
- dfilter['submitter'] = Person.objects.filter(id =
- filter[key])[0]
- elif parts[0] == 'delegate_id':
- dfilter['delegate'] = Person.objects.filter(id =
- filter[key])[0]
- elif parts[0] == 'state_id':
- dfilter['state'] = State.objects.filter(id =
- filter[key])[0]
- elif parts[0] == 'max_count':
- max_count = filter[key]
- else:
- dfilter[key] = filter[key]
-
- patches = Patch.objects.filter(**dfilter)
-
- if max_count > 0:
- return map(patch_to_dict, patches[:max_count])
- else:
- return map(patch_to_dict, patches)
-
- except:
- return []
-
-@xmlrpc_method(False)
-def patch_get(patch_id):
- """Return structure for the given patch ID."""
- try:
- patch = Patch.objects.filter(id = patch_id)[0]
- return patch_to_dict(patch)
- except:
- return {}
-
-@xmlrpc_method(False)
-def patch_get_by_hash(hash):
- """Return structure for the given patch hash."""
- try:
- patch = Patch.objects.filter(hash = hash)[0]
- return patch_to_dict(patch)
- except:
- return {}
-
-@xmlrpc_method(False)
-def patch_get_by_project_hash(project, hash):
- """Return structure for the given patch hash."""
- try:
- patch = Patch.objects.filter(project__linkname = project,
- hash = hash)[0]
- return patch_to_dict(patch)
- except:
- return {}
-
-@xmlrpc_method(False)
-def patch_get_mbox(patch_id):
- """Return mbox string for the given patch ID."""
- try:
- patch = Patch.objects.filter(id = patch_id)[0]
- return patch_to_mbox(patch).as_string(True)
- except:
- return ""
-
-@xmlrpc_method(False)
-def patch_get_diff(patch_id):
- """Return diff for the given patch ID."""
- try:
- patch = Patch.objects.filter(id = patch_id)[0]
- return patch.content
- except:
- return ""
-
-@xmlrpc_method(True)
-def patch_set(user, patch_id, params):
- """Update a patch with the key,value pairs in params. Only some parameters
- can be set"""
- try:
- ok_params = ['state', 'commit_ref', 'archived']
-
- patch = Patch.objects.get(id = patch_id)
-
- if not patch.is_editable(user):
- raise Exception('No permissions to edit this patch')
-
- for (k, v) in params.iteritems():
- if k not in ok_params:
- continue
-
- if k == 'state':
- patch.state = State.objects.get(id = v)
-
- else:
- setattr(patch, k, v)
-
- patch.save()
-
- return True
-
- except:
- raise
-
-@xmlrpc_method(False)
-def state_list(search_str="", max_count=0):
- """Get a list of state structures matching the given search string."""
- try:
- if len(search_str) > 0:
- states = State.objects.filter(name__icontains = search_str)
- else:
- states = State.objects.all()
-
- if max_count > 0:
- return map(state_to_dict, states)[:max_count]
- else:
- return map(state_to_dict, states)
- except:
- return []
-
-@xmlrpc_method(False)
-def state_get(state_id):
- """Return structure for the given state ID."""
- try:
- state = State.objects.filter(id = state_id)[0]
- return state_to_dict(state)
- except:
- return {}