Merge branch 'master' of git://github.com/rmt/seantis-questionnaire

Conflicts:
	questionnaire/views.py
EmailTemplateFixes
Ian Ward 2010-01-05 16:05:30 -05:00
commit 46a8468f4d
5 changed files with 216 additions and 13 deletions

File diff suppressed because one or more lines are too long

View File

@ -5,6 +5,7 @@ admin.autodiscover()
urlpatterns = patterns('', urlpatterns = patterns('',
(r'q/', include('questionnaire.urls')), (r'q/', include('questionnaire.urls')),
(r'^take/(?P<questionnaire_id>[0-9]+)/$', 'questionnaire.views.generate_run'),
(r'^$', 'page.views.page', {'page' : 'index'}), (r'^$', 'page.views.page', {'page' : 'index'}),
(r'^(?P<page>.*)\.html$', 'page.views.page'), (r'^(?P<page>.*)\.html$', 'page.views.page'),
(r'^(?P<lang>..)/(?P<page>.*)\.html$', 'page.views.langpage'), (r'^(?P<lang>..)/(?P<page>.*)\.html$', 'page.views.langpage'),

45
questionnaire/README Normal file
View File

@ -0,0 +1,45 @@
About Seantis Questionnaire
===========================
Seantis Questionnaire is a django questionnaire app which is easily customised
and includes advanced dependency support using boolean expressions.
It allows an administrator to create and edit questionnaires in the django
admin interface, with support for multiple languages.
It was originally created to support an annually recurring questionnnaire for a
medical study.
This repository only contains the Questionnaire side of the application. We
also developed a management interface and extensions to the models that are
specific to the study, and have therefore not been made public. Seantis GmbH
could, however, provide a similar end-to-end solution for your organisation.
Alternatives
============
There are two other questionnaire-type applications that I stumbled upon, but
they both didn't quite scratch my itch, but they may scratch yours.
Django Questionnaire - http://djangoquest.aperte-it.com/
Django Survey - http://code.google.com/p/django-survey/
Requirements
============
Seantis Questionnaire has some external dependencies that you must
install.
django-transmeta - used for simple internationalisation
http://code.google.com/p/django-transmeta/source/checkout
pyparsing - used for the boolean dependency parser
http://pyparsing.wikispaces.com/Download+and+Installation
$ easy_install pyparsing
textile - used for marking up the questions
$ easy_install textile

View File

@ -69,6 +69,12 @@ class Questionnaire(models.Model):
QuestionSet.objects.filter(questionnaire=self).order_by('sortid') QuestionSet.objects.filter(questionnaire=self).order_by('sortid')
return self.__qscache return self.__qscache
class Meta:
permissions = (
("export", "Can export questionnaire answers"),
("management", "Management Tools")
)
class QuestionSet(models.Model): class QuestionSet(models.Model):
__metaclass__ = TransMeta __metaclass__ = TransMeta

View File

@ -6,21 +6,29 @@ from django.http import HttpResponse, Http404, \
from django.template import RequestContext, Context, Template, loader from django.template import RequestContext, Context, Template, loader
from django.views.decorators.cache import cache_control from django.views.decorators.cache import cache_control
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.contrib.auth.decorators import login_required, user_passes_test from django.contrib.auth.decorators import login_required, permission_required
from django.shortcuts import render_to_response, get_object_or_404 from django.shortcuts import render_to_response, get_object_or_404
from django.contrib.sites.models import Site from django.contrib.sites.models import Site
from django.db import transaction from django.db import transaction
from django.conf import settings from django.conf import settings
from models import *
from questionnaire import QuestionProcessors, Processors, AnswerException, \
questionset_done, questionnaire_done
from datetime import datetime from datetime import datetime
from django.utils import translation from django.utils import translation
import time, os, smtplib, rfc822 from questionnaire import QuestionProcessors
from parsers import * from questionnaire import questionnaire_done
from utils import numal_sort, split_numal, calc_alignment from questionnaire import questionset_done
from emails import send_emails, _send_email from questionnaire import AnswerException
from questionnaire import Processors
from questionnaire.models import *
from questionnaire.parsers import *
from questionnaire.emails import send_emails, _send_email
from questionnaire.utils import numal_sort, split_numal, calc_alignment
import smtplib
import logging import logging
import random
import rfc822
import time
import md5
import os
def r2r(tpl, request, **contextdict): def r2r(tpl, request, **contextdict):
@ -119,8 +127,9 @@ def questionnaire(request, runcode=None, qs=None):
""" """
# if runcode provided as query string, redirect to the proper page # if runcode provided as query string, redirect to the proper page
if not runcode and request.GET.has_key("runcode"): if not runcode:
transaction.commit() if not request.GET.get('runcode', None):
return HttpResponseRedirect("/")
return HttpResponseRedirect( return HttpResponseRedirect(
reverse("questionnaire", reverse("questionnaire",
args=[request.GET['runcode']])) args=[request.GET['runcode']]))
@ -406,7 +415,7 @@ def set_language(request, runinfo=None, next=None):
return response return response
@user_passes_test(lambda u: u.has_perm('questionnaire.change_answer')) @permission_required("questionnaire.export")
def export_csv(request, qid): # questionnaire_id def export_csv(request, qid): # questionnaire_id
""" """
For a given questionnaire id, generaete a CSV containing all the For a given questionnaire id, generaete a CSV containing all the
@ -520,10 +529,40 @@ def dep_check(expr, runinfo, answerdict):
return check_answer[1:].strip() != actual_answer.strip() return check_answer[1:].strip() != actual_answer.strip()
return check_answer.strip() == actual_answer.strip() return check_answer.strip() == actual_answer.strip()
@login_required @permission_required("questionnaire.management")
def send_email(request, runinfo_id): def send_email(request, runinfo_id):
if request.method != "POST": if request.method != "POST":
return HttpResponse("This page MUST be called as a POST request.") return HttpResponse("This page MUST be called as a POST request.")
runinfo = get_object_or_404(RunInfo, pk=int(runinfo_id)) runinfo = get_object_or_404(RunInfo, pk=int(runinfo_id))
successful = _send_email(runinfo) successful = _send_email(runinfo)
return r2r("emailsent.html", request, runinfo=runinfo, successful=successful) return r2r("emailsent.html", request, runinfo=runinfo, successful=successful)
def generate_run(request, questionnaire_id):
"""
A view that can generate a RunID instance anonymously,
and then redirect to the questionnaire itself.
It uses a Subject with the givenname of 'Anonymous' and the
surname of 'User'. If this Subject does not exist, it will
be created.
This can be used with a URL pattern like:
(r'^take/(?P<questionnaire_id>[0-9]+)/$', 'questionnaire.views.generate_run'),
"""
qu = get_object_or_404(Questionnaire, id=questionnaire_id)
qs = qu.questionsets()[0]
su = Subject.objects.filter(givenname='Anonymous', surname='User')[0:1]
if su:
su = su[0]
else:
su = Subject(givenname='Anonymous', surname='User')
su.save()
hash = md5.new()
hash.update("".join(map(lambda i: chr(random.randint(0, 255)), range(16))))
hash.update(settings.SECRET_KEY)
key = hash.hexdigest()
run = RunInfo(subject=su, random=key, runid=key, questionset=qs)
run.save()
return HttpResponseRedirect(reverse('questionnaire', kwargs={'runcode': key}))