def create_surveys(self): tasklist_template = [] survey1 = Survey.create( tasklist_template, program_label=self.program_label, organization_id='Organization_Foo', project_cohort_id='ProjectCohort_Foo', ordinal=1, ) survey2 = Survey.create( tasklist_template, program_label=self.program_label, organization_id='Organization_Foo', project_cohort_id='ProjectCohort_Foo', ordinal=2, ) survey1.put() survey2.put() # Simulate consistency. survey1.key.get() survey2.key.get() return survey1, survey2
def test_survey_task_body(self): """Tasks should get dynamic properties from their definition.""" program_label = 'demo-program' task_label = 'task_foo' survey = Survey.create( [], program_label='demo-program', organization_id='Organization_Foo', ordinal=1, ) task_template = { 'label': task_label, 'body': "<p>Demo body.</p>", } Program.mock_program_config( program_label, { 'surveys': [{ 'survey_tasklist_template': [{ 'tasks': [task_template] }] }] }) t = Task.create(task_label, 1, 'checkpoint_foo', parent=survey, program_label=program_label) t_dict = t.to_client_dict() self.assertIsInstance(t_dict['body'], basestring) self.assertGreater(len(t_dict['body']), 0)
def test_metric_labels(self): """Client dict should have portal-friendly metric labels.""" team_id = 'Team_foo' m1 = Metric.create(name='Foo Condition', label='foo_condition') m2 = Metric.create(name='Bar Condition', label='bar_condition') Metric.put_multi([m1, m2]) survey = Survey.create(team_id=team_id, metrics=[m1.uid, m2.uid]) survey.put() user = User.create(name='foo', email='*****@*****.**', owned_teams=[team_id]) user.put() response = self.testapp.get( '/api/surveys/{}'.format(survey.uid), headers=self.login_headers(user), ) logging.info(response.body) self.assertEqual( json.loads(response.body)['metric_labels'], { m1.uid: 'foo_condition', m2.uid: 'bar_condition' }, )
def test_delete_removes_team(self): user = User.create(name='foo', email='*****@*****.**') team = Team.create(name='Team Foo', captain_id=user.uid, program_id=self.demo_program.uid) team.put() user.owned_teams = [team.uid] user.put() survey = Survey.create(team_id=team.uid) survey.put() url = '/api/teams/{}'.format(team.uid) headers = self.login_headers(user) # Delete the team. self.testapp.delete(url, headers=headers, status=204) # Expect the team is gone from the db. self.assertIsNone(Team.get_by_id(team.uid)) # Api should show a 404. self.testapp.get(url, headers=headers, status=404) self.testapp.delete(url, headers=headers, status=404)
def test_delete_removes_all_ownership(self): captain = User.create(name='cap', email='*****@*****.**') member = User.create(name='member', email='*****@*****.**') team = Team.create(name='Team Foo', captain_id=captain.uid, program_id=self.demo_program.uid) team.put() captain.owned_teams = [team.uid] member.owned_teams = [team.uid] captain.put() member.put() survey = Survey.create(team_id=team.uid) survey.put() url = '/api/teams/{}'.format(team.uid) headers = self.login_headers(captain) # Delete the team. self.testapp.delete(url, headers=headers, status=204) # All users should now not be associated to the deleted team. self.assertNotIn(team.uid, User.get_by_id(captain.uid).owned_teams) self.assertNotIn(team.uid, User.get_by_id(member.uid).owned_teams)
def set_up(self): # Let ConsistencyTestCase set up the datastore testing stub. super(TestApiSurveys, self).set_up() application = webapp2.WSGIApplication(api_routes, config={ 'webapp2_extras.sessions': { 'secret_key': self.cookie_key } }, debug=True) self.testapp = webtest.TestApp(application) with mysql_connection.connect() as sql: sql.reset({ 'classroom': Classroom.get_table_definition(), 'metric': Metric.get_table_definition(), 'program': Program.get_table_definition(), 'survey': Survey.get_table_definition(), 'team': Team.get_table_definition(), 'user': User.get_table_definition(), }) self.ep_program = Program.create( name="Engagement Project", label='ep18', active=True, preview_url='foo.com', ) self.ep_program.put()
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_all_metrics_on_by_default(self): metric1 = Metric.create(name='Metric One', label='m1') metric2 = Metric.create(name='Metric One', label='m2') Metric.put_multi([metric1, metric2]) survey = Survey.create(team_id='Team_foo', code='trout viper') self.assertEqual(set(survey.metrics), {metric1.uid, metric2.uid})
def create_pd_context(self): program_label = 'demo-program' program_config = Program.get_config(program_label) template = program_config['surveys'][0]['survey_tasklist_template'] project_cohort = ProjectCohort.create( program_label=program_label, organization_id='Organization_foo', project_id='Project_foo', cohort_label='2018', ) project_cohort.put() survey = Survey.create( template, program_label=program_label, organization_id='Organization_foo', project_cohort_id=project_cohort.uid, ordinal=1, ) survey.put() participant = Participant.create(name='Pascal', organization_id='PERTS') participant.put() return (project_cohort, survey, participant)
def test_should_notify(self): team_id = 'Team_foo' survey = Survey.create(team_id=team_id) cycle = Cycle.create( team_id=team_id, ordinal=1, start_date=datetime.date.today() - datetime.timedelta(days=1), end_date=datetime.date.today() + datetime.timedelta(days=1), ) cycle.put() today = datetime.date.today() now = datetime.datetime.now() # Case 1: notified time is not set. self.assertEqual(survey.should_notify(today), cycle) # Case 2: sent within cycle survey.notified = now - datetime.timedelta(days=1) self.assertEqual(survey.should_notify(today), False) # Case 3: sent before cycle survey.notified = now - datetime.timedelta(days=10) self.assertEqual(survey.should_notify(today), cycle) # import pdb # pdb.set_trace() # Case 4: today is not in any cycle (there is no current cycle) self.assertEqual( survey.should_notify(today - datetime.timedelta(days=10)), False, )
def test_create_for_team(self): """Should populate only with metrics active by default.""" metric1 = Metric.create(name="Community of Helpers", label='community_of_helpers') metric1.put() metric2 = Metric.create(name="Feedback for Growth", label='feedback_for_growth') metric2.put() program = Program.create( name='Test Program', label='test-program', metrics=[ { 'uid': metric1.uid, 'default_active': True }, { 'uid': metric2.uid, 'default_active': False }, ], preview_url='foo.com', ) program.put() team = Team.create( captain_id='User_captain', program_id=program.uid, ) survey = Survey.create_for_team(team) self.assertEqual(survey.metrics, [metric1.uid]) self.assertEqual(survey.open_responses, [metric1.uid]) self.assertEqual(survey.meta, {})
def test_delete_not_implemented(self): user, team_dict = self.test_create_team_creates_survey() survey = Survey.get(team_id=team_dict['uid'])[0] self.testapp.delete( '/api/surveys/{}'.format(survey.uid), headers=self.login_headers(user), status=405, )
def test_join_org(self): """Joining an org makes task reminders for all contained tasklists.""" program_label = 'demo-program' cohort_label = 'demo-cohort' # Guarantee the dates will work by mocking the cohort config. cohort_config = { 'label': cohort_label, 'name': 'Demo Cohort', 'open_date': str(datetime.date.today()), 'close_date': str(datetime.date.today()), } Program.mock_program_config( program_label, {'cohorts': { cohort_label: cohort_config }}, ) program = Program.get_config(program_label) tasklist_template = program['surveys'][0]['survey_tasklist_template'] owner = User.create(email='*****@*****.**', user_type='user') org = Organization.create(name="Foo Org", liaison_id=owner.uid) owner.owned_organizations = [org.uid] project = Project.create(program_label=program_label, organization_id=org.uid) survey = Survey.create(tasklist_template, project_id=project.uid, organization_id=org.uid, program_label=program_label, ordinal=1, cohort_label=cohort_label) owner.put() org.put() project.put() survey.put() # The assumption here is the org and its contents are long-standing, # so force consistency with all. org.key.get() project.key.get() survey.key.get() joiner = User.create(email='*****@*****.**', user_type='user') joiner.put() self.testapp.post_json( '/api/users/{}/organizations'.format(joiner.uid), org.to_client_dict(), headers=login_headers(owner.uid), ) # One TaskReminder for each of: org tasklist, project tasklist, survey # tasklist. self.assertEqual(len(TaskReminder.get(ancestor=joiner)), 3) return (org, project, survey, owner, joiner)
def assert_created_with_program(self, team_dict, program): # Survey should have program's metrics. survey = Survey.get(team_id=team_dict['uid'])[0] self.assertEqual(survey.metrics, program.metrics) self.assertEqual(survey.open_responses, program.metrics) # Correct number of cycles created. cycles = Cycle.get(team_id=team_dict['uid']) self.assertEqual(len(cycles), program.min_cycles or 0)
def get(self, id=None): # convert short_uid to uid if needed if id is not None: id = Survey.get_long_uid(id) response = self.api.get_by_id(id) else: params = self.get_params() response = self.api.get('Survey', ancestor=self.api.user, **params) self.write(response)
def test_get_metrics(self): metric1 = Metric.create(name="Community of Helpers", label='community_of_helpers') metric1.put() metric2 = Metric.create(name="Feedback for Growth", label='feedback_for_growth') metric2.put() survey = Survey.create(team_id='Team_foo', metrics=[metric1.uid, metric2.uid]) self.assertEqual(set(survey.get_metrics()), {metric1, metric2})
def makeANewSurvey(name, description, question, body, note, start_date, end_date): survey = Survey(name=name, description=description, question=question, body=body, note=note, start_date=start_date, end_date=end_date) session.add(survey) session.commit() return jsonify(survey=survey.serialize)
def set_up(self): # Let ConsistencyTestCase set up the datastore testing stub. super(TestSurveys, self).set_up() with mysql_connection.connect() as sql: sql.reset({ 'cycle': Cycle.get_table_definition(), 'metric': Metric.get_table_definition(), 'program': Program.get_table_definition(), 'survey': Survey.get_table_definition(), 'team': Team.get_table_definition(), })
def test_program_access(self): """Should be able to look up program config through project.""" tasklist_template = [] survey = Survey.create( tasklist_template, program_label='demo-program', organization_id='Organization_Foo', ordinal=1, ) s_dict = survey.to_client_dict() self.assertEqual(s_dict['name'], 'Intervention')
def test_put_artifact_url_sends_email(self): """PUT response has special jwt giving permission on Neptune.""" user = User.create(name='foo', email='*****@*****.**') team = Team.create(name="Foo Team", captain_id=user.uid, program_id="Program_foo") survey = Survey.create(team_id=team.uid) user.owned_teams = [team.uid] team.put() survey.put() user.put() artifact_url = 'https://www.example.com/artifact' # Not changing the artifact does NOT trigger an email. self.testapp.put_json( '/api/surveys/{}'.format(survey.uid), {'meta': { 'artifact_url': '' }}, headers=self.login_headers(user), ) self.assertEqual(Email.count(), 0) # Changing it triggers an email. self.testapp.put_json( '/api/surveys/{}'.format(survey.uid), {'meta': { 'artifact_use': 'true', 'artifact_url': artifact_url }}, headers=self.login_headers(user), ) emails = Email.get() self.assertEqual(len(emails), 1) email = emails[0] self.assertIn(artifact_url, email.html) self.assertIn(team.name, email.html) # Sending a PUT **without** changing it does NOT trigger an email. self.testapp.put_json( '/api/surveys/{}'.format(survey.uid), {'meta': { 'artifact_use': 'true', 'artifact_url': artifact_url }}, headers=self.login_headers(user), ) self.assertEqual(Email.count(), 1) # same as before
def test_checkpoint_creation(self): program_label = 'demo-program' program_config = Program.get_config(program_label) template = program_config['surveys'][0]['survey_tasklist_template'] survey = Survey.create( template, program_label=program_label, organization_id='Organization_Foo', ordinal=1, ) survey.put() checkpoints = Checkpoint.get(parent_id=survey.uid) self.assertEqual(len(checkpoints), len(template))
def test_config_enabled(self): program = Program.create( name='Program Foo', label='TP1', survey_config_enabled=True, preview_url='foo.com', ) program.put() team = Team.create(captain_id='User_captain', program_id=program.uid) team.put() survey = Survey.create(team_id=team.uid) survey.put() # Enabled by program. self.assertEqual(Survey.config_enabled(survey.uid), True) # Disabled by program. program.survey_config_enabled = False program.put() self.assertEqual(Survey.config_enabled(survey.uid), False)
def post(self, team_id, date_str=None): survey = Survey.get(team_id=team_id)[0] if date_str: today = datetime.strptime(date_str, config.iso_date_format).date() else: today = date.today() # Cycle ultimately comes from Cycle.get_current_for_team() and so is # guaranteed to have start and end dates. cycle = survey.should_notify(today) if not cycle: # This task is run every week, but only actually send notifications # if the date matches the survey interval. return team = Team.get_by_id(survey.team_id) program = Program.get_by_id(team.program_id) classrooms = Classroom.get(team_id=team_id) users = User.query_by_team(team_id) if len(classrooms) == 0: pct_complete_by_id = {} else: ppn = get_participation(cycle, classrooms) pct_complete_by_id = self.participation_to_pct(ppn, classrooms) # Get all the responses once to save trips to the db. Redact them later # according to the relevate user. unsafe_responses = Response.get_for_teams_unsafe([team_id], parent_id=cycle.uid) to_put = [] for user in users: if user.receive_email: safe_responses = Response.redact_private_responses( unsafe_responses, user) email = cycle_emailers.create_cycle_email( program.label, user, # recipient users, team, classrooms, safe_responses, cycle, pct_complete_by_id, ) to_put.append(email) ndb.put_multi(to_put)
def create(self, program_label): program = Program.create( name="Foo", label=program_label, active=True, preview_url='foo.com', ) program.put() captain = User.create(email='*****@*****.**', name="Captain PERTS") team = Team.create(name='Team Foo', program_id=program.uid, captain_id=captain.uid) team.put() classrooms = [ Classroom.create(name='Class A', team_id=team.uid, num_students=5, contact_id='User_contact', code='foo'), Classroom.create(name='Class B', team_id=team.uid, num_students=5, contact_id='User_contact', code='bar'), Classroom.create(name='Class C', team_id=team.uid, num_students=5, contact_id='User_contact', code='baz'), ] Classroom.put_multi(classrooms) survey = Survey.create(team_id=team.uid) survey.put() captain.owned_teams = [team.uid] captain.put() start_date = datetime.date.today() - datetime.timedelta(days=7) end_date = datetime.date.today() + datetime.timedelta(days=7) cycle = Cycle.create(team_id=team.uid, ordinal=1, start_date=start_date, end_date=end_date) cycle.put() return (program, captain, team, classrooms, cycle)
def test_task_creation(self): program_label = 'demo-program' program_config = Program.get_config(program_label) template = program_config['surveys'][0]['survey_tasklist_template'] survey = Survey.create( template, program_label=program_label, organization_id='Organization_Foo', ordinal=1, ) survey.put() tasks = Task.get(ancestor=survey) num_tasks = sum([len(checkpoint['tasks']) for checkpoint in template]) self.assertEqual(len(tasks), num_tasks)
def add_survey_ids_to_project_cohorts(project_cohort): """To make batch querying of dashboard views easier, we want to be able to get surveys by id, given project cohorts. This is a change to how pcs are created, so legacy data needs updating. Configure this job in map_reduce.yaml and initiate it from /mapreduce. You must be signed in as a project admin. See: https://sookocheff.com/post/appengine/mapreduce/mapreduce-yaml/ """ if len(project_cohort.survey_ids) > 0: return keys = Survey.get(project_cohort_id=project_cohort.uid, keys_only=True) project_cohort.survey_ids = [k.id() for k in keys] yield op.db.Put(project_cohort)
def test_change_team_ignored(self): team_id = 'Team_foo' survey = Survey.create(team_id=team_id) survey.put() user = User.create(name='foo', email='*****@*****.**', owned_teams=[team_id]) user.put() response = self.testapp.put_json( '/api/surveys/{}'.format(survey.uid), {'team_id': 'Team_other'}, headers=self.login_headers(user), ) # team id shoud be unchanged self.assertEqual(json.loads(response.body)['team_id'], team_id)
def test_survey_creation(self): """Creating a project cohort should create surveys.""" # Hack eventual consistency for just this test because we don't # expect surveys to be available instantly upon creation, unlike the # project cohort itself. self.policy = datastore_stub_util.PseudoRandomHRConsistencyPolicy( probability=1) self.testbed.init_datastore_v3_stub(consistency_policy=self.policy) pc, user = self.test_join_cohort() program_label = 'demo-program' program = Program.get_config(program_label) surveys = Survey.get(project_cohort_id=pc.uid) self.assertEqual(len(surveys), len(program['surveys']))
def set_up(self): # Let ConsistencyTestCase set up the datastore testing stub. super(TestApiTeams, self).set_up() application = webapp2.WSGIApplication( api_routes, config={ 'webapp2_extras.sessions': { 'secret_key': self.cookie_key } }, debug=True ) self.testapp = webtest.TestApp(application) with mysql_connection.connect() as sql: sql.reset({ 'classroom': Classroom.get_table_definition(), 'cycle': Cycle.get_table_definition(), 'program': Program.get_table_definition(), 'organization': Organization.get_table_definition(), 'report': Report.get_table_definition(), 'survey': Survey.get_table_definition(), 'team': Team.get_table_definition(), 'user': User.get_table_definition(), }) # See #848. Remove once resolved. self.demo_program = Program.create( name="Demo Program", label='demoprogram', min_cycles=3, active=True, preview_url='foo.com', ) self.demo_program.put() self.ep_program = Program.create( name="Engagement Project", label='ep19', min_cycles=3, active=True, preview_url='foo.com', ) self.ep_program.put()
def test_put_clears_dashboard_queries(self): org_id = 'Organization_Foo' program_label = 'demo-program' pc = ProjectCohort.create( program_label='demo-program', organization_id=org_id, project_id='Project_Foo', cohort_label='2018', ) pc.put() program_config = Program.get_config(program_label) template = program_config['surveys'][0]['survey_tasklist_template'] survey = Survey.create( template, program_label=program_label, organization_id=org_id, project_cohort_id=pc.uid, ordinal=1, ) survey.put() org_key = util.cached_query_key( 'SuperDashboard', organization_id=pc.organization_id, ) program_cohort_key = util.cached_query_key( 'SuperDashboard', program_label=pc.program_label, cohort_label=pc.cohort_label, ) memcache.set(org_key, {'foo': 1}) memcache.set(program_cohort_key, {'foo': 1}) # Re-fetch the org so it doesn't have an associated tasklist, which # saves checkpoints. This should clear memcache without relying on those # checkpoints. survey = survey.key.get() survey.status = 'ready' survey.put() self.assertIsNone(memcache.get(org_key)) self.assertIsNone(memcache.get(program_cohort_key))