def combine_player_info(player_id, dbsession=DBSESSION):
    """
    Get player's name, club, recent scores, upcoming fixtures, and
    upcoming predictions if available
    """
    info_dict = {"player_id": player_id}
    p = get_player(player_id, dbsession=dbsession)
    info_dict["player_name"] = p.name
    team = p.team(CURRENT_SEASON, NEXT_GAMEWEEK)
    info_dict["team"] = team
    # get recent scores for the player
    rs = get_recent_scores_for_player(p, dbsession=dbsession)
    recent_scores = [{"gameweek": k, "score": v} for k, v in rs.items()]
    info_dict["recent_scores"] = recent_scores
    # get upcoming fixtures
    fixtures = get_fixtures_for_player(p, dbsession=dbsession)[:3]
    info_dict["fixtures"] = []
    for f in fixtures:
        home_or_away = "home" if f.home_team == team else "away"
        opponent = f.away_team if home_or_away == "home" else f.home_team
        info_dict["fixtures"].append({
            "gameweek": f.gameweek,
            "opponent": opponent,
            "home_or_away": home_or_away
        })
    try:
        tag = get_latest_prediction_tag(dbsession=dbsession)
        predicted_points = get_predicted_points_for_player(p,
                                                           tag,
                                                           dbsession=dbsession)
        info_dict["predictions"] = predicted_points
    except (RuntimeError):
        pass
    return info_dict
def calc_predicted_points_for_player(
    player,
    team_model,
    df_player,
    df_bonus,
    df_saves,
    df_cards,
    season,
    gw_range=None,
    fixtures_behind=3,
    tag="",
    dbsession=session,
):
    """
    Use the team-level model to get the probs of scoring or conceding
    N goals, and player-level model to get the chance of player scoring
    or assisting given that their team scores.
    """

    message = "Points prediction for player {}".format(player.name)

    if not gw_range:
        # by default, go for next three matches
        gw_range = list(
            range(NEXT_GAMEWEEK, min(NEXT_GAMEWEEK + 3, 38))
        )  # don't go beyond gw 38!
    team = player.team(
        season, gw_range[0]
    )  # assume player stays with same team from first gameweek in range
    position = player.position(season)
    fixtures = get_fixtures_for_player(
        player, season, gw_range=gw_range, dbsession=dbsession
    )

    # use same recent_minutes from previous gameweeks for all predictions
    recent_minutes = get_recent_minutes_for_player(
        player,
        num_match_to_use=fixtures_behind,
        season=season,
        last_gw=min(gw_range) - 1,
        dbsession=dbsession,
    )
    if len(recent_minutes) == 0:
        # e.g. for gameweek 1
        # this should now be dealt with in get_recent_minutes_for_player, so
        # throw error if not.
        # recent_minutes = estimate_minutes_from_prev_season(
        #    player, season=season, dbsession=session
        # )
        raise ValueError("Recent minutes is empty.")

    expected_points = defaultdict(float)  # default value is 0.
    predictions = []  # list that will hold PlayerPrediction objects

    for fixture in fixtures:
        gameweek = fixture.gameweek
        is_home = fixture.home_team == team
        opponent = fixture.away_team if is_home else fixture.home_team
        home_or_away = "at home" if is_home else "away"
        message += "\ngameweek: {} vs {}  {}".format(gameweek, opponent, home_or_away)
        points = 0.0
        expected_points[gameweek] = points

        if sum(recent_minutes) == 0:
            # 'recent_minutes' contains the number of minutes that player played
            # for in the past few matches. If these are all zero, we will for sure
            # predict zero points for this player, so we don't need to call all the
            # functions to calculate appearance points, defending points, attacking points.
            points = 0.0

        elif is_injured_or_suspended(player.fpl_api_id, gameweek, season, dbsession):
            # Points for fixture will be zero if suspended or injured
            points = 0.0

        else:
            # now loop over recent minutes and average
            points = 0
            for mins in recent_minutes:
                points += (
                    get_appearance_points(mins)
                    + get_attacking_points(
                        player.player_id,
                        position,
                        team,
                        opponent,
                        is_home,
                        mins,
                        team_model,
                        df_player,
                    )
                    + get_defending_points(
                        position, team, opponent, is_home, mins, team_model
                    )
                )
                if df_bonus is not None:
                    points += get_bonus_points(player.player_id, mins, df_bonus)
                if df_cards is not None:
                    points += get_card_points(player.player_id, mins, df_cards)
                if df_saves is not None:
                    points += get_save_points(
                        position, player.player_id, mins, df_saves
                    )

            points = points / len(recent_minutes)

        # create the PlayerPrediction for this player+fixture
        predictions.append(make_prediction(player, fixture, points, tag))
        expected_points[gameweek] += points
        # and return the per-gameweek predictions as a dict
        message += "\nExpected points: {:.2f}".format(points)

    print(message)
    return predictions