Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .github/workflows/docker-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,20 @@ jobs:
run: |
cd server/installer
cp .env.example .env
mkdir -p .ci-slicks/src/slicks
cat > .ci-slicks/pyproject.toml <<'EOF'
[project]
name = "slicks"
version = "0.0.0"
[build-system]
requires = ["setuptools>=68"]
build-backend = "setuptools.build_meta"
[tool.setuptools.packages.find]
where = ["src"]
EOF
touch .ci-slicks/README.md
echo '__version__ = "0.0.0"' > .ci-slicks/src/slicks/__init__.py
echo "SLICKS_HOST_PATH=${PWD}/.ci-slicks" >> .env

- name: Pull pre-built images
run: |
Expand Down
16 changes: 14 additions & 2 deletions server/installer/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@
DBC_FILE_PATH=example.dbc

# ------------------------------------------------------------
# File uploader — team DBCs from GitHub (optional)
# Team DBC from GitHub (used by file-uploader and data-downloader)
# ------------------------------------------------------------
# Fine-grained PAT or classic PAT with contents:read on Western-Formula-Racing/DBC
GITHUB_DBC_TOKEN=
# GITHUB_DBC_REPO=Western-Formula-Racing/DBC
# GITHUB_DBC_BRANCH=main
# Data-downloader auto-selects the most recently committed .dbc in the repo.
# Set GITHUB_DBC_PATH to pin a specific file instead (e.g. WFR26.dbc).
# GITHUB_DBC_PATH=

# Optional limits for .zip uploads (file-uploader); defaults are generous for team use
# UPLOAD_ZIP_MAX_ARCHIVE_BYTES=2147483648
Expand Down Expand Up @@ -86,10 +89,19 @@ SENSOR_LOOKBACK_DAYS=365
SCAN_INTERVAL_SECONDS=3600

VITE_API_BASE_URL=http://localhost:8000
ALLOWED_ORIGINS=http://localhost:3000,http://localhost:5173
ALLOWED_ORIGINS=http://localhost:3000,http://localhost:5173,https://daq.westernformularacing.org

# End Data Downloader configuration

# ------------------------------------------------------------
# Health monitor
# ------------------------------------------------------------
# All defaults match the standard docker-compose stack; only override if needed.
# HEALTH_MONITOR_INTERVAL_SECONDS=60
# HEALTH_MONITOR_TIMESCALEDB_CONTAINER=timescaledb
# HEALTH_MONITOR_SCANNER_CONTAINER=data-downloader-scanner
# HEALTH_MONITOR_SCANNER_API_URL=http://data-downloader-api:8000

# ------------------------------------------------------------
# AI Code Generation — MiniMax (Anthropic-compatible SDK)
# ------------------------------------------------------------
Expand Down
20 changes: 0 additions & 20 deletions server/installer/data-downloader/.env.example

This file was deleted.

31 changes: 31 additions & 0 deletions server/installer/data-downloader/backend/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from pydantic import BaseModel

from backend.config import get_settings
from backend.dbc_utils import group_sensors_by_message, load_dbc_db, refresh_dbc
from backend.services import DataDownloaderService


Expand Down Expand Up @@ -124,6 +125,36 @@ def list_sensors(season: str | None = None) -> dict:
return service.get_sensors(season=season)


@app.get("/api/sensors/grouped")
def list_sensors_grouped(season: str | None = None) -> dict:
"""
Return sensors grouped by their DBC CAN message and transmitter node.

Each entry in ``messages`` contains only signals that actually have data in
TimescaleDB (left-join: DB is truth, DBC is the categorisation guide).
Sensors with no matching DBC entry appear in ``ungrouped``.

Falls back gracefully to ``{"messages": [], "ungrouped": all_sensors}``
when no DBC is configured.
"""
sensor_payload = service.get_sensors(season=season)
sensor_names: list[str] = sensor_payload.get("sensors", [])
db, source = load_dbc_db(settings)
grouped = group_sensors_by_message(sensor_names, db)
return {
"updated_at": sensor_payload.get("updated_at"),
"dbc_source": source,
**grouped,
}


@app.post("/api/dbc/refresh")
def dbc_refresh() -> dict:
"""Force-reload the DBC from GitHub (or local file). Returns the new source."""
source = refresh_dbc(settings)
return {"status": "ok", "dbc_source": source}


@app.get("/api/scanner-status")
def scanner_status() -> dict:
return service.get_scanner_status()
Expand Down
19 changes: 19 additions & 0 deletions server/installer/data-downloader/backend/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,25 @@ class Settings(BaseModel):
default_factory=lambda: _parse_origins(os.getenv("ALLOWED_ORIGINS", "*"))
)

# DBC source — GitHub takes priority over local file
github_dbc_token: str = Field(
default_factory=lambda: os.getenv("GITHUB_DBC_TOKEN", "")
)
github_dbc_repo: str = Field(
default_factory=lambda: os.getenv("GITHUB_DBC_REPO", "Western-Formula-Racing/DBC")
)
github_dbc_branch: str = Field(
default_factory=lambda: os.getenv("GITHUB_DBC_BRANCH", "main")
)
# Specific file path within the repo, e.g. "WFR26.dbc"
github_dbc_path: str = Field(
default_factory=lambda: os.getenv("GITHUB_DBC_PATH", "")
)
# Fallback: local DBC file path
dbc_file_path: str | None = Field(
default_factory=lambda: os.getenv("DBC_FILE_PATH") or None
)


@lru_cache(maxsize=1)
def get_settings() -> Settings:
Expand Down
Loading
Loading