← Blog
Golfzon League System

Golfzon League System

pythonstreamlitocrside-projectgolf

A few friends and I play golf regularly on Golfzon simulators through a local Avid Caddy spot. We wanted to run a proper league. teams, weekly standings, cumulative leaderboards. but Golfzon’s platform doesn’t offer any of that. No API, no data export, no league management. You get a scorecard on screen when you’re done, and that’s it.

So I built one.

The Problem

Golfzon simulators are great for playing, terrible for organizing. Here’s what we were dealing with:

  • No API access. Golfzon doesn’t expose player data programmatically. Scores live on their platform and you can’t get them out.
  • No league features. The platform tracks individual rounds but has zero support for teams, weekly matchups, or standings.
  • Manual everything. Someone was keeping scores in a shared spreadsheet, manually typing numbers off phone screenshots. It was slow, error-prone, and nobody wanted to do it.

We needed a system that could intake scores quickly, calculate net scores with handicaps, and track team standings across weeks.

The Solution

I built a Python + Streamlit web app that does three things:

  1. OCR scorecard extraction. Upload a photo of the Golfzon scorecard screen, and it pulls out player names, gross scores, and handicaps automatically.
  2. Team & league management. Create leagues, form teams, assign players.
  3. Leaderboards. Weekly and cumulative standings, with team scores based on each team’s top 2 net scores.

Architecture

graph TD
    A[Phone Camera] -->|Screenshot| B[Streamlit UI]
    B --> C[Image Preprocessing]
    C -->|Grayscale + CLAHE| D[Tesseract OCR / Google Cloud Vision]
    D --> E[Regex Parser]
    E -->|Player, Gross, Handicap| F[Net Score Calculator]
    F --> G[SQLite via SQLAlchemy]
    G --> H[Leaderboard Engine]
    H --> B
    G --> I[CSV Export]

    subgraph Data Layer
        G
        J[Alembic Migrations]
    end

    subgraph Processing Pipeline
        C
        D
        E
        F
    end

How Scoring Works

The scoring system mirrors what our league agreed on:

  • Each team’s weekly score = sum of the top 2 net scores from its members
  • Net score = gross score minus strokes given (handicap ÷ 2 for 9-hole rounds)
  • Lowest team score wins the week
  • Cumulative standings sum all weekly team scores

This means a team of 4 can absorb a bad round. only your best two count. It keeps things competitive without punishing one rough week.

The OCR Pipeline

This was the most interesting part to build. Golfzon scorecards have a consistent layout, but OCR on phone photos of a simulator screen is messy. The pipeline:

  1. Preprocessing. Convert to grayscale, apply CLAHE (contrast-limited adaptive histogram equalization) to handle uneven screen lighting
  2. OCR. Tesseract for local processing, with Google Cloud Vision as a fallback for tricky images
  3. Regex parsing. Extract the pattern PlayerName GrossScore(+/-X) Handicap from raw OCR text
  4. Cleanup. Strip the “G” prefix Golfzon adds to names, validate score ranges, handle duplicates and misreads

It’s not perfect. maybe 90% accurate on clean photos. but it beats manual entry every time. The UI lets you review and correct before saving.

Screenshots

Here’s the actual app in action:

Submit Scores

Upload a scorecard screenshot, and the OCR extracts player data. You can also do manual or bulk entry.

Submit Scores

Team Management

Create teams, assign players, manage rosters within each league.

Team Management

Leaderboard

Weekly and cumulative standings with team scores calculated from top 2 net scores per team.

Leaderboard

Tech Stack

  • Python 3.10+ with Poetry for dependency management
  • Streamlit for the UI. fast to build, easy to deploy, good enough for a friends league
  • pytesseract + OpenCV for OCR and image preprocessing
  • Google Cloud Vision as a secondary OCR engine
  • SQLAlchemy + SQLite for persistence
  • Alembic for database migrations
  • Docker for deployment

I chose Streamlit over a full React frontend because this is a tool for ~12 people, and I wanted to ship in a weekend. Streamlit let me build the entire UI in Python with minimal frontend work.

What I Learned

OCR is harder than it looks. Phone photos of screens have glare, uneven lighting, and resolution issues. The CLAHE preprocessing step was the single biggest improvement. without it, Tesseract choked on about half the images.

Regex parsing is fragile but fast. The Golfzon scorecard format is consistent enough that regex works, but edge cases (names with numbers, OCR misreading “8” as “B”) required a lot of validation logic.

“Good enough” ships. This isn’t production-grade software. It’s a tool for friends. The review-before-save step covers the gap between OCR accuracy and perfection. Sometimes the pragmatic choice is to let humans fix the last 10%.

Results

We’ve been running our league on this for several months now. Score submission went from a 15-minute spreadsheet ordeal to a 30-second photo upload. Nobody argues about standings anymore because the leaderboard updates automatically. And I got to write OCR code, which is just fun.

The code is open source: github.com/lcrostarosa/golfzon-net-handicap-scorecard