Ejemplo n.º 1
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()
Ejemplo n.º 2
0
    def resolve_id_mismatch(klass, user, new_id):
        """Change all references to user's id to a new id.

        N.B. this is obviously brittle; when the relationship schema changes,
        this will also have to change.
        """
        # The auth server has a different id for this user; defer to it.

        teams = Team.get(captain_id=user.uid)
        for t in teams:
            t.captain_id = new_id
        Team.put_multi(teams)

        classrooms = Classroom.get(contact_id=user.uid)
        for c in classrooms:
            c.contact_id = new_id
        Classroom.put_multi(classrooms)

        params = {'uid': new_id, 'short_uid': SqlModel.convert_uid(new_id)}
        with mysql_connection.connect() as sql:
            sql.update_row(klass.table, 'uid', user.uid, **params)

        for k, v in params.items():
            setattr(user, k, v)

        return user
Ejemplo n.º 3
0
    def post(self, team_id, date_str=None):
        survey = Survey.get(team_id=team_id)[0]
        if date_str:
            today = datetime.strptime(date_str, config.iso_date_format).date()
        else:
            today = date.today()

        # Cycle ultimately comes from Cycle.get_current_for_team() and so is
        # guaranteed to have start and end dates.
        cycle = survey.should_notify(today)
        if not cycle:
            # This task is run every week, but only actually send notifications
            # if the date matches the survey interval.
            return

        team = Team.get_by_id(survey.team_id)
        program = Program.get_by_id(team.program_id)
        classrooms = Classroom.get(team_id=team_id)
        users = User.query_by_team(team_id)

        if len(classrooms) == 0:
            pct_complete_by_id = {}
        else:
            ppn = get_participation(cycle, classrooms)
            pct_complete_by_id = self.participation_to_pct(ppn, classrooms)

        # Get all the responses once to save trips to the db. Redact them later
        # according to the relevate user.
        unsafe_responses = Response.get_for_teams_unsafe([team_id],
                                                         parent_id=cycle.uid)

        to_put = []
        for user in users:
            if user.receive_email:
                safe_responses = Response.redact_private_responses(
                    unsafe_responses, user)
                email = cycle_emailers.create_cycle_email(
                    program.label,
                    user,  # recipient
                    users,
                    team,
                    classrooms,
                    safe_responses,
                    cycle,
                    pct_complete_by_id,
                )

                to_put.append(email)

        ndb.put_multi(to_put)
Ejemplo n.º 4
0
    def after_put(self, init_kwargs, *args, **kwargs):
        """Reset memcache for related objects

        * Include _all_ those the user is joining (e.g. on creation) as well as
          any the user is leaving.
        * Include name changes stored elsewhere
        """
        rels = ((Team, 'owned_teams'), (Organization, 'owned_organizations'))
        for model, attr in rels:
            original_ids = set(init_kwargs[attr])
            new_ids = set(getattr(self, attr))
            leaving_ids = original_ids.difference(new_ids)

            for uid in set(new_ids).union(leaving_ids):
                model.update_cached_properties(uid)

        # If this user is the contact for any classrooms, and their name has
        # changed, update the name of the classroom.
        if init_kwargs['name'] != self.name:
            for c in Classroom.get(contact_id=self.uid):
                key = util.cached_properties_key(c.uid)
                cached_props = memcache.get(key) or {}
                memcache.set(key, dict(cached_props, contact_name=self.name))
Ejemplo n.º 5
0
    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,
        )