def get_challenges_metadata( session: Session, event_bus: ChallengeEventBus, challenges: List[UserChallenge], ) -> List[Dict]: # Break it up into map per challenge type challenge_map: Dict[str, List[str]] = defaultdict(lambda: []) specifier_metadata_map: Dict[str, Dict] = {} for challenge in challenges: challenge_map[challenge.challenge_id].append(challenge.specifier) for challenge_type, specifiers in challenge_map.items(): manager = event_bus.get_manager(challenge_type) metadatas = manager.get_metadata(session, specifiers) for i, specifier in enumerate(specifiers): metadata = metadatas[i] specifier_metadata_map[specifier] = metadata # Finally, re-sort the metadata return [specifier_metadata_map[c.specifier] for c in challenges]
def get_challenges( user_id: int, show_historical: bool, session: Session, event_bus: ChallengeEventBus, ) -> List[ChallengeResponse]: challenges_and_disbursements: List[Tuple[UserChallenge, ChallengeDisbursement]] = ( session.query(UserChallenge, ChallengeDisbursement) # Need to do outerjoin because some challenges # may not have disbursements .outerjoin( ChallengeDisbursement, and_( ChallengeDisbursement.specifier == UserChallenge.specifier, ChallengeDisbursement.challenge_id == UserChallenge.challenge_id, ), ).filter(UserChallenge.user_id == user_id) ).all() # Filter to challenges that have active managers # (in practice, all challenge should) challenges_and_disbursements = [ c for c in challenges_and_disbursements if event_bus.does_manager_exist(c[0].challenge_id) ] # Combine aggregates all_challenges: List[Challenge] = (session.query(Challenge)).all() all_challenges_map = {challenge.id: challenge for challenge in all_challenges} # grab user challenges # if not historical, filter only to *active* challenges existing_user_challenges: List[UserChallenge] = [ i[0] for i in challenges_and_disbursements if show_historical or all_challenges_map[i[0].challenge_id].active ] disbursements: List[ChallengeDisbursement] = [ i[1] for i in challenges_and_disbursements ] regular_user_challenges: List[ChallengeResponse] = [] aggregate_user_challenges_map: DefaultDict[str, List[UserChallenge]] = defaultdict( lambda: [] ) # Get extra metadata existing_metadata = get_challenges_metadata( session, event_bus, existing_user_challenges ) for i, user_challenge in enumerate(existing_user_challenges): parent_challenge = all_challenges_map[user_challenge.challenge_id] if parent_challenge.type == ChallengeType.aggregate: # Filter out aggregate user_challenges that aren't complete. # this probably shouldn't even happen (what does it mean?) if user_challenge.is_complete: aggregate_user_challenges_map[user_challenge.challenge_id].append( user_challenge ) else: # If we're a trending challenge, don't add if the user_challenge is incomplete if ( parent_challenge.type == ChallengeType.trending and not user_challenge.is_complete ): continue user_challenge_dict = to_challenge_response( user_challenge, parent_challenge, disbursements[i], existing_metadata[i], ) override_step_count = event_bus.get_manager( parent_challenge.id ).get_override_challenge_step_count(session, user_id) if override_step_count is not None and not user_challenge.is_complete: user_challenge_dict["current_step_count"] = override_step_count regular_user_challenges.append(user_challenge_dict) rolled_up: List[ChallengeResponse] = [] for (challenge_id, challenges) in aggregate_user_challenges_map.items(): parent_challenge = all_challenges_map[challenge_id] rolled_up.append(rollup_aggregates(challenges, parent_challenge)) # Return empty user challenges for active challenges that are non-hidden # and visible for the current user active_non_hidden_challenges: List[Challenge] = [ challenge for challenge in all_challenges if ( challenge.active and not challenge.type == ChallengeType.trending and event_bus.get_manager(challenge.id).should_show_challenge_for_user( session, user_id ) ) ] existing_challenge_ids = { user_challenge.challenge_id for user_challenge in existing_user_challenges } needs_user_challenge = [ challenge for challenge in active_non_hidden_challenges if challenge.id not in existing_challenge_ids ] empty_metadata = get_empty_metadata(event_bus, needs_user_challenge) empty_challenges = create_empty_user_challenges( user_id, needs_user_challenge, empty_metadata ) combined = regular_user_challenges + rolled_up + empty_challenges return combined
def get_empty_metadata(event_bus: ChallengeEventBus, challenges: List[Challenge]): return [event_bus.get_manager(c.id).get_default_metadata() for c in challenges]