def test_reanswer_selection_in_form_question_creates_flow_to_same_question(self): ''' :return: ''' q1 = Question.objects.create(qset_id=self.qset.id, response_validation_id=1, identifier='test1', text='test1', answer_type=DateAnswer.choice_name()) q2 = Question.objects.create(qset_id=self.qset.id, response_validation_id=1, identifier='test2', text='test2', answer_type=DateAnswer.choice_name()) q3 = Question.objects.create(qset_id=self.qset.id, response_validation_id=1, identifier='test3', text='test3', answer_type=DateAnswer.choice_name()) q4 = Question.objects.create(qset_id=self.qset.id, response_validation_id=1, identifier='test4', text='test4', answer_type=DateAnswer.choice_name()) q5 = Question.objects.create(qset_id=self.qset.id, response_validation_id=1, identifier='test5', text='test5', answer_type=DateAnswer.choice_name()) self.batch.start_question = q1 QuestionFlow.objects.create(question_id=q1.id, next_question_id=q2.id) QuestionFlow.objects.create(question_id=q2.id, next_question_id=q3.id) QuestionFlow.objects.create(question_id=q3.id, next_question_id=q4.id) QuestionFlow.objects.create(question_id=q4.id, next_question_id=q5.id) test_condition = 'between' test_param_upper = datetime.now() test_param_lower = datetime.now() - timedelta(days=3) form_data = { 'action': LogicForm.REANSWER, 'condition': test_condition, 'min_value': test_param_lower, 'max_value': test_param_upper } l = LogicForm(q2, data=form_data) if l.is_valid(): l.save() # now check if equivalent Question flow and test arguments were # created try: qf = QuestionFlow.objects.get( question_id=q2.id, next_question_id=q2.id) TextArgument.objects.get( flow=qf, position=0, param=test_param_lower) TextArgument.objects.create( flow=qf, position=1, param=test_param_upper) QuestionFlow.objects.get( question_id=q1.id, next_question_id=q2.id) QuestionFlow.objects.get( question_id=q2.id, next_question_id=q3.id) self.assertTrue(True) return except QuestionFlow.DoesNotExist: self.assertFalse(False, 'flow not existing') pass except TextArgument: self.assertTrue(False, 'text agrunments not saved') pass else: self.assertTrue(False, 'Invalid form')
def test_end_interview_selection_in_form_question_creates_flow_to_with_no_next_question(self): ''' :return: ''' yes = 'yes' no = 'no' q1 = Question.objects.create(qset_id=self.qset.id, response_validation_id=1, identifier='test1', text='test1', answer_type=DateAnswer.choice_name()) q2 = Question.objects.create(qset_id=self.qset.id, response_validation_id=1, identifier='test2', text='test2', answer_type=MultiChoiceAnswer.choice_name()) q_o1 = QuestionOption.objects.create(question_id=q2.id, text=yes, order=1) QuestionOption.objects.create(question_id=q2.id, text=no, order=2) q3 = Question.objects.create(qset_id=self.qset.id, response_validation_id=1, identifier='test3', text='test3', answer_type=DateAnswer.choice_name()) q4 = Question.objects.create(qset_id=self.qset.id, response_validation_id=1, identifier='test4', text='test4', answer_type=DateAnswer.choice_name()) q5 = Question.objects.create(qset_id=self.qset.id, response_validation_id=1, identifier='test5', text='test5', answer_type=DateAnswer.choice_name()) self.batch.start_question = q1 QuestionFlow.objects.create(question_id=q1.id, next_question_id=q2.id) QuestionFlow.objects.create(question_id=q2.id, next_question_id=q3.id) QuestionFlow.objects.create(question_id=q3.id, next_question_id=q4.id) QuestionFlow.objects.create(question_id=q4.id, next_question_id=q5.id) test_condition = MultiChoiceAnswer.validators()[0].__name__ form_data = { 'action': LogicForm.END_INTERVIEW, 'condition': test_condition, 'option': q_o1.order } l = LogicForm(q2, data=form_data) if l.is_valid(): l.save() # now check if equivalent Question flow and test arguments were # created try: qf = QuestionFlow.objects.get( question_id=q2.id, next_question_id__isnull=True) TextArgument.objects.get(flow=qf, position=0, param=q_o1.order) QuestionFlow.objects.get( question_id=q1.id, next_question_id=q2.id) QuestionFlow.objects.get( question_id=q2.id, next_question_id=q3.id) self.assertTrue(True) return except QuestionFlow.DoesNotExist: self.assertTrue(False, 'flow not existing') pass except TextArgument: self.assertTrue(False, 'text agrunments not saved') pass else: self.assertFalse(False, 'Invalid form')
def __init__(self, question, initial=None, *args, **kwargs): super(LogicForm, self).__init__(initial=initial, *args, **kwargs) data = kwargs.get('data', None) batch = question.qset self.question = question self.batch = batch self.fields['condition'] = forms.ChoiceField( label='Eligibility criteria', choices=[], widget=forms.Select, required=False) self.fields['attribute'] = forms.ChoiceField( label='Attribute', choices=[ ('value', 'Value'), ], widget=forms.Select, required=False) self.fields['condition'].choices = [ (validator.__name__, validator.__name__.upper()) for validator in Answer.get_class( question.answer_type).validators()] if question.answer_type in [ MultiChoiceAnswer.choice_name(), MultiSelectAnswer.choice_name()]: self.fields['option'] = forms.ChoiceField( label='', choices=[], widget=forms.Select, required=True) self.fields['option'].choices = [ (option.text, option.text) for option in question.options.all()] else: self.fields['value'] = forms.CharField(label='', required=False) self.fields['min_value'] = forms.CharField( label='', required=False, widget=forms.TextInput( attrs={ 'placeholder': 'Min Value'})) self.fields['max_value'] = forms.CharField( label='', required=False, widget=forms.TextInput( attrs={ 'placeholder': 'Max Value'})) if question.answer_type == DateAnswer.choice_name(): self.fields['value'].widget.attrs['class'] = 'datepicker' self.fields['min_value'].widget.attrs['class'] = 'datepicker' self.fields['max_value'].widget.attrs['class'] = 'datepicker' # validate_with_question = forms.ChoiceField(label='', choices=[], widget=forms.Select, required=False) self.fields['action'] = forms.ChoiceField( label='Then', choices=[], widget=forms.Select, required=True) flows = self.question.flows.all() [f.next_question.pk for f in flows if f.next_question] next_q_choices = [(q.pk, q.text) for q in batch.questions_inline( ) if q.pk is not self.question.pk] # and q.pk not in existing_nexts] next_q_choices.extend([(q.pk, q.text) for q in batch.zombie_questions()]) self.fields['next_question'] = forms.ChoiceField( label='', choices=next_q_choices, widget=forms.Select, required=False) #self.fields['next_question'].widget.attrs['class'] = 'chzn-select' self.fields['action'].choices = self.ACTIONS.items() data.get('action', None) if data else None
def __init__(self, *args, **kwargs): super(AnswerForm, self).__init__(*args, **kwargs) # self.fields['uid'] = forms.CharField(initial=access.user_identifier, widget=forms.HiddenInput) if question.answer_type == DateAnswer.choice_name(): self.fields['value'] = forms.DateField( label='Answer', input_formats=[ settings.DATE_FORMAT, ], widget=forms.DateInput(attrs={ 'placeholder': 'Answer', 'class': 'datepicker' }, format=settings.DATE_FORMAT)) if question.answer_type == GeopointAnswer.choice_name(): model_field = get_form_field_no_validation(forms.CharField) self.fields['value'] = model_field( label='Answer', widget=forms.TextInput( attrs={ 'placeholder': 'Lat[space4]Long[space4' 'Altitude[space4]Precision' })) if question.answer_type == MultiChoiceAnswer.choice_name(): self.fields['value'] = forms.ChoiceField( choices=[(opt.order, opt.text) for opt in question.options.all()], widget=forms.RadioSelect) self.fields['value'].empty_label = None if access.choice_name() == USSDAccess.choice_name(): self.fields['value'].widget = forms.NumberInput() if question.answer_type == MultiSelectAnswer.choice_name(): self.fields['value'] = forms.ModelMultipleChoiceField( queryset=question.options.all(), widget=forms.CheckboxSelectMultiple) accept_types = { AudioAnswer.choice_name(): 'audio/*', VideoAnswer.choice_name(): 'video/*', ImageAnswer.choice_name(): 'image/*' } if question.answer_type in [ AudioAnswer.choice_name(), VideoAnswer.choice_name(), ImageAnswer.choice_name() ]: self.fields['value'].widget.attrs = { 'accept': accept_types.get(question.answer_type, '|'.join(accept_types.values())) } if access.choice_name() == USSDAccess.choice_name(): self.fields['value'].label = '' else: self.fields['value'].label = 'Answer'
def _create_test_non_group_questions(self, qset): # Basically create questions for this question set which is not having groups self._create_ussd_non_group_questions(qset) # Multiselect questions data = { 'answer_type': MultiSelectAnswer.choice_name(), 'text': 'multi select answer text', 'identifier': 'multi2_select_identifier_%s' % random.randint(1, 100), 'qset': qset.id, 'options': ['Y', 'N', 'MB'] } self._save_question(qset, data) # Date answer data = { 'answer_type': DateAnswer.choice_name(), 'text': 'date answer text', 'identifier': 'date2_identifier_%s' % random.randint(1, 100), 'qset': qset.id, } self._save_question(qset, data) # Geopoint answer data = { 'answer_type': GeopointAnswer.choice_name(), 'text': 'geo point text', 'identifier': 'geo2_identifier_%s' % random.randint(1, 100), 'qset': qset.id } self._save_question(qset, data) # Image answer data = { 'answer_type': ImageAnswer.choice_name(), 'text': 'image answer text', 'identifier': 'image2_identifier_%s' % random.randint(1, 100), 'qset': qset.id } self._save_question(qset, data) # Audio answer data = { 'answer_type': AudioAnswer.choice_name(), 'text': 'audio answer text', 'identifier': 'audio2_identifier_%s' % random.randint(1, 100), 'qset': qset.id } self._save_question(qset, data) # Video answer data = { 'answer_type': VideoAnswer.choice_name(), 'text': 'video answer text', 'identifier': 'video2_identifier_%s' % random.randint(1, 100), 'qset': qset.id } self._save_question(qset, data) self.qset.refresh_from_db()
def test_answer_value_methods(self): value = 'me doing somthing' test_answer1 = 'nothing good' self.assertFalse(Answer.equals(test_answer1, value)) self.assertTrue(Answer.equals(value, value)) self.assertTrue(Answer.starts_with(value, 'me d')) self.assertFalse(Answer.ends_with(value, 'no thing')) self.assertTrue(Answer.ends_with(value, 'somthing')) self.assertFalse(Answer.greater_than(5, 9)) self.assertTrue(Answer.greater_than(9, 5)) self.assertTrue(Answer.less_than(5, 9)) self.assertFalse(Answer.less_than(9, 5)) self.assertFalse(Answer.between(9, 5, 7)) self.assertTrue(Answer.between(9, 5, 11)) self.assertTrue(Answer.passes_test('17 > 10')) self.assertFalse(NumericalAnswer.greater_than(5, 9)) self.assertTrue(NumericalAnswer.greater_than(9, 5)) self.assertTrue(NumericalAnswer.less_than(5, 9)) self.assertFalse(NumericalAnswer.less_than(9, 5)) self.assertFalse(NumericalAnswer.between(9, 5, 7)) self.assertTrue(NumericalAnswer.between(9, 5, 11)) self.assertFalse(TextAnswer.equals(test_answer1, value)) self.assertTrue(TextAnswer.equals(value, value)) self.assertFalse(MultiChoiceAnswer.equals(test_answer1, value)) self.assertTrue(MultiChoiceAnswer.equals(value, value)) self.assertFalse(MultiSelectAnswer.equals(test_answer1, value)) self.assertTrue(MultiSelectAnswer.equals(value, value)) self.assertFalse(DateAnswer.greater_than('12-09-2017', '12-09-2017')) self.assertTrue(DateAnswer.greater_than('13-09-2017', '12-09-2017')) self.assertFalse(DateAnswer.less_than('18-09-2017', '12-09-2017')) self.assertTrue(DateAnswer.less_than('13-09-2017', '17-09-2017')) self.assertFalse( DateAnswer.between('18-09-2017', '12-09-2017', '16-09-2017')) self.assertTrue( DateAnswer.between('14-09-2017', '12-09-2017', '16-09-2017'))
def __init__(self, *args, **kwargs): super(AnswerForm, self).__init__(*args, **kwargs) # self.fields['uid'] = forms.CharField(initial=access.user_identifier, widget=forms.HiddenInput) if question.answer_type == DateAnswer.choice_name(): self.fields['value'] = forms.DateField( label='Answer', input_formats=[ settings.DATE_FORMAT, ], widget=forms.DateInput( attrs={ 'placeholder': 'Answer', 'class': 'datepicker'}, format=settings.DATE_FORMAT)) if question.answer_type == GeopointAnswer.choice_name(): model_field = get_form_field_no_validation(forms.CharField) self.fields['value'] = model_field(label='Answer', widget=forms.TextInput( attrs={'placeholder': 'Lat[space4]Long[space4' 'Altitude[space4]Precision'})) if question.answer_type == MultiChoiceAnswer.choice_name(): self.fields['value'] = forms.ChoiceField(choices=[(opt.order, opt.text) for opt in question.options.all()], widget=forms.RadioSelect) self.fields['value'].empty_label = None if access.choice_name() == USSDAccess.choice_name(): self.fields['value'].widget = forms.NumberInput() if question.answer_type == MultiSelectAnswer.choice_name(): self.fields['value'] = forms.ModelMultipleChoiceField( queryset=question.options.all(), widget=forms.CheckboxSelectMultiple) accept_types = {AudioAnswer.choice_name(): 'audio/*', VideoAnswer.choice_name(): 'video/*', ImageAnswer.choice_name(): 'image/*' } if question.answer_type in [ AudioAnswer.choice_name(), VideoAnswer.choice_name(), ImageAnswer.choice_name()]: self.fields['value'].widget.attrs = { 'accept': accept_types.get( question.answer_type, '|'.join( accept_types.values()))} if access.choice_name() == USSDAccess.choice_name(): self.fields['value'].label = '' else: self.fields['value'].label = 'Answer'
def test_odk_answer_methods(self): # test odk contain path = '/qset/qset1/surveyQuestions/q1' value = 'me doing somthing' self.assertEquals(Answer.odk_contains(path, value), "regex(%s, '.*(%s).*')" % (path, value)) self.assertEquals(Answer.odk_starts_with(path, value), "regex(%s, '^(%s).*')" % (path, value)) self.assertEquals(Answer.odk_ends_with(path, value), "regex(%s, '.*(%s)$')" % (path, value)) value = 4 upperlmt = 10 self.assertEquals(Answer.odk_greater_than(path, value), "%s > '%s'" % (path, value)) self.assertEquals(Answer.odk_less_than(path, value), "%s < '%s'" % (path, value)) self.assertEquals( Answer.odk_between(path, value, upperlmt), "(%s > '%s') and (%s <= '%s')" % (path, value, path, upperlmt)) self.assertEquals(NumericalAnswer.odk_less_than(path, value), "%s < %s" % (path, value)) self.assertEquals( NumericalAnswer.odk_between(path, value, upperlmt), "(%s > %s) and (%s <= %s)" % (path, value, path, upperlmt)) value = '20-07-2017' self.assertEquals(DateAnswer.odk_greater_than(path, value), "%s > %s" % (path, DateAnswer.to_odk_date(value))) self.assertEquals(DateAnswer.odk_less_than(path, value), "%s < %s" % (path, DateAnswer.to_odk_date(value))) upperlmt = '25-08-2017' self.assertEquals( DateAnswer.odk_between(path, value, upperlmt), "(%s > %s) and (%s <= %s)" % (path, DateAnswer.to_odk_date(value), path, DateAnswer.to_odk_date(upperlmt)))
def __init__(self, initial=None, question=None, *args, **kwargs): super(LogicForm, self).__init__(initial=initial, *args, **kwargs) data = kwargs.get('data', None) batch = question.batch self.question = question self.batch = batch self.fields['condition'] = forms.ChoiceField( label='Eligibility criteria', choices=[], widget=forms.Select, required=False) self.fields['attribute'] = forms.ChoiceField(label='Attribute', choices=[ ('value', 'Value'), ], widget=forms.Select, required=False) self.fields['condition'].choices = [(validator.__name__, validator.__name__.upper()) \ for validator in Answer.get_class(question.answer_type).validators()] if question.answer_type in [ MultiChoiceAnswer.choice_name(), MultiSelectAnswer.choice_name() ]: self.fields['option'] = forms.ChoiceField(label='', choices=[], widget=forms.Select, required=True) self.fields['option'].choices = [ (option.order, option.text) for option in question.options.all() ] else: self.fields['value'] = forms.CharField(label='', required=False) self.fields['min_value'] = forms.CharField( label='', required=False, widget=forms.TextInput(attrs={'placeholder': 'Min Value'})) self.fields['max_value'] = forms.CharField( label='', required=False, widget=forms.TextInput(attrs={'placeholder': 'Max Value'})) if question.answer_type == DateAnswer.choice_name(): self.fields['value'].widget.attrs['class'] = 'datepicker' self.fields['min_value'].widget.attrs['class'] = 'datepicker' self.fields['max_value'].widget.attrs['class'] = 'datepicker' # validate_with_question = forms.ChoiceField(label='', choices=[], widget=forms.Select, required=False) self.fields['action'] = forms.ChoiceField(label='Then', choices=[], widget=forms.Select, required=True) flows = self.question.flows.all() existing_nexts = [f.next_question.pk for f in flows if f.next_question] next_q_choices = [(q.pk, q.text) for q in batch.questions_inline() if q.pk is not self.question.pk] # and q.pk not in existing_nexts] next_q_choices.extend([(q.pk, q.text) for q in batch.zombie_questions()]) self.fields['next_question'] = forms.ChoiceField( label='', choices=next_q_choices, widget=forms.Select, required=False) self.fields['next_question'].widget.attrs['class'] = 'chzn-select' self.fields['action'].choices = self.ACTIONS.items() action_sent = data.get('action', None) if data else None
def handle(self, *args, **kwargs): self.stdout.write('Creating permissions....') content_type = ContentType.objects.get_for_model(User) Permission.objects.get_or_create(codename='can_enter_data', name='Can enter data', content_type=content_type) Permission.objects.get_or_create(codename='can_view_batches', name='Can view Batches', content_type=content_type) Permission.objects.get_or_create(codename='can_view_interviewers', name='Can view Interviewers', content_type=content_type) Permission.objects.get_or_create(codename='can_view_aggregates', name='Can view Aggregates', content_type=content_type) Permission.objects.get_or_create(codename='view_completed_survey', name='Can view Completed Surveys', content_type=content_type) Permission.objects.get_or_create(codename='can_view_households', name='Can view Households', content_type=content_type) Permission.objects.get_or_create(codename='can_view_locations', name='Can view Locations', content_type=content_type) Permission.objects.get_or_create(codename='can_view_users', name='Can view Users', content_type=content_type) Permission.objects.get_or_create(codename='can_view_household_groups', name='Can view Household Groups', content_type=content_type) self.stdout.write('Permissions.') self.stdout.write('Creating answer definition... ') #ussd definition AnswerAccessDefinition.objects.get_or_create(channel=USSDAccess.choice_name(), answer_type=NumericalAnswer.choice_name()) AnswerAccessDefinition.objects.get_or_create(channel=USSDAccess.choice_name(), answer_type=TextAnswer.choice_name()) AnswerAccessDefinition.objects.get_or_create(channel=USSDAccess.choice_name(), answer_type=MultiChoiceAnswer.choice_name()) #ODK definition AnswerAccessDefinition.objects.get_or_create(channel=ODKAccess.choice_name(), answer_type=NumericalAnswer.choice_name()) AnswerAccessDefinition.objects.get_or_create(channel=ODKAccess.choice_name(), answer_type=TextAnswer.choice_name()) AnswerAccessDefinition.objects.get_or_create(channel=ODKAccess.choice_name(), answer_type=MultiChoiceAnswer.choice_name()) AnswerAccessDefinition.objects.get_or_create(channel=ODKAccess.choice_name(), answer_type=MultiSelectAnswer.choice_name()) AnswerAccessDefinition.objects.get_or_create(channel=ODKAccess.choice_name(), answer_type=ImageAnswer.choice_name()) AnswerAccessDefinition.objects.get_or_create(channel=ODKAccess.choice_name(), answer_type=GeopointAnswer.choice_name()) AnswerAccessDefinition.objects.get_or_create(channel=ODKAccess.choice_name(), answer_type=DateAnswer.choice_name()) AnswerAccessDefinition.objects.get_or_create(channel=ODKAccess.choice_name(), answer_type=AudioAnswer.choice_name()) AnswerAccessDefinition.objects.get_or_create(channel=ODKAccess.choice_name(), answer_type=VideoAnswer.choice_name()) #web form definition AnswerAccessDefinition.objects.get_or_create(channel=WebAccess.choice_name(), answer_type=NumericalAnswer.choice_name()) AnswerAccessDefinition.objects.get_or_create(channel=WebAccess.choice_name(), answer_type=TextAnswer.choice_name()) AnswerAccessDefinition.objects.get_or_create(channel=WebAccess.choice_name(), answer_type=MultiChoiceAnswer.choice_name()) AnswerAccessDefinition.objects.get_or_create(channel=WebAccess.choice_name(), answer_type=MultiSelectAnswer.choice_name()) AnswerAccessDefinition.objects.get_or_create(channel=WebAccess.choice_name(), answer_type=ImageAnswer.choice_name()) AnswerAccessDefinition.objects.get_or_create(channel=WebAccess.choice_name(), answer_type=GeopointAnswer.choice_name()) AnswerAccessDefinition.objects.get_or_create(channel=WebAccess.choice_name(), answer_type=DateAnswer.choice_name()) AnswerAccessDefinition.objects.get_or_create(channel=WebAccess.choice_name(), answer_type=AudioAnswer.choice_name()) AnswerAccessDefinition.objects.get_or_create(channel=WebAccess.choice_name(), answer_type=VideoAnswer.choice_name()) self.stdout.write('Successfully imported!')
def test_end_interview_selection_in_form_question_creates_flow_to_with_no_next_question( self): ''' :return: ''' yes = 'yes' no = 'no' q1 = Question.objects.create(qset_id=self.qset.id, response_validation_id=1, identifier='test1', text='test1', answer_type=DateAnswer.choice_name()) q2 = Question.objects.create( qset_id=self.qset.id, response_validation_id=1, identifier='test2', text='test2', answer_type=MultiChoiceAnswer.choice_name()) q_o1 = QuestionOption.objects.create(question_id=q2.id, text=yes, order=1) QuestionOption.objects.create(question_id=q2.id, text=no, order=2) q3 = Question.objects.create(qset_id=self.qset.id, response_validation_id=1, identifier='test3', text='test3', answer_type=DateAnswer.choice_name()) q4 = Question.objects.create(qset_id=self.qset.id, response_validation_id=1, identifier='test4', text='test4', answer_type=DateAnswer.choice_name()) q5 = Question.objects.create(qset_id=self.qset.id, response_validation_id=1, identifier='test5', text='test5', answer_type=DateAnswer.choice_name()) self.batch.start_question = q1 QuestionFlow.objects.create(question_id=q1.id, next_question_id=q2.id) QuestionFlow.objects.create(question_id=q2.id, next_question_id=q3.id) QuestionFlow.objects.create(question_id=q3.id, next_question_id=q4.id) QuestionFlow.objects.create(question_id=q4.id, next_question_id=q5.id) test_condition = MultiChoiceAnswer.validators()[0].__name__ form_data = { 'action': LogicForm.END_INTERVIEW, 'condition': test_condition, 'option': q_o1.order } l = LogicForm(q2, data=form_data) if l.is_valid(): l.save() # now check if equivalent Question flow and test arguments were # created try: qf = QuestionFlow.objects.get(question_id=q2.id, next_question_id__isnull=True) TextArgument.objects.get(flow=qf, position=0, param=q_o1.order) QuestionFlow.objects.get(question_id=q1.id, next_question_id=q2.id) QuestionFlow.objects.get(question_id=q2.id, next_question_id=q3.id) self.assertTrue(True) return except QuestionFlow.DoesNotExist: self.assertTrue(False, 'flow not existing') pass except TextArgument: self.assertTrue(False, 'text agrunments not saved') pass else: self.assertFalse(False, 'Invalid form')
def test_specify_wrong_min_value_gives_form_error(self): ''' :return: ''' q1 = Question.objects.create(qset_id=self.qset.id, response_validation_id=1, identifier='test1', text='test1', answer_type=DateAnswer.choice_name()) q2 = Question.objects.create(qset_id=self.qset.id, response_validation_id=1, identifier='test2', text='test2', answer_type=DateAnswer.choice_name()) q3 = Question.objects.create(qset_id=self.qset.id, response_validation_id=1, identifier='test3', text='test3', answer_type=DateAnswer.choice_name()) q4 = Question.objects.create(qset_id=self.qset.id, response_validation_id=1, identifier='test4', text='test4', answer_type=DateAnswer.choice_name()) q5 = Question.objects.create(qset_id=self.qset.id, response_validation_id=1, identifier='test5', text='test5', answer_type=DateAnswer.choice_name()) self.batch.start_question = q1 QuestionFlow.objects.create(question_id=q1.id, next_question_id=q2.id) QuestionFlow.objects.create(question_id=q2.id, next_question_id=q3.id) QuestionFlow.objects.create(question_id=q3.id, next_question_id=q4.id) QuestionFlow.objects.create(question_id=q4.id, next_question_id=q5.id) test_condition = 'between' test_param_upper = datetime.now() test_param_lower = 'some time ago' form_data = { 'action': LogicForm.REANSWER, 'condition': test_condition, 'min_value': test_param_lower, 'max_value': test_param_upper } l = LogicForm(q2, data=form_data) if l.is_valid(): l.save() # now check if equivalent Question flow and test arguments were # created try: qf = QuestionFlow.objects.get(question_id=q2.id, next_question_id=q2.id) TextArgument.objects.get(flow=qf, position=0, param=test_param_lower) TextArgument.objects.create(flow=qf, position=1, param=test_param_upper) QuestionFlow.objects.get(question_id=q1.id, next_question_id=q2.id) QuestionFlow.objects.get(question_id=q2.id, next_question_id=q3.id) self.assertTrue( False, 'completely wrong. bad values was saved as good!!') return except QuestionFlow.DoesNotExist: self.assertTrue(False, 'form valid flow not existing') pass except TextArgument: self.assertTrue(False, 'Form valid but text agrunments not saved') pass else: self.assertTrue(True)
def test_fetch_methods(self): qset = self.qset self._create_test_non_group_questions(qset) date_question = Question.objects.filter(answer_type=DateAnswer.choice_name()).first() smallest = '2017-01-20' medium = '2017-05-12' largest = '2017-10-20' answer1 = DateAnswer.create(self.interview, date_question, medium) answer2 = DateAnswer.create(self.interview, date_question, smallest) answer3 = DateAnswer.create(self.interview, date_question, largest) self.assertEquals(DateAnswer.fetch_greater_than('as_value', '2017-01-21').filter(as_text__in=[medium, largest]).count(), 2) self.assertEquals(DateAnswer.fetch_less_than('as_value', '2017-01-21').count(), 1) self.assertEquals(DateAnswer.fetch_less_than('as_value', '2017-01-21').first().as_text, smallest) fetched_inbetween = DateAnswer.fetch_between('as_value', '2017-01-21', '2017-05-13') self.assertEquals(fetched_inbetween.count(), 1) self.assertEquals(fetched_inbetween.first().as_text, medium) fetched_inbetween = DateAnswer.fetch_between('as_value', '2017-01-21', '2017-05-13') self.assertEquals(fetched_inbetween.count(), 1) self.assertEquals(fetched_inbetween.first().as_text, medium) self.assertEquals(DateAnswer.fetch_equals('as_value', medium).count(), 1) self.assertEquals(DateAnswer.fetch_equals('as_value', medium).first().as_text, medium) # check answers attribute is date class date_question = Question.objects.filter(answer_type=DateAnswer.choice_name()).first() self.assertEquals(date_question.answers().first().__class__, DateAnswer)
def test_answer_validators(self): validators = [ # supported validators 'starts_with', 'ends_with', 'equals', 'between', 'less_than', 'greater_than', 'contains', ] for func in Answer.validators(): self.assertIn(func.__name__, validators) # test Numeric Answer Validators validators = [ # supported validators 'equals', 'between', 'less_than', 'greater_than', ] for func in NumericalAnswer.validators(): self.assertIn(func.__name__, validators) # test Text Answer Validators validators = [ # supported validators 'starts_with', 'ends_with', 'equals', 'contains', ] for func in TextAnswer.validators(): self.assertIn(func.__name__, validators) # Multichoice validators = [ # supported validators 'equals', ] for func in MultiChoiceAnswer.validators(): self.assertIn(func.__name__, validators) # test Multiselect Answer Validators validators = [ # supported validators 'equals', 'contains', ] for func in MultiSelectAnswer.validators(): self.assertIn(func.__name__, validators) # test Date Answer Validators validators = [ # supported validators 'equals', 'between', 'less_than', 'greater_than', ] for func in DateAnswer.validators(): self.assertIn(func.__name__, validators) # GeoPoint Answer validators = [ # supported validators 'equals', ] for func in GeopointAnswer.validators(): self.assertIn(func.__name__, validators) # file answers should return no validators for answer_class in [VideoAnswer, AudioAnswer, ImageAnswer]: self.assertEquals(len(answer_class.validators()), 0)
def test_reanswer_selection_in_form_question_creates_flow_to_same_question( self): ''' :return: ''' q1 = Question.objects.create(qset_id=self.qset.id, response_validation_id=1, identifier='test1', text='test1', answer_type=DateAnswer.choice_name()) q2 = Question.objects.create(qset_id=self.qset.id, response_validation_id=1, identifier='test2', text='test2', answer_type=DateAnswer.choice_name()) q3 = Question.objects.create(qset_id=self.qset.id, response_validation_id=1, identifier='test3', text='test3', answer_type=DateAnswer.choice_name()) q4 = Question.objects.create(qset_id=self.qset.id, response_validation_id=1, identifier='test4', text='test4', answer_type=DateAnswer.choice_name()) q5 = Question.objects.create(qset_id=self.qset.id, response_validation_id=1, identifier='test5', text='test5', answer_type=DateAnswer.choice_name()) self.batch.start_question = q1 QuestionFlow.objects.create(question_id=q1.id, next_question_id=q2.id) QuestionFlow.objects.create(question_id=q2.id, next_question_id=q3.id) QuestionFlow.objects.create(question_id=q3.id, next_question_id=q4.id) QuestionFlow.objects.create(question_id=q4.id, next_question_id=q5.id) test_condition = 'between' test_param_upper = datetime.now() test_param_lower = datetime.now() - timedelta(days=3) form_data = { 'action': LogicForm.REANSWER, 'condition': test_condition, 'min_value': test_param_lower, 'max_value': test_param_upper } l = LogicForm(q2, data=form_data) if l.is_valid(): l.save() # now check if equivalent Question flow and test arguments were # created try: qf = QuestionFlow.objects.get(question_id=q2.id, next_question_id=q2.id) TextArgument.objects.get(flow=qf, position=0, param=test_param_lower) TextArgument.objects.create(flow=qf, position=1, param=test_param_upper) QuestionFlow.objects.get(question_id=q1.id, next_question_id=q2.id) QuestionFlow.objects.get(question_id=q2.id, next_question_id=q3.id) self.assertTrue(True) return except QuestionFlow.DoesNotExist: self.assertFalse(False, 'flow not existing') pass except TextArgument: self.assertTrue(False, 'text agrunments not saved') pass else: self.assertTrue(False, 'Invalid form')
def test_fetch_methods(self): qset = self.qset self._create_test_non_group_questions(qset) date_question = Question.objects.filter( answer_type=DateAnswer.choice_name()).first() smallest = '2017-01-20' medium = '2017-05-12' largest = '2017-10-20' answer1 = DateAnswer.create(self.interview, date_question, medium) answer2 = DateAnswer.create(self.interview, date_question, smallest) answer3 = DateAnswer.create(self.interview, date_question, largest) self.assertEquals( DateAnswer.fetch_greater_than( 'as_value', '2017-01-21').filter(as_text__in=[medium, largest]).count(), 2) self.assertEquals( DateAnswer.fetch_less_than('as_value', '2017-01-21').count(), 1) self.assertEquals( DateAnswer.fetch_less_than('as_value', '2017-01-21').first().as_text, smallest) fetched_inbetween = DateAnswer.fetch_between('as_value', '2017-01-21', '2017-05-13') self.assertEquals(fetched_inbetween.count(), 1) self.assertEquals(fetched_inbetween.first().as_text, medium) fetched_inbetween = DateAnswer.fetch_between('as_value', '2017-01-21', '2017-05-13') self.assertEquals(fetched_inbetween.count(), 1) self.assertEquals(fetched_inbetween.first().as_text, medium) self.assertEquals( DateAnswer.fetch_equals('as_value', medium).count(), 1) self.assertEquals( DateAnswer.fetch_equals('as_value', medium).first().as_text, medium) # check answers attribute is date class date_question = Question.objects.filter( answer_type=DateAnswer.choice_name()).first() self.assertEquals(date_question.answers().first().__class__, DateAnswer)
class QuestionForm(ModelForm, FormOrderMixin): VALIDATION_ANSWER_TYPES = [DateAnswer.choice_name(), TextAnswer.choice_name(), NumericalAnswer.choice_name(), AutoResponse.choice_name()] options = forms.CharField(max_length=50, widget=forms.HiddenInput(), required=False) response_validation = ValidationField(queryset=ResponseValidation.objects.all(), required=False) def __init__( self, qset, data=None, initial=None, parent_question=None, instance=None, prev_question=None): super(QuestionForm, self).__init__( data=data, initial=initial, instance=instance) self.fields['identifier'].label = "Variable name" self.fields['qset'].widget = forms.HiddenInput() self.fields['qset'].initial = qset.pk self.qset = qset self.prev_question = prev_question # depending on type of ussd/odk access of qset restrict the answer # type self.fields['answer_type'].choices = [ choice for choice in self.fields['answer_type'].choices if choice[0] in qset.answer_types] self.fields['answer_type'].choices.insert( 0, ('', '----Select Answer Type----')) if instance: self.help_text = ' and '.join(AnswerAccessDefinition.access_channels(instance.answer_type)) self.fields['answer_type'].help_text = self.help_text self.answer_map = {} definitions = AnswerAccessDefinition.objects.all() for defi in definitions: self.answer_map[defi.answer_type] = self.answer_map.get(defi.answer_type, []) self.answer_map[defi.answer_type].append(defi.channel) self.fields['response_validation'].icons = {'add': {'data-toggle': "modal", 'data-target': "#add_validation", 'id': 'add_validation_button', 'title': 'Add Validation'}, } self.parent_question = parent_question self.order_fields(['module', 'group', 'identifier', 'text', 'answer_type', 'mandatory']) class Meta: model = model_class exclude = [] widgets = { 'text': forms.Textarea( attrs={ "rows": 5, "cols": 30, "maxlength": "150", }), } def clean_group(self): group = self.cleaned_data['group'] if group: qset = QuestionSet.get(id=self.qset.pk) identifiers = group.parameter_questions().values_list('identifier', flat=True) existing_identifiers = Question.objects.filter(identifier__in=identifiers, qset__pk=self.qset.pk).values_list('identifier', flat=True) if existing_identifiers.exists(): raise ValidationError( '%s already exist in this %s. ' 'Consider creating a question with modified identifier name and using skip logic in your %s' % (','.join(existing_identifiers), qset.verbose_name(), qset.verbose_name())) if hasattr(qset, 'survey') and qset.survey.listing_form: existing_identifiers = qset.survey.listing_form.questions.filter(identifier__in=identifiers ).values_list('identifier', flat=True) if existing_identifiers.exists(): raise ValidationError( '%s already exist as a listing question for this %s. ' 'Consider creating a question with modified identifier name ' 'and using skip logic in your %s' % (','.join(existing_identifiers), qset.verbose_name(), qset.verbose_name())) return group def clean_options(self): options = dict(self.data).get('options') if options: options = filter(lambda text: text.strip(), options) # options = map(lambda option: re.sub("[%s]" % settings.USSD_IGNORED_CHARACTERS, '', option), options) options = map( lambda option: re.sub( " ", ' ', option), options) options = map(lambda option: option.strip(), options) self.cleaned_data['options'] = options return options def clean_identifier(self): identifier = self.cleaned_data['identifier'] pattern = '^[a-zA-Z][0-9a-zA-Z_]+$' if re.match(pattern, identifier) is None: raise ValidationError( 'Identifier must start with a letter, and must contain alphanumeric values or _') if Question.objects.filter( identifier__iexact=identifier, qset__pk=self.qset.pk).exists(): if self.instance and self.instance.identifier == identifier: pass else: raise ValidationError( '%s already in use for this %s' % (identifier, model_class.type_name())) # if this is a batch question also check if there are parameter # questions with this name qset = QuestionSet.get(id=self.qset.pk) if hasattr( qset, 'parameter_list') and qset.parameter_list and qset.parameter_list.parameters.filter( identifier__iexact=identifier).exists(): raise ValidationError( '%s is already in captured as a group parameter for this %s' % (identifier, qset.verbose_name())) # for sampled surveys, check if this is already implemented in listing if hasattr(qset, 'survey') and qset.survey.listing_form and qset.survey.listing_form.questions.filter( identifier__iexact=identifier).exists(): raise ValidationError( '%s is already in captured as a listing question for this %s' % (identifier, qset.verbose_name())) return self.cleaned_data['identifier'] def clean_text(self): """Make sure any field referenced here belongs to same batch. Field refs are denoted by {{.+}} brackets :return: """ pattern = '{{ *([0-9a-zA-Z_]+) *}}' label = self.data.get('text', '') requested_identifiers = re.findall(pattern, label) if requested_identifiers: ids = self.qset.questions.filter( identifier__in=requested_identifiers).values_list( 'identifier', flat=True) ids = list(ids) if len(set(ids)) != len(set(requested_identifiers)): raise ValidationError( '%s is not in %s' % (', '.join( set(requested_identifiers).difference(ids)), self.qset.name)) return self.cleaned_data['text'] def clean(self): answer_type = self.cleaned_data.get('answer_type', None) options = self.cleaned_data.get('options', None) response_validation = self.cleaned_data.get('response_validation', None) text = self.cleaned_data.get('text', None) self._check__multichoice_and_options_compatibility( answer_type, options) self._strip_special_characters_for_ussd(text) if answer_type: answer_class = Answer.get_class(answer_type) validator_names = [validator.__name__ for validator in answer_class.validators()] if response_validation and response_validation.validation_test not in validator_names: raise ValidationError('Selected Validation is not compatible with chosen answer type') return self.cleaned_data def _check__multichoice_and_options_compatibility( self, answer_type, options): if answer_type in [ MultiChoiceAnswer.choice_name(), MultiSelectAnswer.choice_name()] and not options: message = 'Question Options missing.' self._errors['answer_type'] = self.error_class([message]) del self.cleaned_data['answer_type'] if answer_type not in [ MultiChoiceAnswer.choice_name(), MultiSelectAnswer.choice_name()] and options: del self.cleaned_data['options'] def _strip_special_characters_for_ussd(self, text): if text: text = re.sub( "[%s]" % settings.USSD_IGNORED_CHARACTERS, '', text) self.cleaned_data['text'] = re.sub(" ", ' ', text) def kwargs_has_batch(self, **kwargs): return 'qset' in kwargs and isinstance(kwargs['qset'], Batch) def options_supplied(self, commit): return commit and self.cleaned_data.get('options', None) def save_question_options(self, question): order = 0 options = self.cleaned_data['options'] question.options.all().delete() # options.sort() for text in options: order += 1 QuestionOption.objects.create( question=question, text=text, order=order) def save(self, commit=True, zombie=False, **kwargs): question = super(QuestionForm, self).save(commit=False) qset = question.qset if commit: if question.pk is None: question.save() # get the last question inline # create a inline flow with current qset qset = question.qset # qset = QuestionSet.get(id=qset.id) if self.prev_question: last_question = self.prev_question else: last_question = qset.last_question_inline() if last_question: if zombie is False: # incase, inline flow with no next quest already # exists flow, _ = QuestionFlow.objects.get_or_create( question=last_question, validation__isnull=True) prev_next_question = flow.next_question flow.next_question = question flow.save() # now connect present question back to the flow QuestionFlow.objects.create( question=question, next_question=prev_next_question) elif qset.start_question is None: qset.start_question = question qset.save() else: question.save() if hasattr(qset, 'survey'): # basicallyy check for Batch scenario SurveyParameterList.update_parameter_list(qset) # self.qset.questions_inline.invalidate() if self.options_supplied(commit): self.save_question_options(question) return question
def test_specify_wrong_min_value_gives_form_error(self): ''' :return: ''' q1 = Question.objects.create(qset_id=self.qset.id, response_validation_id=1, identifier='test1', text='test1', answer_type=DateAnswer.choice_name()) q2 = Question.objects.create(qset_id=self.qset.id, response_validation_id=1, identifier='test2', text='test2', answer_type=DateAnswer.choice_name()) q3 = Question.objects.create(qset_id=self.qset.id, response_validation_id=1, identifier='test3', text='test3', answer_type=DateAnswer.choice_name()) q4 = Question.objects.create(qset_id=self.qset.id, response_validation_id=1, identifier='test4', text='test4', answer_type=DateAnswer.choice_name()) q5 = Question.objects.create(qset_id=self.qset.id, response_validation_id=1, identifier='test5', text='test5', answer_type=DateAnswer.choice_name()) self.batch.start_question = q1 QuestionFlow.objects.create(question_id=q1.id, next_question_id=q2.id) QuestionFlow.objects.create(question_id=q2.id, next_question_id=q3.id) QuestionFlow.objects.create(question_id=q3.id, next_question_id=q4.id) QuestionFlow.objects.create(question_id=q4.id, next_question_id=q5.id) test_condition = 'between' test_param_upper = datetime.now() test_param_lower = 'some time ago' form_data = { 'action': LogicForm.REANSWER, 'condition': test_condition, 'min_value': test_param_lower, 'max_value': test_param_upper } l = LogicForm(q2, data=form_data) if l.is_valid(): l.save() # now check if equivalent Question flow and test arguments were # created try: qf = QuestionFlow.objects.get( question_id=q2.id, next_question_id=q2.id) TextArgument.objects.get( flow=qf, position=0, param=test_param_lower) TextArgument.objects.create( flow=qf, position=1, param=test_param_upper) QuestionFlow.objects.get( question_id=q1.id, next_question_id=q2.id) QuestionFlow.objects.get( question_id=q2.id, next_question_id=q3.id) self.assertTrue( False, 'completely wrong. bad values was saved as good!!') return except QuestionFlow.DoesNotExist: self.assertTrue(False, 'form valid flow not existing') pass except TextArgument: self.assertTrue( False, 'Form valid but text agrunments not saved') pass else: self.assertTrue(True)