def register_athlete(strava_athlete, token_dict): """ Ensure specified athlete is added to database, returns athlete orm. :return: The added athlete model object. :rtype: :class:`bafs.orm.Athlete` """ athlete = meta.scoped_session().query(Athlete).get(strava_athlete.id) if athlete is None: athlete = Athlete() athlete.id = strava_athlete.id athlete.name = '{0} {1}'.format(strava_athlete.firstname, strava_athlete.lastname).strip() # Temporary; we will update this in disambiguation phase. (This isn't optimal; needs to be # refactored.... # # Where does the disambiguation phase get called now? Nowhere... # so let's fix it here for now. # * Do not override already set display_names. # * Use a first name and last initial (if available). # See also: # https://github.com/freezingsaddles/freezing-web/issues/80 # https://github.com/freezingsaddles/freezing-web/issues/75 # https://github.com/freezingsaddles/freezing-web/issues/73 # - @obscurerichard] if athlete.display_name is None: if strava_athlete.lastname is None: athlete.display_name = strava_athlete.firstname else: athlete.display_name = '{0} {1}'.format( strava_athlete.firstname.strip(), strava_athlete.lastname.strip(), ) athlete.profile_photo = strava_athlete.profile athlete.access_token = token_dict['access_token'] athlete.refresh_token = token_dict['refresh_token'] athlete.expires_at = token_dict['expires_at'] meta.scoped_session().add(athlete) # We really shouldn't be committing here, since we want to disambiguate names after registering meta.scoped_session().commit() return athlete
def register_athlete(self, strava_athlete: sm.Athlete, access_token: str) -> Athlete: """ Ensure specified athlete is added to database, returns athlete model. :return: The added athlete model object. :rtype: :class:`bafs.model.Athlete` """ session = meta.scoped_session() athlete = session.query(Athlete).get(strava_athlete.id) if athlete is None: athlete = Athlete() athlete.id = strava_athlete.id athlete_name = f"{strava_athlete.firstname} {strava_athlete.lastname}" athlete.profile_photo = strava_athlete.profile athlete.access_token = access_token def already_exists(display_name) -> bool: return ( session.query(Athlete).filter(Athlete.id != athlete.id).filter( Athlete.display_name == display_name).count() > 0) def unambiguous_display_name() -> str: display_name = f"{strava_athlete.firstname} {strava_athlete.lastname[0]}" if already_exists(display_name): self.logger.info( f"display_name '{display_name}' conflicts, using '{athlete_name}'" ) display_name = athlete_name return display_name # Only update the display name if it is either: # a new athlete, or the athlete name has changed try: if athlete_name != athlete.name: self.logger.info( f"Athlete '{athlete_name}' was renamed '{athlete.name}'") athlete.display_name = unambiguous_display_name() except: self.logger.exception( f"Athlete name disambiguation error for {strava_athlete.id}", exc_info=True, ) athlete.display_name = athlete_name finally: athlete.name = athlete_name session.add(athlete) return athlete
def refresh_access_token(self, athlete: Athlete): assert athlete, "No athlete ID or Athlete object provided." if (athlete.refresh_token is not None): an_hour_from_now = time.time() + 60 * 60 if (athlete.expires_at < an_hour_from_now): refresh_token = athlete.refresh_token self.logger.info( "access token for athlete %s is stale, expires_at=%s", athlete.id, athlete.expires_at, ) else: # Access token is still valid - no action needed refresh_token = None self.logger.info( "access token for athlete %s is still valid ", athlete.id, ) elif (athlete.access_token is not None): # Athlete has an access token but no refresh token yet. # Upgrade the forever token to the new tokens as described in: # https://developers.strava.com/docs/oauth-updates/#migration-instructions refresh_token = athlete.access_token else: raise ValueError( "athlete %s had no access or refresh token".format(athlete.id)) if (refresh_token): self.logger.info("saving refresh token for athlete %s", athlete.id) token_dict = super().refresh_access_token( Config.STRAVA_CLIENT_ID, Config.STRAVA_CLIENT_SECRET, refresh_token, ) self.access_token = token_dict['access_token'] athlete.access_token = token_dict['access_token'] athlete.refresh_token = token_dict['refresh_token'] athlete.expires_at = token_dict['expires_at'] meta.scoped_session().add(athlete) meta.scoped_session().commit()
def register_athlete(self, strava_athlete: sm.Athlete, access_token: str): """ Ensure specified athlete is added to database, returns athlete model. :return: The added athlete model object. :rtype: :class:`bafs.model.Athlete` """ session = meta.scoped_session() athlete = session.query(Athlete).get(strava_athlete.id) if athlete is None: athlete = Athlete() athlete.id = strava_athlete.id athlete.name = '{0} {1}'.format(strava_athlete.firstname, strava_athlete.lastname).strip() display_name = strava_athlete.firstname + ' ' + strava_athlete.lastname[ 0] def already_exists(display_name): return session.query(Athlete).filter(Athlete.id != athlete.id)\ .filter(Athlete.display_name == display_name)\ .count() > 0 charidx = 1 while already_exists(display_name): try: display_name += str(strava_athlete.lastname[charidx]) charidx += 1 except IndexError: self.logger.warning( "Ran out of last-name letters to disambiguate {}".format( athlete)) break athlete.display_name = display_name athlete.profile_photo = strava_athlete.profile athlete.access_token = access_token session.add(athlete) return athlete
def register_athlete_team(self, strava_athlete: sm.Athlete, athlete_model: Athlete) -> Team: """ Updates db with configured team that matches the athlete's teams. Updates the passed-in Athlete model object with created/updated team. :param strava_athlete: The Strava model object for the athlete. :param athlete_model: The athlete model object. :return: The :class:`bafs.model.Team` object will be returned which matches configured teams. :raise MultipleTeamsError: If this athlete is registered for multiple of the configured teams. That won't work. :raise NoTeamsError: If no teams match. """ all_teams = config.COMPETITION_TEAMS self.logger.info("Checking {0!r} against {1!r}".format( strava_athlete.clubs, all_teams)) try: if strava_athlete.clubs is None: raise NoTeamsError( "Athlete {0} ({1} {2}): No clubs returned- {3}. {4}.". format( strava_athlete.id, strava_athlete.firstname, strava_athlete.lastname, "Full Profile Access required", "Please re-authorize", )) matches = [c for c in strava_athlete.clubs if c.id in all_teams] self.logger.debug("Matched: {0!r}".format(matches)) athlete_model.team = None if len(matches) > 1: # you can be on multiple teams # as long as only one is an official team matches = [ c for c in matches if c.id not in config.OBSERVER_TEAMS ] if len(matches) > 1: self.logger.info("Multiple teams matched for {}: {}".format( strava_athlete, matches, )) raise MultipleTeamsError(matches) if len(matches) == 0: # Fall back to main team if it is the only team they are in matches = [ c for c in strava_athlete.clubs if c.id == config.MAIN_TEAM ] if len(matches) == 0: raise NoTeamsError("Athlete {0} ({1} {2}): {3} {4}".format( strava_athlete.id, strava_athlete.firstname, strava_athlete.lastname, "No teams matched ours. Teams defined:", strava_athlete.clubs, )) else: club = matches[0] # create the team row if it does not exist team = meta.scoped_session().query(Team).get(club.id) if team is None: team = Team() team.id = club.id team.name = club.name team.leaderboard_exclude = club.id in config.OBSERVER_TEAMS athlete_model.team = team meta.scoped_session().add(team) return team finally: meta.scoped_session().commit()