Beispiel #1
0
    def test_create(self):
        son = {
            'title': 'Foo report',
            'description': "Foo bar baz",
            'text': "SELECT * FROM organisation",
        }
        with base.mock_user('super_admin'):
            response_son = self.fetch(
                "/custom_query.json", method='POST',
                body=json_encode(son), expected=200, decode=True)

        for k in son:
            self.assertIn(k, response_son)
            self.assertEqual(son[k], response_son[k])

        qid = response_son['id']
        with base.mock_user('super_admin'):
            response_son = self.fetch(
                "/custom_query/%s.json" % qid, method='GET',
                decode=True)

        self.assertEqual(qid, response_son['id'])
        for k in son:
            self.assertIn(k, response_son)
            self.assertEqual(son[k], response_son[k])
Beispiel #2
0
    def test_get_statistics(self):
        with model.session_scope() as session:
            program = session.query(model.Program).one()
            organisation = (session.query(
                model.Organisation).filter_by(name='Utility').one())
            survey = (session.query(
                model.Survey).filter_by(title='Survey 2').one())

            self.program_id = str(program.id)
            self.organisation_id = str(organisation.id)
            self.survey_id = str(survey.id)

        with base.mock_user('consultant'):
            self.fetch("/report/sub/stats/program"
                       "/%s/survey/%s.json?approval=reviewed" %
                       (self.program_id, self.survey_id),
                       method='GET',
                       expected=200,
                       decode=False)

        with base.mock_user('authority'):
            self.fetch("/report/sub/stats/program"
                       "/%s/survey/%s.json?approval=reviewed" %
                       (self.program_id, self.survey_id),
                       method='GET',
                       expected=200,
                       decode=False)

        # Before purchase survey
        with base.mock_user('clerk'):
            self.fetch("/report/sub/stats/program"
                       "/%s/survey/%s.json?approval=reviewed" %
                       (self.program_id, self.survey_id),
                       method='GET',
                       expected=403,
                       decode=False)

        # After purchase survey
        with base.mock_user('admin'):
            self.purchase_program()

        with base.mock_user('clerk'):
            self.fetch("/report/sub/stats/program"
                       "/%s/survey/%s.json?approval=reviewed" %
                       (self.program_id, self.survey_id),
                       method='GET',
                       expected=200,
                       decode=False)
Beispiel #3
0
    def test_modify_user(self):
        with model.session_scope() as session:
            # TODO: Refactor this to make it reusable.
            user = (session.query(
                model.AppUser).filter(model.AppUser.email == 'clerk').one())

            to_son = ToSon(
                r'/id$',
                r'/title$',
                r'/name$',
                r'/surveygroups$',
                r'/organisation$',
                r'/[0-9]+$',
                # Exclude password
                r'!/password$',
            )
            user_son = to_son(user)

        users = [('consultant', 403, "are not the owner"),
                 ('authority', 403, "are not the owner"),
                 ('author', 403, "are not the owner"), ('clerk', 200, 'OK'),
                 ('org_admin', 200, 'OK'), ('admin', 200, 'OK')]

        for user_email, code, reason in users:
            post_data = user_son.copy()
            post_data['organisation'] = post_data['organisation'].copy()
            with base.mock_user(user_email):
                response = self.fetch("/user/%s.json" % user_son['id'],
                                      method='PUT',
                                      body=json_encode(post_data))
                self.assertIn(reason, response.reason, msg=user_email)
                self.assertEqual(code, response.code, msg=user_email)

        users = [
            ('clerk', 403, "can't add that user"),
            ('org_admin', 200, 'OK'),
        ]

        for user_email, code, reason in users:
            post_data = user_son.copy()
            post_data['organisation'] = post_data['organisation'].copy()
            post_data['deleted'] = True
            with base.mock_user(user_email):
                response = self.fetch("/user/%s.json" % user_son['id'],
                                      method='PUT',
                                      body=json_encode(post_data))
                self.assertIn(reason, response.reason, msg=user_email)
                self.assertEqual(code, response.code, msg=user_email)
Beispiel #4
0
    def modify_org(self, user_email, org_name, code, reason):
        with model.session_scope() as session:
            org = session.query(model.Organisation).\
                filter(func.lower(model.Organisation.name) ==
                       func.lower(org_name)).one()

            to_son = ToSon(
                r'/id$',
                r'/name$',
                r'/title$',
                r'/locations.*$',
                r'/meta.*$',
                r'/surveygroups$',
                r'/[0-9]+$',
            )
            to_son.exclude(
                r'/locations/[0-9]+/id$',
                r'/locations/[0-9]+/organisation.*$',
                r'/meta/id$',
                r'/meta/organisation.*$',
            )
            org_son = to_son(org)

        with base.mock_user(user_email):
            post_data = org_son.copy()
            response = self.fetch("/organisation/%s.json" % org_son['id'],
                                  method='PUT',
                                  body=json_encode(post_data),
                                  expected=code)
        self.assertIn(reason, response.reason,
                      "%s operating on %s" % (user_email, org_name))
Beispiel #5
0
    def test_list_org(self):
        with base.mock_user('authority'):
            response = self.fetch("/organisation.json",
                                  method='GET',
                                  expected=200)

        orgs_son = json_decode(response.body)
        self.assertIsInstance(orgs_son, list)
        self.assertEqual(len(orgs_son), 2)
        del orgs_son[0]['id']
        del orgs_son[1]['id']
        expected = [{
            'name': 'Primary',
            'deleted': False,
            'locations': [{
                'description': 'Nowhere',
            }],
            'meta': {
                'assetTypes': ['water wholesale'],
            }
        }, {
            'name': 'Utility',
            'deleted': False,
            'locations': [{
                'description': 'Somewhere',
            }],
            'meta': {
                'assetTypes': ['water local'],
            }
        }]
        self.assertEqual(orgs_son, expected)
Beispiel #6
0
    def modify_org_in_user(self, user_email, old_org_name, new_org_name, code,
                           reason):
        with model.session_scope() as session:
            org = (session.query(model.Organisation).filter(
                func.lower(model.Organisation.name) == func.lower(
                    new_org_name)).one())

            to_son = ToSon(
                r'/id$',
                r'/name$',
                r'/surveygroups$',
                r'/[0-9]+$',
            )
            org_son = to_son(org)

            user = (session.query(model.AppUser).filter(
                func.lower(model.AppUser.email) == func.lower(
                    user_email)).one())

            user_son = to_son(user)
            user_son['organisation'] = org_son

            with base.mock_user(user_email):
                response = self.fetch("/user/%s.json" % user_son['id'],
                                      method='PUT',
                                      body=json_encode(user_son),
                                      expected=code)
                self.assertIn(reason, response.reason, msg=user_email)
Beispiel #7
0
    def test_list_org(self):
        with base.mock_user('admin'):
            response = self.fetch("/user.json?pageSize=2&page=0",
                                  method='GET',
                                  expected=200)

        orgs_son = json_decode(response.body)
        self.assertIsInstance(orgs_son, list)
        self.assertEqual(len(orgs_son), 2)
        del orgs_son[0]['id']
        del orgs_son[0]['organisation']['id']
        del orgs_son[1]['id']
        del orgs_son[1]['organisation']['id']
        expected = [{
            'name': 'Admin',
            'deleted': False,
            'organisation': {
                'name': 'Primary',
                'deleted': False,
            }
        }, {
            'name': 'Author',
            'deleted': False,
            'organisation': {
                'name': 'Primary',
                'deleted': False,
            }
        }]
        self.assertEqual(orgs_son, expected)
Beispiel #8
0
    def test_create_org(self):
        users = [('clerk', 403, "can't add that organisation"),
                 ('org_admin', 403, "can't add that organisation"),
                 ('consultant', 403, "can't add that organisation"),
                 ('authority', 403, "can't add that organisation"),
                 ('author', 403, "can't add that organisation"),
                 ('admin', 200, 'OK')]

        with model.session_scope() as session:
            surveygroups_son = self.get_groups_son(session, 'apple')

        for i, (user, code, reason) in enumerate(users):
            post_data = {
                "name": "Foo %d" % i,
                "url": "http://foo%d.com" % i,
                'locations': [{
                    'description': 'Foo',
                    'region': 'Foo',
                }],
                'meta': {
                    'numberOfCustomers': 0,
                },
                'surveygroups': surveygroups_son,
            }
            with base.mock_user(user):
                response = self.fetch("/organisation.json",
                                      method='POST',
                                      body=json_encode(post_data),
                                      expected=code)
                self.assertIn(reason, response.reason, "%s" % (user))
Beispiel #9
0
    def create_user(self,
                    prefix,
                    i,
                    user_email,
                    org_name,
                    role,
                    code,
                    reason,
                    surveygroup_names,
                    custom_data=None):
        with model.session_scope() as session:
            surveygroups_son = self.get_groups_son(session, *surveygroup_names)

        post_data = {
            'email': 'user%s%s' % (prefix, i),
            'name': 'foo',
            'password': '******',
            'role': role,
            'organisation': {
                'id': str(self.org_id(org_name))
            },
            'surveygroups': surveygroups_son,
        }
        if custom_data:
            post_data.update(custom_data)

        with base.mock_user(user_email), \
                mock.patch(
                    'crud.user.test_password', lambda x: (1.0, 0.1, {})):
            response = self.fetch("/user.json",
                                  method='POST',
                                  body=json_encode(post_data),
                                  expected=code)
            self.assertIn(reason, response.reason, msg=user_email)
Beispiel #10
0
    def test_password_strength(self):
        response = self.fetch("/password.json",
                              method='POST',
                              body=json_encode({'password': '******'}),
                              expected=403)

        with base.mock_user('clerk'):
            response = self.fetch("/password.json",
                                  method='POST',
                                  body=json_encode({'password': '******'}),
                                  expected=200)
        son = json_decode(response.body)
        self.assertLess(son['strength'], 0.5)
        self.assertIn('charmix', son['improvements'])
        self.assertIn('length', son['improvements'])

        with base.mock_user('clerk'):
            response = self.fetch("/password.json",
                                  method='POST',
                                  body=json_encode({'password': '******'}),
                                  expected=200)
        son = json_decode(response.body)
        self.assertNotIn('charmix', son['improvements'])
        self.assertIn('length', son['improvements'])

        with base.mock_user('clerk'):
            response = self.fetch(
                "/password.json",
                method='POST',
                body=json_encode({'password': '******'}),
                expected=200)
        son = json_decode(response.body)
        self.assertIn('charmix', son['improvements'])
        self.assertNotIn('length', son['improvements'])

        with base.mock_user('clerk'):
            response = self.fetch("/password.json",
                                  method='POST',
                                  body=json_encode(
                                      {'password': '******'}),
                                  expected=200)
        son = json_decode(response.body)
        self.assertGreater(son['strength'], 0.9)
        self.assertNotIn('length', son['improvements'])
        self.assertNotIn('charmix', son['improvements'])
Beispiel #11
0
    def test_create(self):
        with model.session_scope() as session:
            program = session.query(model.Program).one()
            organisation = (session.query(
                model.Organisation).filter_by(name='Utility').one())
            survey = (session.query(
                model.Survey).filter_by(title='Survey 2').one())

            program_id = str(program.id)
            organisation_id = str(organisation.id)
            survey_id = str(survey.id)

        # Try to create a submission against a suvery that hasn't been
        # purchased
        with base.mock_user('org_admin'):
            submission_son = {'title': "Submission"}
            submission_son = self.fetch(
                "/submission.json?organisationId=%s&programId=%s&surveyId=%s" %
                (organisation_id, program_id, survey_id),
                method='POST',
                body=json_encode(submission_son),
                expected=403,
                decode=False)

        # Grant access
        with base.mock_user('admin'):
            self.fetch("/organisation/%s/survey/%s.json?programId=%s" %
                       (organisation_id, survey_id, program_id),
                       method='PUT',
                       body='',
                       expected=200)

        # Retry
        with base.mock_user('org_admin'):
            submission_son = {
                'title': "Submission",
                'created': datetime.datetime(2012, 1, 1).timestamp(),
            }
            submission_son = self.fetch(
                "/submission.json?organisationId=%s&programId=%s&surveyId=%s" %
                (organisation_id, program_id, survey_id),
                method='POST',
                body=json_encode(submission_son),
                expected=200,
                decode=True)
Beispiel #12
0
    def test_submission_exporter(self):
        with base.mock_user('admin'):
            self.purchase_program()

        with base.mock_user('clerk'):
            self.add_submission()

        with model.session_scope() as session:
            submission = (session.query(model.Submission).filter(
                model.Submission.program_id == self.program_id,
                model.Submission.organisation_id == self.organisation_id,
                model.Submission.survey_id == self.survey_id).first())
            submission_id = submission.id

        with base.mock_user('author'):
            self.fetch("/report/sub/export/%s/tabular.xlsx" % submission_id,
                       method='GET',
                       expected=403,
                       decode=False)
            self.fetch("/report/sub/export/%s/nested.xlsx" % submission_id,
                       method='GET',
                       expected=403,
                       decode=False)

        with base.mock_user('consultant'):
            self.fetch("/report/sub/export/%s/tabular.xlsx" % submission_id,
                       method='GET',
                       expected=200,
                       decode=False)
            self.fetch("/report/sub/export/%s/nested.xlsx" % submission_id,
                       method='GET',
                       expected=200,
                       decode=False)

        with base.mock_user('clerk'):
            self.fetch("/report/sub/export/%s/tabular.xlsx" % submission_id,
                       method='GET',
                       expected=200,
                       decode=False)
            self.fetch("/report/sub/export/%s/nested.xlsx" % submission_id,
                       method='GET',
                       expected=200,
                       decode=False)
Beispiel #13
0
 def test_structure_exporter_with_purchase(self):
     with base.mock_user('admin'):
         self.fetch("/report/prog/export/%s/survey/%s/nested.xlsx" %
                    (self.program_id, self.survey_id),
                    method='GET',
                    expected=200)
         self.fetch("/report/prog/export/%s/survey/%s/tabular.xlsx" %
                    (self.program_id, self.survey_id),
                    method='GET',
                    expected=200)
Beispiel #14
0
    def test_recalculate(self):
        aid = self.create_submission()
        with model.session_scope() as session:
            submission = session.query(model.Submission).get(aid)
            sid = submission.program_id
            process_id = submission.survey.qnodes[0].children[0].id
            function_2_id = submission.survey.qnodes[1].id

        # Move a process (qnode) to a different function
        with base.mock_user('author'):
            url = "/qnode/{}.json?programId={}".format(process_id, sid)
            qnode_son = self.fetch(url,
                                   method='GET',
                                   expected=200,
                                   decode=True)
            qnode_son = self.fetch(url + "&parentId={}".format(function_2_id),
                                   method='PUT',
                                   expected=200,
                                   decode=True,
                                   body=json_encode(qnode_son))

        # Check that rnode score is out of date
        with model.session_scope() as session:
            submission = session.query(model.Submission).get(aid)
            functions = list(submission.rnodes)
            self.assertAlmostEqual(functions[0].score, 3 + 6 + 11 + 13)
            self.assertAlmostEqual(functions[1].score, 0)
            self.assertAlmostEqual(functions[0].qnode.total_weight, 11 + 13)
            self.assertAlmostEqual(functions[1].qnode.total_weight, 3 + 6)

        # Run recalculation script
        config = utils.get_config("recalculate.yaml")
        messages = None

        def send(config, msg):
            messages.append(msg)

        messages = []
        with mock.patch('recalculate.send', send):
            recalculate.process_once(config)
            self.assertEqual(len(messages), 0)

        # Check that rnode score is no longer out of date
        with model.session_scope() as session:
            submission = session.query(model.Submission).get(aid)
            functions = list(submission.rnodes)
            self.assertAlmostEqual(functions[0].score, 11 + 13)
            self.assertAlmostEqual(functions[1].score, 3 + 6)
            self.assertAlmostEqual(functions[0].qnode.total_weight, 11 + 13)
            self.assertAlmostEqual(functions[1].qnode.total_weight, 3 + 6)
Beispiel #15
0
    def test_impersonate_expired(self):
        '''
        Ensure superusers can't keep impersonating after jurisdiction
        changes.
        '''
        with model.session_scope() as session:
            admin = (session.query(
                model.AppUser).filter(model.AppUser.email == 'admin').first())
            clerk = (session.query(
                model.AppUser).filter(model.AppUser.email == 'clerk').first())

            with base.mock_user(user_email='clerk', super_email='admin'):
                # Wrong group; fails
                self.set_groups(clerk, 'banana')
                session.commit()
                response = self.fetch("/program.json".format(),
                                      method='GET',
                                      follow_redirects=False,
                                      expected=403)
                self.assertIn("not a memeber of that survey group",
                              response.reason)

                # Right group; works
                self.set_groups(clerk, 'apple')
                session.commit()
                response = self.fetch("/program.json".format(),
                                      method='GET',
                                      follow_redirects=False,
                                      expected=200)
                self.assertIn("OK", response.reason)

                # Impersonated user disabled; fails
                clerk.deleted = True
                admin.deleted = False
                session.commit()
                response = self.fetch("/program.json".format(),
                                      method='GET',
                                      follow_redirects=False,
                                      expected=403)
                self.assertIn("account has been disabled", response.reason)

                # Superuser (true user) disabled; fails
                clerk.deleted = False
                admin.deleted = True
                session.commit()
                response = self.fetch("/program.json".format(),
                                      method='GET',
                                      follow_redirects=False,
                                      expected=403)
                self.assertIn("account has been disabled", response.reason)
Beispiel #16
0
    def test_recalculate_failure(self):
        aid = self.create_submission()
        with model.session_scope() as session:
            submission = session.query(model.Submission).get(aid)
            sid = submission.program_id
            process_id = submission.survey.qnodes[0].children[0].id
            function_2_id = submission.survey.qnodes[1].id

        # Move a process (qnode) to a different function
        with base.mock_user('author'):
            url = "/qnode/{}.json?programId={}".format(process_id, sid)
            qnode_son = self.fetch(url,
                                   method='GET',
                                   expected=200,
                                   decode=True)
            qnode_son = self.fetch(url + "&parentId={}".format(function_2_id),
                                   method='PUT',
                                   expected=200,
                                   decode=True,
                                   body=json_encode(qnode_son))

        # Run recalculation script
        config = utils.get_config("recalculate.yaml")
        messages = None

        def send(config, msg, to):
            messages.append(msg)

        messages = []
        with mock.patch('recalculate.send', send), \
                mock.patch('response_type.ResponseType.validate',
                           side_effect=ResponseTypeError):
            count, n_errors = recalculate.process_once(config)
            self.assertEqual(n_errors, 1)

        messages = []
        with mock.patch('recalculate.send', send), \
                mock.patch('recalculate.process_once',
                           side_effect=ExpectedError), \
                self.assertRaises(ExpectedError), \
                mock.patch('recalculate.time.sleep',
                           side_effect=UnexpectedError):
            recalculate.process_loop()
        self.assertEqual(len(messages), 1)
Beispiel #17
0
    def test_impersonate(self):
        users = [
            ('clerk', 'author', 403, "rank is too low"),
            ('org_admin', 'clerk', 403, "rank is too low"),
            ('author', 'clerk', 403, "rank is too low"),
            ('consultant', 'clerk', 403, "rank is too low"),
            ('authority', 'clerk', 403, "rank is too low"),
            ('author', 'clerk', 403, "rank is too low"),
            ('clerk', 'clerk_b', 403, "rank is too low"),
            ('super_admin', 'clerk', 200, 'OK'),
            ('super_admin', 'org_admin', 200, 'OK'),
            ('super_admin', 'author', 200, 'OK'),
            ('super_admin', 'consultant', 200, 'OK'),
            ('super_admin', 'authority', 200, 'OK'),
            ('super_admin', 'author', 200, 'OK'),
            ('super_admin', 'admin', 200, 'OK'),
            ('super_admin', 'clerk_b', 200, 'OK'),
            ('admin', 'clerk', 200, 'OK'),
            ('admin', 'org_admin', 200, 'OK'),
            ('admin', 'consultant', 200, 'OK'),
            ('admin', 'authority', 200, 'OK'),
            ('admin', 'author', 200, 'OK'),
            ('admin', 'admin', 200, 'OK'),
            ('admin', 'super_admin', 403, 'rank is too low'),
            ('admin', 'clerk_b', 403, 'not a memeber of that survey group'),
        ]

        for super_email, user_email, code, reason in users:
            with model.session_scope() as session:
                user = (session.query(model.AppUser).filter(
                    model.AppUser.email == user_email).first())
                user_id = user.id
            with base.mock_user(user_email, super_email):
                response = self.fetch("/impersonate/{}".format(user_id),
                                      method='PUT',
                                      body='')
                self.assertIn(
                    reason, response.reason,
                    "{} failed to impersonate {}".format(
                        super_email, user_email))
                self.assertEqual(code, response.code)
Beispiel #18
0
    def test_set_password(self):
        with model.session_scope() as session:
            surveygroups_son = self.get_groups_son(session, 'apple')

        post_data = {
            'email': 'passwordTest',
            'name': 'ptest',
            'password': '******',
            'role': 'clerk',
            'organisation': {
                'id': str(self.org_id('utility'))
            },
            'surveygroups': surveygroups_son,
        }

        with base.mock_user('admin'):
            response = self.fetch("/user.json",
                                  method='POST',
                                  body=json_encode(post_data),
                                  expected=403)
            self.assertIn('not strong enough', response.reason)
Beispiel #19
0
 def test_authenticated_root(self):
     with base.mock_user('admin'):
         response = self.fetch("/", expected=200)
     self.assertIn("Sign out", response.body.decode('utf8'))
Beispiel #20
0
    def test_extern(self):
        '''Check that variables can depend on each other'''
        with model.session_scope() as session:
            user = (session.query(
                model.AppUser).filter_by(email='clerk').one())
            survey = (session.query(
                model.Survey).filter_by(title='Survey 1').one())
            program = survey.program

            # Add a response type that has an extenal variable
            rt = next(rt for rt in TEST_RESPONSE_TYPES
                      if rt['id'] == 'external-var')
            ext_response_type = model.ResponseType(program=program,
                                                   name=rt['name'],
                                                   parts=rt['parts'],
                                                   formula=rt['formula'])
            session.add(ext_response_type)

            # Attach response type to a measure
            target_qm = survey.qnodes[0].children[0].qnode_measures[1]
            target_qm.measure.response_type = ext_response_type
            self.assertEqual(1, len(ext_response_type.measures))

            # Bind variable to link measures
            source_qm = survey.qnodes[0].children[0].qnode_measures[0]
            session.add(
                model.MeasureVariable(program=program,
                                      survey=survey,
                                      source_qnode_measure=source_qm,
                                      source_field='_score',
                                      target_qnode_measure=target_qm,
                                      target_field='ext'))

            submission = model.Submission(program=program,
                                          organisation=user.organisation,
                                          survey=survey,
                                          title="Intermeasure Variables Test",
                                          approval='draft')
            session.add(submission)
            session.flush()

            submission_id = str(submission.id)
            mid_111 = str(
                survey.qnodes[0].children[0].qnode_measures[0].measure_id)
            mid_112 = str(
                survey.qnodes[0].children[0].qnode_measures[1].measure_id)

        # Put dependant response with errors. Check that the error refers to
        # missing dependency.
        with base.mock_user('clerk'):
            response_son = {
                'notRelevant': False,
                'responseParts': [],
                'comment': "Incomplete dependant response",
                'approval': 'draft',
            }
            response_son = self.fetch("/submission/%s/response/%s.json" %
                                      (submission_id, mid_112),
                                      method='PUT',
                                      body=response_son,
                                      expected=200,
                                      decode=True)

        with model.session_scope() as session:
            response = (session.query(model.Response).get(
                (submission_id, mid_112)))
            self.assertIn('depends on', response.error)
            self.assertIn('measure has an error', response.parent.error)
            self.assertIn('sub-category has an error',
                          response.parent.parent.error)
            self.assertIn('category has an error', response.submission.error)

        # Put dependency, and check that error of dependant has changed.
        with base.mock_user('clerk'):
            response_son = {
                'notRelevant': False,
                'responseParts': [{
                    'note': 'Yes',
                    'index': 1
                }],
                'comment': "Dependency",
                'approval': 'draft',
            }
            response_son = self.fetch("/submission/%s/response/%s.json" %
                                      (submission_id, mid_111),
                                      method='PUT',
                                      body=response_son,
                                      expected=200,
                                      decode=True)

        with model.session_scope() as session:
            response = (session.query(model.Response).get(
                (submission_id, mid_111)))
            self.assertIs(response.error, None)
            # Parent still has an error due to sibling
            self.assertIn('measure has an error', response.parent.error)

            response = (session.query(model.Response).get(
                (submission_id, mid_112)))
            # Error has changed: dependency has been provided, but response
            # is still incomplete
            self.assertIn('undefined variable', response.error)

        # Fix dependant response and check that errors are resolved.
        with base.mock_user('clerk'):
            response_son = self.fetch("/submission/%s/response/%s.json" %
                                      (submission_id, mid_112),
                                      method='GET',
                                      expected=200,
                                      decode=True)
            response_son['response_parts'] = [{'value': 1}]
            response_son['comment'] = "Complete dependant response"
            response_son = self.fetch("/submission/%s/response/%s.json" %
                                      (submission_id, mid_112),
                                      method='PUT',
                                      body=response_son,
                                      expected=200,
                                      decode=True)

        with model.session_scope() as session:
            response = (session.query(model.Response).get(
                (submission_id, mid_112)))
            # Error has been resolved.
            self.assertIs(response.error, None)
            self.assertIs(response.error, response.parent.error)
            self.assertIs(response.error, response.parent.parent.error)
            self.assertIs(response.error, response.submission.error)
Beispiel #21
0
    def test_timeline(self):
        # Delete a qnode; this should subscribe to the program and add an event
        # to the timeline
        with base.mock_user('author'):
            program_sons = self.fetch("/program.json",
                                      method='GET',
                                      expected=200,
                                      decode=True)
            sid = program_sons[0]['id']

            survey_sons = self.fetch(
                "/survey.json?programId=%s&term=Survey%%201" % sid,
                method='GET',
                expected=200,
                decode=True)
            hid = survey_sons[0]['id']

            url = ("/qnode.json?programId=%s&surveyId=%s&root=&deleted=false" %
                   (sid, hid))
            qnode_sons = self.fetch(url,
                                    method='GET',
                                    expected=200,
                                    decode=True)
            self.assertTrue(all(q['deleted'] == False for q in qnode_sons))
            qid1 = qnode_sons[0]['id']
            qid2 = qnode_sons[1]['id']

            a_son = self.fetch("/activity.json?period=604800",
                               method='GET',
                               expected=200,
                               decode=True)
            self.assertEqual(len(a_son['actions']), 0)

            sub_son = self.fetch("/subscription/qnode/{},{}.json".format(
                qid1, sid),
                                 method='GET',
                                 expected=200,
                                 decode=True)
            ss = [sub['subscribed'] for sub in sub_son]
            self.assertTrue(all(s is None for s in ss))

            self.fetch("/qnode/{}.json?programId={}".format(qid1, sid),
                       method='DELETE',
                       expected=200)

            sub_son = self.fetch("/subscription/qnode/{},{}.json".format(
                qid1, sid),
                                 method='GET',
                                 expected=200,
                                 decode=True)
            ss = [sub['subscribed'] for sub in sub_son]
            self.assertTrue(any(s is not None for s in ss))
            self.assertTrue(any(s is None for s in ss))

            a_son = self.fetch("/activity.json?period=604800",
                               method='GET',
                               expected=200,
                               decode=True)
            self.assertEqual(len(a_son['actions']), 1)

        config = utils.get_config("notification.yaml")
        messages = None

        def send(config, msg, to):
            messages[to] = msg

        # Send message to everyone in 'apple' group
        with base.mock_user('admin'), model.session_scope() as session:
            self.fetch("/activity.json",
                       method='POST',
                       expected=200,
                       decode=True,
                       body=json_encode({
                           'to':
                           'all',
                           'sticky':
                           True,
                           'message':
                           "Foo",
                           'surveygroups':
                           self.get_groups_son(session, 'apple'),
                       }))

        messages = {}
        with mock.patch('notifications.send', send):
            n_sent = notifications.process_once(config)
            self.assertEqual(n_sent, 6)
            self.assertEqual(len(messages), 6)

        # Check message URLs
        with model.session_scope() as session:
            app_base_url = app_config.get_setting(session, 'app_base_url')

        author_checked = False
        for to, m in messages.items():
            self.assertIn("\nAdmin said:\nFoo\n", str(m))

            # Assumes base URL is used in the notification email, this could
            # be optional
            self.assertIn(app_base_url, str(m))

            if to == 'author':
                self.assertIn(
                    '\nFunction 1\nAuthor deleted this survey category\n',
                    str(m))
                log.info("Notification email: %s", str(m))
                author_checked = True
        self.assertTrue(author_checked)

        time.sleep(0.1)

        # Delete another qnode
        with base.mock_user('author'):
            self.fetch("/qnode/{}.json?programId={}".format(qid2, sid),
                       method='DELETE',
                       expected=200)

        # Run again, and make sure no nofications send (because not enough time
        # has elapsed since the last email)
        messages = {}
        with mock.patch('notifications.send', send):
            n_sent = notifications.process_once(config)
            self.assertEqual(n_sent, 0)
            self.assertEqual(len(messages), 0)

        time.sleep(0.1)

        sa_func_now = sa.func.now

        def next_week():
            return sa_func_now() + datetime.timedelta(days=7)

        # Run again, pretending to be in the future, and check that another
        # notification is sent
        messages = {}
        with mock.patch('notifications.send', send), \
                mock.patch('notifications.func.now', next_week):
            n_sent = notifications.process_once(config)
            self.assertEqual(n_sent, 1)
            self.assertEqual(len(messages), 1)

        author_checked = False
        for to, m in messages.items():
            if to == 'author':
                self.assertIn(
                    '\nFunction 2\nAuthor deleted this survey category\n',
                    str(m))
                log.info("Notification email: %s", str(m))
                author_checked = True
        self.assertTrue(author_checked)
Beispiel #22
0
    def test_duplicate(self):
        # Respond to a survey
        with model.session_scope() as session:
            user = (session.query(
                model.AppUser).filter_by(email='clerk').one())
            survey_1 = (session.query(
                model.Survey).filter_by(title='Survey 1').one())
            survey_2 = (session.query(
                model.Survey).filter_by(title='Survey 2').one())

            submission = self.create_submission(survey_1, user)
            organisation_id = str(user.organisation.id)
            first_submission_id = str(submission.id)
            survey_1_id = str(survey_1.id)
            survey_2_id = str(survey_2.id)

        # Duplicate program
        with base.mock_user('author'):
            program_sons = self.fetch("/program.json",
                                      method='GET',
                                      expected=200,
                                      decode=True)
            self.assertEqual(len(program_sons), 1)
            original_program_id = program_sons[0]['id']

            program_son = self.fetch("/program/%s.json" % original_program_id,
                                     method='GET',
                                     expected=200,
                                     decode=True)

            program_son['title'] = "Duplicate program"

            program_son = self.fetch("/program.json?duplicateId=%s" %
                                     original_program_id,
                                     method='POST',
                                     body=json_encode(program_son),
                                     expected=200,
                                     decode=True)
            new_program_id = program_son['id']

        # Open (purchase) both new surveys for organisation
        with base.mock_user('admin'):
            self.fetch("/organisation/%s/survey/%s.json?programId=%s" %
                       (organisation_id, survey_1_id, new_program_id),
                       method='PUT',
                       body='',
                       expected=200)
            self.fetch("/organisation/%s/survey/%s.json?programId=%s" %
                       (organisation_id, survey_2_id, new_program_id),
                       method='PUT',
                       body='',
                       expected=200)

        # Duplicate submission, once for each survey, in the new program
        with base.mock_user('org_admin'):
            submission_son = {
                'title': "Second submission",
                'created': datetime.datetime(2013, 1, 1).timestamp(),
            }
            submission_son = self.fetch(
                "/submission.json?organisationId=%s&programId=%s&"
                "surveyId=%s&duplicateId=%s" %
                (organisation_id, new_program_id, survey_1_id,
                 first_submission_id),
                method='POST',
                body=json_encode(submission_son),
                expected=200,
                decode=True)
            second_submission_id = submission_son['id']

            submission_son = {
                'title': "Third submission",
                'created': datetime.datetime(2013, 1, 1).timestamp(),
            }
            submission_son = self.fetch(
                "/submission.json?organisationId=%s&programId=%s&"
                "surveyId=%s&duplicateId=%s" %
                (organisation_id, new_program_id, survey_2_id,
                 first_submission_id),
                method='POST',
                body=json_encode(submission_son),
                expected=200,
                decode=True)
            third_submission_id = submission_son['id']

        self.assertNotEqual(first_submission_id, second_submission_id)
        self.assertNotEqual(first_submission_id, third_submission_id)
        self.assertNotEqual(second_submission_id, third_submission_id)

        # Check contents
        with model.session_scope() as session:
            submission_1 = (session.query(
                model.Submission).get(first_submission_id))
            submission_2 = (session.query(
                model.Submission).get(second_submission_id))
            submission_3 = (session.query(
                model.Submission).get(third_submission_id))

            self.assertEqual(submission_1.survey_id, submission_2.survey_id)
            self.assertNotEqual(submission_1.survey_id, submission_3.survey_id)

            # Submission 1 has responses against six measures. Two are
            # descendants of deleted qnodes.
            # Submission 2 uses the same survey as the source submission,
            # so it should have the same number of responses (four, because
            # the deleted ones are not copied).
            # Submission 3 uses a different survey with only one common
            # measure, so it should have a different number of responses (two).
            self.assertEqual(len(submission_1.responses), 6)
            self.assertEqual(len(submission_2.responses), 4)
            self.assertEqual(len(submission_3.responses), 2)
            self.assertEqual(session.query(model.Response).count(), 12)

            # Make sure the number of rnodes matches the number of qnodes in
            # the survey (no leftovers).
            self.assertEqual(
                session.query(model.ResponseNode).filter_by(
                    submission_id=submission_1.id).count(), 4)
            self.assertEqual(
                session.query(model.ResponseNode).filter_by(
                    submission_id=submission_2.id).count(), 4)
            self.assertEqual(
                session.query(model.ResponseNode).filter_by(
                    submission_id=submission_3.id).count(), 3)

            # Check scores. Submissions 1 and 2 should have the same score:
            # 100 + 200 = 300 (due to weighting of the measures). Submission 3
            # should have just 200.
            self.assertEqual([r.score for r in submission_1.ordered_responses],
                             [3, 6, 11, 13])
            self.assertEqual(list(submission_1.rnodes)[0].score, 33)
            self.assertEqual(list(submission_1.rnodes)[1].score, 0)

            self.assertEqual([r.score for r in submission_2.ordered_responses],
                             [3, 6, 11, 13])
            self.assertEqual(list(submission_2.rnodes)[0].score, 33)
            self.assertEqual(list(submission_2.rnodes)[1].score, 0)

            self.assertEqual([r.score for r in submission_3.ordered_responses],
                             [6, 11])
            self.assertEqual(list(submission_3.rnodes)[0].score, 17)
            self.assertEqual(list(submission_3.rnodes)[1].score, 0)

            # When a submission is duplicated, all of its responses are set to
            # 'draft'.
            self.assertEqual(submission_1.approval, 'final')
            self.assertTrue(
                all(r.approval == 'final' for r in submission_1.responses))
            self.assertEqual(list(submission_1.rnodes)[0].n_draft, 4)
            self.assertEqual(list(submission_1.rnodes)[1].n_draft, 0)
            self.assertEqual(list(submission_1.rnodes)[0].n_final, 4)
            self.assertEqual(list(submission_1.rnodes)[1].n_final, 0)

            self.assertEqual(submission_2.approval, 'draft')
            self.assertTrue(
                all(r.approval == 'draft' for r in submission_2.responses))
            self.assertEqual(list(submission_2.rnodes)[0].n_draft, 4)
            self.assertEqual(list(submission_2.rnodes)[1].n_draft, 0)
            self.assertEqual(list(submission_2.rnodes)[0].n_final, 0)
            self.assertEqual(list(submission_2.rnodes)[1].n_final, 0)

            self.assertEqual(submission_3.approval, 'draft')
            self.assertTrue(
                all(r.approval == 'draft' for r in submission_3.responses))
            self.assertEqual(list(submission_3.rnodes)[0].n_draft, 2)
            self.assertEqual(list(submission_3.rnodes)[1].n_draft, 0)
            self.assertEqual(list(submission_3.rnodes)[0].n_final, 0)
            self.assertEqual(list(submission_3.rnodes)[1].n_final, 0)

            # Check attachment duplication
            self.assertNotEqual(str(submission_1.id), str(submission_2.id))
            self.assertNotEqual(str(original_program_id), str(new_program_id))
            for r1, r2 in zip(submission_1.ordered_responses,
                              submission_2.ordered_responses):
                self.assertEqual(str(r1.submission_id), str(submission_1.id))
                self.assertEqual(str(r2.submission_id), str(submission_2.id))
                self.assertEqual(str(r1.program_id), str(original_program_id))
                self.assertEqual(str(r2.program_id), str(new_program_id))
                self.assertEqual(str(r1.survey_id), str(r2.survey_id))
                self.assertEqual(str(r1.measure_id), str(r2.measure_id))
                self.assertEqual(len(r1.attachments), 3)
                self.assertEqual(len(r2.attachments), 3)
                attachments_1 = sorted(r1.attachments,
                                       key=lambda a: a.file_name)
                attachments_2 = sorted(r2.attachments,
                                       key=lambda a: a.file_name)
                for a1, a2 in zip(attachments_1, attachments_2):
                    self.assertNotEqual(str(a1.id), str(a2.id))
                    self.assertEqual(a1.file_name, a2.file_name)
                    self.assertEqual(a1.url, a2.url)
                    self.assertEqual(a1.blob, a2.blob)
Beispiel #23
0
    def test_timeline_groups(self):
        config = utils.get_config("notification.yaml")
        messages = None

        def send(config, msg, to):
            messages[to] = msg

        def clear_timeline():
            with model.session_scope() as session:
                for activity in session.query(model.Activity).all():
                    activity.surveygroups = set()
                    session.delete(activity)
                for user in session.query(model.AppUser).all():
                    user.email_time = None

        # Send message to everyone in 'banana' group
        clear_timeline()
        with base.mock_user('super_admin'), model.session_scope() as session:
            self.fetch("/activity.json",
                       method='POST',
                       expected=200,
                       decode=True,
                       body=json_encode({
                           'to':
                           'all',
                           'sticky':
                           True,
                           'message':
                           "Foo",
                           'surveygroups':
                           self.get_groups_son(session, 'banana'),
                       }))

        messages = {}
        with mock.patch('notifications.send', send):
            n_sent = notifications.process_once(config)
            self.assertEqual(n_sent, 1)
            self.assertEqual(len(messages), 1)

        # Send message to everyone
        clear_timeline()
        with base.mock_user('super_admin'), model.session_scope() as session:
            self.fetch("/activity.json",
                       method='POST',
                       expected=200,
                       decode=True,
                       body=json_encode({
                           'to':
                           'all',
                           'sticky':
                           True,
                           'message':
                           "Foo",
                           'surveygroups':
                           self.get_groups_son(session, 'apple', 'banana'),
                       }))

        messages = {}
        with mock.patch('notifications.send', send):
            n_sent = notifications.process_once(config)
            self.assertEqual(n_sent, 7)
            self.assertEqual(len(messages), 7)