Exemple #1
0
    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
        )
Exemple #2
0
    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))
Exemple #3
0
 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]])
Exemple #4
0
    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)
Exemple #5
0
 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
     )
Exemple #6
0
    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,
        )
Exemple #8
0
    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()
Exemple #9
0
    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']))
Exemple #10
0
    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()
Exemple #11
0
    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)
Exemple #14
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)
Exemple #15
0
    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
Exemple #16
0
    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']))
Exemple #17
0
    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(),
            })
Exemple #18
0
    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(),
            })
Exemple #19
0
    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()
Exemple #21
0
    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)
Exemple #22
0
    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()
Exemple #23
0
    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]
        )
Exemple #24
0
    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)
Exemple #25
0
    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)
Exemple #28
0
    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)
Exemple #29
0
    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,
        )