Initial commit: aplicatie de cantarire si etichetare
Aplicatie Flask + SQLite pentru productie alimentara: cantarirea componentelor unui produs finit si tiparirea etichetei prin QZ Tray. - produse finite (retete) formate din componente cantarite - designer vizual de etichete (mm/px, zona sigura, campuri variabile, cod QR cu continut din mai multe bucati) - legare produs finit -> sablon de eticheta - navigare pe doua taburi: Cantarire / Setari - administrare: componente, produse finite, sabloane, campuri, istoric - backup, resetare baza de date, checklist de pregatire, /healthz - CLI de administrare si serviciu systemd pentru productie - vendor/syncpulse-scale-bridge (GPL-3.0-or-later) pentru citirea cantarului
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
"""Pagina de cantarire si setarile aplicatiei."""
|
||||
from __future__ import annotations
|
||||
|
||||
from flask import (Blueprint, current_app, flash, jsonify, redirect, render_template, request,
|
||||
send_file, url_for)
|
||||
|
||||
from .. import maintenance
|
||||
from ..db import all_settings, get_setting, set_setting
|
||||
from ..repositories import (
|
||||
get_open_package,
|
||||
get_recipe,
|
||||
get_template,
|
||||
list_components,
|
||||
list_label_fields,
|
||||
list_recipes,
|
||||
list_templates,
|
||||
package_state,
|
||||
package_stats,
|
||||
recent_packages,
|
||||
)
|
||||
from ..scale import get_scale
|
||||
|
||||
bp = Blueprint("main", __name__)
|
||||
|
||||
|
||||
@bp.get("/")
|
||||
def dashboard():
|
||||
scale = get_scale(current_app)
|
||||
profiles = scale.profiles()
|
||||
active_profile = get_setting("active_scale_profile")
|
||||
if active_profile not in {p["name"] for p in profiles}:
|
||||
active_profile = profiles[0]["name"] if profiles else ""
|
||||
|
||||
open_package = get_open_package()
|
||||
initial = package_state(open_package["id"]) if open_package else None
|
||||
|
||||
settings = all_settings()
|
||||
template_id = settings.get("default_template_id") or ""
|
||||
if template_id and get_template(int(template_id)) is None:
|
||||
template_id = ""
|
||||
|
||||
recipes = [get_recipe(item["id"]) for item in list_recipes()]
|
||||
recipes = [recipe for recipe in recipes if recipe is not None]
|
||||
|
||||
return render_template(
|
||||
"dashboard.html",
|
||||
recipes=recipes,
|
||||
components=list_components(),
|
||||
initial=initial,
|
||||
recent=recent_packages(6),
|
||||
stats=package_stats(),
|
||||
profiles=profiles,
|
||||
active_profile=active_profile,
|
||||
scale_ready=bool(profiles),
|
||||
default_operator=settings.get("default_operator", ""),
|
||||
default_template_id=template_id,
|
||||
auto_print=settings.get("auto_print", "1") == "1" and current_app.config["AUTO_PRINT"],
|
||||
poll_ms=700,
|
||||
scale_error=scale.error,
|
||||
setup=maintenance.readiness_summary(),
|
||||
)
|
||||
|
||||
|
||||
@bp.get("/healthz")
|
||||
def healthz():
|
||||
"""Verificare simpla pentru monitorizare / reverse proxy."""
|
||||
status = maintenance.database_status()
|
||||
summary = maintenance.readiness_summary()
|
||||
healthy = status["integrity"] == "ok"
|
||||
payload = {
|
||||
"ok": healthy,
|
||||
"version": "0.1.0",
|
||||
"database": {
|
||||
"path": status["path"],
|
||||
"size": status["size"],
|
||||
"integrity": status["integrity"],
|
||||
"schema_version": status["schema_version"],
|
||||
"counts": status["counts"],
|
||||
},
|
||||
"setup": {
|
||||
"ready": summary["ready"],
|
||||
"missing": [item["label"] for item in summary["missing"]],
|
||||
},
|
||||
}
|
||||
return jsonify(payload), (200 if healthy else 503)
|
||||
|
||||
|
||||
@bp.get("/setari")
|
||||
def settings_page():
|
||||
scale = get_scale(current_app)
|
||||
return render_template(
|
||||
"settings.html",
|
||||
profiles=scale.profiles(),
|
||||
scale_available=scale.available,
|
||||
scale_error=scale.error,
|
||||
templates=list_templates(),
|
||||
components=list_components(include_inactive=True),
|
||||
recipes=list_recipes(include_inactive=True),
|
||||
fields_count=len(list_label_fields(include_inactive=True)),
|
||||
db_status=maintenance.database_status(),
|
||||
backups=maintenance.list_backups()[:5],
|
||||
setup=maintenance.readiness_summary(),
|
||||
)
|
||||
|
||||
|
||||
@bp.get("/setari/backup")
|
||||
def settings_backup():
|
||||
"""Descarca un backup proaspat al bazei (inainte de o actualizare)."""
|
||||
path = maintenance.backup_database("manual")
|
||||
maintenance.prune_backups()
|
||||
return send_file(path, as_attachment=True, download_name=path.name)
|
||||
|
||||
|
||||
@bp.post("/setari")
|
||||
def settings_save():
|
||||
scale = get_scale(current_app)
|
||||
valid_profiles = {p["name"] for p in scale.profiles()}
|
||||
active = (request.form.get("active_scale_profile") or "").strip()
|
||||
if active and active not in valid_profiles:
|
||||
flash("Profilul de cantar selectat nu exista.", "error")
|
||||
return redirect(url_for("main.settings_page"))
|
||||
|
||||
template_id = (request.form.get("default_template_id") or "").strip()
|
||||
if template_id and (not template_id.isdigit() or get_template(int(template_id)) is None):
|
||||
flash("Sabloul implicit selectat nu exista.", "error")
|
||||
return redirect(url_for("main.settings_page"))
|
||||
|
||||
for key in ("company_name", "company_address", "company_phone", "default_operator"):
|
||||
set_setting(key, (request.form.get(key) or "").strip()[:200])
|
||||
set_setting("active_scale_profile", active)
|
||||
set_setting("default_template_id", template_id)
|
||||
set_setting("auto_print", "1" if request.form.get("auto_print") else "0")
|
||||
|
||||
flash("Setarile au fost salvate.", "success")
|
||||
return redirect(url_for("main.settings_page"))
|
||||
Reference in New Issue
Block a user