def test_get_current_no_extended_end_date(self): team_id = 'Team_001' strictly_past_cycle = Cycle.create( team_id=team_id, ordinal=1, start_date=date.today() - timedelta(days=2), end_date=date.today() - timedelta(days=1), ) strictly_past_cycle.put() strictly_current_cycle = Cycle.create( team_id=team_id, ordinal=2, start_date=date.today() - timedelta(days=1), end_date=date.today() + timedelta(days=1), ) strictly_current_cycle.put() strictly_future_cycle = Cycle.create( team_id=team_id, ordinal=3, start_date=date.today() + timedelta(days=1), end_date=date.today() + timedelta(days=2), ) strictly_future_cycle.put() self.assertEqual( Cycle.get_current_for_team(team_id), strictly_current_cycle )
def test_delete(self): """Only team captains can delete cycles.""" other, teammate, captain, team, cycles = self.create() other = User.create(name='other', email='*****@*****.**') other.put() # Forbidden by non-captains. for user in (teammate, other): self.testapp.delete( '/api/cycles/{}'.format(cycles[1].uid), headers=jwt_headers(user), status=403, ) # Successful by captain. self.testapp.delete( '/api/cycles/{}'.format(cycles[1].uid), headers=jwt_headers(captain), status=204, ) self.assertIsNone(Cycle.get_by_id(cycles[1].uid)) # Forbidden if cycles are too few. self.testapp.delete( '/api/cycles/{}'.format(cycles[2].uid), headers=jwt_headers(captain), status=403, ) # The last cycle wasn't deleted. self.assertIsNotNone(Cycle.get_by_id(cycles[2].uid))
def test_reorders(self): """ Cycles without dates are placed at the end ordered by ordinal. """ cycles = [ Cycle.create( team_id="Team_001", ordinal=1, start_date=None, end_date=None ), Cycle.create( team_id="Team_001", ordinal=2, start_date=None, end_date=None ), Cycle.create( team_id="Team_001", ordinal=3, start_date=date(2018, 1, 1), end_date=date(2019, 1, 1) ), ] reordered = Cycle.reorder_and_extend(cycles) self.assertEqual(reordered, [cycles[2], cycles[0], cycles[1]])
def test_overlap_forbidden(self): other, teammate, captain, team, cycles = self.create() # Try to create a new cycle that wraps the others. before = cycles[1].start_date - datetime.timedelta(days=1) after = cycles[2].end_date + datetime.timedelta(days=1) wrapping_params = { 'team_id': team.uid, 'ordinal': 1, 'start_date': before.strftime(config.iso_date_format), 'end_date': after.strftime(config.iso_date_format), } self.testapp.post_json( '/api/cycles', wrapping_params, headers=jwt_headers(captain), status=400, ) # Nothing created in db. self.assertEqual(len(Cycle.get()), 3) # Same story for updating. self.testapp.put_json( '/api/cycles/{}'.format(cycles[1].uid), wrapping_params, headers=jwt_headers(captain), status=400, ) # Cycle's dates haven't changed. fetched = Cycle.get_by_id(cycles[1].uid) self.assertEqual(fetched.start_date, cycles[1].start_date) self.assertEqual(fetched.end_date, cycles[1].end_date)
def test_extended_end_dates_all_set(self): cycles = [ Cycle.create( team_id="Team_001", ordinal=1, start_date=date(2019, 1, 1), end_date=date(2019, 1, 15) ), Cycle.create( team_id="Team_001", ordinal=2, start_date=date(2019, 2, 1), end_date=date(2019, 2, 15) ), Cycle.create( team_id="Team_001", ordinal=3, start_date=date(2019, 3, 1), end_date=date(2019, 3, 15) ), ] reordered = Cycle.reorder_and_extend(cycles) self.assertEqual( reordered[0].extended_end_date, date(2019, 1, 31), # day before 2/1 ) self.assertEqual( reordered[1].extended_end_date, date(2019, 2, 28), # day before 3/1 ) self.assertEqual( reordered[2].extended_end_date, date(2019, 6, 30) # last day of program )
def test_extended_end_dates_some_unset(self): cycles = [ Cycle.create( team_id="Team_001", ordinal=1, start_date=date(2019, 1, 1), end_date=date(2019, 1, 15) ), Cycle.create( team_id="Team_001", ordinal=2, start_date=None, end_date=None, ), Cycle.create( team_id="Team_001", ordinal=3, start_date=None, end_date=None, ), ] # Should match output of Cycle.cycleless_end_date(). last_day_of_program = date(2019, 6, 30) reordered = Cycle.reorder_and_extend(cycles) self.assertEqual(reordered[0].extended_end_date, last_day_of_program) self.assertEqual(reordered[1].extended_end_date, None) self.assertEqual(reordered[2].extended_end_date, None)
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 set_up(self): # Let ConsistencyTestCase set up the datastore testing stub. super(TestApiParticipants, self).set_up() application = self.patch_webapp(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(), 'participant': Participant.get_table_definition(), 'program': Program.get_table_definition(), 'team': Team.get_table_definition(), 'user': User.get_table_definition(), }) self.program = Program.create( name="Engagement Project", label='ep18', min_cycles=3, active=True, preview_url='foo.com', ) self.program.put()
def test_create(self): """Only captains can add cycles.""" other, teammate, captain, team, cycles = self.create() last = cycles[-1] start_date = last.start_date + datetime.timedelta(weeks=5) end_date = last.end_date + datetime.timedelta(weeks=5) # Forbidden for non-captains. for user in (other, teammate): self.testapp.post_json( '/api/cycles', { 'team_id': team.uid, 'start_date': start_date.strftime(config.iso_date_format), 'end_date': end_date.strftime(config.iso_date_format), }, headers=jwt_headers(user), status=403, ) # Successful for captains. response = self.testapp.post_json( '/api/cycles', { 'team_id': team.uid, 'start_date': start_date.strftime(config.iso_date_format), 'end_date': end_date.strftime(config.iso_date_format), }, headers=jwt_headers(captain), ) self.assertIsNotNone(Cycle.get_by_id(json.loads(response.body)['uid']))
def post(self, team_id, date_str=None): if date_str: today = datetime.strptime(date_str, config.iso_date_format).date() else: today = date.today() # Guaranteed to have start and end dates. cycle = Cycle.get_current_for_team(team_id, today) if not cycle: logging.info( "Either the team doesn't exist, or they don't have a cycle " "matching the current date. Doing nothing.") return team = Team.get_by_id(team_id) classrooms = Classroom.get(team_id=team_id) if len(classrooms) == 0: logging.info("No classrooms, setting participation to 0.") cycle.students_completed = 0 else: ppn = get_participation(cycle, classrooms) num_complete = 0 for code, counts in ppn.items(): complete_count = next( (c for c in counts if c['value'] == '100'), None) num_complete += complete_count['n'] if complete_count else 0 cycle.students_completed = num_complete cycle.put()
def create_for_dashboard(self, org, x=0): x_label = str(x).rjust(2, '0') team = Team.create( name='Team {}'.format(x_label), captain_id='User_captain_{}'.format(x_label), organization_ids=[org.uid], program_id=self.program.uid, ) user = User.create(name='User {}'.format(x_label), email='foo.{}@bar.com'.format(x_label), owned_teams=[team.uid]) cycle = Cycle.create( team_id=team.uid, ordinal=1, start_date=datetime.date.today() - datetime.timedelta(days=1), end_date=datetime.date.today() + datetime.timedelta(days=1), ) response = Response.create( type=Response.USER_LEVEL_SYMBOL, user_id=user.uid, team_id=team.uid, parent_id=cycle.uid, module_label='DemoModule', progress=50, page=1, body={ 'question1': { 'modified': '2019-01-01T00:00:00Z', 'value': 'foo', }, }, ) return (team, user, cycle, response)
def test_team_in_program_with_use_cycles_false_gets_single_cycle(self): user = User.create(name='foo', email='*****@*****.**') user.put() cycleless_program = Program.create( label='cycleless', name='Cycleless Program', preview_url='http://cycle.less', use_cycles=False, min_cycles=1, max_cycles=1 ) cycleless_program.put() cycleless_team_params = { "name": 'Cycleless Team', "uid": 'Team_cycleless', "program_id": cycleless_program.uid } response = self.testapp.post_json( '/api/teams', cycleless_team_params, headers=self.login_headers(user) ) team_uid = json.loads(response.body)['uid'] cycles = Cycle.get(team_id=team_uid) self.assertEqual(len(cycles), 1)
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 index(): form1 = InputForm(request.form, prefix="form1") form2 = ImageSet(request.form, prefix="form2") form3 = Cycle(request.form, prefix="form3") results = dict() if form1.validate_on_submit() and form1.imgurl.data: session['face_id'] = simface.get_face_id(form1.imgurl.data) session['k'] = 0 if 'face_id' in session: if form2.validate_on_submit() and form2.imageset.data: session['k'] = 0 session['idx'] = form2.imageset.data list_id = simface.imageset[session['idx']] session['similars'] = simface.face_similar(session['face_id'], list_id) results = simface.get_info( session['idx'], session['similars'][0]['persistedFaceId']) results['confidence'] = session['similars'][0]['confidence'] if 'idx' not in session: session['idx'] = 'movie' list_id = simface.imageset[session['idx']] session['similars'] = simface.face_similar(session['face_id'], list_id) results = simface.get_info( session['idx'], session['similars'][0]['persistedFaceId']) results['confidence'] = session['similars'][0]['confidence'] if form3.validate_on_submit() and form3.cycle.data: if session['k'] < (len(session['similars']) - 1): session['k'] = session['k'] + 1 else: session['k'] = 0 results = simface.get_info( session['idx'], session['similars'][session['k']]['persistedFaceId']) results['confidence'] = session['similars'][session['k']]['confidence'] return render_template('view.html', form1=form1, form2=form2, form3=form3, results=results)
def create(self): program = Program.create( name="Demo", label='demo', min_cycles=1, preview_url='foo.com', ) program.put() team = Team.create(name='foo', program_id=program.uid) captain = User.create(name='captain', email='*****@*****.**', owned_teams=[team.uid]) team.captain_id = captain.uid teammate = User.create(name='teammate', email='*****@*****.**', owned_teams=[team.uid]) other = User.create(name='other', email='*****@*****.**') User.put_multi((other, teammate, captain)) team.put() cycles = ( # From a different team. Cycle.create( team_id='Team_other', ordinal=1, start_date=datetime.date(2000, 1, 1), end_date=datetime.date(2000, 1, 1), ), Cycle.create( team_id=team.uid, ordinal=1, start_date=datetime.date(2000, 1, 1), end_date=datetime.date(2000, 2, 1), ), # Current. Cycle.create( team_id=team.uid, ordinal=2, start_date=datetime.date.today(), end_date=datetime.date.today() + datetime.timedelta(weeks=4), ), ) Cycle.put_multi(cycles) return other, teammate, captain, team, cycles
def test_dashboard(self): org = Organization.create( name='Org Foo', program_id=self.program.uid, ) org.put() org_admin = User.create( name='Org Admin', email='*****@*****.**', owned_organizations=[org.uid], ) org_admin.put() zipped = [] for x in range(5): zipped.append(self.create_for_dashboard(org, x)) teams, users, cycles, responses = zip(*zipped) Team.put_multi(teams) User.put_multi(users) Cycle.put_multi(cycles) Response.put_multi(responses) raw_result = self.testapp.get( '/api/organization_dashboards/{}'.format(org.uid), headers=self.login_headers(org_admin), ) result = json.loads(raw_result.body) # Expected ids. team_ids = set(t.uid for t in teams) user_ids = set(u.uid for u in users) cycle_ids = set(c.uid for c in cycles) response_ids = set(r.uid for r in responses) # All ids present. self.assertEqual(set(t['uid'] for t in result['teams']), team_ids) self.assertEqual(set(u['uid'] for u in result['users']), user_ids) self.assertEqual(set(c['uid'] for c in result['cycles']), cycle_ids) self.assertEqual(set(r['uid'] for r in result['responses']), response_ids) # Responses have no body. self.assertTrue(all(len(r['body']) == 0 for r in result['responses']))
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 set_up(self): # Let ConsistencyTestCase set up the datastore testing stub. super(TestCycles, self).set_up() with mysql_connection.connect() as sql: sql.reset({ 'classroom': Classroom.get_table_definition(), 'cycle': Cycle.get_table_definition(), 'program': Program.get_table_definition(), 'team': Team.get_table_definition(), 'user': User.get_table_definition(), })
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 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 create(self): captain = User.create(email='*****@*****.**', name="Captain PERTS") teacher = User.create(email='*****@*****.**', name="Edward Teach") team = Team.create(name='Team Foo', program_id='Program_ep', captain_id=captain.uid) classroom = Classroom.create(name='Class Foo', team_id=team.uid, contact_id=teacher.uid) cycle = Cycle.create( team_id=team.uid, ordinal=1, start_date=datetime.date.today() - datetime.timedelta(days=7), end_date=datetime.date.today() + datetime.timedelta(days=7), ) captain.owned_teams = [team.uid] teacher.owned_teams = [team.uid] return (captain, teacher, team, classroom, cycle)
def set_up(self): # Let ConsistencyTestCase set up the datastore testing stub. super(TestApiOrganizations, 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({ 'cycle': Cycle.get_table_definition(), 'organization': Organization.get_table_definition(), 'program': Program.get_table_definition(), 'response': Response.get_table_definition(), 'team': Team.get_table_definition(), 'user': User.get_table_definition(), }) self.program = Program.create( name="Engagement Project", label='ep18', min_cycles=3, active=True, preview_url='foo.com', ) self.program.put() self.beleset_program = Program.create( name="Copilot-Elevate", label='beleset19', min_cycles=2, active=True, preview_url='foo.com', ) self.beleset_program.put()
def test_create_insert(self): """Server reorders team cycles and returns them in an envelope.""" other, teammate, captain, team, cycles = self.create() # Move the last cycle ahead one month so we have room to insert. last = cycles[-1] orig_start_date = last.start_date orig_end_date = last.end_date last.start_date = orig_start_date + datetime.timedelta(weeks=5) last.end_date = orig_end_date + datetime.timedelta(weeks=5) last.put() # Now insert a penultimate cycle. response = self.testapp.post_json( '/api/cycles?envelope=team_cycles', { 'team_id': team.uid, 'ordinal': last.ordinal + 1, 'start_date': orig_start_date.strftime(config.iso_date_format), 'end_date': orig_end_date.strftime(config.iso_date_format), }, headers=jwt_headers(captain), ) response_dict = json.loads(response.body) # New cycle is in the 'data' property. self.assertIsNotNone(Cycle.get_by_id(response_dict['data']['uid'])) # Other metadata is present in the envelope. self.assertEqual(response_dict['status'], 200) # Specifically the list of other cycles for this team. team_cycles = response_dict['team_cycles'] self.assertEqual(len(team_cycles), 3) # The one we created should be second-to-last. self.assertEqual(team_cycles[-2]['uid'], response_dict['data']['uid']) # All the ordinals should be updated and sorted. self.assertEqual( [c['ordinal'] for c in team_cycles], [1, 2, 3] )
def test_team_participation(self): program, captain, team, classrooms, cycle = self.create('ep19') # Mock Neptune's participation API. class MockFetchResult(object): status_code = 200 content = json.dumps( {c.code: [{ 'value': '100', 'n': 3 }] for c in classrooms}) with patch.object(urlfetch, 'fetch', return_value=MockFetchResult()) as _: handler = task_handlers.TeamParticipation() # Run the task. handler.post(team.uid) # The cycle should show 3 * 3 = 9 students complete self.assertEqual(Cycle.get_by_id(cycle.uid).students_completed, 9)
def set_up(self): # Let ConsistencyTestCase set up the datastore testing stub. super(TestApiCycles, 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({ 'cycle': Cycle.get_table_definition(), 'program': Program.get_table_definition(), 'team': Team.get_table_definition(), 'user': User.get_table_definition(), })
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 create(self): org = Organization.create(name='Foo Community', program_id=self.program.uid) org.put() team = Team.create( name='foo', captain_id='User_captain', program_id=self.program.uid, organization_ids=[org.uid], ) teammate = User.create(name='teammate', email='*****@*****.**', owned_teams=[team.uid]) other = User.create(name='other', email='*****@*****.**') User.put_multi((other, teammate)) team.put() cycles = ( Cycle.create( team_id=team.uid, ordinal=1, start_date=datetime.date(2000, 1, 1), end_date=datetime.date(2000, 2, 1), ), Cycle.create( team_id=team.uid, ordinal=2, start_date=datetime.date.today(), end_date=datetime.date.today() + datetime.timedelta(weeks=4), ), ) Cycle.put_multi(cycles) responses = { # User-level, not related to our team or user, inaccessible. 'user_other_other': Response.create( user_id=other.uid, team_id='Team_other', parent_id='Cycle_other', module_label='ModuleFoo', body=self.default_body(), ), # Related to our user but not our team. 'user_other_user': Response.create( user_id=teammate.uid, team_id='Team_other', parent_id='Cycle_isolated', module_label='ModuleFoo', body=self.default_body(), ), # Related to both our team and user; two different cycles. 'user_team_user1': Response.create( user_id=teammate.uid, team_id=team.uid, parent_id=cycles[0].uid, module_label='ModuleFoo', body=self.default_body(), ), 'user_team_user2': Response.create( user_id=teammate.uid, team_id=team.uid, parent_id=cycles[1].uid, module_label='ModuleFoo', body=self.default_body(), ), # Related to our team but not our user; body should stay secret 'user_team_other': Response.create( user_id='User_other-teammate', team_id=team.uid, parent_id=cycles[0].uid, module_label='ModuleFoo', body=self.default_body(), ), # Team-level response, readable for all team members 'team_team': Response.create( type='Team', user_id='', team_id=team.uid, parent_id='launch-step', module_label='ModuleFoo', body=self.default_body(), ), # Team-level, but for a different team. 'team_other': Response.create( type='Team', user_id='', team_id='Team_other', parent_id='launch-step', module_label='ModuleFoo', body=self.default_body(), ), } Response.put_multi(responses.values()) return (other, teammate, team, cycles, responses)
def test_check_roster_cycle_data(self): team = Team.create(name='foo', captain_id="User_cap", program_id=self.program.uid) classroom = Classroom.create( name="CompSci 101", team_id=team.uid, contact_id="User_contact", code="foo bar", num_students=1, ) ppt = Participant.create(team_id=team.uid, classroom_ids=[classroom.uid], student_id='STUDENTID001') today = datetime.date.today() cycle1 = Cycle.create( team_id=team.uid, ordinal=1, # schedule to not be current (starts and ends in the past) start_date=today - datetime.timedelta(days=3), end_date=today - datetime.timedelta(days=2), ) cycle1.put() team.put() classroom.put() ppt.put() # Without a current cycle, no cycle data response = self.testapp.get( '/api/codes/{}/participants/{}'.format(classroom.url_code, ppt.student_id), status=200, ) self.assertEqual( json.loads(response.body), { 'uid': ppt.uid, 'team_id': ppt.team_id }, ) # Add a new cycle that is current. cycle2 = Cycle.create( team_id=team.uid, ordinal=2, # schedule to not be current (starts and ends in the past) start_date=today - datetime.timedelta(days=1), end_date=today + datetime.timedelta(days=1), ) cycle2.put() # Cycle data present. response = self.testapp.get( '/api/codes/{}/participants/{}'.format(classroom.url_code, ppt.student_id), status=200, ) expected = { 'uid': ppt.uid, 'team_id': ppt.team_id, 'cycle': { 'uid': cycle2.uid, 'team_id': cycle2.team_id, 'ordinal': cycle2.ordinal, 'start_date': util.datelike_to_iso_string(cycle2.start_date), 'end_date': util.datelike_to_iso_string(cycle2.end_date), } } self.assertEqual(json.loads(response.body), expected)
def test_update(self): """Only captains can update cycles.""" other, teammate, captain, team, cycles = self.create() # Forbidden for non-captains. for user in (teammate, other): self.testapp.put_json( '/api/cycles/{}'.format(cycles[1].uid), {'start_date': 'foo'}, headers=jwt_headers(user), status=403, ) # Successful for captains. # This moves both dates earlier. response = self.testapp.put_json( '/api/cycles/{}?envelope=team_cycles'.format(cycles[2].uid), {'start_date': '1999-01-01', 'end_date': '1999-02-01'}, headers=jwt_headers(captain), ) fetched = Cycle.get_by_id(cycles[2].uid) self.assertEqual(fetched.start_date, datetime.date(1999, 1, 1)) self.assertEqual(fetched.end_date, datetime.date(1999, 2, 1)) # Should be reordered (this one is now first/earliest on the team). self.assertEqual(fetched.ordinal, 1) # Response should have all team cycles as meta data. response_dict = json.loads(response.body) data_cycle = response_dict['data'] team_cycles = response_dict['team_cycles'] self.assertEqual( [c['uid'] for c in team_cycles], [cycles[2].uid, cycles[1].uid], # reversed from earlier ) self.assertEqual( [c['ordinal'] for c in team_cycles], [1, 2], # correctly ordered by date ) # Main data object matches corresponding cycle in envelope. self.assertEqual( response_dict['data'], next(c for c in team_cycles if c['uid'] == data_cycle['uid']), ) # Should also be possible to move just the end date forward (overlap # code should not see this as a problem). new_end = cycles[1].end_date + datetime.timedelta(weeks=1) response = self.testapp.put_json( '/api/cycles/{}?envelope=team_cycles'.format(cycles[1].uid), {'end_date': new_end.strftime(config.iso_date_format)}, headers=jwt_headers(captain), ) fetched = Cycle.get_by_id(cycles[1].uid) self.assertEqual(fetched.end_date, new_end) response_dict = json.loads(response.body) data_cycle = response_dict['data'] team_cycles = response_dict['team_cycles'] # Main data object matches corresponding cycle in envelope. self.assertEqual( response_dict['data'], next(c for c in team_cycles if c['uid'] == data_cycle['uid']), )
def get(self, user_id=None, team_id=None): complete = False # Determine authenticated user based on JWT token # @todo: can we apply jti or some other rule to make sure this URL isn't # inappropriately shareable? token = self.request.get('token', None) payload, error = jwt_helper.decode(token) if not payload or error: return self.http_forbidden() auth_user = User.get_by_id(payload['user_id']) user = User.get_by_id(user_id) team = Team.get_by_id(team_id) if not user or not team: return self.http_not_found() # The authenticated user can only retrieve their own certificate. # The authenticated user must own the team that they are requesting the # certificate for. if not auth_user == user and not owns(auth_user, team): return self.http_forbidden() classrooms = Classroom.get( contact_id=user.uid, team_id=team_id, ) cycles = Cycle.get( team_id=team_id, order='ordinal', ) if len(classrooms) > 0 and len(cycles) > 0: cycle_participation = self.get_cycle_participation_pct( cycles, classrooms, ) participation_complete = self.has_completed_three_cycles( cycle_participation) else: cycle_participation = [{ 'ordinal': c.ordinal, 'pct': 0, } for c in cycles] participation_complete = False exit_survey_complete = self.has_completed_exit_survey( user, team_id, ) if (exit_survey_complete and participation_complete): complete = True if (complete): # If a teacher has successfully completed participation for three # cycles, the certificate should not show any incomplete cycles # because they aren't relevant for the requirement of receiving the # completion certificate. See #1223. cycles_to_display = [ c for c in cycle_participation if c['pct'] >= 80 ][0:3] else: cycles_to_display = cycle_participation if util.is_localhost(): neptune_protocol = 'http' neptune_domain = 'localhost:8080' else: neptune_protocol = 'https' neptune_domain = os.environ['NEPTUNE_DOMAIN'] self.write( 'completion.html', neptune_protocol=neptune_protocol, neptune_domain=neptune_domain, complete=complete, user_to_display=user, team=team, cycles_to_display=cycles_to_display, exit_survey_complete=exit_survey_complete, )