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:
33
ml/templates/_layout.html
Normal file
33
ml/templates/_layout.html
Normal file
@@ -0,0 +1,33 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="it">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>ML — {% block title %}{{ page|capitalize }}{% endblock %}</title>
|
||||
<link href="/static/styles/style.css" rel="stylesheet">
|
||||
<link href="/static/styles/ml.css" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1>Modelli ML</h1>
|
||||
<nav class="ml-nav">
|
||||
<a href="/datasets" class="{% if page=='datasets' %}active{% endif %}">Datasets</a>
|
||||
<a href="/models" class="{% if page=='models' %}active{% endif %}">Modelli</a>
|
||||
<a href="/train" class="{% if page=='train' %}active{% endif %}">Train</a>
|
||||
<a href="/test" class="{% if page=='test' %}active{% endif %}">Test</a>
|
||||
<a href="/results" class="{% if page=='results' %}active{% endif %}">Results</a>
|
||||
</nav>
|
||||
<div class="profile">
|
||||
<p id="username">{{ user.username }}</p>
|
||||
<button id="logout-btn">Logout</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
|
||||
<script src="/static/js/common.js"></script>
|
||||
{% block scripts %}{% endblock %}
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,39 @@
|
||||
{% extends "_layout.html" %}
|
||||
{% block title %}Datasets{% endblock %}
|
||||
{% block content %}
|
||||
<div class="page-head">
|
||||
<h2>Datasets</h2>
|
||||
<button class="prominent" id="btn-upload">+ Carica CSV</button>
|
||||
</div>
|
||||
|
||||
<div id="datasets-list" class="list"></div>
|
||||
|
||||
<dialog id="upload-dlg">
|
||||
<form id="upload-form" method="dialog">
|
||||
<h3>Carica dataset</h3>
|
||||
<label>Nome<input type="text" name="nome" required></label>
|
||||
<label>Tipo
|
||||
<select name="dataset_type">
|
||||
<option value="custom">custom</option>
|
||||
<option value="imported">imported</option>
|
||||
</select>
|
||||
</label>
|
||||
<label>Formato
|
||||
<select name="format">
|
||||
<option value="csv">csv</option>
|
||||
<option value="json">json</option>
|
||||
</select>
|
||||
</label>
|
||||
<label>Tags (virgola)<input type="text" name="tags"></label>
|
||||
<label>Descrizione<textarea name="description"></textarea></label>
|
||||
<label>File<input type="file" name="file" required></label>
|
||||
<menu>
|
||||
<button type="button" id="upload-cancel">Annulla</button>
|
||||
<button type="submit" class="prominent">Carica</button>
|
||||
</menu>
|
||||
</form>
|
||||
</dialog>
|
||||
{% endblock %}
|
||||
{% block scripts %}
|
||||
<script src="/static/js/datasets.js"></script>
|
||||
{% endblock %}
|
||||
|
||||
57
ml/templates/models.html
Normal file
57
ml/templates/models.html
Normal file
@@ -0,0 +1,57 @@
|
||||
{% extends "_layout.html" %}
|
||||
{% block title %}Modelli{% endblock %}
|
||||
{% block content %}
|
||||
<div class="page-head">
|
||||
<h2>Modelli</h2>
|
||||
<button class="prominent" id="btn-add-model">+ Aggiungi modello</button>
|
||||
</div>
|
||||
|
||||
<div id="models-list" class="list"></div>
|
||||
|
||||
<div id="model-detail" class="detail hidden">
|
||||
<button id="btn-close-detail">×</button>
|
||||
<h3 id="md-name"></h3>
|
||||
<p id="md-meta"></p>
|
||||
<section>
|
||||
<h4>Branch / Commits</h4>
|
||||
<select id="md-branch"></select>
|
||||
<ul id="md-commits"></ul>
|
||||
</section>
|
||||
<section>
|
||||
<h4>model.yml</h4>
|
||||
<pre id="md-spec"></pre>
|
||||
</section>
|
||||
<section>
|
||||
<h4>Note</h4>
|
||||
<ul id="md-notes"></ul>
|
||||
<form id="md-note-form">
|
||||
<textarea name="text" placeholder="Nuova nota"></textarea>
|
||||
<button type="submit" class="prominent">Aggiungi</button>
|
||||
</form>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<dialog id="add-model-dlg">
|
||||
<form id="add-model-form" method="dialog">
|
||||
<h3>Nuovo modello</h3>
|
||||
<label>Nome<input type="text" name="name" required></label>
|
||||
<label>Tipo
|
||||
<select name="type">
|
||||
<option>xgboost</option>
|
||||
<option>lstm</option>
|
||||
<option>sklearn</option>
|
||||
<option>other</option>
|
||||
</select>
|
||||
</label>
|
||||
<label>Repo Gitea (owner/repo)<input type="text" name="gitea_repo" required></label>
|
||||
<label>Branch<input type="text" name="default_branch" value="main"></label>
|
||||
<menu>
|
||||
<button type="button" id="add-model-cancel">Annulla</button>
|
||||
<button type="submit" class="prominent">Crea</button>
|
||||
</menu>
|
||||
</form>
|
||||
</dialog>
|
||||
{% endblock %}
|
||||
{% block scripts %}
|
||||
<script src="/static/js/models.js"></script>
|
||||
{% endblock %}
|
||||
@@ -1,89 +1,33 @@
|
||||
<!DOCTYPE html>
|
||||
{% extends "_layout.html" %}
|
||||
{% block title %}Risultati{% endblock %}
|
||||
{% block content %}
|
||||
<div class="page-head">
|
||||
<h2>Risultati training</h2>
|
||||
<button id="btn-compare" class="prominent">Confronta selezionati</button>
|
||||
</div>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<title>Risultati</title>
|
||||
<link href="../static/styles/style.css" rel="stylesheet">
|
||||
<div id="results-list" class="list"></div>
|
||||
|
||||
<style>
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
}
|
||||
<section id="compare-panel" class="hidden">
|
||||
<h3>Confronto</h3>
|
||||
<div class="charts">
|
||||
<canvas id="cmp-loss"></canvas>
|
||||
</div>
|
||||
<table id="cmp-table"></table>
|
||||
<div id="cmp-plots"></div>
|
||||
</section>
|
||||
|
||||
.picker {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.picker .header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1>Risultati</h1>
|
||||
<div class="profile">
|
||||
<p>Utente</p>
|
||||
<button>Logout</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
|
||||
<div class="picker">
|
||||
|
||||
<div class="header">
|
||||
<h2>
|
||||
Seleziona
|
||||
</h2>
|
||||
|
||||
<p>
|
||||
una sessione di training eseguita per visualizzarne i risultati
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="grid">
|
||||
|
||||
<div class="card">
|
||||
<h3>sessione 1</h3>
|
||||
<div class="train-info">
|
||||
<p>24/03/2026</p>
|
||||
<p>12:00</p>
|
||||
<p>dataset: d-1</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h3>sessione 2</h3>
|
||||
<p>24/03/2026</p>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
||||
<script>
|
||||
|
||||
</script>
|
||||
</html>
|
||||
<section id="detail-panel" class="hidden">
|
||||
<h3>Dettaglio training <code id="dt-id"></code></h3>
|
||||
<div id="dt-meta"></div>
|
||||
<div class="charts">
|
||||
<canvas id="dt-loss"></canvas>
|
||||
<canvas id="dt-res"></canvas>
|
||||
</div>
|
||||
<div id="dt-plots"></div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
{% block scripts %}
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<script src="/static/js/results.js"></script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
{% extends "_layout.html" %}
|
||||
{% block title %}Test{% endblock %}
|
||||
{% block content %}
|
||||
<div class="page-head">
|
||||
<h2>Test modello</h2>
|
||||
<div id="slot-info" class="queue-info">Slot: <span id="slot-count">–</span>/2</div>
|
||||
</div>
|
||||
|
||||
<div id="slot-full" class="info-panel hidden">
|
||||
<div class="icon">🚧</div>
|
||||
<h3>Slot test pieni</h3>
|
||||
<p>Massimo 2 utenti possono eseguire test contemporaneamente. Riprova tra qualche minuto.</p>
|
||||
</div>
|
||||
|
||||
<form id="test-start" class="form-row">
|
||||
<label>Modello<select id="t-model"></select></label>
|
||||
<label>Training<select id="t-training"></select></label>
|
||||
<button type="submit" class="prominent">Avvia sessione</button>
|
||||
</form>
|
||||
|
||||
<section id="test-session" class="hidden">
|
||||
<h3>Sessione <code id="ts-id"></code></h3>
|
||||
<form id="inputs-form"></form>
|
||||
<button id="btn-run" class="prominent">Esegui test</button>
|
||||
<button id="btn-end">Chiudi sessione</button>
|
||||
|
||||
<h4>Risultati</h4>
|
||||
<div id="runs-list"></div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
{% block scripts %}
|
||||
<script src="/static/js/test.js"></script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
{% extends "_layout.html" %}
|
||||
{% block title %}Train{% endblock %}
|
||||
{% block content %}
|
||||
<div class="page-head">
|
||||
<h2>Avvia training</h2>
|
||||
<div class="queue-info">Coda: <span id="queue-count">–</span></div>
|
||||
</div>
|
||||
|
||||
<form id="train-form" class="form-row">
|
||||
<label>Modello<select name="model_id" id="f-model"></select></label>
|
||||
<label>Branch<select name="branch" id="f-branch"></select></label>
|
||||
<label>Commit<select name="patch" id="f-patch"></select></label>
|
||||
<label>Versione<input type="text" name="version" placeholder="1.0.0" required></label>
|
||||
<label>Dataset<select name="dataset_id" id="f-dataset"></select></label>
|
||||
<button type="submit" class="prominent">Avvia</button>
|
||||
</form>
|
||||
|
||||
<section id="live-panel" class="hidden">
|
||||
<h3>Training <code id="live-id"></code> — <span id="live-status">queued</span></h3>
|
||||
<div class="charts">
|
||||
<canvas id="chart-loss"></canvas>
|
||||
<canvas id="chart-cpu"></canvas>
|
||||
</div>
|
||||
<pre id="live-logs" class="logs"></pre>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h3>Recenti</h3>
|
||||
<div id="recent-trainings" class="list"></div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
{% block scripts %}
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<script src="/static/js/train.js"></script>
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user