pablogrois Claude Opus 4.8 commited on
Commit
6cf2e5a
·
1 Parent(s): 1931f95

Similitud p=2, perfil+similares plegables, menos logs (HF drawer)

Browse files

- Similitud = 100·2^(-(d/dbar)²) (p=2): el más parecido ~84% (antes ~71%).
- Perfil de equipo y Equipos similares ahora plegables con flechita (como top equipos).
- uvicorn access_log=False + log_level warning: en HF cada request a stdout abría el
drawer de logs; ahora solo se loguean errores.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

src/racing_reports/team_vars.py CHANGED
@@ -289,8 +289,8 @@ def similar_teams(league: str, season: str, team: str, variables: list[str],
289
  dist = dist[~is_self.values]
290
  order = dist.sort_values().head(top).index
291
  # Similitud 0-100% calibrada: d̄ = distancia promedio entre TODOS los pares del pool
292
- # (referencia del 50%). sim = 100·2^(-d/d̄) → cada d̄ de distancia, se divide por dos.
293
- # 100% en d=0; 84% en 0.25·d̄; 71% en 0.5·d̄; 50% en d̄; 25% en 2·d̄.
294
  from scipy.spatial.distance import pdist
295
  Z = zmat.to_numpy()
296
  if len(Z) > 1500: # cota para pools enormes (scope "todas")
@@ -299,7 +299,7 @@ def similar_teams(league: str, season: str, team: str, variables: list[str],
299
  dbar = float(pdist(Z).mean()) if len(Z) > 1 else 0.0
300
 
301
  def _sim(d):
302
- return round(100.0 * (2.0 ** (-float(d) / dbar)), 1) if dbar > 1e-9 else (100.0 if d < 1e-9 else 0.0)
303
 
304
  teams = [{
305
  "equipo": str(pool.loc[i, "equipo"]),
 
289
  dist = dist[~is_self.values]
290
  order = dist.sort_values().head(top).index
291
  # Similitud 0-100% calibrada: d̄ = distancia promedio entre TODOS los pares del pool
292
+ # (referencia del 50%). sim = 100·2^(-(d/d̄)²) (p=2: aplana cerca de 0, cae fuerte lejos).
293
+ # 100% en d=0; 95.8% en 0.25·d̄; 84.1% en 0.5·d̄; 50% en d̄; 6.25% en 2·d̄.
294
  from scipy.spatial.distance import pdist
295
  Z = zmat.to_numpy()
296
  if len(Z) > 1500: # cota para pools enormes (scope "todas")
 
299
  dbar = float(pdist(Z).mean()) if len(Z) > 1 else 0.0
300
 
301
  def _sim(d):
302
+ return round(100.0 * (2.0 ** (-(float(d) / dbar) ** 2)), 1) if dbar > 1e-9 else (100.0 if d < 1e-9 else 0.0)
303
 
304
  teams = [{
305
  "equipo": str(pool.loc[i, "equipo"]),
src/racing_reports/web/app.py CHANGED
@@ -22,6 +22,7 @@ from racing_reports.api.routes_meta import router as api_meta_router
22
  from racing_reports.api.routes_reports import router as api_reports_router
23
  from racing_reports.api.routes_team_vars import router as api_team_vars_router
24
  from racing_reports.api.routes_ejes import router as api_ejes_router
 
25
  from racing_reports.api.routes_lowblock import router as api_lowblock_router
26
  from racing_reports.config import DEFAULT_SETTINGS
27
  from racing_reports.datastore import DataStore
@@ -50,6 +51,7 @@ app.include_router(api_reports_router)
50
  app.include_router(api_jobs_router)
51
  app.include_router(api_team_vars_router)
52
  app.include_router(api_ejes_router)
 
53
  app.include_router(api_lowblock_router)
54
 
55
  # Estado en memoria (single container en HF Space). Re-export para retrocompat.
@@ -316,6 +318,10 @@ def main() -> None:
316
  reload=False,
317
  proxy_headers=True,
318
  forwarded_allow_ips="*",
 
 
 
 
319
  )
320
 
321
 
 
22
  from racing_reports.api.routes_reports import router as api_reports_router
23
  from racing_reports.api.routes_team_vars import router as api_team_vars_router
24
  from racing_reports.api.routes_ejes import router as api_ejes_router
25
+ from racing_reports.api.routes_goalkicks import router as api_goalkicks_router
26
  from racing_reports.api.routes_lowblock import router as api_lowblock_router
27
  from racing_reports.config import DEFAULT_SETTINGS
28
  from racing_reports.datastore import DataStore
 
51
  app.include_router(api_jobs_router)
52
  app.include_router(api_team_vars_router)
53
  app.include_router(api_ejes_router)
54
+ app.include_router(api_goalkicks_router)
55
  app.include_router(api_lowblock_router)
56
 
57
  # Estado en memoria (single container en HF Space). Re-export para retrocompat.
 
318
  reload=False,
319
  proxy_headers=True,
320
  forwarded_allow_ips="*",
321
+ # Sin access-log por request: en HF Spaces cada línea a stdout abre el drawer
322
+ # de logs. Los errores igual se ven (log_level warning).
323
+ access_log=False,
324
+ log_level="warning",
325
  )
326
 
327
 
src/racing_reports/web/templates/home.html CHANGED
@@ -441,9 +441,12 @@
441
  <p class="text-xs text-red-300 mt-2" x-show="tvpError" x-text="tvpError"></p>
442
 
443
  <!-- gráfico de z-scores (barras centradas en 0, banda ±1 sombreada) -->
444
- <div x-show="tvpResult && tvpResult.items && tvpResult.items.length" class="mt-5">
445
- <div class="text-sm font-medium mb-1"><span x-text="tvpResult?.equipo"></span>
 
 
446
  <span class="text-xs text-gray-400" x-text="'('+tvpResult?.competencia+' · '+tvpResult?.temporada+') · vs '+tvpResult?.scope+' · '+tvpResult?.pool_size+' equipos'"></span></div>
 
447
  <div class="space-y-1.5">
448
  <template x-for="it in (tvpResult?.items || [])" :key="it.variable">
449
  <div class="flex items-center gap-2 text-xs">
@@ -489,6 +492,7 @@
489
  <img :src="tvpResult?.radar_b64 ? 'data:image/png;base64,'+tvpResult.radar_b64 : ''" alt="Radar" class="rounded-lg max-w-md w-full"/>
490
  </div>
491
  <p x-show="tvpResult && tvpResult.items && tvpResult.items.length && !tvpResult.radar_b64" class="text-xs text-gray-500 mt-2">El radar necesita al menos 3 variables.</p>
 
492
  </div>
493
  </section>
494
 
@@ -530,9 +534,11 @@
530
  </div>
531
  <p class="text-xs text-gray-500 mt-1" x-show="tvsPool.length">Con pool armado, "Buscar en" queda ignorado: los similares se buscan en esas ligas-temporadas.</p>
532
  <p class="text-xs text-red-300 mt-2" x-show="tvsError" x-text="tvsError"></p>
533
- <div x-show="tvsResult && tvsResult.teams" class="mt-3">
534
- <div class="text-xs text-gray-400 mb-1">Más parecidos a <span class="text-gray-200" x-text="tvsResult?.equipo"></span> · vs <span x-text="tvsResult?.scope"></span> (<span x-text="tvsResult?.pool_size"></span> equipos)</div>
535
- <table class="w-full text-xs">
 
 
536
  <thead><tr class="text-gray-400 text-left"><th class="py-1">#</th><th>Equipo</th><th>Liga</th><th>Temp.</th><th class="text-right">Similitud</th></tr></thead>
537
  <tbody>
538
  <template x-for="(t, i) in (tvsResult?.teams || [])" :key="t.competencia+t.temporada+t.equipo">
 
441
  <p class="text-xs text-red-300 mt-2" x-show="tvpError" x-text="tvpError"></p>
442
 
443
  <!-- gráfico de z-scores (barras centradas en 0, banda ±1 sombreada) -->
444
+ <div x-show="tvpResult && tvpResult.items && tvpResult.items.length" class="mt-5" x-data="{ pOpen: true }">
445
+ <div class="text-sm font-medium mb-1 cursor-pointer select-none flex items-center gap-2" @click="pOpen=!pOpen">
446
+ <span class="text-xs transition-transform" :class="pOpen ? 'rotate-90' : ''">▶</span>
447
+ <span x-text="tvpResult?.equipo"></span>
448
  <span class="text-xs text-gray-400" x-text="'('+tvpResult?.competencia+' · '+tvpResult?.temporada+') · vs '+tvpResult?.scope+' · '+tvpResult?.pool_size+' equipos'"></span></div>
449
+ <div x-show="pOpen">
450
  <div class="space-y-1.5">
451
  <template x-for="it in (tvpResult?.items || [])" :key="it.variable">
452
  <div class="flex items-center gap-2 text-xs">
 
492
  <img :src="tvpResult?.radar_b64 ? 'data:image/png;base64,'+tvpResult.radar_b64 : ''" alt="Radar" class="rounded-lg max-w-md w-full"/>
493
  </div>
494
  <p x-show="tvpResult && tvpResult.items && tvpResult.items.length && !tvpResult.radar_b64" class="text-xs text-gray-500 mt-2">El radar necesita al menos 3 variables.</p>
495
+ </div>
496
  </div>
497
  </section>
498
 
 
534
  </div>
535
  <p class="text-xs text-gray-500 mt-1" x-show="tvsPool.length">Con pool armado, "Buscar en" queda ignorado: los similares se buscan en esas ligas-temporadas.</p>
536
  <p class="text-xs text-red-300 mt-2" x-show="tvsError" x-text="tvsError"></p>
537
+ <div x-show="tvsResult && tvsResult.teams" class="mt-3" x-data="{ sOpen: true }">
538
+ <div class="text-xs text-gray-400 mb-1 cursor-pointer select-none flex items-center gap-2" @click="sOpen=!sOpen">
539
+ <span class="transition-transform" :class="sOpen ? 'rotate-90' : ''">▶</span>
540
+ Más parecidos a <span class="text-gray-200" x-text="tvsResult?.equipo"></span> · vs <span x-text="tvsResult?.scope"></span> (<span x-text="tvsResult?.pool_size"></span> equipos)</div>
541
+ <table class="w-full text-xs" x-show="sOpen">
542
  <thead><tr class="text-gray-400 text-left"><th class="py-1">#</th><th>Equipo</th><th>Liga</th><th>Temp.</th><th class="text-right">Similitud</th></tr></thead>
543
  <tbody>
544
  <template x-for="(t, i) in (tvsResult?.teams || [])" :key="t.competencia+t.temporada+t.equipo">