Ir al contenido

Scouting

Scouting registra jugadores externos que el club observa (rivales, objetivos de transferencia, libres). Importa los mismos PDFs Wyscout / SIX que Rendimiento, pero sin filtrar al plantel propio: cualquier jugador del informe queda como external_player con radar de ejes, shortlist, status (alta / seguir / contactar / descartado) y notas.

  • Problema que resuelve: el área deportiva ve PDFs de partidos de rivales o ligas extranjeras y necesita registrar a un jugador específico sin armar otra base aparte. CÉNIT lo guarda en la misma DB que el plantel propio, con perfil radar comparable.
  • Casos de uso típicos [NEEDS_USER: CASE_STUDY caso real]:
    • Mercado: cargar 5 PDFs de jugadores objetivo y comparar perfiles.
    • Rivales: registrar al 10 del rival con su perfil técnico para cruzar con el análisis táctico.
    • Shortlist: marcar 8-10 jugadores prioritarios, cada uno con status de scouting y nota libre.
  • Planes que lo incluyen: esencial, pro y enterprise. El parser IA está disponible para todos los planes (mismo backend que Rendimiento).
  • Diferenciador: reutilización del parser PDF de partidos — ningún competidor parsea informes Wyscout/SIX directamente para scouting con merge de múltiples observaciones del mismo jugador.

Roles con acceso a /dashboard/scouting: hop, dir, rtp principalmente (la página exige sesión + organization_id válida; todos los staff autenticados pueden leer, las mutaciones usan requireSession). El catálogo de rivales se comparte con Rendimiento.

Tabs del módulo:

  1. Lista — grilla de jugadores externos con foto, equipo, posición, edad, status, shortlist toggle.
  2. Importar PDF rival — sube informe Wyscout/SIX y deja al scout marcar de la grilla extraída cuáles guardar.
  3. Comparativa — radar de ejes para cruzar jugador externo vs jugador propio (consume ownPlayers con axes calculados a partir de tactical_uploads).

Flujo principal — importar y guardar externos:

  1. parseRivalPdfAction(pdfText) corre el mismo parser que Rendimiento, pero sin filtro de plantel (squadNames = []).
  2. La grilla muestra todos los jugadores del PDF.
  3. El scout edita nombre / equipo / posición / edad por jugador y marca cuáles guardar.
  4. upsertExternalPlayersAction(players[]) corre el merge:
    • Match por nombre normalizado (normName: lowercase, sin acentos, espacios colapsados).
    • Si existe: axes se promedian ponderando por match_count (mergeAxes), suma 1 al match_count, actualiza last_match_date, source. Team/position/age se pisan solo si vienen no-vacíos.
    • Si no existe: insert nuevo con match_count = 1.

Flujos secundarios:

  • Toggle shortlist desde la grilla (toggleShortlistAction): si pasa a true también setea status = 'follow'.
  • Cambiar status (updateStatusAction): high / follow / contact / discarded.
  • Editar nota libre (updateNoteAction).

[NEEDS_USER: SCREENSHOT de la comparativa radar externo vs propio]

  • Comparte competitions y rival_teams con Rendimiento (mismo catálogo, sin duplicación).
  • ANTHROPIC_API_KEY requerida en Workers Secrets para el parser.
  • “Cargué el mismo jugador dos veces y los ejes cambiaron” — esperado. mergeAxes promedia ponderando por match_count, así que cada partido adicional suaviza el perfil.
  • “Aparece un jugador con nombre raro” — el parser no filtra por plantel propio. Editar el nombre en la grilla antes de guardar.
  • “El radar del externo está en 0” — el PDF no tenía minutos suficientes o el parser falló en extraer stats. Reintentar con otro informe del mismo jugador para acumular.

Player surface: N/A. Scouting es módulo interno de staff. Los jugadores externos nunca tienen auth_user_id ni acceso al sistema.

  • axes por jugador externo: promedio ponderado por match_count. Fórmula: axes[k] = round((existing[k] * count + incoming[k]) / (count + 1)).
  • Status implícito: shortlisted=truestatus='follow'. Desmarcar shortlist nullea el status. Las otras transiciones (high, contact, discarded) son libres.
  • PDF Wyscout / SIX — mismo parser que Rendimiento (modelo claude-haiku-4-5-20251001, prompt en app/dashboard/rendimiento/ actions.ts). En Scouting se invoca sin squadNames para extraer todos los jugadores del informe.
  • Alta manual: hoy no hay UI de alta manual de externos sin PDF. Si se necesita, requiere modificar _components/scouting-shell.tsx.
  • Tabla: external_players (id, org_id, name, team, position, age, nationality, axes JSONB, match_count, last_match_date, source, shortlisted, status, note, updated_at). RLS por org_id.
  • Tabla relacionada: transfer_reports — informes de transferencia ligados a un external_player (UI en perfil de jugador, no en Scouting).
  • Rendimiento — comparte parser, catálogo de competiciones y rivales, y la función toAxes.
  • PlantelgetOwnPlayersWithAxes calcula el radar del plantel propio promediando tactical_uploads.axes por jugador para la comparativa.
  • Anthropic — modelo Haiku para parseo de PDFs.
  • Sin integración con APIs de scouting (Wyscout API, Transfermarkt API): todo entra por PDF. Las URLs de scrapers tipo Sofascore / Transfermarkt están bloqueadas por CHECK constraint en players.photo_url (migration-082) — convención del proyecto.
  • No hay alta manual de externos: requiere subir un PDF aunque sea con un solo jugador.
  • transfer_reports no se ve desde Scouting — vive en el perfil del externo en Plantel. Falta UI integrada.
  • Sin export del shortlistexcel_export prometido pero no implementado (gap comercial transversal).