""" 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; } """