def get(self, template, context_id): # Attempt to treat id as as project cohort (it might be a program, or # invalid). project_cohort = ProjectCohort.get_by_id(context_id) def todt(s): return datetime.datetime.strptime(s, config.iso_date_format) if project_cohort: # This is a "real" set of instructions with data filled in. organization = Organization.get_by_id( project_cohort.organization_id) liaison = User.get_by_id(organization.liaison_id) program = Program.get_config(project_cohort.program_label) cohort = program['cohorts'][project_cohort.cohort_label] participation_open_date = todt(cohort['open_date']) # See notes in program config for why we take a day off for display. participation_close_date = (todt(cohort['close_date']) - datetime.timedelta(1)) else: # This is a generic example version of the document. try: program = Program.get_config(context_id) except ImportError: return self.http_not_found() organization = None liaison = None project_cohort = None cohort = None participation_open_date = None participation_close_date = None if template == 'custom_portal_technical_guide': # This template doesn't vary by program. template_filename = '{}.html'.format(template) else: template_filename = '{}_{}.html'.format(program['label'], template) self.write( template_filename, organization=organization, liaison=liaison, program_name=program['name'], cohort_name=cohort['name'] if cohort else '', program_description=program['description'], project_cohort=project_cohort, participation_open_date=participation_open_date, participation_close_date=participation_close_date, )
def create_project_cohort(self, cohort_date=datetime.datetime.today()): program_label = 'demo-program' cohort_label = 'demo-cohort' program = Program.get_config(program_label) org_id = 'Org_Foo' liaison_id = 'User_liaison' project = Project.create(organization_id=org_id, program_label=program_label) project.put() one_day = datetime.timedelta(days=1) cohort_config = { 'label': cohort_label, 'name': 'Demo Cohort', 'open_date': str(cohort_date - one_day), # yesterday 'close_date': str(cohort_date + one_day), # tomorrow } program['cohorts'][cohort_label] = cohort_config Program.mock_program_config( program_label, {'cohorts': { cohort_label: cohort_config }}, ) pc = ProjectCohort.create( project_id=project.uid, organization_id=org_id, program_label=program_label, cohort_label=cohort_label, liaison_id=liaison_id, ) pc.put() return pc
def changed_project_task(user, project, task, project_cohort_id=None): """If change made by an org admin, notify related account manager, or all the program owners. Otherwise, notify project liaison, or all the org admins. """ t_dict = task.to_client_dict() program_config = Program.get_config(project.program_label) if project_cohort_id: link = '/dashboard/{pc}/tasks/{ckpt}/{task}'.format( pc=DatastoreModel.convert_uid(project_cohort_id), ckpt=DatastoreModel.convert_uid(task.checkpoint_id), task=task.uid ) else: link = '/dashboard' params = { 'task_id': task.uid, 'context_id': project.uid, 'subject': "Task updated", 'body': u"{} updated \"{}\" in {}.".format( user.name, t_dict['name'], program_config['name']), 'link': link, 'autodismiss': True, } if user.non_admin: # Send to account managers (usu. set via program config -> # default_account_manager). parents = get_project_program_recipients(project) else: parents = get_project_organization_recipients(project) notes = [Notification.create(parent=p, **params) for p in parents] filtered_notes = Notification.filter_redundant(notes) ndb.put_multi(filtered_notes)
def joined_cohort(user, project_cohort): """Notify program and super admins.""" # This always happens along with creating a program. pc = project_cohort program_admins = User.get(owned_programs=pc.program_label) super_admins = User.get(user_type='super_admin') organization = Organization.get_by_id(pc.organization_id) program_config = Program.get_config(pc.program_label) cohort_name = program_config['cohorts'][pc.cohort_label]['name'] notes = [] for admin in program_admins + super_admins: note = Notification.create( parent=admin, context_id=pc.uid, subject=u"{org} joined a cohort".format(org=organization.name), body=( u"{org} joined {cohort} in {program}. The organization is " "currently {status}." ).format( org=organization.name, cohort=cohort_name, program=program_config['name'], status=organization.status, ), link='/organizations/{}'.format(organization.short_uid), autodismiss=True, ) notes.append(note) ndb.put_multi(notes)
def post(self, project_id, slug): """A project has been identified as new. Send them a welcome.""" project = Project.get_by_id(project_id) program = Program.get_config(project.program_label) org = Organization.get_by_id(project.organization_id) # The Org liaison is preferred, but is not guaranteed to be set (users # choose their org liaison explicitly as one of the org tasks). Default # to the Project liaison, which is set on creation in # add_program.controller.js@joinProgram org_liaison = User.get_by_id(org.liaison_id) project_liaison = User.get_by_id(project.liaison_id) liaison = org_liaison or project_liaison email = Email.create( to_address=liaison.email, mandrill_template=slug, mandrill_template_content={ 'program_name': program['name'], 'organization_name': org.name, 'liaison_name': liaison.name, 'join_date': util.datelike_to_iso_string(project.created), }, ) email.put()
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_checkpoint_creation(self): project = Project.create(program_label='demo-program', organization_id='Organization_Foo') project.put() checkpoints = Checkpoint.get(parent_id=project.uid) program_config = Program.get_config(project.program_label) template = program_config['project_tasklist_template'] self.assertEqual(len(checkpoints), len(template))
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 test_task_creation(self): project = Project.create(program_label='demo-program', organization_id='Organization_Foo') project.put() tasks = Task.get(ancestor=project) program_config = Program.get_config(project.program_label) template = program_config['project_tasklist_template'] num_tasks = sum([len(checkpoint['tasks']) for checkpoint in template]) self.assertEqual(len(tasks), num_tasks)
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_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 test_get_single_cohort(self): user = User.create(email="*****@*****.**", user_type='super_admin') user.put() query = ''' query GetSingleProgramCohort( $program_label: String!, $cohort_label: String!, ) { program_cohort( program_label: $program_label, cohort_label: $cohort_label, ) { close_date label name open_date program_description program_label program_name registration_close_date registration_open_date } } ''' response = self.testapp.post_json( '/api/graphql', # See http://graphql.org/learn/serving-over-http/#post-request { 'query': query, 'variables': { 'program_label': 'demo-program', 'cohort_label': '2018', }, }, headers=login_headers(user.uid), ) conf = Program.get_config('demo-program') cohort = conf['cohorts']['2018'] cohort['program_label'] = conf['label'] cohort['program_name'] = conf['name'] cohort = OrderedDict((k, cohort[k]) for k in sorted(cohort.keys())) self.assertEqual( response.body, json.dumps({'program_cohort': cohort}), )
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 test_join_cohort(self, cohort_date=datetime.date.today()): """Allowed for org admin owner of project.""" # Existing things to relate to. program_label = 'demo-program' cohort_label = 'demo-cohort' program = Program.get_config(program_label) org_id = 'Org_Foo' user = User.create(email="*****@*****.**", owned_organizations=[org_id]) project = Project.create(organization_id=org_id, program_label=program_label) user.put() project.put() # Guarantee the dates will work by mocking the cohort config. one_day = datetime.timedelta(days=1) cohort_config = { 'label': cohort_label, 'name': 'Demo Cohort', 'open_date': str(cohort_date - one_day), # yesterday 'close_date': str(cohort_date + one_day), # tomorrow } program['cohorts'][cohort_label] = cohort_config Program.mock_program_config( program_label, {'cohorts': {cohort_label: cohort_config}}, ) # Create the project cohort through the api. Any response other than # 200 will fail the test. response = self.testapp.post_json( '/api/project_cohorts', { 'project_id': project.uid, 'organization_id': org_id, 'program_label': program_label, 'cohort_label': cohort_label, 'liaison_id': user.uid, }, headers=login_headers(user.uid) ) response_dict = json.loads(response.body) return (ProjectCohort.get_by_id(response_dict['uid']), user)
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))
def test_get_all_for_program(self): user = User.create(email="*****@*****.**", user_type='super_admin') user.put() query = ''' query GetProgramCohorts($program_label: String!) { program_cohorts(program_label: $program_label) { label } } ''' response = self.testapp.post_json( '/api/graphql', {'query': query, 'variables': {'program_label': 'demo-program'}}, headers=login_headers(user.uid), ) received = json.loads(response.body) for l in Program.get_config('demo-program')['cohorts'].keys(): self.assertIn({'label': l}, received['program_cohorts'])
def downloaded_identifiers(user, project_cohort_id): supers = User.get(user_type='super_admin') notes = [] project_cohort = ProjectCohort.get_by_id(project_cohort_id) organization = Organization.get_by_id(project_cohort.organization_id) program = Program.get_config(project_cohort.program_label) cohort_name = program['cohorts'][project_cohort.cohort_label]['name'] for sup in supers: note = Notification.create( parent=sup, context_id=project_cohort_id, subject="IDs Downloaded", body=u"{} ({}) downloaded IDs for {}: {} {}.".format( user.name, user.email, organization.name, program['name'], cohort_name), link='/dashboard/{}'.format(project_cohort.short_uid), autodismiss=True, ) notes.append(note) ndb.put_multi(notes)
def create_project_cohort(self, cohort_date=datetime.datetime.today()): program = Program.get_config(self.program_label) liaison = User.create(email='*****@*****.**') org = Organization.create(name="Org Foo", liaison_id=liaison.uid) liaison.owned_organizations.append(org.uid) project = Project.create(organization_id=org.uid, program_label=self.program_label) liaison.put() org.put() project.put() pc = ProjectCohort.create( project_id=project.uid, organization_id=org.uid, program_label=self.program_label, cohort_label=self.cohort_label, liaison_id=liaison.uid, ) pc.put() surveys = Survey.create_for_project_cohort(program['surveys'], pc) ndb.put_multi(surveys) return liaison, org, project, pc, surveys
def test_default_portal_type(self): """Portal type should be based on program config""" program = Program.get_config(self.program_label) pc = ProjectCohort.create(program_label=self.program_label, ) self.assertEqual(pc.portal_type, program['default_portal_type'])