"""Model tests""" from base64 import b64encode from collections import OrderedDict import json import datetime from decimal import Decimal import os from statistics import pstdev, stdev import uuid import unittest from tests.python.util import ( DokoTest, setUpModule, tearDownModule, dont_run_in_a_transaction ) utils = (setUpModule, tearDownModule) import psycopg2 import dateutil.tz import dateutil.parser from sqlalchemy import func, or_ from sqlalchemy.exc import IntegrityError, DataError from sqlalchemy.orm.exc import FlushError from psycopg2.extras import NumericRange, DateRange, DateTimeRange import dokomoforms.models as models from dokomoforms.models.answer import IntegerAnswer import dokomoforms.exc as exc from dokomoforms.models.survey import Bucket from dokomoforms.models.util import column_search from dokomoforms.handlers.api.v0.serializer import ModelJSONSerializer class TestBase(unittest.TestCase): def test_str(self): self.assertEqual( models.Base.__str__(models.User(name='base')), json.dumps( OrderedDict(( ('id', None), ('deleted', None), ('name', 'base'), ('emails', []), ('role', 'enumerator'), ('preferences', None), ('allowed_surveys', []), ('last_update_time', None), )), indent=4 ) ) def test_model_json_encoder(self): self.assertRaises( TypeError, models.ModelJSONEncoder().default, object() ) def test_create_engine(self): engine1 = models.create_engine() self.assertEqual(engine1.echo, None) engine2 = models.create_engine(echo=True) self.assertEqual(engine2.echo, True) engine3 = models.create_engine(echo=False) self.assertEqual(engine3.echo, False) self.assertEqual(engine3.pool.size(), 5) engine4 = models.create_engine(pool_size=10) self.assertEqual(engine4.pool.size(), 10) self.assertEqual(engine3.pool._max_overflow, 10) engine5 = models.create_engine(max_overflow=20) self.assertEqual(engine5.pool._max_overflow, 20) class TestUtil(DokoTest): def test_jsonify(self): freaky_things = ( ( 'model', models.construct_node( title={'English': 'a'}, type_constraint='integer', ) ), ('bytes', b'a'), ('datetime', datetime.datetime.now()), ('decimal', Decimal('2.3')), ('range', psycopg2.extras.Range()), ('not actually', 'freaky'), ) for k, v in freaky_things[:-1]: self.assertRaises(TypeError, json.dumps, {k: v}) self.assertIsNotNone( json.dumps({k: models.jsonify(v) for k, v in freaky_things}) ) def test_column_search_like_percent_escaping(self): with self.session.begin(): self.session.add_all(( models.construct_node( title={'English': 'a%a'}, type_constraint='integer', ), models.construct_node( title={'English': 'aa'}, type_constraint='integer', ), )) like_search = column_search( self.session.query(models.Node), model_cls=models.Node, column_name='title', search_term='%', language='English', ).all() self.assertEqual(len(like_search), 1, msg=like_search) found_node = like_search[0] self.assertIs( ( self.session .query(models.Node) .filter(models.Node.title['English'].astext == 'a%a') .one() ), found_node ) def test_column_search_like_underscore_escaping(self): with self.session.begin(): self.session.add_all(( models.construct_node( title={'English': 'a_a'}, type_constraint='integer', ), models.construct_node( title={'English': 'aa'}, type_constraint='integer', ), )) like_search = column_search( self.session.query(models.Node), model_cls=models.Node, column_name='title', search_term='_', language='English', ).all() self.assertEqual(len(like_search), 1, msg=like_search) found_node = like_search[0] self.assertIs( ( self.session .query(models.Node) .filter(models.Node.title['English'].astext == 'a_a') .one() ), found_node ) def test_column_search_like_backslash_escaping(self): with self.session.begin(): self.session.add_all(( models.construct_node( title={'English': r'a\a'}, type_constraint='integer', ), models.construct_node( title={'English': 'aa'}, type_constraint='integer', ), )) like_search = column_search( self.session.query(models.Node), model_cls=models.Node, column_name='title', search_term='\\', language='English', ).all() self.assertEqual(len(like_search), 1, msg=like_search) found_node = like_search[0] self.assertIs( ( self.session .query(models.Node) .filter(models.Node.title['English'].astext == r'a\a') .one() ), found_node ) class TestColumnProperties(DokoTest): def _create_survey_node(self, type_constraint='integer'): with self.session.begin(): survey = models.construct_survey( survey_type='public', creator=models.Administrator(name='creator'), title={'English': 'survey'}, nodes=[ models.construct_survey_node( node=models.construct_node( type_constraint=type_constraint, title={'English': 'title'}, allow_multiple=True, ), ), ], ) self.session.add(survey) return self.session.query(models.SurveyNode).one() def _create_ten_answers(self): with self.session.begin(): survey = self.session.query(models.Survey).one() survey.submissions.append( models.construct_submission( submission_type='public_submission', answers=[ models.construct_answer( survey_node=survey.nodes[0], type_constraint='integer', answer=i, ) for i in range(-2, 8) ], ) ) self.session.add(survey) def test_answer_mode(self): with self.session.begin(): creator = models.Administrator( name='creator', surveys=[ models.construct_survey( survey_type='public', title={'English': 'survey'}, nodes=[ models.construct_survey_node( node=models.construct_node( type_constraint='integer', title={'English': 'integer'}, allow_multiple=True, ), ), ], ), ], ) self.session.add(creator) submission = models.construct_submission( submission_type='public_submission', survey=creator.surveys[0], answers=[ models.construct_answer( type_constraint='integer', survey_node=creator.surveys[0].nodes[0], answer=2, ), models.construct_answer( type_constraint='integer', survey_node=creator.surveys[0].nodes[0], answer=3, ), models.construct_answer( type_constraint='integer', survey_node=creator.surveys[0].nodes[0], answer=2, ), ], ) self.session.add(submission) sn = self.session.query(models.SurveyNode).one() self.assertEqual(models.answer_mode(sn), 2) with self.session.begin(): survey = self.session.query(models.Survey).one() survey.submissions.append( models.construct_submission( submission_type='public_submission', survey=survey, answers=[ models.construct_answer( type_constraint='integer', survey_node=survey.nodes[0], answer=3, ), ], ), ) self.session.add(survey) sn = self.session.query(models.SurveyNode).one() # Postgres MODE() picks the first value if there are multiple # most-common values (standard sort). self.assertEqual(models.answer_mode(sn), 2) with self.session.begin(): survey = self.session.query(models.Survey).one() survey.submissions.append( models.construct_submission( submission_type='public_submission', survey=survey, answers=[ models.construct_answer( type_constraint='integer', survey_node=survey.nodes[0], answer=3, ), ], ), ) self.session.add(survey) sn = self.session.query(models.SurveyNode).one() self.assertEqual(models.answer_mode(sn), 3) def test_answer_mode_multiple_choice(self): with self.session.begin(): creator = models.Administrator( name='creator', surveys=[ models.construct_survey( survey_type='public', title={'English': 'survey'}, nodes=[ models.construct_survey_node( node=models.construct_node( type_constraint='multiple_choice', title={'English': 'mc'}, allow_multiple=True, choices=[ models.Choice( choice_text={'English': '1'}, ), models.Choice( choice_text={'English': '2'}, ), ], ), ), ], ), ], ) self.session.add(creator) self.session.flush() submission = models.construct_submission( submission_type='public_submission', survey=creator.surveys[0], answers=[ models.construct_answer( type_constraint='multiple_choice', survey_node=creator.surveys[0].nodes[0], answer=creator.surveys[0].nodes[0].node.choices[0].id, ), ], ) self.session.add(submission) sn = self.session.query(models.SurveyNode).one() self.assertEqual( models.answer_mode(sn), creator.surveys[0].nodes[0].node.choices[0] ) with self.session.begin(): survey = self.session.query(models.Survey).one() survey.submissions.extend( models.construct_submission( submission_type='public_submission', survey=survey, answers=[ models.construct_answer( type_constraint='multiple_choice', survey_node=survey.nodes[0], answer=survey.nodes[0].node.choices[1].id, ), ], ) for _ in range(2) ) self.session.add(survey) sn = self.session.query(models.SurveyNode).one() self.assertEqual( models.answer_mode(sn), creator.surveys[0].nodes[0].node.choices[1] ) def test_answer_mode_wrong_type(self): sn = self._create_survey_node('photo') self.assertRaises(exc.InvalidTypeForOperation, models.answer_mode, sn) def test_answer_min(self): sn = self._create_survey_node() self.assertEqual(models.answer_min(sn), None) self._create_ten_answers() self.assertEqual(models.answer_min(sn), -2) def test_answer_min_wrong_type(self): sn = self._create_survey_node('text') self.assertRaises(exc.InvalidTypeForOperation, models.answer_min, sn) def test_answer_max(self): sn = self._create_survey_node() self.assertEqual(models.answer_max(sn), None) self._create_ten_answers() self.assertEqual(models.answer_max(sn), 7) def test_answer_sum(self): sn = self._create_survey_node() self.assertEqual(models.answer_sum(sn), None) self._create_ten_answers() self.assertEqual( models.answer_sum(sn), 25, msg=self.session.query(IntegerAnswer.main_answer).all() ) def test_answer_avg(self): sn = self._create_survey_node() self.assertEqual(models.answer_avg(sn), None) self._create_ten_answers() self.assertAlmostEqual(models.answer_avg(sn), 2.5) def test_answer_stddev_pop(self): sn = self._create_survey_node() self.assertEqual(models.answer_stddev_pop(sn), None) self._create_ten_answers() self.assertEqual( float(models.answer_stddev_pop(sn)), pstdev(r for r, in self.session.query(IntegerAnswer.main_answer)), ) def test_answer_stddev_samp(self): sn = self._create_survey_node() self.assertEqual(models.answer_stddev_samp(sn), None) self._create_ten_answers() self.assertEqual( float(models.answer_stddev_samp(sn)), stdev(r for r, in self.session.query(IntegerAnswer.main_answer)), ) def test_question_stats(self): survey_id = self._create_survey_node().root_survey_id survey = self.session.query(models.Survey).get(survey_id) blank_stats = next(models.generate_question_stats(survey))['stats'] self.assertCountEqual( blank_stats, [ {'query': 'count', 'result': 0}, {'query': 'min', 'result': None}, {'query': 'max', 'result': None}, {'query': 'sum', 'result': None}, {'query': 'avg', 'result': None}, {'query': 'mode', 'result': None}, {'query': 'stddev_pop', 'result': None}, {'query': 'stddev_samp', 'result': None}, ] ) self._create_ten_answers() stats = next(models.generate_question_stats(survey))['stats'] self.assertCountEqual( stats, [ {'query': 'count', 'result': 10}, {'query': 'min', 'result': -2}, {'query': 'max', 'result': 7}, {'query': 'sum', 'result': 25}, {'query': 'avg', 'result': 2.5}, {'query': 'mode', 'result': -2}, { 'query': 'stddev_pop', 'result': models.answer_stddev_pop(survey.nodes[0]) }, { 'query': 'stddev_samp', 'result': models.answer_stddev_samp(survey.nodes[0]) }, ] ) def test_question_stats_weird_type(self): survey_id = self._create_survey_node('photo').root_survey_id survey = self.session.query(models.Survey).get(survey_id) blank_stats = next(models.generate_question_stats(survey))['stats'] self.assertCountEqual( blank_stats, [ {'query': 'count', 'result': 0}, ] ) class TestUser(DokoTest): def test_construct_user(self): enumerator = models.construct_user(role='enumerator', name='e') self.assertIs(type(enumerator), models.User) admin = models.construct_user(role='administrator', name='a') self.assertIs(type(admin), models.Administrator) def test_construct_user_bogus_type(self): self.assertRaises( TypeError, models.construct_user, role='bogus', name='b' ) def test_to_json(self): with self.session.begin(): new_user = models.User(name='a') new_user.emails = [models.Email(address='b@b')] self.session.add(new_user) user = self.session.query(models.User).one() self.assertEqual( json.loads( ModelJSONSerializer.serialize(None, user), object_pairs_hook=OrderedDict, ), OrderedDict(( ('id', user.id), ('deleted', False), ('name', 'a'), ('emails', ['b@b']), ('role', 'enumerator'), ('preferences', {'default_language': 'English'}), ('allowed_surveys', []), ('last_update_time', user.last_update_time.isoformat()), )) ) def test_valid_email(self): with self.assertRaises(IntegrityError): with self.session.begin(): user = models.User( name='a', emails=[models.Email(address='not legit')], ) self.session.add(user) def test_email_asdict(self): with self.session.begin(): new_user = models.User(name='a') new_user.emails = [models.Email(address='b@b')] self.session.add(new_user) email = self.session.query(models.Email).one() self.assertEqual( email._asdict(), OrderedDict(( ('id', email.id), ('address', 'b@b'), ('user', 'a'), ('last_update_time', email.last_update_time), )) ) def test_must_specify_default_language(self): with self.assertRaises(IntegrityError): with self.session.begin(): user = models.User( name='a', preferences={'a': 'b'}, ) self.session.add(user) def test_administrator_asdict(self): with self.session.begin(): new_user = models.Administrator(name='a') new_user.emails = [models.Email(address='b@b')] new_user.surveys.append( models.Survey(title={'English': 'some title'}) ) self.session.add(new_user) user = self.session.query(models.User).one() self.assertEqual( user._asdict(), OrderedDict(( ('id', user.id), ('deleted', False), ('name', 'a'), ('emails', ['b@b']), ('role', 'administrator'), ('preferences', {'default_language': 'English'}), ('allowed_surveys', []), ('last_update_time', user.last_update_time), ('surveys', [self.session.query(models.Survey.id).scalar()]), ('admin_surveys', []), ('token_expiration', user.token_expiration), )), msg=user ) def test_administrator_asdict_with_admin_survey(self): with self.session.begin(): new_user = models.Administrator(name='a') new_user.emails = [models.Email(address='b@b')] new_user.surveys.append( models.Survey(title={'English': 'some title'}) ) self.session.add(new_user) admin = models.Administrator(name='admin') admin.admin_surveys = [new_user.surveys[0]] self.session.add(admin) self.assertEqual( admin._asdict(), OrderedDict(( ('id', admin.id), ('deleted', False), ('name', 'admin'), ('emails', []), ('role', 'administrator'), ('preferences', {'default_language': 'English'}), ('allowed_surveys', []), ('last_update_time', admin.last_update_time), ('surveys', []), ( 'admin_surveys', [self.session.query(models.Survey.id).scalar()] ), ('token_expiration', admin.token_expiration), )), msg=admin ) def test_deleting_user_clears_email(self): with self.session.begin(): new_user = models.User(name='a') new_user.emails = [models.Email(address='b@b')] self.session.add(new_user) self.assertEqual( self.session.query(func.count(models.Email.id)).scalar(), 1 ) with self.session.begin(): self.session.delete(self.session.query(models.User).one()) self.assertEqual( self.session.query(func.count(models.Email.id)).scalar(), 0 ) def test_email_identifies_one_user(self): """No duplicate e-mail address allowed.""" with self.assertRaises(IntegrityError): with self.session.begin(): user_a = models.User(name='a') user_a.emails = [models.Email(address='@')] self.session.add(user_a) user_b = models.User(name='b') user_b.emails = [models.Email(address='@')] self.session.add(user_b) def test_most_recent_surveys(self): with self.session.begin(): self.session.add_all(( models.Administrator( name='this one', surveys=[ models.construct_survey( survey_type='enumerator_only', title={'English': 'survey'}, administrators=[models.Administrator(name='adm')], enumerators=[models.User(name='enumerator')], ), models.construct_survey( survey_type='public', title={'English': 'public survey'}, ), ], ), models.Administrator( name='not this one', surveys=[ models.construct_survey( survey_type='enumerator_only', title={'English': 'not this survey'}, administrators=[models.Administrator(name='no_a')], enumerators=[models.User(name='no enumerator')], ), ], ), )) user = ( self.session.query(models.User).filter_by(name='this one').one() ) recent_surveys = models.most_recent_surveys( self.session, user.id ) self.assertCountEqual(recent_surveys, user.surveys) self.assertEqual( len(models.most_recent_surveys(self.session, user.id, 1).all()), 1 ) admin_id = ( self.session .query(models.User.id) .filter_by(name='adm') .scalar() ) self.assertEqual( len(models.most_recent_surveys(self.session, admin_id).all()), 1 ) self.assertIs( models.most_recent_surveys(self.session, admin_id).first(), ( self.session .query(models.Survey) .filter(models.Survey.title['English'].astext == 'survey') .first() ) ) enumerator_id = ( self.session .query(models.User.id) .filter_by(name='enumerator') .scalar() ) self.assertEqual( len(models.most_recent_surveys(self.session, enumerator_id).all()), 0 ) def test_most_recent_submissions(self): with self.session.begin(): self.session.add_all(( models.Administrator( name='this one', surveys=[ models.construct_survey( survey_type='public', title={'English': 'survey'}, administrators=[models.Administrator(name='adm')], submissions=[ models.construct_submission( submission_type='public_submission', submitter_name='sub1', ), models.construct_submission( submission_type='public_submission', submitter_name='sub2', ), ], ), models.construct_survey( survey_type='public', title={'English': 'public survey too'}, submissions=[ models.construct_submission( submission_type='public_submission', submitter_name='sub3', ), models.construct_submission( submission_type='public_submission', submitter_name='sub4', ), ], ), ], ), models.Administrator( name='not this one', surveys=[ models.construct_survey( survey_type='public', title={'English': 'not this survey'}, submissions=[ models.construct_submission( submission_type='public_submission', submitter_name='sub5', ), models.construct_submission( submission_type='public_submission', submitter_name='sub6', ), ], ), ], ), models.User(name='nobody'), )) user = ( self.session.query(models.User).filter_by(name='this one').one() ) recent_submissions = models.most_recent_submissions( self.session, user.id ) self.assertCountEqual( recent_submissions, user.surveys[0].submissions + user.surveys[1].submissions ) self.assertEqual( len( models .most_recent_submissions(self.session, user.id, 3) .all() ), 3 ) admin_id = ( self.session .query(models.User.id) .filter_by(name='adm') .scalar() ) self.assertEqual( len(models.most_recent_submissions(self.session, admin_id).all()), 2 ) self.assertCountEqual( models.most_recent_submissions(self.session, admin_id).all(), ( self.session .query(models.Submission) .filter(or_( models.Submission.submitter_name == 'sub1', models.Submission.submitter_name == 'sub2' )) .all() ) ) nobody_id = ( self.session .query(models.User.id) .filter_by(name='nobody') .scalar() ) self.assertEqual( len(models.most_recent_submissions(self.session, nobody_id).all()), 0 ) class TestNode(DokoTest): def test_non_instantiable(self): self.assertRaises(TypeError, models.Node) def test_construct_node(self): with self.session.begin(): self.session.add(models.construct_node( type_constraint='text', title={'English': 'test'}, )) node = self.session.query(models.Node).one() self.assertEqual(node.title, {'English': 'test'}) question = self.session.query(models.Question).one() self.assertEqual(question.title, {'English': 'test'}) def test_title_is_dict(self): with self.assertRaises(IntegrityError): with self.session.begin(): self.session.add(models.construct_node( type_constraint='text', title=['English', 'test'], )) def test_construct_node_with_languages(self): with self.session.begin(): self.session.add(models.construct_node( type_constraint='text', languages=['French', 'German'], title={ 'German': 'german test', 'Italian': 'italian test', 'French': 'french test', }, hint={ 'German': 'german hint ', 'Italian': 'italian hint', 'French': 'french hint', }, )) node = self.session.query(models.Node).one() self.assertEqual(node.languages, ('French', 'German')) def test_construct_node_with_no_languages(self): with self.assertRaises(IntegrityError): with self.session.begin(): self.session.add(models.construct_node( type_constraint='text', languages=[], title={ 'German': 'german test', 'Italian': 'italian test', 'French': 'french test', }, hint={ 'German': 'german hint ', 'Italian': 'italian hint', 'French': 'french hint', }, )) def test_construct_node_with_missing_translations(self): with self.assertRaises(IntegrityError): with self.session.begin(): self.session.add(models.construct_node( type_constraint='text', languages=['French', 'German', 'Spanish'], title={ 'German': 'german test', 'Italian': 'italian test', 'French': 'french test', }, hint={ 'German': 'german hint ', 'Italian': 'italian hint', 'French': 'french hint', }, )) def test_asdict(self): with self.session.begin(): self.session.add(models.construct_node( type_constraint='text', title={'English': 'test'}, )) node = self.session.query(models.Node).one() self.assertEqual( node._asdict(), OrderedDict(( ('id', node.id), ('deleted', False), ('languages', ('English',)), ('title', {'English': 'test'}), ('hint', {'English': ''}), ('allow_multiple', False), ('allow_other', False), ('type_constraint', 'text'), ('logic', {}), ('last_update_time', node.last_update_time) )) ) def test_construct_node_wrong_type(self): self.assertRaises( exc.NoSuchNodeTypeError, models.construct_node, type_constraint='wrong' ) def test_enforce_non_answerable(self): with self.assertRaises(AssertionError): with self.session.begin(): creator = models.Administrator( name='creator', surveys=[ models.Survey( title={'English': 'non_answerable'}, nodes=[ models.NonAnswerableSurveyNode( node=models.construct_node( title={'English': 'should be note'}, type_constraint='integer', ), ) ], ), ], ) self.session.add(creator) def test_enforce_answerable(self): with self.assertRaises(AssertionError): with self.session.begin(): creator = models.Administrator( name='creator', surveys=[ models.Survey( title={'English': 'answerable'}, nodes=[ models.AnswerableSurveyNode( node=models.construct_node( title={ 'English': 'should be question' }, type_constraint='note', ), ) ], ), ], ) self.session.add(creator) def test_construct_node_all_types(self): with self.session.begin(): for node_type in models.NODE_TYPES: if node_type == 'facility': self.session.add(models.construct_node( type_constraint=node_type, title={'English': 'test_' + node_type}, logic={ 'nlat': 0, 'slat': 0, 'wlng': 0, 'elng': 0, }, )) else: self.session.add(models.construct_node( type_constraint=node_type, title={'English': 'test_' + node_type}, )) self.assertEqual( self.session.query(func.count(models.Node.id)).scalar(), 11, ) self.assertEqual( self.session.query(func.count(models.Note.id)).scalar(), 1, ) self.assertEqual( self.session.query(func.count(models.Question.id)).scalar(), 10, ) def test_facility_question_requires_bounds(self): with self.assertRaises(IntegrityError): with self.session.begin(): self.session.add(models.construct_node( type_constraint='facility', title={'English': 'missing_bound'}, logic={ 'slat': 0, 'wlng': 0, 'elng': 0, }, )) def test_construct_survey_node_with_the_node(self): with self.session.begin(): node = models.construct_node( type_constraint='note', title={'English': 'some title'} ) self.session.add(node) node = self.session.query(models.Node).one() with self.assertRaises(TypeError): with self.session.begin(): creator = models.Administrator( name='creator', surveys=[ models.Survey( title={'English': 'survey'}, nodes=[ models.construct_survey_node( node=node, the_node=node, ), ], ), ], ) self.session.add(creator) def test_construct_survey_node_without_specifying_node(self): with self.session.begin(): node = models.construct_node( type_constraint='note', title={'English': 'some title'} ) self.session.add(node) node_id = self.session.query(models.Node.id).scalar() with self.session.begin(): creator = models.Administrator( name='creator', surveys=[ models.Survey( title={'English': 'survey'}, nodes=[ models.construct_survey_node( type_constraint='note', node_id=node_id, node_languages=['English'], ), ], ), ], ) self.session.add(creator) self.assertEqual( self.session.query(func.count(models.SurveyNode.id)).scalar(), 1 ) def test_note_asdict(self): with self.session.begin(): creator = models.Administrator(name='creator') survey = models.Survey( title={'English': 'survey'}, nodes=[ models.construct_survey_node( node=models.construct_node( type_constraint='note', title={'English': 'a note'}, ), ), ], ) creator.surveys = [survey] self.session.add(creator) note = self.session.query(models.Node).one() self.assertEqual( note._asdict(), OrderedDict(( ('id', note.id), ('deleted', False), ('languages', ('English',)), ('title', {'English': 'a note'}), ('hint', {'English': ''}), ('type_constraint', 'note'), ('logic', {}), ('last_update_time', note.last_update_time), )), note._asdict() ) def test_multiple_choice_question_asdict(self): with self.session.begin(): creator = models.Administrator(name='creator') survey = models.Survey( title={'English': 'survey'}, nodes=[ models.construct_survey_node( node=models.construct_node( type_constraint='multiple_choice', title={ 'English': 'mc question', 'German': 'German', 'French': 'French', }, choices=[ models.Choice( choice_text={ 'English': 'one', 'German': 'German choice', 'French': 'French choice', }, ), ], ), ), ], ) creator.surveys = [survey] self.session.add(creator) question = self.session.query(models.Node).one() self.assertEqual( question._asdict(), OrderedDict(( ('id', question.id), ('deleted', False), ( 'title', OrderedDict(( ('English', 'mc question'), ('French', 'French'), ('German', 'German'), )) ), ('hint', {'English': ''}), ( 'choices', [OrderedDict(( ('choice_id', question.choices[0].id), ( 'choice_text', OrderedDict(( ('English', 'one'), ('French', 'French choice'), ('German', 'German choice'), )) ), ))] ), ('allow_multiple', False), ('allow_other', False), ('type_constraint', 'multiple_choice'), ('logic', {}), ('last_update_time', question.last_update_time), )) ) class TestQuestion(DokoTest): def test_non_instantiable(self): self.assertRaises(TypeError, models.Question) class TestChoice(DokoTest): def test_automatic_numbering(self): with self.session.begin(): q = models.construct_node( title={'English': 'test_automatic_numbering'}, type_constraint='multiple_choice', ) q.choices = [models.Choice(choice_text={ 'English': str(i) }) for i in range(3)] self.session.add(q) question = self.session.query(models.MultipleChoiceQuestion).one() choices = self.session.query(models.Choice).order_by( models.Choice.choice_number).all() self.assertEqual(question.choices, choices) self.assertEqual(choices[0].choice_number, 0) self.assertEqual(choices[1].choice_number, 1) self.assertEqual(choices[2].choice_number, 2) def test_unique_choice_text(self): with self.assertRaises(IntegrityError): with self.session.begin(): q = models.construct_node( title={'English': 'test_automatic_numbering'}, type_constraint='multiple_choice', ) q.choices = [models.Choice(choice_text={ 'English': 'choice' }) for i in range(2)] self.session.add(q) def test_asdict(self): with self.session.begin(): q = models.construct_node( title={'English': 'some MC question'}, type_constraint='multiple_choice', ) q.choices = [models.Choice(choice_text={'English': 'some choice'})] self.session.add(q) choice = self.session.query(models.Choice).one() self.assertEqual( choice._asdict(), OrderedDict(( ('id', choice.id), ('deleted', False), ('choice_text', OrderedDict((('English', 'some choice'),))), ('choice_number', 0), ( 'question', OrderedDict(( ('question_id', choice.question_id), ( 'question_title', OrderedDict(( ('English', 'some MC question'), )) ), )) ), ('last_update_time', choice.last_update_time), )) ) def test_question_delete_cascades_to_choices(self): with self.session.begin(): q = models.construct_node( title={'English': 'test_question_delete_cascades_to_choices'}, type_constraint='multiple_choice', ) q.choices = [models.Choice(choice_text={'English': 'deleteme'})] self.session.add(q) self.assertEqual( self.session.query(func.count(models.Choice.id)).scalar(), 1 ) with self.session.begin(): self.session.delete( self.session.query(models.MultipleChoiceQuestion).one() ) self.assertEqual( self.session.query(func.count(models.Choice.id)).scalar(), 0 ) def test_wrong_question_type(self): with self.session.begin(): q = models.construct_node( title={'English': 'test_wrong_question_type'}, type_constraint='text', ) q.choices = [models.Choice(choice_text='should not show up')] self.session.add(q) self.assertEqual( self.session.query(func.count(models.Choice.id)).scalar(), 0 ) class TestSurvey(DokoTest): def test_construct_survey_wrong_type(self): self.assertRaises( TypeError, models.construct_survey, survey_type='wrong' ) def test_sequentialization(self): cn = models.construct_node with self.session.begin(): self.session.add( models.Administrator( name='creator', surveys=[ models.construct_survey( survey_type='public', title={'English': 'survey'}, nodes=[ models.construct_survey_node( node=cn( type_constraint='integer', title={'English': 'A'}, ), sub_surveys=[ models.SubSurvey( nodes=[ models.construct_survey_node( node=cn( type_constraint=( 'integer' ), title={'English': 'B'}, ), ), models.construct_survey_node( node=cn( type_constraint=( 'integer' ), title={'English': 'C'}, ), ), ], ), models.SubSurvey( nodes=[ models.construct_survey_node( node=cn( type_constraint=( 'integer' ), title={'English': 'D'}, ), ), ], ), ], ), models.construct_survey_node( node=cn( type_constraint='integer', title={'English': 'E'}, ), ), ], ), ], ) ) seq = self.session.query(models.Survey).one()._sequentialize() self.assertListEqual( [sn.node.title['English'] for sn in seq], list('ABCDE') ) def test_sequentialization_with_non_answerable(self): cn = models.construct_node with self.session.begin(): self.session.add( models.Administrator( name='creator', surveys=[ models.construct_survey( survey_type='public', title={'English': 'survey'}, nodes=[ models.construct_survey_node( node=cn( type_constraint='integer', title={'English': 'A'}, ), sub_surveys=[ models.SubSurvey( nodes=[ models.construct_survey_node( node=cn( type_constraint=( 'note' ), title={'English': 'B'}, ), ), models.construct_survey_node( node=cn( type_constraint=( 'integer' ), title={'English': 'C'}, ), ), ], ), models.SubSurvey( nodes=[ models.construct_survey_node( node=cn( type_constraint=( 'integer' ), title={'English': 'D'}, ), ), ], ), ], ), models.construct_survey_node( node=cn( type_constraint='integer', title={'English': 'E'}, ), ), ], ), ], ) ) seq = self.session.query(models.Survey).one()._sequentialize() self.assertListEqual( [sn.node.title['English'] for sn in seq], list('ABCDE') ) seq_answerable = ( self.session .query(models.Survey) .one() ._sequentialize(include_non_answerable=False) ) self.assertListEqual( [sn.node.title['English'] for sn in seq_answerable], list('ACDE') ) def test_administrator_filter(self): with self.session.begin(): self.session.add( models.Administrator( name='creator', surveys=[ models.construct_survey( survey_type='enumerator_only', title={'English': 'survey'}, administrators=[models.Administrator(name='adm')], enumerators=[models.User(name='enumerator')], ), ], ) ) survey_query = self.session.query(func.count(models.Survey.id)) creator_id = ( self.session .query(models.Administrator.id) .filter_by(name='creator') .scalar() ) admin_id = ( self.session .query(models.User.id) .filter_by(name='adm') .scalar() ) enumerator_id = ( self.session .query(models.User.id) .filter_by(name='enumerator') .scalar() ) admin_filter = models.administrator_filter self.assertEqual(survey_query.scalar(), 1) self.assertEqual( survey_query.filter(admin_filter(creator_id)).scalar(), 1 ) self.assertEqual( survey_query.filter(admin_filter(admin_id)).scalar(), 1 ) self.assertEqual( survey_query.filter(admin_filter(enumerator_id)).scalar(), 0 ) def test_num_submissions(self): with self.session.begin(): self.session.add( models.Administrator( name='creator', surveys=[ models.construct_survey( survey_type='public', title={'English': 'survey'}, ), ], ) ) self.assertEqual( self.session.query(models.Survey.num_submissions).scalar(), 0 ) with self.session.begin(): survey = self.session.query(models.Survey).one() survey.submissions.extend([ models.construct_submission( submission_type='public_submission' ), models.construct_submission( submission_type='public_submission' ), ]) self.session.add(survey) self.assertEqual( self.session.query(models.Survey.num_submissions).scalar(), 2 ) def test_earliest_submission_time(self): with self.session.begin(): self.session.add( models.Administrator( name='creator', surveys=[ models.construct_survey( survey_type='public', title={'English': 'survey'}, ), ], ) ) self.assertEqual( ( self.session .query(models.Survey.earliest_submission_time) .scalar() ), None ) with self.session.begin(): survey = self.session.query(models.Survey).one() survey.submissions.extend([ models.construct_submission( submission_type='public_submission', save_time=dateutil.parser.parse('2015/7/29 1:00'), ), models.construct_submission( submission_type='public_submission', save_time=dateutil.parser.parse('2015/7/29 2:00'), ), ]) self.session.add(survey) self.assertEqual( ( self.session .query(models.Survey.earliest_submission_time) .scalar() .time() .isoformat() ), '01:00:00' ) def test_latest_submission_time(self): with self.session.begin(): self.session.add( models.Administrator( name='creator', surveys=[ models.construct_survey( survey_type='public', title={'English': 'survey'}, ), ], ) ) self.assertEqual( ( self.session .query(models.Survey.latest_submission_time) .scalar() ), None ) with self.session.begin(): survey = self.session.query(models.Survey).one() survey.submissions.extend([ models.construct_submission( submission_type='public_submission', save_time=dateutil.parser.parse('2015/7/29 1:00'), ), models.construct_submission( submission_type='public_submission', save_time=dateutil.parser.parse('2015/7/29 2:00'), ), ]) self.session.add(survey) self.assertEqual( ( self.session .query(models.Survey.latest_submission_time) .scalar() .time() .isoformat() ), '02:00:00' ) def test_administrators(self): with self.session.begin(): creator = models.Administrator(name='creator') admin = models.Administrator(name='admin') creator.surveys = [ models.EnumeratorOnlySurvey( title={'English': 'survey'}, administrators=[admin] ), ] self.session.add(creator) self.assertIs( self.session.query(models.Survey).one().administrators[0], self.session.query(models.User).filter_by(name='admin').one() ) def test_unique_administrators(self): with self.assertRaises(IntegrityError): with self.session.begin(): creator = models.Administrator(name='creator') admin = models.Administrator(name='admin') creator.surveys = [ models.EnumeratorOnlySurvey( title={'English': 'survey'}, administrators=[admin, admin] ), ] self.session.add(creator) def test_unique_enumerators(self): with self.assertRaises(IntegrityError): with self.session.begin(): creator = models.Administrator(name='creator') enmrtr = models.User(name='enmrtr') creator.surveys = [ models.EnumeratorOnlySurvey( title={'English': 'survey'}, enumerators=[enmrtr, enmrtr] ), ] self.session.add(creator) def test_url_slug_empty(self): with self.session.begin(): self.session.add( models.Administrator( name='creator', surveys=[ models.construct_survey( survey_type='public', title={'English': 'no_url_slug'}, ), ], ) ) self.session.add( models.Administrator( name='creator2', surveys=[ models.construct_survey( survey_type='public', title={'English': 'no_url_slug2'}, ), ], ) ) surveys = self.session.query(models.Survey).all() self.assertEqual(len(surveys), 2) self.assertIsNone(surveys[0].url_slug) self.assertIsNone(surveys[1].url_slug) def test_url_slug_valid(self): with self.session.begin(): self.session.add( models.Administrator( name='creator', surveys=[ models.construct_survey( survey_type='public', title={'English': 'url_slug'}, url_slug='cool_survey', ), ], ) ) survey = self.session.query(models.Survey).one() self.assertEqual(survey.url_slug, 'cool_survey') def test_url_slug_unique(self): with self.assertRaises(IntegrityError): with self.session.begin(): self.session.add( models.Administrator( name='creator', surveys=[ models.construct_survey( survey_type='public', title={'English': 'url_slug'}, url_slug='cool_survey', ), ], ) ) self.session.add( models.Administrator( name='creator2', surveys=[ models.construct_survey( survey_type='public', title={'English': 'url_slug2'}, url_slug='cool_survey', ), ], ) ) def test_url_slug_invalid_character(self): for bad_character in '%#;/?:@&=+$, ': with self.subTest(bad_character=bad_character): with self.assertRaises(IntegrityError): with self.session.begin(): self.session.add( models.Administrator( name='creator', surveys=[ models.construct_survey( survey_type='public', title={'English': 'url_slug'}, url_slug=( 'co{}ol_survey'.format( bad_character ) ), ), ], ) ) def test_url_slug_is_not_uuid(self): with self.assertRaises(IntegrityError): with self.session.begin(): self.session.add( models.Administrator( name='creator', surveys=[ models.construct_survey( survey_type='public', title={'English': 'url_slug'}, url_slug=str(uuid.uuid4()), ), ], ) ) def test_url_slug_is_not_empty_string(self): with self.assertRaises(IntegrityError): with self.session.begin(): self.session.add( models.Administrator( name='creator', surveys=[ models.construct_survey( survey_type='public', title={'English': 'url_slug'}, url_slug='', ), ], ) ) def test_one_node_surveys(self): number_of_questions = 11 with self.session.begin(): creator = models.Administrator( name='creator', emails=[models.Email(address='email@email')], ) node_types = list(models.NODE_TYPES) for node_type in node_types: if node_type == 'facility': survey = models.Survey( title={'English': node_type + '_survey'}, nodes=[ models.construct_survey_node( node=models.construct_node( type_constraint=node_type, title={'English': node_type + '_node'}, logic={ 'nlat': 0, 'slat': 0, 'wlng': 0, 'elng': 0, }, ) ), ], ) else: survey = models.Survey( title={'English': node_type + '_survey'}, nodes=[ models.construct_survey_node( node=models.construct_node( type_constraint=node_type, title={'English': node_type + '_node'}, ) ), ], ) creator.surveys.append(survey) self.session.add(creator) the_creator = self.session.query(models.Administrator).one() self.assertEqual( len(the_creator.surveys), number_of_questions, msg='not all {} surveys were created'.format(number_of_questions) ) self.assertListEqual( [the_creator.surveys[n].nodes[0].type_constraint for n in range(number_of_questions)], node_types, msg='the surveys were not created in the right order' ) self.assertListEqual( [len(the_creator.surveys[n].nodes) for n in range(number_of_questions)], [1] * number_of_questions, msg='there is a survey with more than one node' ) def test_two_surveys_same_node(self): with self.session.begin(): node = models.construct_node( title={'English': 'some node'}, type_constraint='integer', ) creator = models.Administrator( name='creator', ) self.session.add_all((node, creator)) with self.session.begin(): self.session.add( models.construct_survey( title={'English': 'first survey'}, creator=creator, survey_type='public', nodes=[ models.construct_survey_node( node=node, ), ], ) ) with self.session.begin(): self.session.add( models.construct_survey( title={'English': 'second survey'}, creator=creator, survey_type='public', nodes=[ models.construct_survey_node( node=node, ), ], ) ) self.assertEqual( self.session.query(func.count(models.Survey.id)).scalar(), 2 ) self.assertEqual( ( self.session .execute('select count(id) from doko_test.survey') .scalar() ), 2 ) def test_asdict(self): with self.session.begin(): creator = models.Administrator( name='creator', emails=[models.Email(address='email@email')], ) survey = models.Survey( title={'English': 'some survey'}, nodes=[ models.construct_survey_node( node=models.construct_node( type_constraint='integer', title={'English': 'integer node'}, ), ), ], ) creator.surveys.append(survey) self.session.add(creator) the_survey = self.session.query(models.Survey).one() self.assertEqual( the_survey._asdict(), OrderedDict(( ('id', the_survey.id), ('deleted', False), ('languages', ('English',)), ('title', OrderedDict((('English', 'some survey'),))), ('url_slug', None), ('default_language', 'English'), ('survey_type', 'public'), ('version', 1), ( 'creator_id', self.session.query(models.Administrator.id).scalar() ), ('creator_name', 'creator'), ('metadata', {}), ('created_on', the_survey.created_on), ('last_update_time', the_survey.last_update_time), ('nodes', [self.session.query(models.SurveyNode).one()]), )) ) def test_title_in_default_language_must_exist(self): with self.assertRaises(IntegrityError): with self.session.begin(): creator = models.Administrator( name='creator', surveys=[ models.Survey( title={'French': 'answerable'}, ), ], ) self.session.add(creator) def test_title_in_default_language_must_not_be_empty(self): with self.assertRaises(IntegrityError): with self.session.begin(): creator = models.Administrator( name='creator', surveys=[ models.Survey( title={'English': ''}, ), ], ) self.session.add(creator) def test_title_in_default_language_must_be_unique_per_user(self): with self.session.begin(): good_creator = models.Administrator( name='good', surveys=[ models.Survey( title={'English': 'title'}, ) ], ) self.session.add(good_creator) bad_creator = models.Administrator( name='bad', surveys=[ models.Survey( title={'English': 'title'}, ) ], ) self.session.add(bad_creator) with self.assertRaises(IntegrityError): with self.session.begin(): bad = ( self.session .query(models.User) .filter_by(name='bad') .one() ) bad.surveys.append( models.Survey(title={'English': 'title'}), ) def test_alternate_default_language(self): with self.session.begin(): creator = models.Administrator( name='creator', surveys=[ models.Survey( title={'French': 'answerable'}, languages=['French'], default_language='French', ), ], ) self.session.add(creator) self.assertEqual( self.session.query(func.count(models.Survey.id)).scalar(), 1 ) def test_bad_default_language(self): with self.assertRaises(IntegrityError): with self.session.begin(): creator = models.Administrator( name='creator', surveys=[ models.Survey( title={'French': 'answerable'}, languages=['French'], default_language='German', ), ], ) self.session.add(creator) def test_repeatable_sub_survey(self): with self.session.begin(): creator = models.Administrator(name='a') survey = models.Survey(title={'English': 'a'}) survey.nodes = [ models.construct_survey_node( node=models.construct_node( type_constraint='integer', title={'English': 'node'}, ), sub_surveys=[ models.SubSurvey( repeatable=True, buckets=[ models.construct_bucket( bucket_type='integer', bucket='[,]' ), ], nodes=[ models.construct_survey_node( repeatable=True, node=models.construct_node( type_constraint='integer', title={'English': 'repeatable'}, ), ), ], ), ], ), ] creator.surveys = [survey] self.session.add(creator) sub_survey = self.session.query(models.SubSurvey).one() self.assertTrue(sub_survey.repeatable) def test_survey_nodes_in_a_repeatable_sub_survey_must_be_repeatable(self): with self.assertRaises(IntegrityError): with self.session.begin(): creator = models.Administrator(name='a') survey = models.Survey(title={'English': 'a'}) survey.nodes = [ models.construct_survey_node( node=models.construct_node( type_constraint='integer', title={'English': 'node'}, ), sub_surveys=[ models.SubSurvey( repeatable=True, buckets=[ models.construct_bucket( bucket_type='integer', bucket='[,]' ), ], nodes=[ models.construct_survey_node( node=models.construct_node( type_constraint='integer', title={'English': 'repeatable'}, ), ), ], ), ], ), ] creator.surveys = [survey] self.session.add(creator) def test_answer_repeatable_but_not_allow_multiple(self): with self.session.begin(): creator = models.Administrator(name='a') survey = models.Survey(title={'English': 'a'}) survey.nodes = [ models.construct_survey_node( node=models.construct_node( type_constraint='integer', title={'English': 'node'}, ), sub_surveys=[ models.SubSurvey( repeatable=True, buckets=[ models.construct_bucket( bucket_type='integer', bucket='[,]' ), ], nodes=[ models.construct_survey_node( repeatable=True, node=models.construct_node( type_constraint='integer', title={'English': 'repeatable'}, ), ), ], ), ], ), ] creator.surveys = [survey] self.session.add(creator) self.assertEqual( self.session.query(func.count(models.Survey.id)).scalar(), 1 ) with self.session.begin(): survey.submissions.append( models.construct_submission( submission_type='public_submission', answers=[ models.construct_answer( type_constraint='integer', survey_node=( survey.nodes[0].sub_surveys[0].nodes[0] ), answer=3, ), models.construct_answer( type_constraint='integer', survey_node=( survey.nodes[0].sub_surveys[0].nodes[0] ), answer=4, ), ], ) ) self.assertEqual( self.session.query(func.count(models.Answer.id)).scalar(), 2 ) def test_answer_repeatable_but_allow_multiple(self): with self.session.begin(): creator = models.Administrator(name='a') survey = models.Survey(title={'English': 'a'}) survey.nodes = [ models.construct_survey_node( node=models.construct_node( type_constraint='integer', title={'English': 'node'}, ), sub_surveys=[ models.SubSurvey( repeatable=True, buckets=[ models.construct_bucket( bucket_type='integer', bucket='[,]' ), ], nodes=[ models.construct_survey_node( repeatable=True, node=models.construct_node( allow_multiple=True, type_constraint='integer', title={'English': 'repeatable'}, ), ), ], ), ], ), ] creator.surveys = [survey] self.session.add(creator) self.assertEqual( self.session.query(func.count(models.Survey.id)).scalar(), 1 ) with self.session.begin(): survey.submissions.append( models.construct_submission( submission_type='public_submission', answers=[ models.construct_answer( type_constraint='integer', survey_node=( survey.nodes[0].sub_surveys[0].nodes[0] ), answer=3, ), models.construct_answer( type_constraint='integer', survey_node=( survey.nodes[0].sub_surveys[0].nodes[0] ), answer=4, ), ], ) ) self.assertEqual( self.session.query(func.count(models.Answer.id)).scalar(), 2 ) class TestSurveyNode(DokoTest): def test_factory_function_missing_type_constraint(self): self.assertRaises(ValueError, models.construct_survey_node) def test_answer_count(self): with self.session.begin(): self.session.add( models.Administrator( name='creator', surveys=[ models.construct_survey( survey_type='public', title={'English': 'survey'}, nodes=[ models.construct_survey_node( node=models.construct_node( type_constraint='integer', title={'English': 'integer'}, allow_multiple=True, ), ), ], ), ], ) ) self.assertEqual( ( self.session .query(models.AnswerableSurveyNode.answer_count) .scalar() ), 0 ) with self.session.begin(): survey = self.session.query(models.Survey).one() survey.submissions.append( models.construct_submission( submission_type='public_submission', answers=[ models.construct_answer( survey_node=survey.nodes[0], type_constraint='integer', answer=1, ), models.construct_answer( survey_node=survey.nodes[0], type_constraint='integer', answer=2, ), ], ), ) self.assertEqual( ( self.session .query(models.AnswerableSurveyNode.answer_count) .scalar() ), 2 ) def test_requested_translations_must_exist(self): with self.assertRaises(IntegrityError): with self.session.begin(): creator = models.Administrator( name='creator', emails=[models.Email(address='email@email')], surveys=[ models.Survey( title={'French': 'french title'}, languages=['French'], default_language='French', nodes=[ models.construct_survey_node( node=models.construct_node( type_constraint='integer', languages=['German'], title={'German': 'german title'}, hint={'German': ''}, ), ), ], ), ], ) self.session.add(creator) def test_requested_translations_must_exist_even_nested(self): with self.assertRaises(IntegrityError): with self.session.begin(): super_nested = models.construct_node( type_constraint='text', languages=['German'], title={'German': 'german question'}, hint={'German': ''}, ) creator = models.Administrator( name='creator', emails=[models.Email(address='email@email')], surveys=[ models.Survey( title={'French': 'french title'}, languages=['French'], default_language='French', nodes=[ models.construct_survey_node( node=models.construct_node( type_constraint='integer', languages=['French'], title={'French': 'French question'}, hint={'French': ''}, ), sub_surveys=[ models.SubSurvey( buckets=[ models.construct_bucket( bucket_type='integer', bucket='[2, 5]', ), ], nodes=[ models.construct_survey_node( node=super_nested, ), ], ), ], ), ], ), ], ) self.session.add(creator) def test_super_nested(self): with self.session.begin(): super_nested = models.construct_node( type_constraint='text', languages=['French'], title={'French': 'french question'}, hint={'French': ''}, ) creator = models.Administrator( name='creator', emails=[models.Email(address='email@email')], surveys=[ models.Survey( title={'French': 'french title'}, languages=['French'], default_language='French', nodes=[ models.construct_survey_node( node=models.construct_node( type_constraint='integer', languages=['French'], title={'French': 'French question'}, hint={'French': ''}, ), sub_surveys=[ models.SubSurvey( buckets=[ models.construct_bucket( bucket_type='integer', bucket='[2, 5]', ), ], nodes=[ models.construct_survey_node( node=super_nested, ), ], ), ], ), ], ), ], ) self.session.add(creator) self.assertEqual( self.session.query(func.count(models.Survey.id)).scalar(), 1 ) self.assertEqual( self.session.query(func.count(models.Node.id)).scalar(), 2 ) def test_asdict(self): with self.session.begin(): creator = models.Administrator( name='creator', emails=[models.Email(address='email@email')], ) survey = models.Survey( title={'English': 'some survey'}, nodes=[ models.construct_survey_node( node=models.construct_node( type_constraint='integer', title={'English': 'integer node'}, ), ), ], ) creator.surveys.append(survey) self.session.add(creator) survey_node = self.session.query(models.SurveyNode).one() self.assertEqual( survey_node._asdict(), OrderedDict(( ('deleted', False), ('languages', ('English',)), ('title', {'English': 'integer node'}), ('hint', {'English': ''}), ('allow_multiple', False), ('allow_other', False), ('type_constraint', 'integer'), ('logic', {}), ('last_update_time', survey_node.last_update_time), ('node_id', self.session.query(models.Node.id).scalar()), ('id', survey_node.id), ('required', False), ('allow_dont_know', False), )) ) def test_asdict_with_sub_survey(self): with self.session.begin(): creator = models.Administrator( name='creator', emails=[models.Email(address='email@email')], ) survey = models.Survey( title={'English': 'some survey'}, nodes=[ models.construct_survey_node( node=models.construct_node( type_constraint='integer', title={'English': 'integer node'}, ), sub_surveys=[ models.SubSurvey(), ], ), ], ) creator.surveys.append(survey) self.session.add(creator) survey_node = self.session.query(models.SurveyNode).one() self.assertEqual( survey_node._asdict(), OrderedDict(( ('deleted', False), ('languages', ('English',)), ('title', {'English': 'integer node'}), ('hint', {'English': ''}), ('allow_multiple', False), ('allow_other', False), ('type_constraint', 'integer'), ('logic', {}), ('last_update_time', survey_node.last_update_time), ('node_id', self.session.query(models.Node.id).scalar()), ('id', survey_node.id), ('required', False), ('allow_dont_know', False), ('sub_surveys', self.session.query(models.SubSurvey).all()), )) ) def test_super_nested_to_json(self): with self.session.begin(): super_nested = models.construct_node( type_constraint='text', languages=['French'], title={'French': 'French question'}, hint={'French': ''}, ) creator = models.Administrator( name='creator', emails=[models.Email(address='email@email')], surveys=[ models.Survey( title={'French': 'french title'}, languages=['French'], default_language='French', nodes=[ models.construct_survey_node( node=models.construct_node( type_constraint='integer', languages=['French'], title={'French': 'French question'}, hint={'French': ''}, ), sub_surveys=[ models.SubSurvey( buckets=[ models.construct_bucket( bucket_type='integer', bucket='[2, 5]', ), ], nodes=[ models.construct_survey_node( node=super_nested, ), ], ), ], ), ], ), ], ) self.session.add(creator) survey = self.session.query(models.Survey).one() integer_question = ( self.session .query(models.Question) .filter_by(type_constraint='integer') .one() ) n = ( self.session .query(models.Question) .filter_by(type_constraint='text') .one() ) n_t = n.last_update_time.isoformat() n_i = n.id self.assertEqual( json.loads( ModelJSONSerializer.serialize(None, survey), object_pairs_hook=OrderedDict, ), OrderedDict(( ('id', survey.id), ('deleted', False), ('languages', ['French']), ('title', OrderedDict((('French', 'french title'),))), ('url_slug', None), ('default_language', 'French'), ('survey_type', 'public'), ('version', 1), ('creator_id', self.session.query(models.User.id).scalar()), ('creator_name', 'creator'), ('metadata', OrderedDict()), ('created_on', survey.created_on.isoformat()), ('last_update_time', survey.last_update_time.isoformat()), ( 'nodes', [ OrderedDict(( ('deleted', False), ('languages', ['French']), ( 'title', OrderedDict((('French', 'French question'),)) ), ('hint', OrderedDict((('French', ''),))), ('allow_multiple', False), ('allow_other', False), ('type_constraint', 'integer'), ('logic', OrderedDict()), ( 'last_update_time', integer_question.last_update_time.isoformat() ), ('node_id', integer_question.id), ( 'id', self.session .query(models.SurveyNode.id) .filter_by(type_constraint='integer') .scalar() ), ('required', False), ('allow_dont_know', False), ( 'sub_surveys', [ OrderedDict(( ('deleted', False), ('buckets', ['[2,6)']), ('repeatable', False), ( 'nodes', [ OrderedDict(( ('deleted', False), ('languages', ['French']), ( 'title', OrderedDict(( ( 'French', 'French' ' question' ), )) ), ( 'hint', OrderedDict(( ('French', ''), )) ), ('allow_multiple', False), ('allow_other', False), ( 'type_constraint', 'text' ), ('logic', OrderedDict()), ('last_update_time', n_t), ('node_id', n_i), ( 'id', self.session .query( models.SurveyNode .id ) .filter_by( type_constraint=( 'text' ) ) .scalar() ), ('required', False), ( 'allow_dont_know', False ), )) ] ), )) ] ), )) ] ), )) ) class TestBucket(DokoTest): def _create_blank_survey(self) -> (models.Administrator, models.Survey): creator = models.Administrator( name='creator', emails=[models.Email(address='email@email')], ) survey = models.Survey(title={'English': 'TestBucket'}) creator.surveys = [survey] return creator, survey def test_bucket(self): self.assertRaises(TypeError, Bucket.bucket) def test_non_instantiable(self): self.assertRaises(TypeError, Bucket) def test_bucket_type_exists(self): self.assertRaises( exc.NoSuchBucketTypeError, models.construct_bucket, bucket_type='wrong', ) def test_integer_bucket(self): with self.session.begin(): creator, survey = self._create_blank_survey() survey.nodes = [ models.construct_survey_node( node=models.construct_node( type_constraint='integer', title={'English': 'node'}, ), sub_surveys=[ models.SubSurvey( buckets=[ models.construct_bucket( bucket_type='integer', bucket='(1, 2]' ), ], ), ], ), ] self.session.add(creator) the_bucket = self.session.query(Bucket).one() self.assertEqual(the_bucket.bucket, NumericRange(2, 3, '[)')) def test_sub_survey_asdict(self): with self.session.begin(): creator, survey = self._create_blank_survey() survey.nodes = [ models.construct_survey_node( node=models.construct_node( type_constraint='integer', title={'English': 'node'}, ), sub_surveys=[ models.SubSurvey( buckets=[ models.construct_bucket( bucket_type='integer', bucket='(1, 2]' ), ], ), ], ), ] self.session.add(creator) sub_survey = self.session.query(models.SubSurvey).one() self.assertEqual( sub_survey._asdict(), OrderedDict(( ('deleted', False), ('buckets', [NumericRange(2, 3, '[)')]), ('repeatable', False), ('nodes', []), )) ) def test_allow_multiple_means_no_sub_surveys(self): with self.assertRaises(IntegrityError): with self.session.begin(): creator, survey = self._create_blank_survey() survey.nodes = [ models.construct_survey_node( node=models.construct_node( type_constraint='integer', allow_multiple=True, title={'English': 'node'}, ), sub_surveys=[ models.SubSurvey( buckets=[ models.construct_bucket( bucket_type='integer', bucket='(1, 2]' ), ], ), ], ), ] self.session.add(creator) def test_bucket_asdict(self): with self.session.begin(): creator, survey = self._create_blank_survey() survey.nodes = [ models.construct_survey_node( node=models.construct_node( type_constraint='integer', title={'English': 'node'}, ), sub_surveys=[ models.SubSurvey( buckets=[ models.construct_bucket( bucket_type='integer', bucket='(1, 2]' ), ], ), ], ), ] self.session.add(creator) bucket = self.session.query(Bucket).one() self.assertEqual( bucket._asdict(), OrderedDict(( ('id', bucket.id), ('bucket_type', 'integer'), ('bucket', NumericRange(2, 3, '[)')), )) ) def test_multiple_choice_bucket_asdict(self): with self.session.begin(): creator, survey = self._create_blank_survey() node = models.construct_node( type_constraint='multiple_choice', title={'English': 'node'}, choices=[ models.Choice( choice_text={'English': 'choice 1'}, ), models.Choice( choice_text={'English': 'choice 2'}, ), ], ) survey.nodes = [ models.construct_survey_node( node=node, sub_surveys=[ models.SubSurvey( buckets=[ models.construct_bucket( bucket_type='multiple_choice', bucket=node.choices[0], ), ], ), ], ), ] self.session.add(creator) bucket = self.session.query(Bucket).one() self.assertEqual( bucket._asdict(), OrderedDict(( ('id', bucket.id), ('bucket_type', 'multiple_choice'), ('bucket', node.choices[0]), )) ) def test_integer_incorrect_bucket_type(self): with self.assertRaises(IntegrityError): with self.session.begin(): creator, survey = self._create_blank_survey() survey.nodes = [ models.construct_survey_node( node=models.construct_node( type_constraint='integer', title={'English': 'node'}, ), sub_surveys=[ models.SubSurvey( buckets=[ models.construct_bucket( bucket_type='decimal', bucket='(1.3, 2.3]' ), ], ), ], ), ] self.session.add(creator) def test_integer_incorrect_range(self): """A decimal is not an integer""" with self.assertRaises(DataError): with self.session.begin(): creator, survey = self._create_blank_survey() survey.nodes = [ models.construct_survey_node( node=models.construct_node( type_constraint='integer', title={'English': 'node'}, ), sub_surveys=[ models.SubSurvey( buckets=[ models.construct_bucket( bucket_type='integer', bucket='(1.3, 2.3]' ), ], ), ], ), ] self.session.add(creator) def test_integer_two_buckets(self): with self.session.begin(): creator, survey = self._create_blank_survey() survey.nodes = [ models.construct_survey_node( node=models.construct_node( type_constraint='integer', title={'English': 'node'}, ), sub_surveys=[ models.SubSurvey( buckets=[ models.construct_bucket( bucket_type='integer', bucket='(1, 2]' ), models.construct_bucket( bucket_type='integer', bucket='(4, 6]' ), ], ), ], ), ] self.session.add(creator) self.assertEqual(self.session.query(func.count(Bucket.id)).scalar(), 2) def test_integer_bucket_no_overlap(self): """The range [,] covers all integers, so (-2, 6] overlaps.""" with self.assertRaises(IntegrityError): with self.session.begin(): creator, survey = self._create_blank_survey() survey.nodes = [ models.construct_survey_node( node=models.construct_node( type_constraint='integer', title={'English': 'node'}, ), sub_surveys=[ models.SubSurvey( buckets=[ models.construct_bucket( bucket_type='integer', bucket='[,]' ), models.construct_bucket( bucket_type='integer', bucket='(-2, 6]' ), ], ), ], ), ] self.session.add(creator) def test_integer_bucket_no_overlap_different_sub_surveys(self): """ Different SubSurveys belonging to the same SurveyNode cannot have overlapping buckets. """ with self.assertRaises(IntegrityError): with self.session.begin(): creator, survey = self._create_blank_survey() survey.nodes = [ models.construct_survey_node( node=models.construct_node( type_constraint='integer', title={'English': 'node'}, ), sub_surveys=[ models.SubSurvey( buckets=[ models.construct_bucket( bucket_type='integer', bucket='[1, 5]' ), ], ), models.SubSurvey( buckets=[ models.construct_bucket( bucket_type='integer', bucket='[3, 7]' ), ], ), ], ), ] self.session.add(creator) def test_two_integer_buckets_different_sub_surveys(self): with self.session.begin(): creator, survey = self._create_blank_survey() survey.nodes = [ models.construct_survey_node( node=models.construct_node( type_constraint='integer', title={'English': 'node'}, ), sub_surveys=[ models.SubSurvey( buckets=[ models.construct_bucket( bucket_type='integer', bucket='[1, 5]' ), ], ), models.SubSurvey( buckets=[ models.construct_bucket( bucket_type='integer', bucket='[8, 13]' ), ], ), ], ), ] self.session.add(creator) self.assertEqual( self.session.query(func.count(Bucket.id)).scalar(), 2 ) def test_integer_bucket_no_empty_range(self): """There are no integers between 2 and 3 exclusive""" with self.assertRaises(IntegrityError): with self.session.begin(): creator, survey = self._create_blank_survey() survey.nodes = [ models.construct_survey_node( node=models.construct_node( type_constraint='integer', title={'English': 'node'}, ), sub_surveys=[ models.SubSurvey( buckets=[ models.construct_bucket( bucket_type='integer', bucket='(2, 3)' ), ], ), ], ), ] self.session.add(creator) def test_integer_overlapping_buckets_different_nodes(self): """Nothing wrong with overlapping buckets on different nodes.""" with self.session.begin(): creator, survey = self._create_blank_survey() survey.nodes = [ models.construct_survey_node( node=models.construct_node( type_constraint='integer', title={'English': 'node1'}, ), sub_surveys=[ models.SubSurvey( buckets=[ models.construct_bucket( bucket_type='integer', bucket='[1, 5]' ), ], ), ], ), models.construct_survey_node( node=models.construct_node( type_constraint='integer', title={'English': 'node2'}, ), sub_surveys=[ models.SubSurvey( buckets=[ models.construct_bucket( bucket_type='integer', bucket='[3, 7]' ), ], ), ], ), ] self.session.add(creator) self.assertEqual(self.session.query(func.count(Bucket.id)).scalar(), 2) def test_decimal_bucket(self): with self.session.begin(): creator, survey = self._create_blank_survey() survey.nodes = [ models.construct_survey_node( node=models.construct_node( type_constraint='decimal', title={'English': 'node'}, ), sub_surveys=[ models.SubSurvey( buckets=[ models.construct_bucket( bucket_type='decimal', bucket='(1.3, 2.3]' ), ], ), ], ), ] self.session.add(creator) the_bucket = self.session.query(Bucket).one() self.assertEqual( the_bucket.bucket, NumericRange(Decimal('1.3'), Decimal('2.3'), '(]'), ) def test_date_bucket(self): with self.session.begin(): creator, survey = self._create_blank_survey() survey.nodes = [ models.construct_survey_node( node=models.construct_node( type_constraint='date', title={'English': 'node'}, ), sub_surveys=[ models.SubSurvey( buckets=[ models.construct_bucket( bucket_type='date', bucket='(2015-1-1, 2015-2-2]' ), ], ), ], ), ] self.session.add(creator) the_bucket = self.session.query(Bucket).one() self.assertEqual( the_bucket.bucket, DateRange( datetime.date(2015, 1, 2), datetime.date(2015, 2, 3), '[)' ), ) def test_timestamp_bucket(self): with self.session.begin(): creator, survey = self._create_blank_survey() survey.nodes = [ models.construct_survey_node( node=models.construct_node( type_constraint='timestamp', title={'English': 'node'}, ), sub_surveys=[ models.SubSurvey( buckets=[ models.construct_bucket( bucket_type='timestamp', bucket='(2015-1-1 1:11, 2015-1-1 2:22]' ), ], ), ], ), ] self.session.add(creator) the_bucket = self.session.query(Bucket).one() tzinfo = the_bucket.bucket.lower.tzinfo self.assertEqual( the_bucket.bucket, DateTimeRange( datetime.datetime(2015, 1, 1, 1, 11, tzinfo=tzinfo), datetime.datetime(2015, 1, 1, 2, 22, tzinfo=tzinfo), '(]' ) ) def test_multiple_choice_bucket(self): with self.session.begin(): creator, survey = self._create_blank_survey() node = models.construct_node( type_constraint='multiple_choice', title={'English': 'node'}, ) choice = models.Choice(choice_text={'English': ''}) node.choices = [choice] survey.nodes = [ models.construct_survey_node( node=node, sub_surveys=[ models.SubSurvey( buckets=[ models.construct_bucket( bucket_type='multiple_choice', bucket=choice, ), ], ), ] ), ] self.session.add(creator) the_bucket = self.session.query(Bucket).one() the_choice = self.session.query(models.Choice).one() self.assertIs(the_bucket.bucket, the_choice) def test_multiple_choice_multiple_buckets(self): with self.session.begin(): creator, survey = self._create_blank_survey() node = models.construct_node( type_constraint='multiple_choice', title={'English': 'node'}, ) choice1 = models.Choice(choice_text={'English': ''}) choice2 = models.Choice(choice_text={'English': 'second choice'}) node.choices = [choice1, choice2] survey.nodes = [ models.construct_survey_node( node=node, sub_surveys=[ models.SubSurvey( buckets=[ models.construct_bucket( bucket_type='multiple_choice', bucket=choice1 ), models.construct_bucket( bucket_type='multiple_choice', bucket=choice2 ), ], ), ] ), ] self.session.add(creator) bucket1 = self.session.query(Bucket).all()[0] choice1 = self.session.query(models.Choice).all()[0] self.assertIs(bucket1.bucket, choice1) bucket2 = self.session.query(Bucket).all()[1] choice2 = self.session.query(models.Choice).all()[1] self.assertIs(bucket2.bucket, choice2) def test_multiple_choice_bucket_no_overlap(self): with self.assertRaises(IntegrityError): with self.session.begin(): creator, survey = self._create_blank_survey() node = models.construct_node( type_constraint='multiple_choice', title='node' ) choice = models.Choice() node.choices = [choice] survey.nodes = [ models.construct_survey_node( node=node, sub_surveys=[ models.SubSurvey( buckets=[ models.construct_bucket( bucket_type='multiple_choice', bucket=choice ), models.construct_bucket( bucket_type='multiple_choice', bucket=choice ), ], ), ] ), ] self.session.add(creator) def test_multiple_choice_bucket_choice_from_wrong_question(self): with self.assertRaises(IntegrityError): with self.session.begin(): creator, survey = self._create_blank_survey() wrong_node = models.construct_node( type_constraint='multiple_choice', title='wrong' ) wrong_choice = models.Choice() wrong_node.choices = [wrong_choice] survey.nodes = [ models.construct_survey_node( node=models.construct_node( type_constraint='multiple_choice', title={'English': 'node'}, choices=[models.Choice()], ), sub_surveys=[ models.SubSurvey( buckets=[ models.construct_bucket( bucket_type='multiple_choice', bucket=wrong_choice ), ], ), ], ), ] self.session.add(creator) class TestSubmission(DokoTest): def test_construct_submission_bogus_type(self): with self.assertRaises(exc.NoSuchSubmissionTypeError): with self.session.begin(): submission = models.construct_submission( submission_type='aaa', ) self.session.add(submission) def test_enumerator_submission(self): with self.session.begin(): creator = models.Administrator(name='creator') enumerator = models.User(name='enumerator') creator.surveys = [ models.EnumeratorOnlySurvey( title={'English': 'survey'}, enumerators=[enumerator] ), ] self.session.add(creator) submission = models.EnumeratorOnlySubmission( survey=creator.surveys[0], enumerator=enumerator, ) self.session.add(submission) self.assertIs( self.session.query(models.Submission).one().enumerator, self.session.query(models.User).filter_by(role='enumerator').one() ) def test_enumerator_only_submission_asdict(self): with self.session.begin(): creator = models.Administrator(name='creator') enumerator = models.User(name='enumerator') creator.surveys = [ models.EnumeratorOnlySurvey( title={'English': 'survey'}, enumerators=[enumerator], nodes=[ models.construct_survey_node( node=models.construct_node( type_constraint='location', title={'English': 'location?'}, ), ), ], ), ] self.session.add(creator) submission = models.EnumeratorOnlySubmission( survey=creator.surveys[0], enumerator=enumerator, answers=[ models.construct_answer( type_constraint='location', survey_node=creator.surveys[0].nodes[0], answer={'lng': 5, 'lat': 3}, ), ], ) self.session.add(submission) the_submission = self.session.query(models.Submission).one() self.assertEqual( the_submission._asdict(), OrderedDict(( ('id', the_submission.id), ('deleted', False), ('survey_id', self.session.query(models.Survey.id).scalar()), ('start_time', None), ('save_time', the_submission.save_time), ('submission_time', the_submission.submission_time), ('last_update_time', the_submission.last_update_time), ('submitter_name', ''), ('submitter_email', ''), ( 'answers', [ OrderedDict(( ('type_constraint', 'location'), ('response_type', 'answer'), ( 'response', {'lng': 5, 'lat': 3} ), ( 'survey_node_id', creator.surveys[0].nodes[0].id ), )), ] ), ( 'enumerator_user_id', self.session .query(models.User.id) .filter_by(role='enumerator') .scalar() ), ('enumerator_user_name', 'enumerator'), )), msg=the_submission ) def test_enumerator_only(self): with self.assertRaises(IntegrityError): with self.session.begin(): creator = models.Administrator(name='creator') enumerator = models.User(name='enumerator') creator.surveys = [ models.EnumeratorOnlySurvey( title={'English': 'survey'}, ), ] creator.enumerators = [enumerator] self.session.add(creator) submission = models.PublicSubmission( survey=creator.surveys[0], ) self.session.add(submission) def test_enumerator_only_submission_requires_enumerator(self): with self.assertRaises(IntegrityError): with self.session.begin(): creator = models.Administrator(name='creator') enumerator = models.User(name='enumerator') creator.surveys = [ models.EnumeratorOnlySurvey( title={'English': 'survey'}, ), ] creator.enumerators = [enumerator] self.session.add(creator) submission = models.EnumeratorOnlySubmission( survey=creator.surveys[0], ) self.session.add(submission) def test_non_enumerator_cannot_submit(self): with self.assertRaises(IntegrityError): with self.session.begin(): creator = models.Administrator(name='creator') creator.surveys = [ models.EnumeratorOnlySurvey( title={'English': 'survey'}, ), ] self.session.add(creator) bad_user = models.User(name='bad') self.session.add(bad_user) submission = models.EnumeratorOnlySubmission( survey=creator.surveys[0], enumerator=bad_user, ) self.session.add(submission) def test_authentication_not_required_for_regular_survey(self): with self.session.begin(): creator = models.Administrator(name='creator') authentic_user = models.User(name='enumerator') creator.surveys = [models.Survey(title={'English': 'survey'})] self.session.add(creator) auth_submission = models.PublicSubmission( survey=creator.surveys[0], enumerator=authentic_user, ) self.session.add(auth_submission) regular_submission = models.PublicSubmission( survey=creator.surveys[0], submitter_name='regular', ) self.session.add(regular_submission) self.assertEqual( self.session.query(func.count(models.Submission.id)).scalar(), 2 ) self.assertEqual( self.session .query(func.count(models.PublicSubmission.id)).scalar(), 2 ) self.assertEqual( self.session .query(func.count(models.EnumeratorOnlySubmission.id)).scalar(), 0 ) def test_public_submission_asdict_non_enumerator(self): with self.session.begin(): creator = models.Administrator(name='creator') creator.surveys = [models.Survey(title={'English': 'survey'})] self.session.add(creator) auth_submission = models.PublicSubmission( survey=creator.surveys[0], submitter_name='not an enumerator', submitter_email='some@email', ) self.session.add(auth_submission) the_submission = self.session.query(models.Submission).one() self.assertEqual( the_submission._asdict(), OrderedDict(( ('id', the_submission.id), ('deleted', False), ('survey_id', self.session.query(models.Survey.id).scalar()), ('start_time', None), ('save_time', the_submission.save_time), ('submission_time', the_submission.submission_time), ('last_update_time', the_submission.last_update_time), ('submitter_name', 'not an enumerator'), ('submitter_email', 'some@email'), ('answers', []), )), msg=the_submission ) def test_submission_bad_email(self): with self.assertRaises(IntegrityError): with self.session.begin(): creator = models.Administrator(name='creator') creator.surveys = [models.Survey(title={'English': 'survey'})] self.session.add(creator) auth_submission = models.PublicSubmission( survey=creator.surveys[0], submitter_name='not an enumerator', submitter_email='no at symbol', ) self.session.add(auth_submission) def test_public_submission_asdict_enumerator(self): with self.session.begin(): creator = models.Administrator(name='creator') authentic_user = models.User(name='enumerator') creator.surveys = [models.Survey(title={'English': 'survey'})] self.session.add(creator) auth_submission = models.PublicSubmission( survey=creator.surveys[0], enumerator=authentic_user, ) self.session.add(auth_submission) the_submission = self.session.query(models.Submission).one() self.assertEqual( the_submission._asdict(), OrderedDict(( ('id', the_submission.id), ('deleted', False), ('survey_id', self.session.query(models.Survey.id).scalar()), ('start_time', None), ('save_time', the_submission.save_time), ('submission_time', the_submission.submission_time), ('last_update_time', the_submission.last_update_time), ('submitter_name', ''), ('submitter_email', ''), ('answers', []), ( 'enumerator_user_id', self.session .query(models.User.id) .filter_by(role='enumerator') .scalar() ), ('enumerator_user_name', 'enumerator'), )), msg=the_submission ) class TestAnswer(DokoTest): def test_non_instantiable(self): self.assertRaises(TypeError, models.Answer) def test_basic_case(self): with self.session.begin(): creator = models.Administrator(name='creator') survey = models.Survey( title={'English': 'survey'}, nodes=[ models.construct_survey_node( node=models.construct_node( type_constraint='integer', title={'English': 'integer question'}, ), ), ], ) creator.surveys = [survey] self.session.add(creator) with self.session.begin(): the_survey = self.session.query(models.Survey).one() submission = models.PublicSubmission( survey=the_survey, answers=[ models.construct_answer( survey_node=the_survey.nodes[0], type_constraint='integer', answer=3, ), ], ) self.session.add(submission) self.assertIs( self.session.query(models.Answer).one(), self.session.query(models.Survey).one().submissions[0].answers[0] ) self.assertEqual( self.session.query(models.Answer).one().answer, 3 ) self.assertDictEqual( self.session.query(models.Answer).one().response, { 'type_constraint': 'integer', 'response_type': 'answer', 'response': 3, } ) def test_asdict(self): with self.session.begin(): creator = models.Administrator(name='creator') survey = models.Survey( title={'English': 'survey'}, nodes=[ models.construct_survey_node( node=models.construct_node( type_constraint='integer', title={'English': 'integer question'}, ), ), ], ) creator.surveys = [survey] self.session.add(creator) with self.session.begin(): the_survey = self.session.query(models.Survey).one() submission = models.PublicSubmission( survey=the_survey, answers=[ models.construct_answer( survey_node=the_survey.nodes[0], type_constraint='integer', answer=3, answer_metadata={'a': 'b'}, ), ], ) self.session.add(submission) answer = self.session.query(models.Answer).one() self.assertEqual( answer._asdict(), OrderedDict(( ('id', answer.id), ('deleted', False), ('answer_number', 0), ('submission_id', answer.submission_id), ('save_time', answer.save_time), ('survey_id', self.session.query(models.Survey.id).scalar()), ( 'survey_node_id', self.session.query(models.SurveyNode.id).scalar() ), ( 'question_id', self.session.query(models.Question.id).scalar() ), ('type_constraint', 'integer'), ('last_update_time', answer.last_update_time), ( 'response', OrderedDict(( ('type_constraint', 'integer'), ('response_type', 'answer'), ('response', 3), )) ), ('metadata', {'a': 'b'}), )), msg=answer ) def test_question_title(self): with self.session.begin(): creator = models.Administrator(name='creator') survey = models.Survey( title={'English': 'survey'}, nodes=[ models.construct_survey_node( node=models.construct_node( type_constraint='integer', title={'English': 'integer question'}, ), ), ], ) creator.surveys = [survey] self.session.add(creator) with self.session.begin(): the_survey = self.session.query(models.Survey).one() submission = models.PublicSubmission( survey=the_survey, answers=[ models.construct_answer( survey_node=the_survey.nodes[0], type_constraint='integer', answer=3, ), ], ) self.session.add(submission) self.assertEqual( self.session.query(models.Answer.question_title).scalar(), {'English': 'integer question'} ) @dont_run_in_a_transaction def test_cannot_answer_a_note(self): with self.session.begin(): creator = models.Administrator( name='creator', surveys=[ models.Survey( title={'English': 'non_answerable'}, nodes=[ models.construct_survey_node( node=models.construct_node( type_constraint='note', title={'English': "can't answer me!"}, ), ), ], ), ], ) self.session.add(creator) with self.assertRaises(exc.NotAnAnswerTypeError): with self.session.begin(): survey = self.session.query(models.Survey).one() submission = models.PublicSubmission( survey=survey, answers=[ models.construct_answer( survey_node=survey.nodes[0], type_constraint='note', ), ], ) self.session.add(submission) with self.assertRaises(FlushError): with self.session.begin(): survey = self.session.query(models.Survey).one() submission = models.PublicSubmission( survey=survey, answers=[ models.construct_answer( survey_node=survey.nodes[0], type_constraint='integer', answer=3, ), ], ) self.session.add(submission) def test_answer_type_matches_question_type(self): with self.session.begin(): creator = models.Administrator(name='creator') survey = models.Survey( title={'English': 'survey'}, nodes=[ models.construct_survey_node( node=models.construct_node( type_constraint='integer', title={'English': 'integer question'}, ), ), ], ) creator.surveys = [survey] self.session.add(creator) with self.assertRaises(IntegrityError): with self.session.begin(): the_survey = self.session.query(models.Survey).one() submission = models.PublicSubmission( survey=the_survey, answers=[ models.construct_answer( survey_node=the_survey.nodes[0], type_constraint='text', answer='not an integer', ), ], ) self.session.add(submission) def test_reject_incorrect_answer_syntax(self): with self.session.begin(): creator = models.Administrator(name='creator') survey = models.Survey( title={'English': 'survey'}, nodes=[ models.construct_survey_node( node=models.construct_node( type_constraint='integer', title={'English': 'integer question'}, ), ), ], ) creator.surveys = [survey] self.session.add(creator) with self.assertRaises(DataError): with self.session.begin(): the_survey = self.session.query(models.Survey).one() submission = models.PublicSubmission( survey=the_survey, answers=[ models.construct_answer( survey_node=the_survey.nodes[0], type_constraint='integer', answer='not an integer', ), ], ) self.session.add(submission) def test_cannot_answer_survey_node_from_another_survey(self): with self.session.begin(): creator = models.Administrator(name='creator') survey1 = models.construct_survey( survey_type='public', title={'English': '1'}, nodes=[ models.construct_survey_node( node=models.construct_node( title={'English': 'node 1'}, type_constraint='integer', ), ), ], ) survey2 = models.construct_survey( survey_type='public', title={'English': '2'}, nodes=[ models.construct_survey_node( node=models.construct_node( title={'English': 'node 2'}, type_constraint='integer', ), ), ], ) creator.surveys = [survey1, survey2] self.session.add(creator) with self.assertRaises(IntegrityError): with self.session.begin(): survey1.submissions.append( models.construct_submission( submission_type='public_submission', answers=[ models.construct_answer( survey_node=survey2.nodes[0], type_constraint='integer', answer=1, ), ], ) ) self.session.add(survey1) def test_text_answer(self): with self.session.begin(): creator = models.Administrator(name='creator') survey = models.Survey( title={'English': 'survey'}, nodes=[ models.construct_survey_node( node=models.construct_node( type_constraint='text', title={'English': 'text_question'}, ), ), ], ) creator.surveys = [survey] self.session.add(creator) with self.session.begin(): the_survey = self.session.query(models.Survey).one() submission = models.PublicSubmission( survey=the_survey, answers=[ models.construct_answer( survey_node=the_survey.nodes[0], type_constraint='text', answer='I can put anything here ಠ_ಠ 你好世界!', ), ], ) self.session.add(submission) self.assertEqual( self.session.query(models.Answer).one().answer, 'I can put anything here ಠ_ಠ 你好世界!' ) def test_photo_answer_no_photo(self): with self.session.begin(): creator = models.Administrator(name='creator') survey = models.Survey( title={'English': 'survey'}, nodes=[ models.construct_survey_node( node=models.construct_node( type_constraint='photo', title={'English': 'photo_question'}, ), ), ], ) creator.surveys = [survey] self.session.add(creator) desired_id = str(uuid.uuid4()) with self.session.begin(): the_survey = self.session.query(models.Survey).one() submission = models.PublicSubmission( survey=the_survey, answers=[ models.construct_answer( survey_node=the_survey.nodes[0], type_constraint='photo', answer=desired_id, ), ], ) self.session.add(submission) self.assertEqual( self.session.query(models.Answer).one().answer, desired_id ) def test_photo_answer_unique(self): with self.session.begin(): creator = models.Administrator(name='creator') survey = models.Survey( title={'English': 'survey'}, nodes=[ models.construct_survey_node( node=models.construct_node( allow_multiple=True, type_constraint='photo', title={'English': 'photo_question'}, ), ), ], ) creator.surveys = [survey] self.session.add(creator) desired_id = str(uuid.uuid4()) with self.assertRaises(IntegrityError): with self.session.begin(): the_survey = self.session.query(models.Survey).one() submission = models.PublicSubmission( survey=the_survey, answers=[ models.construct_answer( survey_node=the_survey.nodes[0], type_constraint='photo', answer=desired_id, ), models.construct_answer( survey_node=the_survey.nodes[0], type_constraint='photo', answer=desired_id, ), ], ) self.session.add(submission) def test_photo_answer_with_photo(self): with self.session.begin(): creator = models.Administrator(name='creator') survey = models.Survey( title={'English': 'survey'}, nodes=[ models.construct_survey_node( node=models.construct_node( type_constraint='photo', title={'English': 'photo_question'}, ), ), ], ) creator.surveys = [survey] self.session.add(creator) desired_id = str(uuid.uuid4()) with self.session.begin(): the_survey = self.session.query(models.Survey).one() submission = models.PublicSubmission( survey=the_survey, answers=[ models.construct_answer( survey_node=the_survey.nodes[0], type_constraint='photo', answer=desired_id, ), ], ) self.session.add(submission) with self.session.begin(): answer = self.session.query(models.Answer).one() photo_path = os.path.join( os.path.abspath('.'), 'dokomoforms/static/src/common/img/favicon.png' ) with open(photo_path, 'rb') as photo_file: b64photo = b64encode(photo_file.read()) answer.photo = models.Photo( id=desired_id, mime_type='png', image=b64photo, ) self.session.add(answer) self.assertEqual( self.session.query(models.Photo.image).scalar(), b64photo ) updated_answer = self.session.query(models.Answer).one() self.assertEqual( updated_answer.main_answer, updated_answer.actual_photo_id ) def test_add_new_photo_to_session(self): with self.session.begin(): creator = models.Administrator(name='creator') survey = models.Survey( title={'English': 'survey'}, nodes=[ models.construct_survey_node( node=models.construct_node( type_constraint='photo', title={'English': 'photo_question'}, ), ), ], ) creator.surveys = [survey] self.session.add(creator) desired_id = str(uuid.uuid4()) with self.session.begin(): the_survey = self.session.query(models.Survey).one() submission = models.PublicSubmission( survey=the_survey, answers=[ models.construct_answer( survey_node=the_survey.nodes[0], type_constraint='photo', answer=desired_id, ), ], ) self.session.add(submission) photo_path = os.path.join( os.path.abspath('.'), 'dokomoforms/static/src/common/img/favicon.png' ) with open(photo_path, 'rb') as photo_file: b64photo = b64encode(photo_file.read()) models.add_new_photo_to_session( self.session, id=desired_id, mime_type='png', image=b64photo, ) self.assertEqual( self.session.query(models.Photo.image).scalar(), b64photo ) updated_answer = self.session.query(models.Answer).one() self.assertEqual( updated_answer.main_answer, updated_answer.actual_photo_id ) def test_add_new_photo_to_session_bogus_id(self): with self.session.begin(): creator = models.Administrator(name='creator') survey = models.Survey( title={'English': 'survey'}, nodes=[ models.construct_survey_node( node=models.construct_node( type_constraint='photo', title={'English': 'photo_question'}, ), ), ], ) creator.surveys = [survey] self.session.add(creator) desired_id = str(uuid.uuid4()) with self.session.begin(): the_survey = self.session.query(models.Survey).one() submission = models.PublicSubmission( survey=the_survey, answers=[ models.construct_answer( survey_node=the_survey.nodes[0], type_constraint='photo', answer=desired_id, ), ], ) self.session.add(submission) photo_path = os.path.join( os.path.abspath('.'), 'dokomoforms/static/src/common/img/favicon.png' ) with open(photo_path, 'rb') as photo_file: b64photo = b64encode(photo_file.read()) with self.assertRaises(exc.PhotoIdDoesNotExistError): models.add_new_photo_to_session( self.session, id=str(uuid.uuid4()), mime_type='png', image=b64photo, ) def test_integer_answer(self): with self.session.begin(): creator = models.Administrator(name='creator') survey = models.Survey( title={'English': 'survey'}, nodes=[ models.construct_survey_node( node=models.construct_node( type_constraint='integer', title={'English': 'integer_question'}, ), ), ], ) creator.surveys = [survey] self.session.add(creator) with self.session.begin(): the_survey = self.session.query(models.Survey).one() submission = models.PublicSubmission( survey=the_survey, answers=[ models.construct_answer( survey_node=the_survey.nodes[0], type_constraint='integer', answer=3, ), ], ) self.session.add(submission) self.assertEqual( self.session.query(models.Answer).one().answer, 3 ) def test_decimal_answer(self): with self.session.begin(): creator = models.Administrator(name='creator') survey = models.Survey( title={'English': 'survey'}, nodes=[ models.construct_survey_node( node=models.construct_node( type_constraint='decimal', title={'English': 'decimal_question'}, ), ), ], ) creator.surveys = [survey] self.session.add(creator) with self.session.begin(): the_survey = self.session.query(models.Survey).one() submission = models.PublicSubmission( survey=the_survey, answers=[ models.construct_answer( survey_node=the_survey.nodes[0], type_constraint='decimal', answer=3.9, ), ], ) self.session.add(submission) self.assertEqual( self.session.query(models.Answer).one().answer, Decimal('3.9') ) def test_date_answer(self): with self.session.begin(): creator = models.Administrator(name='creator') survey = models.Survey( title={'English': 'survey'}, nodes=[ models.construct_survey_node( node=models.construct_node( type_constraint='date', title={'English': 'date_question'}, ), ), ], ) creator.surveys = [survey] self.session.add(creator) with self.session.begin(): the_survey = self.session.query(models.Survey).one() submission = models.PublicSubmission( survey=the_survey, answers=[ models.construct_answer( survey_node=the_survey.nodes[0], type_constraint='date', answer='2015/6/22', ), ], ) self.session.add(submission) self.assertEqual( self.session.query(models.Answer).one().answer, datetime.date(2015, 6, 22) ) def test_time_answer(self): with self.session.begin(): creator = models.Administrator(name='creator') survey = models.Survey( title={'English': 'survey'}, nodes=[ models.construct_survey_node( node=models.construct_node( type_constraint='time', title={'English': 'time_question'}, ), ), ], ) creator.surveys = [survey] self.session.add(creator) with self.session.begin(): the_survey = self.session.query(models.Survey).one() submission = models.PublicSubmission( survey=the_survey, answers=[ models.construct_answer( survey_node=the_survey.nodes[0], type_constraint='time', answer='1:57 PM', ), ], ) self.session.add(submission) self.assertEqual( self.session.query(models.Answer).one().answer, datetime.time( 13, 57, tzinfo=psycopg2.tz.FixedOffsetTimezone(offset=0) ) ) def test_timestamp_answer(self): with self.session.begin(): creator = models.Administrator(name='creator') survey = models.Survey( title={'English': 'survey'}, nodes=[ models.construct_survey_node( node=models.construct_node( type_constraint='timestamp', title={'English': 'timestamp_question'}, ), ), ], ) creator.surveys = [survey] self.session.add(creator) with self.session.begin(): the_survey = self.session.query(models.Survey).one() submission = models.PublicSubmission( survey=the_survey, answers=[ models.construct_answer( survey_node=the_survey.nodes[0], type_constraint='timestamp', answer='2015/06/22 1:57 PM', ), ], ) self.session.add(submission) self.assertEqual( self.session.query(models.Answer).one().answer, datetime.datetime( 2015, 6, 22, 13, 57, tzinfo=psycopg2.tz.FixedOffsetTimezone(offset=0) ) ) def test_location_answer(self): with self.session.begin(): creator = models.Administrator(name='creator') survey = models.Survey( title={'English': 'survey'}, nodes=[ models.construct_survey_node( node=models.construct_node( type_constraint='location', title={'English': 'location_question'}, ), ), ], ) creator.surveys = [survey] self.session.add(creator) with self.session.begin(): the_survey = self.session.query(models.Survey).one() submission = models.PublicSubmission( survey=the_survey, answers=[ models.construct_answer( survey_node=the_survey.nodes[0], type_constraint='location', answer={'lng': 5, 'lat': -5}, ), ], ) self.session.add(submission) self.assertEqual( json.loads(self.session.query(models.Answer).one().answer), {'type': 'Point', 'coordinates': [5, -5]} ) def test_facility_answer(self): with self.session.begin(): creator = models.Administrator(name='creator') survey = models.Survey( title={'English': 'survey'}, nodes=[ models.construct_survey_node( node=models.construct_node( type_constraint='facility', title={'English': 'facility_question'}, logic={ 'nlat': 0, 'slat': 0, 'wlng': 0, 'elng': 0, }, ), ), ], ) creator.surveys = [survey] self.session.add(creator) with self.session.begin(): the_survey = self.session.query(models.Survey).one() submission = models.PublicSubmission( survey=the_survey, answers=[ models.construct_answer( survey_node=the_survey.nodes[0], type_constraint='facility', answer={ 'lng': 5, 'lat': -5, 'facility_id': '1', 'facility_name': 'SEL', 'facility_sector': 'engineering', }, ), ], ) self.session.add(submission) answer = self.session.query(models.Answer).one().answer self.assertEqual( json.loads(answer['facility_location']), {'type': 'Point', 'coordinates': [5, -5]} ) self.assertEqual(answer['facility_id'], '1') self.assertEqual(answer['facility_name'], 'SEL') self.assertEqual(answer['facility_sector'], 'engineering') def test_multiple_choice_answer(self): with self.session.begin(): creator = models.Administrator(name='creator') survey = models.Survey( title={'English': 'survey'}, nodes=[ models.construct_survey_node( node=models.construct_node( type_constraint='multiple_choice', title={'English': 'multiple_choice_question'}, choices=[models.Choice(choice_text={ 'English': 'one' })], ), ), ], ) creator.surveys = [survey] self.session.add(creator) with self.session.begin(): the_survey = self.session.query(models.Survey).one() submission = models.PublicSubmission( survey=the_survey, answers=[ models.construct_answer( survey_node=the_survey.nodes[0], type_constraint='multiple_choice', answer=the_survey.nodes[0].node.choices[0].id, ), ], ) self.session.add(submission) self.assertEqual( self.session.query(models.Answer).one().choice.choice_text, {'English': 'one'} ) def test_answer_choice_belongs_to_question(self): with self.session.begin(): creator = models.Administrator(name='creator') survey = models.Survey( title={"English": "survey"}, nodes=[ models.construct_survey_node( node=models.construct_node( type_constraint='multiple_choice', title={"English": "multiple_choice_question_1"}, choices=[models.Choice( choice_text={'English': 'only one'} )], ), ), models.construct_survey_node( node=models.construct_node( type_constraint='multiple_choice', title={"English": "multiple_choice_question_2"}, choices=[models.Choice( choice_text={'English': 'only one'} )], ), ), ], ) creator.surveys = [survey] self.session.add(creator) with self.assertRaises(IntegrityError): with self.session.begin(): the_survey = self.session.query(models.Survey).one() choice_id = the_survey.nodes[1].node.choices[0].id submission = models.PublicSubmission( survey=the_survey, answers=[ models.construct_answer( survey_node=the_survey.nodes[0], type_constraint='multiple_choice', answer=choice_id, ), ], ) self.session.add(submission) def test_cant_answer_other_by_default(self): with self.session.begin(): creator = models.Administrator(name='creator') survey = models.Survey( title={'English': 'survey'}, nodes=[ models.construct_survey_node( node=models.construct_node( type_constraint='integer', title={'English': 'other_not_allowed'}, ), ), ], ) creator.surveys = [survey] self.session.add(creator) with self.assertRaises(IntegrityError): with self.session.begin(): the_survey = self.session.query(models.Survey).one() submission = models.PublicSubmission( survey=the_survey, answers=[ models.construct_answer( survey_node=the_survey.nodes[0], type_constraint='integer', other='other', ), ], ) self.session.add(submission) def test_only_MC_can_allow_other(self): with self.assertRaises(IntegrityError): with self.session.begin(): creator = models.Administrator(name='creator') survey = models.Survey( title={'English': 'survey'}, nodes=[ models.construct_survey_node( node=models.construct_node( type_constraint='integer', title={'English': 'integer bad'}, allow_other=True, ), ), ], ) creator.surveys = [survey] self.session.add(creator) def test_answer_other(self): with self.session.begin(): creator = models.Administrator(name='creator') survey = models.Survey( title={'English': 'survey'}, nodes=[ models.construct_survey_node( node=models.construct_node( type_constraint='multiple_choice', title={'English': 'other_allowed'}, allow_other=True, ), ), ], ) creator.surveys = [survey] self.session.add(creator) with self.session.begin(): the_survey = self.session.query(models.Survey).one() submission = models.PublicSubmission( survey=the_survey, answers=[ models.construct_answer( survey_node=the_survey.nodes[0], type_constraint='multiple_choice', other='other answer', ), ], ) self.session.add(submission) self.assertEqual( self.session.query(models.Answer).one().other, 'other answer' ) self.assertDictEqual( self.session.query(models.Answer).one().response, { 'type_constraint': 'multiple_choice', 'response_type': 'other', 'response': 'other answer', } ) def test_answer_while_other_allowed(self): with self.session.begin(): creator = models.Administrator(name='creator') survey = models.Survey( title={'English': 'survey'}, nodes=[ models.construct_survey_node( node=models.construct_node( type_constraint='multiple_choice', title={'English': 'other_allowed'}, allow_other=True, choices=[ models.Choice( choice_text={'English': 'choice'}, ), ], ), ), ], ) creator.surveys = [survey] self.session.add(creator) with self.session.begin(): the_survey = self.session.query(models.Survey).one() submission = models.PublicSubmission( survey=the_survey, answers=[ models.construct_answer( survey_node=the_survey.nodes[0], type_constraint='multiple_choice', answer=the_survey.nodes[0].node.choices[0].id, ), ], ) self.session.add(submission) choice = self.session.query(models.Choice).one() self.assertEqual( self.session.query(models.Answer).one().answer, choice.id ) self.assertDictEqual( self.session.query(models.Answer).one().response, { 'type_constraint': 'multiple_choice', 'response_type': 'answer', 'response': { 'choice_number': 0, 'choice_text': {'English': 'choice'}, 'id': choice.id, }, } ) def test_cant_give_answer_and_other(self): with self.session.begin(): creator = models.Administrator(name='creator') survey = models.Survey( title={'English': 'survey'}, nodes=[ models.construct_survey_node( node=models.construct_node( type_constraint='multiple_choice', title={'English': 'other allowed'}, allow_other=True, choices=[ models.Choice( choice_text={'English': 'choice'}, ), ], ), ), ], ) creator.surveys = [survey] self.session.add(creator) with self.assertRaises(IntegrityError): with self.session.begin(): the_survey = self.session.query(models.Survey).one() submission = models.PublicSubmission( survey=the_survey, answers=[ models.construct_answer( survey_node=the_survey.nodes[0], type_constraint='multiple_choice', answer=( self.session.query(models.Choice.id).scalar() ), other='other answer', ), ], ) self.session.add(submission) def test_cant_answer_dont_know_by_default(self): with self.session.begin(): creator = models.Administrator(name='creator') survey = models.Survey( title={'English': 'survey'}, nodes=[ models.construct_survey_node( node=models.construct_node( type_constraint='integer', title={'English': 'dont_know_not_allowed'}, ), ), ], ) creator.surveys = [survey] self.session.add(creator) with self.assertRaises(IntegrityError): with self.session.begin(): the_survey = self.session.query(models.Survey).one() submission = models.PublicSubmission( survey=the_survey, answers=[ models.construct_answer( survey_node=the_survey.nodes[0], type_constraint='integer', dont_know='dont_know', ), ], ) self.session.add(submission) def test_answer_dont_know(self): with self.session.begin(): creator = models.Administrator(name='creator') survey = models.Survey( title={'English': 'survey'}, nodes=[ models.construct_survey_node( node=models.construct_node( type_constraint='integer', title={'English': 'dont_know_not_allowed'}, ), allow_dont_know=True, ), ], ) creator.surveys = [survey] self.session.add(creator) with self.session.begin(): the_survey = self.session.query(models.Survey).one() submission = models.PublicSubmission( survey=the_survey, answers=[ models.construct_answer( survey_node=the_survey.nodes[0], type_constraint='integer', dont_know='dont_know answer', ), ], ) self.session.add(submission) self.assertEqual( self.session.query(models.Answer).one().dont_know, 'dont_know answer' ) self.assertDictEqual( self.session.query(models.Answer).one().response, { 'type_constraint': 'integer', 'response_type': 'dont_know', 'response': 'dont_know answer' } ) def test_answer_while_dont_know_allowed(self): with self.session.begin(): creator = models.Administrator(name='creator') survey = models.Survey( title={'English': 'survey'}, nodes=[ models.construct_survey_node( node=models.construct_node( type_constraint='integer', title={'English': 'dont_know_allowed'}, ), allow_dont_know=True, ), ], ) creator.surveys = [survey] self.session.add(creator) with self.session.begin(): the_survey = self.session.query(models.Survey).one() submission = models.PublicSubmission( survey=the_survey, answers=[ models.construct_answer( survey_node=the_survey.nodes[0], type_constraint='integer', answer=3, ), ], ) self.session.add(submission) self.assertEqual( self.session.query(models.Answer).one().answer, 3 ) self.assertDictEqual( self.session.query(models.Answer).one().response, { 'type_constraint': 'integer', 'response_type': 'answer', 'response': 3, } ) def test_cant_give_answer_and_dont_know(self): with self.session.begin(): creator = models.Administrator(name='creator') survey = models.Survey( title={'English': 'survey'}, nodes=[ models.construct_survey_node( node=models.construct_node( type_constraint='integer', title={'English': 'dont_know allowed'}, ), allow_dont_know=True, ), ], ) creator.surveys = [survey] self.session.add(creator) with self.assertRaises(IntegrityError): with self.session.begin(): the_survey = self.session.query(models.Survey).one() submission = models.PublicSubmission( survey=the_survey, answers=[ models.construct_answer( survey_node=the_survey.nodes[0], type_constraint='integer', answer=3, dont_know='dont_know answer', ), ], ) self.session.add(submission) def test_response_answer(self): with self.session.begin(): creator = models.Administrator(name='creator') survey = models.Survey( title={'English': 'survey'}, nodes=[ models.construct_survey_node( node=models.construct_node( type_constraint='integer', title={'English': 'integer response'}, ), ), ], ) creator.surveys = [survey] self.session.add(creator) with self.session.begin(): the_survey = self.session.query(models.Survey).one() submission = models.PublicSubmission( survey=the_survey, answers=[ models.construct_answer( survey_node=the_survey.nodes[0], type_constraint='integer', response={ 'response_type': 'answer', 'response': 3, }, ), ], ) self.session.add(submission) self.assertEqual( self.session.query(models.Answer).one().answer, 3 ) self.assertDictEqual( self.session.query(models.Answer).one().response, { 'type_constraint': 'integer', 'response_type': 'answer', 'response': 3, } ) def test_response_other(self): with self.session.begin(): creator = models.Administrator(name='creator') survey = models.Survey( title={'English': 'survey'}, nodes=[ models.construct_survey_node( node=models.construct_node( type_constraint='multiple_choice', title={'English': 'other response'}, allow_other=True, ), ), ], ) creator.surveys = [survey] self.session.add(creator) with self.session.begin(): the_survey = self.session.query(models.Survey).one() submission = models.PublicSubmission( survey=the_survey, answers=[ models.construct_answer( survey_node=the_survey.nodes[0], type_constraint='multiple_choice', response={ 'response_type': 'other', 'response': 'other answer', }, ), ], ) self.session.add(submission) self.assertEqual( self.session.query(models.Answer).one().other, 'other answer' ) self.assertDictEqual( self.session.query(models.Answer).one().response, { 'type_constraint': 'multiple_choice', 'response_type': 'other', 'response': 'other answer', } ) def test_response_dont_know(self): with self.session.begin(): creator = models.Administrator(name='creator') survey = models.Survey( title={'English': 'survey'}, nodes=[ models.construct_survey_node( node=models.construct_node( type_constraint='integer', title={'English': 'dont_know response'}, ), allow_dont_know=True, ), ], ) creator.surveys = [survey] self.session.add(creator) with self.session.begin(): the_survey = self.session.query(models.Survey).one() submission = models.PublicSubmission( survey=the_survey, answers=[ models.construct_answer( survey_node=the_survey.nodes[0], type_constraint='integer', response={ 'response_type': 'dont_know', 'response': "I don't know!", }, ), ], ) self.session.add(submission) self.assertEqual( self.session.query(models.Answer).one().dont_know, "I don't know!" ) self.assertDictEqual( self.session.query(models.Answer).one().response, { 'type_constraint': 'integer', 'response_type': 'dont_know', 'response': "I don't know!", } ) def test_response_legitimate(self): with self.session.begin(): creator = models.Administrator(name='creator') survey = models.Survey( title={'English': 'survey'}, nodes=[ models.construct_survey_node( node=models.construct_node( type_constraint='integer', title={'English': 'integer response'}, ), ), ], ) creator.surveys = [survey] self.session.add(creator) with self.assertRaises(exc.NotAResponseTypeError): with self.session.begin(): the_survey = self.session.query(models.Survey).one() submission = models.PublicSubmission( survey=the_survey, answers=[ models.construct_answer( survey_node=the_survey.nodes[0], type_constraint='integer', response={ 'response_type': 'id', 'response': 3, }, ), ], ) self.session.add(submission) def test_answer_multiple_not_allowed(self): with self.session.begin(): creator = models.Administrator(name='creator') survey = models.Survey( title={'English': 'survey'}, nodes=[ models.construct_survey_node( node=models.construct_node( type_constraint='integer', title={'English': 'integer response'}, ), ), ], ) creator.surveys = [survey] self.session.add(creator) with self.assertRaises(IntegrityError): with self.session.begin(): the_survey = self.session.query(models.Survey).one() submission = models.PublicSubmission( survey=the_survey, answers=[ models.construct_answer( survey_node=the_survey.nodes[0], type_constraint='integer', response={ 'response_type': 'answer', 'response': 3, }, ), models.construct_answer( survey_node=the_survey.nodes[0], type_constraint='integer', response={ 'response_type': 'answer', 'response': 4, }, ), ], ) self.session.add(submission) def test_answer_multiple_allowed(self): with self.session.begin(): creator = models.Administrator(name='creator') survey = models.Survey( title={'English': 'survey'}, nodes=[ models.construct_survey_node( node=models.construct_node( type_constraint='integer', title={'English': 'integer response'}, allow_multiple=True, ), ), ], ) creator.surveys = [survey] self.session.add(creator) with self.session.begin(): the_survey = self.session.query(models.Survey).one() submission = models.PublicSubmission( survey=the_survey, answers=[ models.construct_answer( survey_node=the_survey.nodes[0], type_constraint='integer', response={ 'response_type': 'answer', 'response': 3, }, ), models.construct_answer( survey_node=the_survey.nodes[0], type_constraint='integer', response={ 'response_type': 'answer', 'response': 4, }, ), ], ) self.session.add(submission) self.assertEqual( self.session.query(func.count(models.Answer.id)).scalar(), 2 ) def test_answer_multiple_allowed_choices(self): with self.session.begin(): creator = models.Administrator(name='creator') survey = models.Survey( title={'English': 'survey'}, nodes=[ models.construct_survey_node( node=models.construct_node( type_constraint='multiple_choice', title={'English': 'choice response'}, allow_multiple=True, choices=[ models.Choice( choice_text={'English': 'one'}, ), models.Choice( choice_text={'English': 'two'}, ), ], ), ), ], ) creator.surveys = [survey] self.session.add(creator) with self.session.begin(): the_survey = self.session.query(models.Survey).one() submission = models.PublicSubmission( survey=the_survey, answers=[ models.construct_answer( survey_node=the_survey.nodes[0], type_constraint='multiple_choice', response={ 'response_type': 'answer', 'response': survey.nodes[0].node.choices[0].id, }, ), models.construct_answer( survey_node=the_survey.nodes[0], type_constraint='multiple_choice', response={ 'response_type': 'answer', 'response': survey.nodes[0].node.choices[1].id, }, ), ], ) self.session.add(submission) self.assertEqual( self.session.query(func.count(models.Answer.id)).scalar(), 2 ) def test_answer_multiple_same_choice_forbidden(self): with self.session.begin(): creator = models.Administrator(name='creator') survey = models.Survey( title={'English': 'survey'}, nodes=[ models.construct_survey_node( node=models.construct_node( type_constraint='multiple_choice', title={'English': 'choice response'}, allow_multiple=True, choices=[ models.Choice( choice_text={'English': 'one'}, ), models.Choice( choice_text={'English': 'two'}, ), ], ), ), ], ) creator.surveys = [survey] self.session.add(creator) with self.assertRaises(IntegrityError): with self.session.begin(): the_survey = self.session.query(models.Survey).one() submission = models.PublicSubmission( survey=the_survey, answers=[ models.construct_answer( survey_node=the_survey.nodes[0], type_constraint='multiple_choice', response={ 'response_type': 'answer', 'response': survey.nodes[0].node.choices[0].id, }, ), models.construct_answer( survey_node=the_survey.nodes[0], type_constraint='multiple_choice', response={ 'response_type': 'answer', 'response': survey.nodes[0].node.choices[0].id, }, ), ], ) self.session.add(submission)