Skip to content

Repository files navigation

DSV Parser

Parser for DSV swim-meet files (.dsv5 through .dsv8, including .dsv8z) into a typed data schema. Usable as a Python library, as a CLI, and as a FastAPI service whose OpenAPI document exposes the same schema to services in other languages.

Format 6, 7 and 8 are covered in full, transcribed from the official specifications of the Deutscher Schwimm-Verband.


Tech Stack

Python Pydantic FastAPI Uvicorn OpenAPI uv Hatch Ruff mypy pytest pytest-cov pip-audit pre-commit GNU Make Docker GitHub Actions


Format Coverage

Format Valid State
8 from 01.08.2026 Complete, specification of 14.03.2026
7 01.01.2023 – 31.12.2026 Complete, specification of 31.08.2022
6 01.09.2015 – 31.07.2023 Complete, specification of 01.11.2015
5 until 31.12.2015 Derived from the Format 6 change log

All four types (Wettkampfdefinitions-, Vereinsmelde-, Vereinsergebnis- and Wettkampfergebnisliste) with every element and attribute. The packed .dsv8z variant is unwrapped transparently.

Checked against nine real EasyWk files in Format 6 and 7, about 26 000 elements, with no errors or warnings. Details in the format reference.


Quick Start

Prerequisites

Tool Version Needed for
uv — dependencies, every make target
Python 3.12 fetched by uv from .python-version
Docker + Compose — make up, make image
pre-commit — the formatting hook before a commit (optional)

Install

As a dependency of another project:

pip install dsv-parser            # library + CLI
pip install "dsv-parser[api]"     # plus the FastAPI service
uv add dsv-parser

Every GitHub Release is published to PyPI automatically, so the version there tracks the tags in this repository.

To work on the parser itself:

make install     # uv sync --extra api

The api extra adds FastAPI and uvicorn. The parser itself needs only Pydantic, so a library-only consumer can leave the extra out.

Run

make dev         # CLI help with every subcommand
make spec        # the element table as implemented
make serve       # FastAPI on :8000 with reload
make up          # the same in Docker
make ps          # container status and health
make logs        # follow the logs
make down        # stop and remove

Use it

from dsv_parser import parse_file

result = parse_file("2026-06-13-Berlin-Pr.DSV8")

print(result.document.meet.name, result.document.file_type)
for swim in result.document.individual_results:
    print(swim.place, swim.name, swim.time_millis)

for entry in result.diagnostics.entries:
    print(entry.render())
dsv-parser parse meet.DSV8 -o meet.json       # document as JSON
dsv-parser parse meet.DSV8 --summary          # header and element counts only
dsv-parser check meet.DSV8                    # validate, exit 1 on data loss
dsv-parser spec --json                        # element table as data

HTTP Surface

Method Path Purpose
POST /parse Parse a file and return the document
POST /check Validate a file without returning the document
GET /spec Element table and all code lists
GET /health Liveness and the supported format versions
GET /openapi.json The schema, for generated clients
curl -F file=@meet.DSV8 http://localhost:8000/parse
curl -F file=@meet.DSV8 http://localhost:8000/check

A readable upload always returns 200. Content problems come back as diagnostics rather than HTTP errors, since a partially readable file is a normal result. Check clean and diagnostics, not the status code.

The document schema in /openapi.json is the same Pydantic model the library returns. See the HTTP API.


Configuration

Variable Default Effect
LOG_LEVEL INFO Log level

There is nothing else. The service holds no state and can be mounted into another app with app.include_router(dsv_parser.api.router).


Design

The attribute layouts live as data in dsv_parser/spec/elements.py, one entry per element, with versions= and file_types= wherever the four format versions or the four list kinds differ. For example, WETTKAMPF has no best-list attribute in a Vereinsmeldeliste, which moves the two qualification attributes one position forward. In the table that is a single file_types= on one attribute.

flowchart LR
    A["bytes<br/>.dsv · .dsv8z"] -->|decoding| B["text"]
    B -->|lexer| C["element lines"]
    C -->|binder| D["element models"]
    D -->|parser| E["DsvDocument"]
    T[("spec table<br/>spec/elements.py")] -.->|layout| C
    T -.->|layout| D
    C -.-> G(["diagnostics"])
    D -.-> G
    E -.-> G
Loading

More in the architecture notes.


Common Commands

make help                     # every target
make test                     # unit tests, offline, no coverage gate
make test-it                  # full run incl. integration and coverage
make lint                     # ruff check
make format                   # ruff: import order and formatting
make format-check             # the same, read-only
make typecheck                # mypy
make audit                    # pip-audit
make image                    # build the runtime image
make clean                    # remove caches and the venv

Further Reading

  • Architecture — pipeline, element table, decisions
  • Format reference — the format and the version differences
  • Data schema — how the document is laid out
  • HTTP API — endpoints, payloads, client generation
  • CLI — subcommands, flags, exit codes
  • Extending — adding elements, versions and code lists

License

MIT — use it, change it, ship it, commercially or not.

About

Standalone parser for DSV swim-meet interchange files (DSV5–DSV8) into a typed data schema — usable as a library, a CLI and a FastAPI service.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages