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 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
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)
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))
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, )