feat(ingestion): add first real-data importer flow

This commit is contained in:
bisco
2026-04-11 00:24:42 +02:00
parent 5c82187b7c
commit 154450f516
8 changed files with 610 additions and 0 deletions

View File

@ -11,6 +11,7 @@ from django.urls import reverse
from .models import (
Competition,
ExternalEntityMapping,
FavoritePlayer,
Player,
PlayerNote,
@ -442,6 +443,89 @@ class SeedScoutingDataCommandTests(TestCase):
self.assertEqual(Specialty.objects.count(), first_counts["specialties"])
class FirstRealIngestionFlowTests(TestCase):
COMMAND_NAME = "import_hoopdata_demo_competition"
SOURCE_NAME = "hoopdata_demo"
def test_importer_command_runs_successfully(self):
call_command(self.COMMAND_NAME)
self.assertGreaterEqual(Player.objects.count(), 2)
def test_importer_creates_expected_core_objects(self):
call_command(self.COMMAND_NAME)
self.assertTrue(Competition.objects.filter(name="Italian Serie A2").exists())
self.assertTrue(Season.objects.filter(name="2025-2026", start_year=2025, end_year=2026).exists())
self.assertTrue(Player.objects.filter(full_name="Andrea Pulse", position="PG").exists())
self.assertTrue(Player.objects.filter(full_name="Matteo Harbor", position="C").exists())
self.assertTrue(PlayerSeason.objects.filter(player__full_name="Andrea Pulse").exists())
self.assertTrue(PlayerSeasonStats.objects.filter(player_season__player__full_name="Andrea Pulse").exists())
self.assertEqual(
ExternalEntityMapping.objects.filter(source_name=self.SOURCE_NAME).count(),
7,
)
def test_importer_is_idempotent_for_same_input(self):
call_command(self.COMMAND_NAME)
first_counts = {
"players": Player.objects.count(),
"teams": Team.objects.count(),
"contexts": PlayerSeason.objects.count(),
"stats": PlayerSeasonStats.objects.count(),
"mappings": ExternalEntityMapping.objects.count(),
}
call_command(self.COMMAND_NAME)
self.assertEqual(Player.objects.count(), first_counts["players"])
self.assertEqual(Team.objects.count(), first_counts["teams"])
self.assertEqual(PlayerSeason.objects.count(), first_counts["contexts"])
self.assertEqual(PlayerSeasonStats.objects.count(), first_counts["stats"])
self.assertEqual(ExternalEntityMapping.objects.count(), first_counts["mappings"])
def test_importer_does_not_overwrite_internal_scouting_fields(self):
role = Role.objects.create(name="internal role", slug="internal-role")
specialty = Specialty.objects.create(name="internal specialty", slug="internal-specialty")
call_command(self.COMMAND_NAME)
player = Player.objects.get(full_name="Andrea Pulse")
player.roles.add(role)
player.specialties.add(specialty)
call_command(self.COMMAND_NAME)
player.refresh_from_db()
self.assertTrue(player.roles.filter(pk=role.pk).exists())
self.assertTrue(player.specialties.filter(pk=specialty.pk).exists())
def test_importer_does_not_interfere_with_user_owned_data(self):
call_command(self.COMMAND_NAME)
user = User.objects.create_user(username="ingest_user", password="pass12345")
player = Player.objects.get(full_name="Andrea Pulse")
favorite = FavoritePlayer.objects.create(user=user, player=player)
note = PlayerNote.objects.create(user=user, player=player, body="Tracked after import")
saved = SavedSearch.objects.create(user=user, name="Imported PG", params={"name": "Andrea"})
call_command(self.COMMAND_NAME)
self.assertTrue(FavoritePlayer.objects.filter(pk=favorite.pk).exists())
self.assertTrue(PlayerNote.objects.filter(pk=note.pk).exists())
self.assertTrue(SavedSearch.objects.filter(pk=saved.pk).exists())
def test_imported_data_is_visible_in_search_and_detail_flows(self):
call_command(self.COMMAND_NAME)
list_response = self.client.get(reverse("scouting:player_list"), {"name": "Andrea"})
self.assertEqual(list_response.status_code, 200)
self.assertContains(list_response, "Andrea Pulse")
player = Player.objects.get(full_name="Andrea Pulse")
detail_response = self.client.get(reverse("scouting:player_detail", args=[player.id]))
self.assertEqual(detail_response.status_code, 200)
self.assertContains(detail_response, "Andrea Pulse")
self.assertContains(detail_response, "PTS 17.2")
class FavoritePlayerViewsTests(TestCase):
@classmethod
def setUpTestData(cls):