From 12da1c3d0dc0c9a0dc2a15e95f77d6fdfb2eee46 Mon Sep 17 00:00:00 2001 From: Helmut Merz Date: Mon, 18 Mar 2013 13:20:12 +0100 Subject: [PATCH] error checks and feedback --- common.py | 3 ++ knowledge/survey/base.py | 5 +--- knowledge/survey/browser.py | 48 +++++++++++++++++++++++++++++--- knowledge/survey/interfaces.py | 2 +- knowledge/survey/view_macros.pt | 28 ++++++++++++++++--- locales/de/LC_MESSAGES/loops.mo | Bin 23228 -> 23318 bytes locales/de/LC_MESSAGES/loops.po | 9 ++++-- 7 files changed, 79 insertions(+), 16 deletions(-) diff --git a/common.py b/common.py index 985f27d..3e566af 100644 --- a/common.py +++ b/common.py @@ -113,6 +113,9 @@ class AdapterBase(object): self.context = context self.__parent__ = context # to get the permission stuff right + def __hash__(self): + return hash(self.context) + def __getattr__(self, attr): self.checkAttr(attr) return getattr(self.context, '_' + attr, None) diff --git a/knowledge/survey/base.py b/knowledge/survey/base.py index a4a5d57..7dc0a19 100644 --- a/knowledge/survey/base.py +++ b/knowledge/survey/base.py @@ -62,7 +62,7 @@ class QuestionGroup(AdapterBase, QuestionGroup): _contextAttributes = list(IQuestionGroup) _adapterAttributes = AdapterBase._adapterAttributes + ( - 'questionnaire', 'questions', 'feedbackItems',) + 'questionnaire', 'questions', 'feedbackItems') _noexportAttributes = _adapterAttributes @property @@ -109,9 +109,6 @@ class Question(AdapterBase, Question): def questionnaire(self): return self.questionGroup.questionnaire - def __hash__(self): - return hash(self.context) - class FeedbackItem(AdapterBase, FeedbackItem): diff --git a/knowledge/survey/browser.py b/knowledge/survey/browser.py index 30c7cdd..7177c15 100644 --- a/knowledge/survey/browser.py +++ b/knowledge/survey/browser.py @@ -23,11 +23,13 @@ surveys and self-assessments. from zope.app.pagetemplate import ViewPageTemplateFile from zope.cachedescriptors.property import Lazy +from zope.i18n import translate from cybertools.knowledge.survey.questionnaire import Response from loops.browser.concept import ConceptView from loops.common import adapted from loops.organize.party import getPersonForUser +from loops.util import _ template = ViewPageTemplateFile('view_macros.pt') @@ -36,6 +38,7 @@ class SurveyView(ConceptView): tabview = 'index.html' data = None + errors = None @Lazy def macro(self): @@ -56,20 +59,57 @@ class SurveyView(ConceptView): value = int(value) self.data[uid] = value response.values[question] = value + self.errors = self.check(response) + if self.errors: + return [] # TODO: store self.data in track - # else: - # get response from track if response is not None: result = response.getGroupedResult() return [dict(category=r[0].title, text=r[1].text, score=int(round(r[2] * 100))) for r in result] + def check(self, response): + errors = [] + values = response.values + for qu in self.adapted.questions: + if qu.required and qu not in values: + errors.append('Please answer the obligatory questions.') + break + qugroups = {} + for qugroup in self.adapted.questionGroups: + qugroups[qugroup] = 0 + for qu in values: + qugroups[qu.questionGroup] += 1 + for qugroup, count in qugroups.items(): + minAnswers = qugroup.minAnswers + if minAnswers in (u'', None): + minAnswers = len(qugroup.questions) + if count < minAnswers: + errors.append('Please answer the minimum number of questions.') + return errors + + def getInfoText(self, qugroup): + lang = self.languageInfo.language + text = qugroup.description + info = None + if qugroup.minAnswers in (u'', None): + info = translate(_(u'Please answer all questions.'), target_language=lang) + elif qugroup.minAnswers > 0: + info = translate(_(u'Please answer at least $minAnswers questions.', + mapping=dict(minAnswers=qugroup.minAnswers)), + target_language=lang) + if info: + text = u'%s
(%s)' % (text, info) + return text + def getValues(self, question): setting = None + # TODO: get response from track if self.data is not None: setting = self.data.get(question.uid) - noAnswer = [dict(value='none', checked=(setting == None))] - return noAnswer + [dict(value=i, checked=(setting == i)) + noAnswer = [dict(value='none', checked=(setting == None), + radio=(not question.required))] + return noAnswer + [dict(value=i, checked=(setting == i), radio=True) for i in reversed(range(question.answerRange))] diff --git a/knowledge/survey/interfaces.py b/knowledge/survey/interfaces.py index 1e23003..7b634b5 100644 --- a/knowledge/survey/interfaces.py +++ b/knowledge/survey/interfaces.py @@ -47,7 +47,7 @@ class IQuestionGroup(IConceptSchema, interfaces.IQuestionGroup): title=_(u'Minimum Number of Answers'), description=_(u'Minumum number of questions that have to be answered. ' 'Empty means all questions have to be answered.'), - default=4, + default=None, required=False) diff --git a/knowledge/survey/view_macros.pt b/knowledge/survey/view_macros.pt index d225219..42757a2 100644 --- a/knowledge/survey/view_macros.pt +++ b/knowledge/survey/view_macros.pt @@ -3,8 +3,16 @@ + tal:define="feedback item/results; + errors item/errors"> +
+
+ +
+

Feedback

@@ -25,8 +33,15 @@
+ - + diff --git a/locales/de/LC_MESSAGES/loops.mo b/locales/de/LC_MESSAGES/loops.mo index e8c66bb176f6eb92cd6c9e1e96f1c9e401cdeff0..39dbbd7b78c9f2902fb16249adcdb9abb4e40d65 100644 GIT binary patch delta 8179 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 delta 8138 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 diff --git a/locales/de/LC_MESSAGES/loops.po b/locales/de/LC_MESSAGES/loops.po index 518d611..383441a 100644 --- a/locales/de/LC_MESSAGES/loops.po +++ b/locales/de/LC_MESSAGES/loops.po @@ -185,7 +185,7 @@ msgid "Minumum number of questions that have to be answered. Empty means all que msgstr "Anzahl der Fragen, die mindestens beantwortet werden müssen. Keine Angabe: Es müssen alle Fragen beantwortet werden." msgid "Required" -msgstr "Erforderlich" +msgstr "Pflichtfrage" msgid "Question must be answered." msgstr "Frage muss unbedingt beantwortet werden." @@ -241,8 +241,11 @@ msgstr "Bitte beantworten Sie mindestens $minAnswers Fragen." msgid "Please answer all questions." msgstr "Bitte beantworten Sie alle Fragen." -msgid "Please answer the obligatory questions marked with an asterisk." -msgstr "Bitte beantworten Sie die mit einem Stern markierten Pflichtfragen." +msgid "Please answer the obligatory questions." +msgstr "Bitte beantworten Sie die Pflichtfragen." + +msgid "Please answer the minimum number of questions." +msgstr "Bitte beantworten Sie die angegebene Mindestanzahl an Fragen je Fragengruppe." msgid "Obligatory question, must be answered" msgstr "Pflichtfrage, muss beantwortet werden"
 
+ +
+ +
  +
+
No answer   + title string:survey_value_${value/value}" /> + *** +