Fix combined search filter semantics across player season joins

This commit is contained in:
Alfredo Di Stasio
2026-03-10 15:47:01 +01:00
parent a1ae380fd5
commit 92c804a474
5 changed files with 314 additions and 60 deletions

View File

@ -154,3 +154,45 @@ def test_player_detail_api_includes_origin_fields(client):
payload = response.json()
assert payload["origin_competition"] == competition.name
assert payload["origin_team"] == team.name
@pytest.mark.django_db
def test_api_combined_filters_respect_same_player_season_context(client):
nationality = Nationality.objects.create(name="Poland", iso2_code="PL", iso3_code="POL")
competition = Competition.objects.create(
name="PLK",
slug="plk",
competition_type=Competition.CompetitionType.LEAGUE,
gender=Competition.Gender.MEN,
country=nationality,
)
season = Season.objects.create(label="2024-2025", start_date=date(2024, 9, 1), end_date=date(2025, 6, 30))
team_a = Team.objects.create(name="Warsaw", slug="warsaw", country=nationality)
team_b = Team.objects.create(name="Gdansk", slug="gdansk", country=nationality)
player = Player.objects.create(first_name="Piotr", last_name="Filter", full_name="Piotr Filter", nationality=nationality)
ps_a = PlayerSeason.objects.create(
player=player,
season=season,
team=team_a,
competition=competition,
games_played=10,
minutes_played=200,
)
PlayerSeasonStats.objects.create(player_season=ps_a, points=7, rebounds=2, assists=3, steals=1, blocks=0, turnovers=1)
ps_b = PlayerSeason.objects.create(
player=player,
season=season,
team=team_b,
competition=competition,
games_played=10,
minutes_played=300,
)
PlayerSeasonStats.objects.create(player_season=ps_b, points=21, rebounds=4, assists=5, steals=1, blocks=0, turnovers=2)
response = client.get(
reverse("api:players"),
data={"team": team_a.id, "season": season.id, "competition": competition.id, "points_per_game_min": "20"},
)
assert response.status_code == 200
assert response.json()["count"] == 0

View File

@ -186,3 +186,149 @@ def test_player_search_results_include_favorite_ids(client):
response = client.get(reverse("players:index"))
assert response.status_code == 200
assert player.id in response.context["favorite_player_ids"]
@pytest.mark.django_db
def test_combined_reverse_join_filters_do_not_match_across_different_player_seasons(client):
nationality = Nationality.objects.create(name="Lithuania", iso2_code="LT", iso3_code="LTU")
position = Position.objects.create(code="SG", name="Shooting Guard")
role = Role.objects.create(code="scorer", name="Scorer")
competition = Competition.objects.create(
name="LKL",
slug="lkl",
competition_type=Competition.CompetitionType.LEAGUE,
gender=Competition.Gender.MEN,
country=nationality,
)
season = Season.objects.create(label="2025-2026", start_date=date(2025, 9, 1), end_date=date(2026, 6, 30))
target_team = Team.objects.create(name="Kaunas", slug="kaunas", country=nationality)
other_team = Team.objects.create(name="Vilnius", slug="vilnius", country=nationality)
player = Player.objects.create(
first_name="Jonas",
last_name="Scope",
full_name="Jonas Scope",
birth_date=date(2001, 1, 1),
nationality=nationality,
nominal_position=position,
inferred_role=role,
)
# Matching team/season row but low scoring.
ps_target = PlayerSeason.objects.create(
player=player,
season=season,
team=target_team,
competition=competition,
games_played=20,
minutes_played=400,
)
PlayerSeasonStats.objects.create(
player_season=ps_target,
points=8.0,
rebounds=3.0,
assists=2.0,
steals=1.0,
blocks=0.2,
turnovers=1.5,
)
# High-scoring row but different team; should not satisfy combined filter.
ps_other = PlayerSeason.objects.create(
player=player,
season=season,
team=other_team,
competition=competition,
games_played=20,
minutes_played=400,
)
PlayerSeasonStats.objects.create(
player_season=ps_other,
points=22.0,
rebounds=4.0,
assists=3.0,
steals=1.2,
blocks=0.3,
turnovers=2.0,
)
response = client.get(
reverse("players:index"),
data={
"team": target_team.id,
"season": season.id,
"competition": competition.id,
"points_per_game_min": "20",
},
)
assert response.status_code == 200
assert list(response.context["players"]) == []
@pytest.mark.django_db
def test_displayed_metrics_are_scoped_to_filtered_context(client):
nationality = Nationality.objects.create(name="Turkey", iso2_code="TR", iso3_code="TUR")
position = Position.objects.create(code="PG", name="Point Guard")
role = Role.objects.create(code="playmaker", name="Playmaker")
competition = Competition.objects.create(
name="BSL",
slug="bsl",
competition_type=Competition.CompetitionType.LEAGUE,
gender=Competition.Gender.MEN,
country=nationality,
)
season = Season.objects.create(label="2025-2026", start_date=date(2025, 9, 1), end_date=date(2026, 6, 30))
target_team = Team.objects.create(name="Ankara", slug="ankara", country=nationality)
other_team = Team.objects.create(name="Izmir", slug="izmir", country=nationality)
player = Player.objects.create(
first_name="Can",
last_name="Context",
full_name="Can Context",
birth_date=date(2000, 2, 2),
nationality=nationality,
nominal_position=position,
inferred_role=role,
)
ps_target = PlayerSeason.objects.create(
player=player,
season=season,
team=target_team,
competition=competition,
games_played=10,
minutes_played=250,
)
PlayerSeasonStats.objects.create(
player_season=ps_target,
points=9.0,
rebounds=2.0,
assists=4.0,
steals=1.0,
blocks=0.1,
turnovers=2.0,
)
ps_other = PlayerSeason.objects.create(
player=player,
season=season,
team=other_team,
competition=competition,
games_played=12,
minutes_played=420,
)
PlayerSeasonStats.objects.create(
player_season=ps_other,
points=24.0,
rebounds=5.0,
assists=7.0,
steals=1.5,
blocks=0.2,
turnovers=3.0,
)
response = client.get(reverse("players:index"), data={"team": target_team.id, "season": season.id})
assert response.status_code == 200
row = list(response.context["players"])[0]
assert float(row.ppg_value) == pytest.approx(9.0)
assert float(row.mpg_value) == pytest.approx(25.0)