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,131 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
ROOT = Path(__file__).resolve().parent.parent
|
||||
if str(ROOT) not in sys.path:
|
||||
sys.path.insert(0, str(ROOT))
|
||||
|
||||
from cantarire import create_app # noqa: E402
|
||||
|
||||
STABLE_READING = {
|
||||
"ok": True,
|
||||
"error": None,
|
||||
"profile": "test-scale",
|
||||
"port": "COM1",
|
||||
"protocol": "mt_sics",
|
||||
"open": True,
|
||||
"value": 250.0,
|
||||
"unit": "g",
|
||||
"stable": True,
|
||||
"stale": False,
|
||||
"overload": False,
|
||||
"age_ms": 120,
|
||||
"measured_at": "2026-10-18T10:00:00.000+03:00",
|
||||
"raw_hex": "53 20 53 20 20 20 20 32 35 30 2E 30",
|
||||
"fields": {},
|
||||
}
|
||||
|
||||
|
||||
class FakeScale:
|
||||
available = True
|
||||
error = None
|
||||
|
||||
def __init__(self):
|
||||
self.reading = dict(STABLE_READING)
|
||||
self.calls = 0
|
||||
self.probed = None
|
||||
|
||||
def profiles(self):
|
||||
return [{"name": "test-scale", "label": "Cântar test", "port": "COM1", "protocol": "mt_sics"}]
|
||||
|
||||
def read(self, profile_name="", **_kwargs):
|
||||
self.calls += 1
|
||||
return dict(self.reading)
|
||||
|
||||
def probe(self, port, baud=9600, framing="8N1", seconds=5.0):
|
||||
self.probed = {"port": port, "baud": baud, "framing": framing, "seconds": seconds}
|
||||
return {
|
||||
"ok": True,
|
||||
"port": port,
|
||||
"settings": f"{port} {baud} {framing}, listen only",
|
||||
"baud": baud,
|
||||
"framing": framing,
|
||||
"seconds": seconds,
|
||||
"error": None,
|
||||
"bytes": 13,
|
||||
"frames": [{"age_ms": 10, "hex": "02 2D 30 20 30 30 30 30 30 30 30 30 30 30 0D",
|
||||
"text": "<STX>-0 000000000000<CR>", "size": 13}],
|
||||
"guesses": [{"protocol": "toledo_cont", "name": "Toledo Continuous",
|
||||
"kind": "continuous", "count": 2, "value": 12.345,
|
||||
"unit": "kg", "stable": True, "specificity": 3}],
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def app(tmp_path):
|
||||
application = create_app({
|
||||
"TESTING": True,
|
||||
"INSTANCE_DIR": tmp_path,
|
||||
"DATABASE": tmp_path / "test.db",
|
||||
"BRIDGE_CONFIG": tmp_path / "bridge.json",
|
||||
"SIMULATE": "",
|
||||
"AUTO_PRINT": False,
|
||||
"SECRET_KEY": "test",
|
||||
})
|
||||
application.extensions["scale"] = FakeScale()
|
||||
return application
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def client(app):
|
||||
return app.test_client()
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def scale(app):
|
||||
return app.extensions["scale"]
|
||||
|
||||
|
||||
def _create_component(client, code: str, name: str, unit: str = "g") -> int:
|
||||
response = client.post("/componente/nou", data={
|
||||
"code": code, "name": name, "unit": unit, "active": "on",
|
||||
})
|
||||
assert response.status_code == 302, response.get_data(as_text=True)
|
||||
with client.application.app_context():
|
||||
from cantarire.repositories import get_component_by_code
|
||||
|
||||
return get_component_by_code(code)["id"]
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def fasole(client):
|
||||
return _create_component(client, "FAS", "Fasole")
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def carne(client):
|
||||
return _create_component(client, "CAR", "Carne")
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def recipe(client, fasole, carne):
|
||||
response = client.post("/retete/nou", data={
|
||||
"code": "PF-001",
|
||||
"name": "Fasole cu carne",
|
||||
"unit": "g",
|
||||
"target_weight": "450",
|
||||
"component_1_id": str(fasole),
|
||||
"component_1_target": "250",
|
||||
"component_2_id": str(carne),
|
||||
"component_2_target": "200",
|
||||
"active": "on",
|
||||
})
|
||||
assert response.status_code == 302, response.get_data(as_text=True)
|
||||
with client.application.app_context():
|
||||
from cantarire.repositories import list_recipes
|
||||
|
||||
return list_recipes()[0]
|
||||
Reference in New Issue
Block a user