Merge branch 'master' of git://github.com/rmt/seantis-questionnaire
Conflicts: questionnaire/views.pyEmailTemplateFixes
commit
46a8468f4d
File diff suppressed because one or more lines are too long
|
@ -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'),
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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}))
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue