feat: Add new API endpoints and HTML pages for ML model management

- Implemented HTML pages for datasets, models, training, testing, and results.
- Created API endpoints for managing repositories, results, tests, and training sessions.
- Added functionality for streaming training progress via Server-Sent Events (SSE).
- Introduced a Dockerfile for the ML runner with necessary dependencies.
- Developed an SDK for user code execution within the runner container.
- Enhanced CSS styles for improved UI layout and navigation.
- Established a layout template for consistent HTML structure across pages.
- Added JavaScript for dynamic interactions on the models page.
- Implemented WebSocket handling for real-time communication with kiosk devices and controllers.
- Implemented model registration and management API at /api/models
- Added Gitea proxy API for repository interactions at /api/repos
- Created results API for listing and comparing training results at /api/results
- Developed training management API for enqueueing and retrieving training jobs at /api/trainings
- Introduced SSE endpoint for live training progress updates
- Added HTML pages for models, datasets, and training management
- Created a Dockerfile for the ML runner with necessary dependencies
- Developed SDK for user code execution within the runner container
- Enhanced CSS styles for improved UI/UX
- Implemented WebSocket communication for real-time device and controller interactions in the kiosk system
This commit is contained in:
Giuseppe Raffa
2026-04-28 09:24:38 +02:00
parent ee478e52ef
commit 0ce879aa44
81 changed files with 7491 additions and 746 deletions

View File

@@ -0,0 +1,124 @@
-- ============================================================
-- Rulesets schema (DB: rules)
-- ------------------------------------------------------------
-- Un ruleset e' una collezione versionata di "items" (paths
-- Signal K per i logs, codici openmeteo per i forecasts, ...).
-- Modello:
-- * 5 tipi fissi: logs | forecast_current | forecast_hourly
-- | marine_current | marine_hourly
-- * Un solo ruleset puo' essere "active" per ciascun tipo.
-- * Le versioni sono triple di interi 1..100 (major.build.patch).
-- * Gli items sono JSONB per massima flessibilita'.
-- * Ogni item ha un "ref" stabile scelto dall'utente: e' la
-- chiave logica che garantisce continuita' su InfluxDB anche
-- se il path del sensore cambia.
-- * Le deployments tracciano quale ruleset-version e' stato
-- pushato ad ogni sensore.
-- ============================================================
CREATE EXTENSION IF NOT EXISTS "pgcrypto";
-- ─────────────────────────────────────────────────────────────
-- RULESETS
-- ─────────────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS rulesets (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
type TEXT NOT NULL
CHECK (type IN ('logs','forecast_current','forecast_hourly','marine_current','marine_hourly')),
version_major SMALLINT NOT NULL DEFAULT 1 CHECK (version_major BETWEEN 1 AND 100),
version_build SMALLINT NOT NULL DEFAULT 0 CHECK (version_build BETWEEN 0 AND 100),
version_patch SMALLINT NOT NULL DEFAULT 0 CHECK (version_patch BETWEEN 0 AND 100),
description TEXT NOT NULL DEFAULT '',
tags TEXT[] NOT NULL DEFAULT '{}',
-- items: [{ ref, path, enabled, meta: {...} }, ...]
-- ref: identificatore logico stabile (chiave su Influx)
-- path: SK path (logs) | codice openmeteo (forecast/marine)
-- meta: { name, unit, measurement, sk_path, group_name, category, ... }
items JSONB NOT NULL DEFAULT '[]'::jsonb,
active BOOLEAN NOT NULL DEFAULT false,
archived BOOLEAN NOT NULL DEFAULT false,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
UNIQUE (type, version_major, version_build, version_patch)
);
-- solo UN ruleset active per tipo (archiviati esclusi)
CREATE UNIQUE INDEX IF NOT EXISTS rulesets_one_active_per_type
ON rulesets (type)
WHERE active = true AND archived = false;
CREATE INDEX IF NOT EXISTS rulesets_type_idx ON rulesets (type);
CREATE INDEX IF NOT EXISTS rulesets_active_idx ON rulesets (type) WHERE active = true;
CREATE INDEX IF NOT EXISTS rulesets_archived_idx ON rulesets (archived);
CREATE INDEX IF NOT EXISTS rulesets_items_gin_idx ON rulesets USING GIN (items);
-- Validazione items: array di oggetti con almeno ref+path
CREATE OR REPLACE FUNCTION rulesets_validate_items() RETURNS trigger AS $$
DECLARE
refs TEXT[];
BEGIN
IF jsonb_typeof(NEW.items) <> 'array' THEN
RAISE EXCEPTION 'items must be a JSON array';
END IF;
-- tutti gli item devono avere ref non vuoto e path (anche vuoto ammesso)
IF EXISTS (
SELECT 1 FROM jsonb_array_elements(NEW.items) it
WHERE jsonb_typeof(it) <> 'object'
OR NULLIF(it->>'ref','') IS NULL
) THEN
RAISE EXCEPTION 'every item must be an object with a non-empty "ref"';
END IF;
-- unicita' ref all'interno dello stesso ruleset
SELECT array_agg(it->>'ref') INTO refs
FROM jsonb_array_elements(NEW.items) it;
IF (SELECT count(DISTINCT x) FROM unnest(refs) x) <> COALESCE(array_length(refs,1),0) THEN
RAISE EXCEPTION 'item refs must be unique within the ruleset';
END IF;
NEW.updated_at := NOW();
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
DROP TRIGGER IF EXISTS rulesets_validate_trigger ON rulesets;
CREATE TRIGGER rulesets_validate_trigger
BEFORE INSERT OR UPDATE ON rulesets
FOR EACH ROW EXECUTE FUNCTION rulesets_validate_items();
-- ─────────────────────────────────────────────────────────────
-- DEPLOYMENTS
-- Traccia quale ruleset-version e' stato pushato ad ogni
-- sensore (per tipo). Un solo ruleset per (sensor,type).
-- ─────────────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS ruleset_deployments (
sensor_name TEXT NOT NULL,
type TEXT NOT NULL,
ruleset_id UUID NOT NULL REFERENCES rulesets(id) ON DELETE CASCADE,
deployed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
acked_at TIMESTAMPTZ,
PRIMARY KEY (sensor_name, type)
);
CREATE INDEX IF NOT EXISTS ruleset_deployments_ruleset_idx
ON ruleset_deployments (ruleset_id);
-- ─────────────────────────────────────────────────────────────
-- AUDIT LOG (opzionale ma utile)
-- ─────────────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS ruleset_changes (
id BIGSERIAL PRIMARY KEY,
ruleset_id UUID,
type TEXT,
action TEXT NOT NULL, -- created | updated | activated | archived | deleted | deployed
user_id TEXT,
payload JSONB,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS ruleset_changes_ruleset_idx ON ruleset_changes (ruleset_id);
CREATE INDEX IF NOT EXISTS ruleset_changes_created_idx ON ruleset_changes (created_at DESC);