def test_nobody_done(self): """If no one finished the survey, counts are zero.""" survey = Survey.create([], ordinal=1, program_label=self.program_label) survey.put() kwargs = { 'key': 'progress', 'program_label': self.program_label, 'project_id': 'Project_12345678', 'cohort_label': '2017_spring', 'project_cohort_id': 'ProjectCohort_12345678', 'code': 'trout viper', 'survey_id': survey.uid, 'survey_ordinal': survey.ordinal, } pd = [ ParticipantData.create(participant_id='Participant_unfinished1', value=1, **kwargs), ParticipantData.create(participant_id='Participant_unfinished2', value=1, **kwargs), ] ParticipantData.put_multi(pd) result = ParticipantData.participation(survey_id=pd[0].survey_id) expected = [ { 'value': '1', 'n': 2, 'survey_ordinal': 1 }, ] self.assertEqual(result, expected)
def test_put_insert_duplicate(self): """put() a new uid but matching an index: raises.""" params = dict(self.context_params, value='1') pd = ParticipantData.create(**params) pd.put() dupe_params = dict(self.context_params, value='2') dupe_pd = ParticipantData.create(**params) with self.assertRaises(IntegrityError): dupe_pd.put()
def test_put_update_duplicate(self): """put() an existing uid but matching an index: raises.""" params1 = dict(self.context_params, value='1', survey_id='Survey_1') pd1 = ParticipantData.create(**params1) pd1.put() params2 = dict(self.context_params, value='1', survey_id='Survey_2') pd2 = ParticipantData.create(**params2) pd2.put() with self.assertRaises(IntegrityError): # Now changing 1 so that it collides with 2. pd1.survey_id = 'Survey_2' pd1.put()
def test_pd_whitelist(self): """Any pd keys other than those whitelisted should not be returned.""" pid = 'Participant_0123456789ABCDEF' context_params = { 'participant_id': pid, 'program_label': 'demo-program', 'project_id': 'Project_12345678', 'code': 'trout viper', 'cohort_label': '2017_spring', 'project_cohort_id': 'ProjectCohort_12345678', 'survey_id': 'Survey_12345678', } portal_pd = [ { 'key': 'link', # whitelisted 'value': '', }, { 'key': 'not_whitelisted', 'value': 'secret', }, ] portal_pd = [ ParticipantData.create(**dict(pd, **context_params)) for pd in portal_pd ] ParticipantData.put_multi(portal_pd) results = ParticipantData.get_by_participant(pid, 'ProjectCohort_12345678') self.assertEqual(len(results), 1) self.assertNotEqual(results[0].value, 'secret')
def test_put_for_index_insert_duplicate(self): """put_for_index() a new uid but matching an index: succeeds.""" params = dict(self.context_params, value='1') pd = ParticipantData.create(**params) pd.put() dupe_params = dict(self.context_params, value='2') dupe_pd = ParticipantData.create(**params) synced_pd = ParticipantData.put_for_index(dupe_pd, 'participant-survey-key') # Returns original uid, not the new one. self.assertEqual(synced_pd.uid, pd.uid) # Db shows values fetched = ParticipantData.get_by_id(pd.uid) self.assertEqual(synced_pd.to_dict(), fetched.to_dict())
def test_put_insert(self): """put() a new uid: succeeds.""" params = dict(self.context_params, value='1') pd = ParticipantData.create(**params) pd.put() # Db shows saved values, and db-provided defaults like timestamps # are present in the saved object. fetched = ParticipantData.get_by_id(pd.uid) self.assertEqual(pd.to_dict(), fetched.to_dict())
def test_put_multi(self): """If no indexes collide: succeeds, otherwise: raises.""" params1 = dict(self.context_params, value='1', survey_id='Survey_1') pd1 = ParticipantData.create(**params1) params2 = dict(self.context_params, value='1', survey_id='Survey_2') pd2 = ParticipantData.create(**params2) affected_rows = ParticipantData.put_multi([pd1, pd2]) self.assertEqual(affected_rows, 2) self.assertIsNotNone(ParticipantData.get_by_id(pd1.uid)) self.assertIsNotNone(ParticipantData.get_by_id(pd2.uid)) pd3 = ParticipantData.create(**params1) with self.assertRaises(IntegrityError): ParticipantData.put_multi([pd3])
def mock_one_finished_one_unfinished(survey_ordinal, unfinished_id, finished_id, pc_id='ProjectCohort_foo', code='cool cat', program_label=None, cohort_label=None, organization_id=None): """Simulating one who finished and one who didn't.""" survey = Survey.create( [], ordinal=survey_ordinal, program_label=program_label, organization_id=organization_id or 'Org_foo', ) survey.put() kwargs = { 'key': 'progress', 'program_label': program_label, 'project_id': 'Project_12345678', 'cohort_label': cohort_label or cohort_label, 'project_cohort_id': pc_id, 'code': code, 'survey_id': survey.uid, 'survey_ordinal': survey.ordinal, } pd1 = ParticipantData.create(participant_id=unfinished_id, value=1, **kwargs) pd2 = ParticipantData.create(participant_id=finished_id, value=1, **kwargs) pd3 = ParticipantData.create(participant_id=finished_id, value=100, **kwargs) return [ ParticipantData.put_for_index(pd1, 'participant-survey-key'), ParticipantData.put_for_index(pd2, 'participant-survey-key'), # Writing this third one should should update the second, so the third # uid will never get to the db. ParticipantData.put_for_index(pd3, 'participant-survey-key'), ]
def test_put_for_index_insert(self): """put_for_index() a new uid: succeeds.""" params = dict(self.context_params, value='1') pd = ParticipantData.create(**params) synced_pd = ParticipantData.put_for_index(pd, 'participant-survey-key') # Returns same uid. self.assertEqual(pd.uid, synced_pd.uid) # Db shows values. fetched = ParticipantData.get_by_id(pd.uid) self.assertEqual(synced_pd.to_dict(), fetched.to_dict())
def test_put_update(self): """put() an exisiting uid: succeeds.""" params = dict(self.context_params, value='1') pd = ParticipantData.create(**params) pd.put() pd.value = '2' pd.put() # Db shows values. fetched = ParticipantData.get_by_id(pd.uid) self.assertEqual(pd.to_dict(), fetched.to_dict())
def test_put_clears_cache(self): pc_id = 'ProjectCohort_foo' survey_id = 'Survey_foo' code = 'foo bar' start1 = datetime.datetime.today() end1 = start1 + datetime.timedelta(days=1) start2 = start1 + datetime.timedelta(days=2) end2 = start1 + datetime.timedelta(days=3) cache_data = { ParticipantData.participation_cache_key(pc_id): { ParticipantData.date_key(start1, end1): ['result1'], ParticipantData.date_key(start2, end2): ['result2'], }, ParticipantData.participation_cache_key(survey_id): { ParticipantData.date_key(start1, end1): ['result1'], ParticipantData.date_key(start2, end2): ['result2'], }, ParticipantData.participation_by_pc_cache_key(pc_id): { ParticipantData.date_key(start1, end1): ['result1'], ParticipantData.date_key(start2, end2): ['result2'], }, ParticipantData.participation_by_pc_cache_key(code): { ParticipantData.date_key(start1, end1): ['result1'], ParticipantData.date_key(start2, end2): ['result2'], }, } memcache.set_multi(cache_data) # Write a pd that relates to the pc and survey, falling in the first # date range. That date range should clear, the other should remain. pd = ParticipantData.create( key='progress', value=1, participant_id='Participant_foo', program_label='demo-program', project_id='Project_foo', cohort_label='2019', project_cohort_id=pc_id, code=code, survey_id=survey_id, survey_ordinal=1, ) ParticipantData.put_for_index(pd, 'participant-survey-key') for cache_key in cache_data.keys(): self.assertEqual(len(memcache.get(cache_key)), 1)
def test_whitelist(self): """Certain pd values should readable, other's shouldn't.""" project_cohort, survey, participant = self.create_pd_context() keys = ( 'progress', 'link', 'condition', 'ep_assent', 'last_login', 'saw_baseline', 'saw_demographics', 'saw_validation', 'secret', # NOT on whitelist; should remain secret ) pds = [ ParticipantData.create( key=k, value='foo', participant_id=participant.uid, program_label=self.program_label, cohort_label=self.cohort_label, project_cohort_id=project_cohort.uid, code=project_cohort.code, survey_id=survey.uid, survey_ordinal=survey.ordinal, ) for k in keys ] ParticipantData.put_multi(pds) url = '/api/participants/{}/data?project_cohort_id={}'.format( participant.uid, project_cohort.uid) result = self.testapp.get(url) result_dict = json.loads(result.body) self.assertEqual(len(result_dict), len(keys) - 1) secret_pd = [pd for pd in result_dict if pd['key'] == 'secret'] self.assertEqual(len(secret_pd), 0)
def test_create_portal_pd(self, testing=False): """Set all the pd potentially required by the portal.""" # Using the same project and project cohort ids from the mock function. context_params = { 'participant_id': 'Participant_0123456789ABCDEF', 'program_label': 'demo-program', 'project_id': 'Project_12345678', 'cohort_label': '2017_spring', 'project_cohort_id': 'ProjectCohort_12345678', 'code': 'trout viper', 'testing': testing, } portal_pd = [ { 'key': 'link', 'value': '', 'survey_id': 'Survey_1', 'survey_ordinal': 1, }, { 'key': 'progress', 'value': '100', 'survey_id': 'Survey_1', 'survey_ordinal': 1, }, { 'key': 'link', 'value': '', 'survey_id': 'Survey_2', 'survey_ordinal': 2, }, { 'key': 'progress', 'value': '33', 'survey_id': 'Survey_2', 'survey_ordinal': 2, }, { 'key': 'consent', 'value': 'true', 'survey_id': None, }, { 'key': 'condition', 'value': 'treatment', 'survey_id': None, }, ] portal_pd = [ ParticipantData.create(**dict(pd, **context_params)) for pd in portal_pd ] ParticipantData.put_multi(portal_pd) # Save one more in a different project cohort, since participants # have an identity at the organization level. other_pc_params = dict( context_params, project_cohort_id='ProjectCohort_other', code='other octopus', key='saw_validation', # N.B. this is whitelisted value='bar', survey_id='Survey_other', ) ParticipantData.create(**other_pc_params).put() return context_params['participant_id']
def test_delete_returns_affected_rows(self): params = dict(self.context_params, value='1') pd = ParticipantData.create(**params) ParticipantData.put(pd) affected_rows = ParticipantData.delete_multi([pd]) self.assertEqual(affected_rows, 1)
def test_completion_anonymous_allowed(self): org_id = 'Organization_foo' pc = ProjectCohort.create( organization_id=org_id, program_label=self.program_label, cohort_label=self.cohort_label, ) pc.put() today = datetime.datetime.now() yesterday = today - datetime.timedelta(days=1) tomorrow = today + datetime.timedelta(days=1) pd_params = { 'key': 'progress', 'value': '100', 'program_label': self.program_label, 'cohort_label': self.cohort_label, 'project_cohort_id': pc.uid, 'code': pc.code, 'survey_id': 'Survey_foo', 'survey_ordinal': 1, } old_pd = ParticipantData.create( created=yesterday.strftime(config.sql_datetime_format), modified=yesterday.strftime(config.sql_datetime_format), participant_id='Participant_foo', **pd_params) # Use a lower-level interface so we can set the modified time. row = ParticipantData.coerce_row_dict(old_pd.to_dict()) with mysql_connection.connect() as sql: sql.insert_or_update(ParticipantData.table, row) current_pd = ParticipantData.create(participant_id='Participant_bar', **pd_params) current_pd.put() user = User.create( email='*****@*****.**', owned_organizations=[org_id], ) user.put() result = self.testapp.get( '/api/project_cohorts/{}/completion'.format(pc.uid), params={ 'start': util.datelike_to_iso_string(today), 'end': util.datelike_to_iso_string(tomorrow), }, headers=login_headers(user.uid), # Authenticated, has permission: 200. status=200, ) expected = [{ 'value': '100', 'survey_ordinal': 1, 'participant_id': current_pd.participant_id, }] self.assertEqual(json.loads(result.body), expected)