"""
open/dulcinea/lib/ui/survey.qpy
"""
from dulcinea.common import format_date
from dulcinea.survey import Survey, Surveyable, SurveysAndQuestions
from dulcinea.ui.form.date_time_widget import get_date_pair_form
from dulcinea.ui.form.survey_widget import get_widget_class, SurveyWidget
from dulcinea.ui.table import Table
from dulcinea.ui.util import set_csv_headers
from qp.fill.directory import Directory
from qp.fill.form import Form
from qp.fill.widget import SubmitWidget, StringWidget, TextWidget
from qp.lib.spec import require
from qp.pub.common import site_now, page
from qp.pub.common import get_user, redirect, header, footer
from qpy import xml, stringify
from types import FunctionType
class SurveyableUI(Directory):
def __init__(self, surveyable, decorate=None, title='Surveys'):
require(surveyable, Surveyable)
self.surveyable = surveyable
self.decorate = decorate
self.title = title
def get_exports(self):
if get_user().is_admin():
yield ('', 'index', 'Existing', 'Existing %s' % self.title)
yield ('new', 'new', 'New', 'New %s' % self.title[:-1])
else:
yield ('new', 'new', None, None)
def new:xml(self):
survey = self.surveyable.get_new_survey()
show_surveys_page([survey], "New %s" % self.title[:-1],
question_form(survey, self.surveyable),
self.decorate)
def survey_index:xml(self, title):
start_date = min(
[survey.get_timestamp()
for survey in self.surveyable.get_surveys()] + [site_now()])
form = get_date_pair_form(start_date=start_date)
if form.has_errors() or not form.get('date_pair'):
return page('Error', form.render())
start, end = form.get('date_pair')
surveys = [survey for survey in self.surveyable.get_surveys()
if start <= survey.get_timestamp() <= end]
questions = list(set([question for survey in surveys for question in
survey.get_questions()]))
table = Table()
table.column(date="Date")
table.column(user="User")
for j, question in enumerate(questions):
table.column(**{str("q%s" % j): stringify(question)})
for survey in surveys:
row = dict(date=format_date(survey.get_timestamp()),
user=survey.get_user().get_id())
for j, question in enumerate(questions):
row[str("q%s" % j)] = survey.get_answer_for_question_key(
question.get_key())
table.row(**row)
def csv:str():
set_csv_headers('survey_data.csv')
table.render_csv()
if form.get('csv'):
return csv()
def render:xml():
form.render()
'
'
'
'
if not surveys:
'
No matches
'
else:
table.render(css_class="pretty")
'
'
show_surveys_page(surveys, title, render(), self.decorate)
def index(self):
num_surveys = len(self.surveyable.get_surveys() or [])
return self.survey_index("%d %s" % (num_surveys, self.title))
def _q_lookup(self, component):
try:
index = int(component)
except ValueError:
return None
survey = self.surveyable.get_survey(index-1)
if survey:
return SurveyUI(survey, self.decorate, title=self.title[:-1])
class SurveyUI(Directory):
def __init__(self, survey, decorate=None, title='Survey'):
self.survey = survey
self.decorate = decorate
self.title = title
self.read_only=True
def get_exports(self):
yield ('', 'index', self.title,
"Taken by %s on %s" % (self.survey.get_user(),
self.survey.get_timestamp().strftime(
"%Y-%m-%d %H:%M")))
def index:xml(self):
show_surveys_page([self.survey], self.title,
question_form(self.survey, None, read_only=self.read_only),
self.decorate)
def get_pretty_id(obj):
return obj.get_id().replace('_', ' ').capitalize()
class SurveysAndQuestionsUI(SurveyableUI):
def __init__(self, surveys_and_questions, decorate=None, title='Surveys and Questions'):
require(surveys_and_questions, SurveysAndQuestions)
self.surveyable = self.questions_database = surveys_and_questions
self.decorate = decorate
self.title = title
def get_exports(self):
if get_user().is_admin():
yield ('', 'index', 'Existing', 'Existing %s %s' % (
get_pretty_id(self.questions_database), self.title))
yield ('new', 'new', 'New', 'New %s %s' % (
get_pretty_id(self.questions_database), self.title[:-1]))
else:
yield ('new', 'new', None, None)
yield ('thanks', 'thanks', '', '')
if get_user().is_admin():
yield('edit', 'edit', 'Edit', 'Edit %s properties' % (
get_pretty_id(self.questions_database)))
def new:xml(self):
survey = self.surveyable.get_new_survey()
page(self.surveyable.get_title(),
'',
question_form(survey, self.surveyable,
description=xml(self.surveyable.get_description()),
redirect_to=str('thanks')), '
')
def edit(self):
return surveys_and_questions_form(self.surveyable)
def thanks:xml(self):
form = Form()
form.add(SubmitWidget, 'submit', 'Done')
if form.is_submitted():
redirect('/')
page('Thanks', 'Your answers have been recorded.
', form.render())
def index(self):
if not get_user().is_admin():
redirect('new')
else:
surveys = self.surveyable.get_surveys()
title = "%s: %d %s" % (
get_pretty_id(self.questions_database),
len(surveys or []),
self.title)
return self.survey_index(title)
def survey_index:xml(self, title):
start_date = min(
[survey.get_timestamp()
for survey in self.surveyable.get_surveys()] + [site_now()])
form = get_date_pair_form(start_date=start_date)
if form.has_errors() or not form.get('date_pair'):
return page('Error', form.render())
start, end = form.get('date_pair')
surveys = [survey for survey in self.surveyable.get_surveys()
if start <= survey.get_timestamp() <= end]
questions = self.questions_database.get_questions()
table = Table()
table.column(date="Date")
table.column(user="User")
for j, question in enumerate(questions):
table.column(**{str("q%s" % j): "Q%d" % question.get_key() })
for survey in surveys:
row = dict(date=format_date(survey.get_timestamp()),
user=survey.get_user().get_id())
for j, question in enumerate(questions):
row[str("q%s" % j)] = survey.get_answer_for_question_key(
question.get_key())
table.row(**row)
def csv:str():
set_csv_headers('survey_data.csv')
table.render_csv()
if form.get('csv'):
return csv()
def render:xml():
form.render()
'
'
''
if not surveys:
'
No matches
'
else:
table.render(css_class="pretty")
'
'
show_surveys_page(surveys, title, render(), self.decorate)
class SurveyedSurveysAndQuestionsUI(SurveysAndQuestionsUI):
def __init__(self, surveyed, surveys_and_questions, read_only=False, decorate=None, title='Surveys'):
SurveysAndQuestionsUI.__init__(self, surveys_and_questions, decorate=decorate, title=title)
self.surveyed = surveyed
self.read_only = read_only
def new:xml(self):
if self.surveyed.get_survey():
survey = self.surveyed.get_survey().copy()
else:
survey = self.surveyable.get_new_survey()
page(self.surveyable.get_title(),
'',
question_form(survey, self.surveyable, surveyed=self.surveyed,
read_only=self.read_only,
description=xml(self.surveyable.get_description())), '
')
def show_surveys_page:xml(surveys, title, body, decorate):
if decorate is None:
header(title)
if len(surveys) == 1 and surveys[0].get_user():
''
"Submitted by user %s on %s" % (
surveys[0].get_user(),
surveys[0].get_timestamp().strftime(str("%Y-%m-%d %H:%M")))
'
'
body
footer(title=title)
else:
decorate(surveys, body, title=title)
def process_survey_answers(form, survey, surveyable):
require(form, Form)
require(survey, Survey)
require(surveyable, Surveyable)
answers = []
for index in range(len(surveyable.get_new_survey().get_answers())):
answer = form.get(str(index))
if answer and answer.has_answer():
answers.append(answer)
if answers:
survey.set_answers(answers, get_user())
survey.set_timestamp()
for existing_survey in surveyable.get_surveys():
if survey == existing_survey:
return
else:
surveyable.add_survey(survey)
def survey_form(surveyables):
"""(surveyables : [(surveyable:Surveyable, decorator:FunctionType)])
Manage a list of surveys using a survey widget
"""
for surveyable, decorator in surveyables:
require(surveyable, Surveyable)
require(decorator, (FunctionType, None))
form = Form()
for index, (surveyable, decorator) in enumerate(surveyables):
survey = surveyable.get_new_survey()
form.add(SurveyWidget, "survey%d" % index,
value=survey, decorator=decorator, title="%d) " % (index+1))
form.add_submit('submit', 'Submit')
form.add_reset('cancel', 'Cancel')
if not form.is_submitted() or form.has_errors():
return form.render()
else:
for index, (surveyable, decorator) in enumerate(surveyables):
survey = form.get("survey%d" % index)
if survey and survey.has_answers():
surveyable.add_survey(survey)
redirect('.')
def surveys_and_questions_form(surveys_and_questions):
form = Form()
form.add(SubmitWidget, 'save', 'Save')
form.add(SubmitWidget, 'cancel', 'Cancel')
if form.get('cancel'):
redirect('.')
form.add(StringWidget, 'title', value=surveys_and_questions.get_title(),
title='Title', size=102)
form.add(TextWidget, 'description', title='Description',
value=surveys_and_questions.get_description(), rows=30, cols=90)
if not form.is_submitted():
return page('Edit %s' % surveys_and_questions.get_id(), form.render())
surveys_and_questions.set_title(form.get('title'))
surveys_and_questions.set_description(form.get('description'))
redirect('.')
def question_form(survey, surveyable, surveyed=None, description=None, redirect_to=".",
read_only=False, **kwargs):
"""(survey : Survey)
Manage a series of question widgets individually
"""
form = Form()
if read_only:
for index, answer in enumerate(survey.get_answers()):
question = answer.get_question()
form.add(get_widget_class(answer.get_question()), str(index),
value=answer,
title=xml('%d) %s
') % (
question.get_key(), question.get_title()),
disabled=(survey.has_answers() or None))
else:
for index, answer in enumerate(surveyable.get_new_survey().get_answers()):
question = answer.get_question()
if survey.get_answer_for_question_key(question.get_key()):
answer = survey.get_answer_for_question_key(question.get_key())
form.add(get_widget_class(answer.get_question()), str(index),
value=answer,
title=xml('%d) %s
') % (
question.get_key(), question.get_title()))
form.add_submit('submit', 'Submit')
form.add_submit('cancel', 'Cancel')
if form.get('cancel'):
redirect('/')
if not form.is_submitted() or form.has_errors():
def render:xml():
description
form.render()
return render()
process_survey_answers(form, survey, surveyable)
if surveyed is not None:
surveyed.connect(survey)
redirect(redirect_to)
def format_survey_css:str():
"""
div.survey {
margin-left: 2em;
}
.survey-sub-title {
font-size: small;
}
table.survey {
padding: 0;
margin: 0;
vertical-align: middle;
text-align: left;
font-size: small;
margin-left: 2em;
}
table.survey th.right {
text-align: right;
padding-right: 1ex;
}
"""