class RespondentGroupCondition(BaseModel): VALIDATION_TESTS = [(validator.__name__, validator.__name__) for validator in Answer.validators()] respondent_group = models.ForeignKey(RespondentGroup, related_name='group_conditions') test_question = models.ForeignKey(ParameterTemplate, related_name='group_condition') validation_test = models.CharField(max_length=200, null=True, blank=True, choices=VALIDATION_TESTS) class Meta: app_label = 'survey' @property def test_params(self): return [t.param for t in self.test_arguments] def params_display(self): params = [] for arg in self.text_arguments: if self.question.answer_type == MultiChoiceAnswer.choice_name(): params.append(self.question.options.get(order=arg.param).text) else: params.append(arg.param) return params @property def test_arguments(self): return GroupTestArgument.objects.filter( group_condition=self).order_by('position')
class RandomizationCriterion(BaseModel): VALIDATION_TESTS = [(validator.__name__, validator.__name__) for validator in Answer.validators()] survey = models.ForeignKey(Survey, related_name='randomization_criteria') listing_question = models.ForeignKey(Question, related_name='criteria') validation_test = models.CharField( max_length=200, choices=VALIDATION_TESTS, ) class Meta: app_label = 'survey' @property def test_params(self): return [t.param for t in self.text_arguments] @property def text_arguments(self): return self.arguments.all() def params_display(self): params = [] for arg in self.text_arguments: if self.listing_question.answer_type == MultiChoiceAnswer.choice_name( ): try: params.append( self.listing_question.options.get( order=arg.param).text) except ValueError: params.append(arg.param) else: params.append(arg.param) return params def passes_test(self, value): answer_class = Answer.get_class(self.listing_question.answer_type) method = getattr(answer_class, self.validation_test, None) if method not in answer_class.validators(): raise ValueError( 'unsupported validator defined on listing question') return method(value, *self.test_params) def qs_passes_test(self, value_key, queryset): answer_class = Answer.get_class(self.listing_question.answer_type) method = getattr(answer_class, 'fetch_%s' % self.validation_test, None) return method(value_key, *self.test_params, qs=queryset) @property def test_arguments(self): return CriterionTestArgument.objects.filter( test_condition=self).values_list('param', flat=True).order_by('position')
class QuestionFlow(BaseModel): VALIDATION_TESTS = [(validator.__name__, validator.__name__) for validator in Answer.validators()] question = models.ForeignKey(Question, related_name='flows') validation_test = models.CharField(max_length=200, null=True, blank=True, choices=VALIDATION_TESTS) name = models.CharField( max_length=200, null=True, blank=True ) #if validation passes, classify this flow response as having this value desc = models.CharField( max_length=200, null=True, blank=True) #this would provide a brief description of this flow next_question = models.ForeignKey(Question, related_name='connecting_flows', null=True, blank=True, on_delete=models.SET_NULL) class Meta: app_label = 'survey' # unique_together = [('question', 'next_question', 'desc', ),] @property def test_params(self): return [t.param for t in self.text_arguments] def params_display(self): params = [] for arg in self.text_arguments: if self.question.answer_type == MultiChoiceAnswer.choice_name(): params.append(self.question.options.get(order=arg.param).text) else: params.append(arg.param) return params @property def text_arguments(self): return TextArgument.objects.filter(flow=self).order_by('position') @property def test_arguments(self): return TestArgument.objects.filter( flow=self).select_subclasses().order_by('position') def save(self, *args, **kwargs): # if self.name is None: # if self.next_question: # identifier = self.next_question.identifier # else: identifier = '' # self.name = "%s %s %s" % (self.question.identifier, self.validation_test or "", identifier) return super(QuestionFlow, self).save(*args, **kwargs)
class IndicatorVariableCriteria(BaseModel): """A variable is essential a filtered set of answers. Hence they need the filter criteria to be defined. This is the purpose of this class """ VALIDATION_TESTS = [(validator.__name__, validator.__name__) for validator in Answer.validators()] variable = models.ForeignKey(IndicatorVariable, related_name='criteria') # batch & parameter_list questions test_question = models.ForeignKey( Question, related_name='indicator_criteria', verbose_name='Filter') validation_test = models.CharField(max_length=200, choices=VALIDATION_TESTS, verbose_name='Condition') class Meta: app_label = 'survey' @property def test_params(self): return [t.param for t in self.test_arguments] def params_display(self): params = [] for arg in self.text_arguments: if self.question.answer_type == MultiChoiceAnswer.choice_name(): params.append(self.question.options.get(order=arg.param).text) else: params.append(arg.param) return params @property def test_arguments(self): return IndicatorCriteriaTestArgument.objects.filter( criteria=self).order_by('position') @property def prepped_args(self): answer_class = Answer.get_class(self.test_question.answer_type) return [answer_class.prep_value(val) for val in self.test_params] def qs_passes_test(self, value_key, queryset): test_args = self.prepped_args method = getattr(Answer, 'fetch_%s' % self.validation_test, None) return method(value_key, *test_args, qs=queryset)
class ResponseValidation(CloneableMixin, BaseModel): VALIDATION_TESTS = [(validator.__name__, validator.__name__) for validator in Answer.validators()] validation_test = models.CharField(max_length=200, choices=VALIDATION_TESTS) # if validation passes, classify this flow response as having this value constraint_message = models.TextField(default='', blank=True, null=True) @property def dconstraint_message(self): if self.constraint_message: return self.constraint_message else: return unicode(self) class Meta: app_label = 'survey' @property def test_params(self): return [t.param for t in self.text_arguments] @property def text_arguments(self): return TextArgument.objects.filter(validation=self).order_by('position') @property def test_arguments(self): return TextArgument.objects.filter(validation=self).order_by('position') def __unicode__(self): return '%s: %s' % (self.validation_test, ', '.join(self.text_arguments.values_list('param', flat=True))) def get_odk_constraint(self, test_question): answer_class = Answer.get_class(test_question.answer_type) return mark_safe(answer_class.print_odk_validation('.', self.validation_test, *self.test_params)) def validate(self, value, test_question): answer_class = Answer.get_class(test_question.answer_type) method = getattr(answer_class, self.validation_test, None) return method(value, *self.test_params)
class QuestionFlow(CloneableMixin, BaseModel): VALIDATION_TESTS = [(validator.__name__, validator.__name__) for validator in Answer.validators()] question = models.ForeignKey(Question, related_name='flows') question_type = models.CharField(max_length=100) validation = models.ForeignKey(ResponseValidation, null=True, blank=True, related_name='flows') # if validation passes, classify this flow response as having this value name = models.CharField(max_length=200, null=True, blank=True) # this would provide a brief description of this flow desc = models.CharField(max_length=200, null=True, blank=True) next_question = models.ForeignKey( Question, related_name='connecting_flows', null=True, blank=True, on_delete=models.SET_NULL) next_question_type = models.CharField(max_length=100) def params_display(self): return self.text_arguments.values_list('param', flat=True) @property def validation_test(self): if self.validation: return self.validation.validation_test @validation_test.setter def validation_test(self, test): if self.validation: self.validation.validation_test = test self.validation.save() else: self.validation = ResponseValidation.objects.create(validation_test=test) class Meta: app_label = 'survey' # unique_together = [('question', 'next_question', 'desc', ),] @property def test_params(self): if self.validation: return self.validation.test_params @property def text_arguments(self): if self.validation: return self.validation.text_arguments else: from survey.models import TextArgument return TextArgument.objects.none() @property def test_arguments(self): if self.validation: return self.validation.test_arguments else: from survey.models import TextArgument return TextArgument.objects.none() def save(self, *args, **kwargs): invalidate_obj(self.question) invalidate_obj(QuestionSet.get(pk=self.question.qset.pk)) if self.next_question: invalidate_obj(self.next_question) return super(QuestionFlow, self).save(*args, **kwargs)