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 test_delete_other_forbidden(self): user, org_dict = self.test_create() other = User.create(name='other', email='*****@*****.**') other.put() response = self.testapp.delete('/api/organizations/{}'.format( org_dict['uid']), headers=self.login_headers(other), status=403) self.assertIsNotNone(Organization.get_by_id(org_dict['uid']))
def test_delete(self): """Owners can delete their orgs.""" user, org_dict = self.test_create() response = self.testapp.delete('/api/organizations/{}'.format( org_dict['uid']), headers=self.login_headers(user), status=204) self.assertIsNone(Organization.get_by_id(org_dict['uid'])) return response, user, org_dict
def test_organization_name_update(self): """Updating an organization name should update associated project""" org_name_1 = 'Foo College' org_name_2 = 'Bar College' organization = Organization.create(name=org_name_1) organization.put() project = Project.create(program_label='demo-program', organization_id=organization.uid) project.put() organization = Organization.get_by_id(organization.uid) organization.name = org_name_2 organization.put() project_dict = project.get_by_id(project.uid).to_client_dict() self.assertEqual(project_dict['organization_name'], organization.name)
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 get_organization_reports(self, organization_id): user = self.get_current_user() org = Organization.get_by_id(organization_id) if not org: return self.http_not_found() if not owns(user, org): return self.http_forbidden("Only org admins can list reports.") # Limit access to preview reports, super admin only. show_preview_reports = True if user.super_admin else False all_reports = Report.get_for_organization(org.uid, show_preview_reports) # Add a custom link for users to access each report. self.write([ dict(r.to_client_dict(), link=self.report_link(r)) for r in all_reports ])
def test_create(self): """Anyone can create an org.""" org_name = 'Org Foo' user = User.create(name='foo', email='*****@*****.**') user.put() response = self.testapp.post_json( '/api/organizations', { 'name': org_name, 'program_id': self.program.uid }, headers=self.login_headers(user), ) response_dict = json.loads(response.body) self.assertEqual(response_dict['name'], org_name) fetched_org = Organization.get_by_id(response_dict['uid']) self.assertIsNotNone(fetched_org) # Remove user's cookie so we can use the test app as other people. self.testapp.reset() return user, response_dict
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 get(self, org_id): user = self.get_current_user() org = Organization.get_by_id(org_id) if not org: return self.http_not_found() if not owns(user, org): return self.http_forbidden() teams = Team.query_by_organization(org_id) team_ids = [t.uid for t in teams] classrooms = Classroom.query_by_teams(team_ids) cycles = Cycle.query_by_teams(team_ids) responses = Response.get_for_teams(user, team_ids) users = User.query_by_team(team_ids) self.write({ 'classrooms': [e.to_client_dict() for e in classrooms], 'cycles': [e.to_client_dict() for e in cycles], 'teams': [e.to_client_dict() for e in teams], 'responses': [e.to_client_dict() for e in responses], 'users': [e.to_client_dict() for e in users], })
def post(self): """Save references to reports, and perhaps the report file itself. Has two modes: accept a file, in which case the file is saved to GCS, or a dataset id. In both cases a Report is inserted referencing the file/dataset. Either `team_id` or `classroom_id` must be provided. If team_id, then the report's classroom_id is empty (these are "team-level" reports). If classroom_id, the corresponding team_id is looked up (these are "classroom-level" reports). """ # Allow RServe to call this endpoint, then fall back on regular auth. user, error = self.authenticate_rserve() if not user: user = self.get_current_user() error = '' # Replaces function of `requires_auth = True`. if user.user_type == 'public': return self.http_unauthorized() if not user.super_admin: return self.http_forbidden() org_id = self.get_param('organization_id', str, None) team_id = self.get_param('team_id', str, None) classroom_id = self.get_param('classroom_id', str, None) params = self.get_params({ 'dataset_id': str, 'filename': unicode, 'issue_date': str, 'notes': unicode, 'preview': bool, 'template': str, }) # Lookup related objects. classroom = None if classroom_id: classroom = Classroom.get_by_id(classroom_id) if not classroom: return self.http_bad_request( "Classroom not found: {}".format(classroom_id)) team_id = classroom.team_id team = None if team_id: # May be set for team reports, or via lookup for class reports. team = Team.get_by_id(team_id) if not team: return self.http_bad_request( "Team not found: {}".format(team_id)) org = None if org_id: org = Organization.get_by_id(org_id) if not org: return self.http_bad_request( "Organization not found: {}".format(org_id)) content_type = self.request.headers['Content-Type'] is_form = 'multipart/form-data' in content_type is_json = 'application/json' in content_type if is_form: report = self.save_file( params['filename'], self.request.POST['file'], org_id, team_id, classroom_id, ) elif is_json: kwargs = { 'classroom_id': classroom_id, 'dataset_id': params['dataset_id'], 'filename': params['filename'], 'issue_date': params.get('issue_date', None), 'notes': params.get('notes', None), 'organization_id': org_id, 'team_id': team_id, 'template': params['template'], } # Some params we may want to avoid including at all, so that if # they're absent they'll take the default value defined in the db. if params.get('preview', None) is not None: kwargs['preview'] = params['preview'] report = Report.create(**kwargs) else: return self.http_bad_request( "Only supported content types are 1) multipart/form-data for " "uploading files and 2) application/json for datasets. Got {}". format(self.request.headers['Content-Type'])) saved_report = Report.put_for_index(report, 'parent-file') self.write(saved_report)