Files
MyClub/DOCS/MANUAL_FACR_MODE.md
Tomas Dvorak dfc079288f hot fix #1
2026-01-26 08:13:18 +01:00

6.7 KiB
Raw Permalink Blame History

Manual FACR Mode (Manual Club Data)

This document describes the manual FACR mode that replaces the automatic FACR scraping when enabled. In manual mode, all club competitions, matches, and tables are stored in the local database and managed via the admin UI or import APIs.

1. Enabling manual mode

Environment variable:

CLUB_DATA_MODE=manual

Values:

  • auto default, uses external FACR integration.
  • manual uses local manual data for club info, matches, and tables.

The public endpoints and JSON shapes stay the same in both modes so the frontend and widgets work unchanged.

2. Data model (manual tables)

Manual mode uses dedicated tables:

  • ManualCompetition competition metadata for the primary club.
  • ManualMatch individual matches (fixtures + results).
  • ManualTableRow standings rows for each competition.

These are tied to the primary club configured in Settings (club ID + type), same as the automatic mode.

3. Admin UI

Manual data is managed under:

  • /admin/manual-data (requires admin role)

Features:

  • Create, update, delete ManualCompetition records.
  • Download CSV templates for matches and tables.
  • Import matches and tables from CSV or Excel (XLSX) files.
  • Import matches and tables from raw JSON payloads.

The page shows a live overview of all manual competitions for the primary club.

4. Backend admin API

All admin routes are under /api/v1/admin/manual (behind auth + CSRF + admin role).

4.1 Competitions CRUD

  • GET /api/v1/admin/manual/competitions
  • POST /api/v1/admin/manual/competitions
  • PUT /api/v1/admin/manual/competitions/:id
  • DELETE /api/v1/admin/manual/competitions/:id

Payload example:

{
  "code": "A1A",
  "name": "SATUM 5. liga mužů",
  "external_id": "<competition-uuid>",
  "matches_link": "https://www.fotbal.cz/souteze/turnaje/hlavni/...",
  "table_link": "https://www.fotbal.cz/souteze/turnaje/table/...",
  "team_count": "14"
}

4.2 CSV/XLSX templates

  • GET /api/v1/admin/manual/matches/template
  • GET /api/v1/admin/manual/tables/template

These return simple CSV header rows you can open in Excel / Google Sheets and then export back to CSV or XLSX.

4.3 File imports (CSV or XLSX)

Both endpoints accept multipart/form-data with a file field containing either:

  • .csv text file, or
  • .xlsx Excel workbook (first sheet is read).

Matches import

  • POST /api/v1/admin/manual/matches/import
  • Form field: file

Expected headers (columns):

  • competition_code
  • competition_external_id
  • round
  • is_home
  • opponent_name
  • opponent_club_link
  • external_match_id
  • kickoff_date (YYYY-MM-DD)
  • kickoff_time (HH:MM)
  • score_fulltime
  • score_halftime
  • match_link
  • venue
  • note

Behavior:

  • Competition is resolved by competition_external_id (if present) or competition_code.
  • opponent_club_link is parsed for a UUID and stored as OpponentExternalID.
  • external_match_id or a UUID from match_link is required and used as the match key.
  • If a match with the same (competition_id, external_match_id) exists, it is updated, otherwise created.

Response JSON:

{
  "imported": 10,
  "updated": 5,
  "errors": [
    "row 3: competition not found for code='A1A' external_id=''"
  ]
}

Tables import

  • POST /api/v1/admin/manual/tables/import
  • Form field: file

Expected headers:

  • competition_code
  • competition_external_id
  • rank
  • team_name
  • team_club_link
  • played
  • wins
  • draws
  • losses
  • score
  • points

Behavior:

  • Competition resolution same as matches.
  • team_club_link is parsed for a UUID and stored as ExternalTeamID.
  • For each competition, existing rows are deleted once on the first row, then new ManualTableRow records are inserted.

Response JSON:

{
  "imported": 42,
  "errors": [
    "row 5: competition not found for code='B3A' external_id=''"
  ]
}

5. JSON import endpoints

These are useful for syncing from external systems or scripts.

5.1 Matches JSON import

  • POST /api/v1/admin/manual/matches/import-json

Body shape:

{
  "items": [
    {
      "competition_code": "A1A",
      "competition_external_id": "<competition-uuid>",
      "round": "2. kolo",
      "is_home": "home",
      "opponent_name": "FC Opponent",
      "opponent_club_link": "https://www.fotbal.cz/kluby/<uuid>",
      "external_match_id": "<match-uuid>",
      "kickoff_date": "2025-03-15",
      "kickoff_time": "17:00",
      "kickoff": "", // optional RFC3339 alternative
      "score_fulltime": "2:1",
      "score_halftime": "1:0",
      "match_link": "https://is.fotbal.cz/public/zapasy/<uuid>",
      "venue": "Kravaře - tráva",
      "note": "Dohrávka"
    }
  ]
}

Semantics:

  • Competition resolution, opponent UUID parsing, and match keying behave exactly like the CSV/XLSX import.
  • kickoff (RFC3339) wins over kickoff_date + kickoff_time if supplied.
  • Existing matches are updated; new ones are inserted.

5.2 Tables JSON import

  • POST /api/v1/admin/manual/tables/import-json

Body shape:

{
  "items": [
    {
      "competition_code": "A1A",
      "competition_external_id": "<competition-uuid>",
      "rank": "1.",
      "team_name": "FC Example",
      "team_club_link": "https://www.fotbal.cz/kluby/<uuid>",
      "played": "13",
      "wins": "9",
      "draws": "2",
      "losses": "2",
      "score": "45:17",
      "points": "29"
    }
  ]
}

Semantics:

  • Competition and team UUIDs resolved the same as in file imports.
  • For each competition, existing rows are cleared once, then replaced by the new items list.

6. How public data is built

When CLUB_DATA_MODE=manual:

  • buildManualClubPayload constructs a FACR-like ClubInfo JSON using ManualCompetition, ManualMatch, and ManualTableRow.
  • The prefetch service writes:
    • facr_club_info.json
    • facr_tables.json
    • matches.json
  • Frontend pages (/matches, /standings, widgets, scoreboard, etc.) consume these JSON files in the same shape as automatic mode.

7. Logos and team matching

Logo resolution in manual mode uses the existing chain:

  1. Logo API / overrides and local cache.
  2. Manual club overrides.
  3. FACR placeholder URLs based on external IDs.

Because opponent and team rows store external UUIDs (from opponent_club_link and team_club_link), manual data can still benefit from logo lookups and alias matching.

8. Summary

  • Toggle CLUB_DATA_MODE to switch between automatic and manual data without changing the frontend.
  • Use /admin/manual-data to manage competitions and import data.
  • Import from CSV, XLSX, or JSON using the endpoints above.
  • Manual mode is designed to mirror automatic FACR data structures so all existing widgets and pages keep working.