wip
This commit is contained in:
parent
825b87bf80
commit
5367cfaa1c
12
TODO.txt
Normal file
12
TODO.txt
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
Funzioni di gestione commesse: creazione, modifica, cancellazione
|
||||
Sistema di gestione diversi modelli di etichetta tramite software Zebra Designer
|
||||
Controllo tester di prova tenuta Tecna T3 tramite interfaccia USB
|
||||
Controllo elettrovalvole di selezione alta/bassa pressione tramite I/O digitali
|
||||
|
||||
Ciclo di lavoro ordinario:
|
||||
acquisizione del barcode del componente sotto test, se previsto da ricetta
|
||||
test tenuta a pressione 1 (es. 5 bar)
|
||||
test tenuta a pressione 2 (es. 20 bar)(opzionale per ricetta)
|
||||
stampa etichetta test OK
|
||||
Salvataggio dati dei test su portale di tracciabilità www.r5portal.it sia OK che scarti
|
||||
Visualizzazione locale archivio test effettuati
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
[test]
|
||||
parameter: default
|
||||
|
||||
[image_saver]
|
||||
location: data/images
|
||||
minimum free space gb: 20
|
||||
suffix: A
|
||||
[vision_saver]
|
||||
time_format: %Y-%m-%d_%H-%M-%S
|
||||
location: ./data/images
|
||||
minimum_disk_free_space_gb: 20
|
||||
|
||||
[archive_synchronizer]
|
||||
archive_endpoint: https://r5portal.it/api/echo/
|
||||
|
|
|
|||
|
|
@ -16,6 +16,10 @@ python -B -u "./src/main.py" \
|
|||
--auto-select \
|
||||
--style windows \
|
||||
$* 2> >(sed $'s/.*/\e[31m&\e[m/' >&2) # &
|
||||
# --about \
|
||||
# --archive \
|
||||
# --autotests-archive \
|
||||
# --sim-archiver \
|
||||
# --users-management \
|
||||
# sudo renice -n -10 $!
|
||||
# fg
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
from .archive_synchronizer import ArchiveSynchronizer
|
||||
from .remote_api import RemoteAPI
|
||||
from .test_component import TestComponent
|
||||
from .vision_saver import VisionSaver
|
||||
|
|
|
|||
|
|
@ -18,8 +18,8 @@ requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning)
|
|||
|
||||
|
||||
class ArchiveSynchronizer(Component):
|
||||
def __init__(self, config=None, name=None, period=1, lazy=True, paused=False):
|
||||
super().__init__(config=config, name=name, period=period, lazy=lazy, paused=paused)
|
||||
def __init__(self, config=None, name=None, period=1, lazy=True, paused=False, threaded=True):
|
||||
super().__init__(config=config, name=name, period=period, lazy=lazy, paused=paused, threaded=threaded)
|
||||
self.simulate = "--sim-archiver" in sys.argv
|
||||
|
||||
def config_changed(self):
|
||||
|
|
@ -61,6 +61,7 @@ class ArchiveSynchronizer(Component):
|
|||
"user": record.user.username,
|
||||
"recipe": record.recipe.name,
|
||||
"test_data": json.dumps(record.test_data),
|
||||
"result": record.result,
|
||||
"overridden": record.overridden,
|
||||
}, timeout=5, verify=False)
|
||||
if r.status_code != 200:
|
||||
|
|
|
|||
|
|
@ -18,20 +18,25 @@ class Component(QObject):
|
|||
period=None, # period to call _get
|
||||
lazy=True, # whether or not accumulate periodic _get calls if falling behind
|
||||
paused=False,
|
||||
threaded=True,
|
||||
):
|
||||
super().__init__()
|
||||
self.config = config
|
||||
self.name = name if name is not None else str(id(self))
|
||||
self._threaded = threaded
|
||||
self._period = period
|
||||
self._single_shot = lazy
|
||||
self._paused = paused
|
||||
self._started = False
|
||||
self._running = False
|
||||
self.sources = {}
|
||||
self._lock = QSemaphore(1)
|
||||
self._lock.acquire(max(self._lock.available(), 1))
|
||||
if self._threaded:
|
||||
self._lock = QSemaphore(1)
|
||||
self._lock.acquire(max(self._lock.available(), 1))
|
||||
self._timer = None
|
||||
self.log = logging.getLogger(f"{self.__class__.__name__} ({self.name})")
|
||||
if not threaded:
|
||||
self.start()
|
||||
|
||||
def _config_changed(self):
|
||||
self.log.info("reconfigure")
|
||||
|
|
@ -51,52 +56,70 @@ class Component(QObject):
|
|||
self._started = True
|
||||
if not self._paused:
|
||||
self._do_resume()
|
||||
else:
|
||||
elif self._threaded:
|
||||
self._lock.release()
|
||||
self.log.info("started")
|
||||
|
||||
@property
|
||||
def started(self):
|
||||
self._lock.acquire(max(self._lock.available(), 1))
|
||||
if self._threaded:
|
||||
self._lock.acquire(max(self._lock.available(), 1))
|
||||
started = self._started
|
||||
self._lock.release()
|
||||
if self._threaded:
|
||||
self._lock.release()
|
||||
return started
|
||||
|
||||
@property
|
||||
def running(self):
|
||||
self._lock.acquire(max(self._lock.available(), 1))
|
||||
if self._threaded:
|
||||
self._lock.acquire(max(self._lock.available(), 1))
|
||||
running = self._running
|
||||
self._lock.release()
|
||||
if self._threaded:
|
||||
self._lock.release()
|
||||
return running
|
||||
|
||||
def wait_ready(self, timeout=5):
|
||||
timeout = round(timeout * 1000)
|
||||
if self._lock.tryAcquire(max(self._lock.available(), 1), timeout):
|
||||
self._lock.release()
|
||||
else:
|
||||
self._lock.release()
|
||||
raise RuntimeError(f"{self.name} was not ready before timeout of {timeout}ms")
|
||||
if self._threaded:
|
||||
timeout = round(timeout * 1000)
|
||||
if self._lock.tryAcquire(max(self._lock.available(), 1), timeout):
|
||||
self._lock.release()
|
||||
else:
|
||||
self._lock.release()
|
||||
raise RuntimeError(f"{self.name} was not ready before timeout of {timeout}ms")
|
||||
|
||||
def pause(self):
|
||||
self._lock.acquire(max(self._lock.available(), 1))
|
||||
if self._threaded:
|
||||
self._lock.acquire(max(self._lock.available(), 1))
|
||||
if self._running is False:
|
||||
self._lock.release()
|
||||
if self._threaded:
|
||||
self._lock.release()
|
||||
return
|
||||
self._pause.emit()
|
||||
self.wait_ready()
|
||||
if self._threaded:
|
||||
self._pause.emit()
|
||||
self.wait_ready()
|
||||
else:
|
||||
self._do_pause()
|
||||
|
||||
def resume(self):
|
||||
self._lock.acquire(max(self._lock.available(), 1))
|
||||
if self._threaded:
|
||||
self._lock.acquire(max(self._lock.available(), 1))
|
||||
if self._running is True:
|
||||
self._lock.release()
|
||||
if self._threaded:
|
||||
self._lock.release()
|
||||
return
|
||||
self._resume.emit()
|
||||
self.wait_ready()
|
||||
if self._threaded:
|
||||
self._resume.emit()
|
||||
self.wait_ready()
|
||||
else:
|
||||
self._do_resume()
|
||||
|
||||
def set_sources(self, sources=None): # sources should be {"source_name": signal_to_connect}
|
||||
self._lock.acquire(max(self._lock.available(), 1))
|
||||
self._set_sources.emit(sources)
|
||||
self.wait_ready()
|
||||
if self._threaded:
|
||||
self._lock.acquire(max(self._lock.available(), 1))
|
||||
self._set_sources.emit(sources)
|
||||
self.wait_ready()
|
||||
else:
|
||||
self._do_set_sources(sources)
|
||||
|
||||
def _init_periodic(self):
|
||||
if self._period is not None:
|
||||
|
|
@ -110,9 +133,12 @@ class Component(QObject):
|
|||
self.log.debug("no init periodic")
|
||||
|
||||
def set_period(self, period=None, lazy=True):
|
||||
self._lock.acquire(max(self._lock.available(), 1))
|
||||
self._set_sources.emit({"period": period, "lazy": lazy})
|
||||
self.wait_ready()
|
||||
if self._threaded:
|
||||
self._lock.acquire(max(self._lock.available(), 1))
|
||||
self._set_sources.emit({"period": period, "lazy": lazy})
|
||||
self.wait_ready()
|
||||
else:
|
||||
self._do_set_period({"period": period, "lazy": lazy})
|
||||
|
||||
def _start_periodic(self):
|
||||
if self._timer is not None:
|
||||
|
|
@ -157,14 +183,16 @@ class Component(QObject):
|
|||
self._connect_sources()
|
||||
self._running = True
|
||||
self.log.info("resumed")
|
||||
self._lock.release()
|
||||
if self._threaded:
|
||||
self._lock.release()
|
||||
|
||||
def _do_pause(self):
|
||||
self._stop_periodic()
|
||||
self._disconnect_sources()
|
||||
self._running = False
|
||||
self.log.info("paused")
|
||||
self._lock.release()
|
||||
if self._threaded:
|
||||
self._lock.release()
|
||||
|
||||
def _do_set_sources(self, sources):
|
||||
if self._running:
|
||||
|
|
@ -173,14 +201,16 @@ class Component(QObject):
|
|||
if self._running:
|
||||
self._connect_sources()
|
||||
self.log.info("set sources")
|
||||
self._lock.release()
|
||||
if self._threaded:
|
||||
self._lock.release()
|
||||
|
||||
def _do_set_period(self, spec):
|
||||
self._period = spec.get("period", None)
|
||||
self._single_shot = spec.get("lazy", True)
|
||||
self._init_periodic()
|
||||
self.log.info("set period")
|
||||
self._lock.release()
|
||||
if self._threaded:
|
||||
self._lock.release()
|
||||
|
||||
def _get(self, data=None):
|
||||
if data is None:
|
||||
|
|
|
|||
|
|
@ -24,8 +24,8 @@ def aupdate_available_msg():
|
|||
class RemoteAPI(Component):
|
||||
api_cmd = pyqtSignal(str)
|
||||
|
||||
def __init__(self, config=None, name=None, period=1, lazy=True, paused=False, main=None):
|
||||
super().__init__(config=config, name=name, period=period, lazy=lazy, paused=paused)
|
||||
def __init__(self, config=None, name=None, period=1, lazy=True, paused=False, main=None, threaded=True):
|
||||
super().__init__(config=config, name=name, period=period, lazy=lazy, paused=paused, threaded=threaded)
|
||||
self.main = main
|
||||
|
||||
@pyqtSlot()
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ class TestComponent(Component):
|
|||
period=1,
|
||||
lazy=True,
|
||||
paused=False,
|
||||
threaded=True,
|
||||
):
|
||||
super().__init__(
|
||||
config=config,
|
||||
|
|
@ -18,6 +19,7 @@ class TestComponent(Component):
|
|||
period=period,
|
||||
lazy=lazy,
|
||||
paused=paused,
|
||||
threaded=threaded,
|
||||
)
|
||||
self.parameter = self.config["test"]["parameter"]
|
||||
|
||||
|
|
|
|||
70
src/components/vision_saver.py
Executable file
70
src/components/vision_saver.py
Executable file
|
|
@ -0,0 +1,70 @@
|
|||
import glob
|
||||
import os
|
||||
import shutil
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
|
||||
from .component import Component
|
||||
|
||||
|
||||
class VisionSaver(Component):
|
||||
def __init__(self, config=None, name=None):
|
||||
super().__init__(config=config, name=name, threaded=False)
|
||||
|
||||
def config_changed(self):
|
||||
self.location = Path(self.config["vision_saver"]["path"])
|
||||
os.makedirs(self.location, exist_ok=True)
|
||||
self.mask_zones = self.config["vision_saver"].get("mask_zones", None)
|
||||
self.minimum_disk_free_space_gb = self.config["vision_saver"].get("minimum_disk_free_space_gb", None)
|
||||
if self.minimum_disk_free_space_gb is not None:
|
||||
self.minimum_disk_free_space_gb = float(self.minimum_disk_free_space_gb)
|
||||
self.time_format = self.config["vision_saver"]["time_format"]
|
||||
|
||||
def save(self, save_time, img, mask=True):
|
||||
timestamp = datetime.fromtimestamp(save_time).strftime(self.time_format)
|
||||
save_dir = self.location / save_time.strftime("%Y") / save_time.strftime("%m")
|
||||
os.makedirs(save_dir, exist_ok=True)
|
||||
out_path = save_dir / f"{timestamp}.png"
|
||||
self.log.info(f"saving {out_path}")
|
||||
img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
|
||||
if mask:
|
||||
height, width, channels = img.shape
|
||||
out = np.full(
|
||||
[height, width, channels],
|
||||
[0] * channels
|
||||
)
|
||||
for zone_name in self.mask_zones:
|
||||
zone = self.bench.zones[zone_name]["box"]
|
||||
out[zone[1]:zone[3], zone[0]:zone[2]] = img[zone[1]:zone[3], zone[0]:zone[2]]
|
||||
else:
|
||||
out = img
|
||||
cv2.imwrite(out_path, out)
|
||||
return out_path
|
||||
|
||||
def remove_older_images_if_needed(self):
|
||||
if self.minimum_disk_free_space_gb is None:
|
||||
return
|
||||
minimum_disk_free_bytes = self.minimum_disk_free_space_gb * 10**9
|
||||
archive = os.path.abspath(self.location)
|
||||
free = shutil.disk_usage(archive)[-1]
|
||||
if free < minimum_disk_free_bytes:
|
||||
self.log.warning(f"LOW DISK SPACE {(free / 10 ** 9):3.2f}GB/{(minimum_disk_free_bytes / 10 ** 9):3.2f}GB), removing older vision saves")
|
||||
sections = sorted([os.path.dirname(section) for section in glob.glob(f"{archive}/*/")])
|
||||
years = sorted({os.path.basename(os.path.dirname(year)) for section in sections for year in glob.glob(f"{section}/*/")})
|
||||
while free < minimum_disk_free_bytes and len(years) > 0:
|
||||
year = years.pop(0)
|
||||
months = sorted({os.path.basename(os.path.dirname(month)) for section in sections for month in glob.glob(f"{section}/{year}/*/")})
|
||||
while free < minimum_disk_free_bytes and len(months) > 0:
|
||||
month = months.pop(0)
|
||||
for section in sections:
|
||||
self.log.info(f"REMOVING '{section}/{year}/{month}'")
|
||||
shutil.rmtree(f"{section}/{year}/{month}", ignore_errors=True)
|
||||
free = shutil.disk_usage(archive)[-1]
|
||||
if len(months) == 0:
|
||||
for section in sections:
|
||||
self.log.info(f"REMOVING '{section}/{year}'")
|
||||
shutil.rmtree(f"{section}/{year}", ignore_errors=True)
|
||||
free = shutil.disk_usage(archive)[-1]
|
||||
|
|
@ -5,10 +5,11 @@ import logging
|
|||
|
||||
from playhouse.sqlite_ext import JSONField
|
||||
|
||||
from .models import Archive, Log, Recipes, Session, Users, db
|
||||
from .models import Archive, Log, Recipes, Session, Users, db, Autotests
|
||||
|
||||
models_reference = {
|
||||
"archive": Archive,
|
||||
"autotests": Autotests,
|
||||
"log": Log,
|
||||
"recipes": Recipes,
|
||||
"users": Users,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
from .archive import Archive
|
||||
from .autotests import Autotests
|
||||
from .base_model import db
|
||||
from .log import Log
|
||||
from .recipes import Recipes
|
||||
|
|
|
|||
|
|
@ -13,18 +13,20 @@ class Archive(BaseModel):
|
|||
time = DateTimeField(unique=True, null=False, default=datetime.now)
|
||||
user = ForeignKeyField(Users, Users.username, null=False)
|
||||
recipe = ForeignKeyField(Recipes, null=False)
|
||||
result = BooleanField(null=False)
|
||||
overridden = BooleanField(null=False)
|
||||
test_data = JSONField(null=False)
|
||||
overridden = BooleanField(null=False, default=False)
|
||||
archived = BooleanField(null=False, default=False)
|
||||
uploaded = BooleanField(null=False, default=False)
|
||||
|
||||
@classmethod
|
||||
@db.atomic()
|
||||
def archive(cls, recipe, test_data, overridden, vision_duration):
|
||||
def archive(cls, recipe, test_data, result, overridden):
|
||||
return cls.create(
|
||||
user=Users.get_session().user,
|
||||
recipe=recipe,
|
||||
test_data=test_data,
|
||||
result=result,
|
||||
overridden=overridden,
|
||||
)
|
||||
|
||||
|
|
|
|||
39
src/lib/db/models/autotests.py
Executable file
39
src/lib/db/models/autotests.py
Executable file
|
|
@ -0,0 +1,39 @@
|
|||
from datetime import datetime
|
||||
|
||||
from peewee import (AutoField, BooleanField, DateTimeField, ForeignKeyField,
|
||||
TextField, fn)
|
||||
from playhouse.sqlite_ext import JSONField
|
||||
|
||||
from .base_model import BaseModel, db
|
||||
from .recipes import Recipes
|
||||
from .users import Users
|
||||
|
||||
|
||||
class Autotests(BaseModel):
|
||||
id = AutoField(primary_key=True, unique=True, null=False)
|
||||
time = DateTimeField(unique=True, null=False, default=datetime.now)
|
||||
user = ForeignKeyField(Users, Users.username, null=False)
|
||||
recipe = ForeignKeyField(Recipes, null=False)
|
||||
result = BooleanField(null=False)
|
||||
overridden = BooleanField(null=False)
|
||||
reason = TextField(null=False)
|
||||
test_data = JSONField(null=False)
|
||||
|
||||
@classmethod
|
||||
@db.atomic()
|
||||
def archive(cls, recipe, test_data, result, overridden, reason):
|
||||
return cls.create(
|
||||
user=Users.get_session().user,
|
||||
recipe=recipe,
|
||||
test_data=test_data,
|
||||
result=result,
|
||||
overridden=overridden,
|
||||
reason=reason,
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def get_last_time():
|
||||
return Autotests.select(fn.MAX(Autotests.time)).scalar()
|
||||
|
||||
class Meta:
|
||||
table_name = "autotests"
|
||||
30
src/main.py
30
src/main.py
|
|
@ -29,7 +29,7 @@ logs_dir = Path(".") / "data" / "logs"
|
|||
os.makedirs(logs_dir, exist_ok=True)
|
||||
logging.basicConfig(
|
||||
format="{asctime}:{name}:{levelname}:{message}",
|
||||
datefmt="%Y-%m-%dT%H:%M:%S%z",
|
||||
datefmt="%Y-%m-%dT%H-%M-%S%z",
|
||||
style="{",
|
||||
level="INFO",
|
||||
handlers=[
|
||||
|
|
@ -54,7 +54,8 @@ if True:
|
|||
from lib.helpers import ConfigReader
|
||||
from PyQt5.QtCore import QObject, QThread, pyqtSignal
|
||||
from PyQt5.QtWidgets import QApplication, QMessageBox
|
||||
from ui import About, Login, Main_Window, Test, Users_Management
|
||||
from ui import (About, Archive, Autotests_Archive, Login, Main_Window,
|
||||
Test, Users_Management)
|
||||
|
||||
|
||||
class Main(QObject):
|
||||
|
|
@ -96,9 +97,19 @@ class Main(QObject):
|
|||
# self.main_window = Main_Window(self.bench)
|
||||
self.main_window = Main_Window()
|
||||
# CONNECT MAIN WINDOW ACTIONS
|
||||
self.main_window.archive_a.triggered.connect(self.open_archive)
|
||||
if "--archive" in sys.argv:
|
||||
self.main_window.archive_a.trigger()
|
||||
self.main_window.autotests_archive_a.triggered.connect(self.open_autotests_archive)
|
||||
if "--autotests-archive" in sys.argv:
|
||||
self.main_window.autotests_archive_a.trigger()
|
||||
self.main_window.about_a.triggered.connect(self.open_about)
|
||||
if "--about" in sys.argv:
|
||||
self.main_window.about_a.trigger()
|
||||
self.main_window.admin_m.menuAction().setVisible(False) # admin menu should not be visible before an admin logs in
|
||||
self.main_window.login_management_a.triggered.connect(self.open_login_management)
|
||||
self.main_window.users_management_a.triggered.connect(self.open_users_management)
|
||||
if "--users-management" in sys.argv:
|
||||
self.main_window.users_management_a.trigger()
|
||||
# OPEN LOGIN TAB
|
||||
self.open_login()
|
||||
# SHOW MAIN WINDOW
|
||||
|
|
@ -110,15 +121,20 @@ class Main(QObject):
|
|||
self.main_window.showFullScreen()
|
||||
else:
|
||||
self.main_window.show()
|
||||
self.main_window.show()
|
||||
|
||||
def open_archive(self):
|
||||
self.main_window.open_dialog(Archive())
|
||||
|
||||
def open_autotests_archive(self):
|
||||
self.main_window.open_dialog(Autotests_Archive())
|
||||
|
||||
def open_users_management(self):
|
||||
self.main_window.open_dialog(Users_Management())
|
||||
|
||||
def open_about(self):
|
||||
about_widget = About()
|
||||
self.main_window.open_dialog(about_widget)
|
||||
|
||||
def open_login_management(self):
|
||||
self.main_window.open_dialog(Users_Management())
|
||||
|
||||
def open_login(self):
|
||||
tab = Login()
|
||||
tab.successful_login.connect(self.logghed_in)
|
||||
|
|
|
|||
291
src/test.py
291
src/test.py
|
|
@ -1,291 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
import faulthandler
|
||||
import signal
|
||||
|
||||
from lib.helpers.mergingbuffer import MergingBuffer
|
||||
|
||||
faulthandler.enable()
|
||||
signal.signal(signal.SIGINT, lambda a, b: quit())
|
||||
|
||||
|
||||
def test(buffer, data, expected, expected_last_popped, function, test_name):
|
||||
print(test_name, "-" * 25, flush=True)
|
||||
print(f"buffer {list(buffer)}", flush=True)
|
||||
print(f"last_popped {buffer.last_popped}", flush=True)
|
||||
print(f"data {data}", flush=True)
|
||||
function(data)
|
||||
print(f"merged buffer {list(buffer)}", flush=True)
|
||||
print(f"last_popped {buffer.last_popped}", flush=True)
|
||||
if list(buffer) != expected:
|
||||
print(f"expected {expected}", flush=True)
|
||||
print(f"failed {test_name}", flush=True)
|
||||
quit()
|
||||
if buffer.last_popped != expected_last_popped:
|
||||
print(f"expected_last_popped {expected_last_popped}", flush=True)
|
||||
print(f"failed {test_name}", flush=True)
|
||||
quit()
|
||||
print(test_name, "_" * 25, flush=True)
|
||||
|
||||
|
||||
buffer = MergingBuffer()
|
||||
# insert
|
||||
data = [{"time": 0, "a": 0}, ]
|
||||
expected = [
|
||||
{
|
||||
"time": 0,
|
||||
"a": 0,
|
||||
"changed": {"time", "a"},
|
||||
},
|
||||
]
|
||||
expected_last_popped = {}
|
||||
test(buffer, data, expected, expected_last_popped, buffer.merge, "insert")
|
||||
|
||||
# merge
|
||||
data = [{"time": 0, "b": 0}, ]
|
||||
expected = [
|
||||
{
|
||||
"time": 0,
|
||||
"a": 0,
|
||||
"b": 0,
|
||||
"changed": {"time", "a", "b"},
|
||||
},
|
||||
]
|
||||
expected_last_popped = {}
|
||||
test(buffer, data, expected, expected_last_popped, buffer.merge, "merge")
|
||||
|
||||
# append
|
||||
data = [{"time": 1, "b": 1}, ]
|
||||
expected = [
|
||||
{
|
||||
"time": 0,
|
||||
"a": 0,
|
||||
"b": 0,
|
||||
"changed": {"time", "a", "b"},
|
||||
},
|
||||
{
|
||||
"time": 1,
|
||||
"b": 1,
|
||||
"changed": {"time", "b"},
|
||||
},
|
||||
]
|
||||
expected_last_popped = {}
|
||||
test(buffer, data, expected, expected_last_popped, buffer.merge, "append")
|
||||
|
||||
# propagation
|
||||
data = [{"time": 2, "a": 2}, ]
|
||||
expected = [
|
||||
{
|
||||
"time": 0,
|
||||
"a": 0,
|
||||
"b": 0,
|
||||
"changed": {"time", "a", "b"},
|
||||
},
|
||||
{
|
||||
"time": 1,
|
||||
"a": 0,
|
||||
"b": 1,
|
||||
"changed": {"time", "b"},
|
||||
},
|
||||
{
|
||||
"time": 2,
|
||||
"a": 2,
|
||||
"changed": {"time", "a"},
|
||||
},
|
||||
]
|
||||
expected_last_popped = {}
|
||||
test(buffer, data, expected, expected_last_popped, buffer.merge, "propagation")
|
||||
|
||||
# unmergeable
|
||||
data = [{"time": 2, "c": 2}, ]
|
||||
expected = [
|
||||
{
|
||||
"time": 2,
|
||||
"a": 2,
|
||||
"c": 2,
|
||||
"changed": {"time", "a", "c"},
|
||||
},
|
||||
]
|
||||
expected_last_popped = {
|
||||
"time": 1,
|
||||
"a": 0,
|
||||
"b": 1,
|
||||
}
|
||||
|
||||
test(buffer, data, expected, expected_last_popped, buffer.merge, "unmergeable")
|
||||
|
||||
# skip
|
||||
data = [{"time": 0, "d": 0}, {"time": 2, "d": 2}, ]
|
||||
expected = [
|
||||
{
|
||||
"time": 2,
|
||||
"a": 2,
|
||||
"c": 2,
|
||||
"d": 2,
|
||||
"changed": {"time", "a", "c", "d"},
|
||||
},
|
||||
]
|
||||
expected_last_popped = {
|
||||
"time": 1,
|
||||
"a": 0,
|
||||
"b": 1,
|
||||
"d": 0,
|
||||
}
|
||||
test(buffer, data, expected, expected_last_popped, buffer.merge, "skip")
|
||||
|
||||
# extra
|
||||
data = [{"time": 2, "b": 2}, ]
|
||||
expected = [
|
||||
{
|
||||
"time": 2,
|
||||
"a": 2,
|
||||
"b": 2,
|
||||
"c": 2,
|
||||
"d": 2,
|
||||
"changed": {"time", "a", "b", "c", "d"},
|
||||
},
|
||||
]
|
||||
expected_last_popped = {
|
||||
"time": 1,
|
||||
"a": 0,
|
||||
"b": 1,
|
||||
"d": 0,
|
||||
}
|
||||
test(buffer, data, expected, expected_last_popped, buffer.merge, "extra")
|
||||
|
||||
# pop_merged
|
||||
data = ["a", "b", "c", "d", ]
|
||||
expected = []
|
||||
expected_last_popped = {
|
||||
"time": 2,
|
||||
"a": 2,
|
||||
"b": 2,
|
||||
"c": 2,
|
||||
"d": 2,
|
||||
}
|
||||
test(buffer, data, expected, expected_last_popped, buffer.pop_merged, "pop_merged")
|
||||
|
||||
# skip_and_no_update_popped
|
||||
data = [{"time": 1, "a": 1}, ]
|
||||
expected = []
|
||||
expected_last_popped = {
|
||||
"time": 2,
|
||||
"a": 2,
|
||||
"b": 2,
|
||||
"c": 2,
|
||||
"d": 2,
|
||||
}
|
||||
test(buffer, data, expected, expected_last_popped, buffer.merge, "skip_and_no_update_popped")
|
||||
|
||||
# skip_and_update_popped
|
||||
data = [{"time": 2, "a": 2.1}, ]
|
||||
expected = []
|
||||
expected_last_popped = {
|
||||
"time": 2,
|
||||
"a": 2.1,
|
||||
"b": 2,
|
||||
"c": 2,
|
||||
"d": 2,
|
||||
}
|
||||
test(buffer, data, expected, expected_last_popped, buffer.merge, "skip_and_update_popped")
|
||||
|
||||
# extra
|
||||
data = [{"time": 4, "e": 4}, ]
|
||||
expected = [
|
||||
{
|
||||
"time": 4,
|
||||
"e": 4,
|
||||
"changed": {"time", "e"},
|
||||
},
|
||||
]
|
||||
expected_last_popped = {
|
||||
"time": 2,
|
||||
"a": 2.1,
|
||||
"b": 2,
|
||||
"c": 2,
|
||||
"d": 2,
|
||||
}
|
||||
test(buffer, data, expected, expected_last_popped, buffer.merge, "extra")
|
||||
|
||||
# skip_unfillable
|
||||
data = [{"time": 3, "a": 3}]
|
||||
expected = [
|
||||
{
|
||||
"time": 4,
|
||||
"e": 4,
|
||||
"changed": {"time", "e"},
|
||||
},
|
||||
]
|
||||
expected_last_popped = {
|
||||
"time": 3,
|
||||
"a": 3,
|
||||
"b": 2,
|
||||
"c": 2,
|
||||
"d": 2,
|
||||
}
|
||||
test(buffer, data, expected, expected_last_popped, buffer.merge, "skip_unfillable")
|
||||
|
||||
# skip_fillable
|
||||
data = [{"time": 3, "e": 3}, ]
|
||||
expected = [
|
||||
{
|
||||
"time": 4,
|
||||
"e": 4,
|
||||
"changed": {"time", "e"},
|
||||
},
|
||||
]
|
||||
expected_last_popped = {
|
||||
"time": 3,
|
||||
"a": 3,
|
||||
"b": 2,
|
||||
"c": 2,
|
||||
"d": 2,
|
||||
"e": 3,
|
||||
}
|
||||
test(buffer, data, expected, expected_last_popped, buffer.merge, "skip_fillable")
|
||||
|
||||
# insert_fillable
|
||||
data = [{"time": 3.5, "f": 3.5}, ]
|
||||
expected = [
|
||||
{
|
||||
"time": 3.5,
|
||||
"e": 3,
|
||||
"f": 3.5,
|
||||
"changed": {"time", "f"},
|
||||
},
|
||||
{
|
||||
"time": 4,
|
||||
"e": 4,
|
||||
"changed": {"time", "e"},
|
||||
},
|
||||
]
|
||||
expected_last_popped = {
|
||||
"time": 3,
|
||||
"a": 3,
|
||||
"b": 2,
|
||||
"c": 2,
|
||||
"d": 2,
|
||||
"e": 3,
|
||||
}
|
||||
test(buffer, data, expected, expected_last_popped, buffer.merge, "insert_fillable")
|
||||
|
||||
# unmergeable_2
|
||||
data = [{"time": 5, "g": 5}]
|
||||
expected = [
|
||||
{
|
||||
"time": 5,
|
||||
"g": 5,
|
||||
"changed": {"time", "g"},
|
||||
},
|
||||
]
|
||||
expected_last_popped = {
|
||||
"time": 4,
|
||||
"a": 3,
|
||||
"b": 2,
|
||||
"c": 2,
|
||||
"d": 2,
|
||||
"e": 4,
|
||||
"f": 3.5,
|
||||
}
|
||||
test(buffer, data, expected, expected_last_popped, buffer.merge, "unmergeable_2")
|
||||
|
||||
print("DONE, all tests ok")
|
||||
|
|
@ -1,4 +1,6 @@
|
|||
from .about import About
|
||||
from .archive import Archive
|
||||
from .autotests_archive import Autotests_Archive
|
||||
from .dialog import Dialog
|
||||
from .login import Login
|
||||
from .main_window import Main_Window
|
||||
|
|
|
|||
|
|
@ -10,9 +10,6 @@
|
|||
<height>262</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_6">
|
||||
|
|
|
|||
1
src/ui/archive/__init__.py
Executable file
1
src/ui/archive/__init__.py
Executable file
|
|
@ -0,0 +1 @@
|
|||
from .archive import Archive
|
||||
70
src/ui/archive/archive.py
Executable file
70
src/ui/archive/archive.py
Executable file
|
|
@ -0,0 +1,70 @@
|
|||
from lib.db import Users
|
||||
from PyQt5.QtWidgets import QAbstractItemView
|
||||
from ui.crud import Crud, Json_External_Dialog_Cell_Widget
|
||||
from ui.helpers import replace_widget
|
||||
from ui.widget import Widget
|
||||
|
||||
|
||||
class Archive(Widget):
|
||||
def __init__(self, printer=None):
|
||||
super().__init__()
|
||||
self.printer = printer
|
||||
session = Users.get_session()
|
||||
if session is not None and session.is_admin:
|
||||
crud_aliases = {
|
||||
"id": "Id",
|
||||
"time": "Data e ora",
|
||||
"user": "Operatore",
|
||||
"recipe": "Ricetta",
|
||||
"result": "Esito",
|
||||
"overridden": "Esito forzato",
|
||||
"test_data": "Dati del test",
|
||||
"archived": "Archiviato sul portale",
|
||||
"uploaded": "Immagine in cloud",
|
||||
}
|
||||
readonly = ["id"]
|
||||
else:
|
||||
crud_aliases = {
|
||||
"time": "Data e ora",
|
||||
"user": "Operatore",
|
||||
"recipe": "Ricetta",
|
||||
"result": "Esito",
|
||||
"overridden": "Esito forzato",
|
||||
"test_data": "Dati del test",
|
||||
}
|
||||
readonly = True
|
||||
self.crud = Crud(
|
||||
"archive",
|
||||
display_name="Archivio",
|
||||
readonly=readonly,
|
||||
select=list(crud_aliases.keys()),
|
||||
fields_aliases=crud_aliases,
|
||||
widget_classes={
|
||||
"test_data": Json_External_Dialog_Cell_Widget,
|
||||
},
|
||||
)
|
||||
replace_widget(self, "crud_w", self.crud)
|
||||
self.selected = None
|
||||
self.print_b.setEnabled(False)
|
||||
self.crud.db_tw.setSelectionBehavior(QAbstractItemView.SelectRows)
|
||||
self.crud.db_tw.setSelectionMode(QAbstractItemView.SingleSelection)
|
||||
self.crud.db_tw.itemSelectionChanged.connect(self.check)
|
||||
self.print_b.clicked.connect(self.print_label)
|
||||
|
||||
def check(self):
|
||||
if not self.crud.modified:
|
||||
selected = self.crud.get_selected_rows()
|
||||
if len(selected) == 1:
|
||||
selected = selected[0] - 1 # - 1 because rn starts from 1 (filters line)
|
||||
if selected >= 0 and selected < len(self.crud.data_index):
|
||||
selected = self.crud.data_index[selected]
|
||||
self.selected = self.crud.db.table_model.get_by_id(selected)
|
||||
self.print_b.setEnabled(True)
|
||||
return
|
||||
self.selected = None
|
||||
self.print_b.setEnabled(False)
|
||||
|
||||
def print_label(self):
|
||||
self.check()
|
||||
if self.selected is not None and self.printer is not None:
|
||||
self.printer.print_archive_label(self.selected)
|
||||
37
src/ui/archive/archive.ui
Executable file
37
src/ui/archive/archive.ui
Executable file
|
|
@ -0,0 +1,37 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Test archive</class>
|
||||
<widget class="QWidget" name="Test archive">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>98</width>
|
||||
<height>61</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="1" column="0">
|
||||
<widget class="QPushButton" name="print_b">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Stampa</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QWidget" name="crud_w" native="true"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<tabstops>
|
||||
<tabstop>print_b</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
1
src/ui/autotests_archive/__init__.py
Executable file
1
src/ui/autotests_archive/__init__.py
Executable file
|
|
@ -0,0 +1 @@
|
|||
from .autotests_archive import Autotests_Archive
|
||||
70
src/ui/autotests_archive/autotests_archive.py
Executable file
70
src/ui/autotests_archive/autotests_archive.py
Executable file
|
|
@ -0,0 +1,70 @@
|
|||
from lib.db import Users
|
||||
from PyQt5.QtWidgets import QAbstractItemView
|
||||
from ui.crud import Crud, Json_External_Dialog_Cell_Widget
|
||||
from ui.helpers import replace_widget
|
||||
from ui.widget import Widget
|
||||
|
||||
|
||||
class Autotests_Archive(Widget):
|
||||
def __init__(self, printer=None):
|
||||
super().__init__()
|
||||
self.printer = printer
|
||||
session = Users.get_session()
|
||||
if session is not None and session.is_admin:
|
||||
crud_aliases = {
|
||||
"id": "Id",
|
||||
"time": "Data e ora",
|
||||
"user": "Operatore",
|
||||
"recipe": "Ricetta",
|
||||
"result": "Esito",
|
||||
"reason": "Motivo",
|
||||
"overridden": "Esito forzato",
|
||||
"test_data": "Dati del test",
|
||||
}
|
||||
readonly = ["id"]
|
||||
else:
|
||||
crud_aliases = {
|
||||
"time": "Data e ora",
|
||||
"user": "Operatore",
|
||||
"recipe": "Ricetta",
|
||||
"result": "Esito",
|
||||
"reason": "Motivo",
|
||||
"overridden": "Esito forzato",
|
||||
"test_data": "Dati del test",
|
||||
}
|
||||
readonly = True
|
||||
self.crud = Crud(
|
||||
"autotests",
|
||||
display_name="Archivio autotest",
|
||||
readonly=readonly,
|
||||
select=list(crud_aliases.keys()),
|
||||
fields_aliases=crud_aliases,
|
||||
widget_classes={
|
||||
"test_data": Json_External_Dialog_Cell_Widget,
|
||||
},
|
||||
)
|
||||
replace_widget(self, "crud_w", self.crud)
|
||||
self.selected = None
|
||||
self.print_b.setEnabled(False)
|
||||
self.crud.db_tw.setSelectionBehavior(QAbstractItemView.SelectRows)
|
||||
self.crud.db_tw.setSelectionMode(QAbstractItemView.SingleSelection)
|
||||
self.crud.db_tw.itemSelectionChanged.connect(self.check)
|
||||
self.print_b.clicked.connect(self.print_label)
|
||||
|
||||
def check(self):
|
||||
if not self.crud.modified:
|
||||
selected = self.crud.get_selected_rows()
|
||||
if len(selected) == 1:
|
||||
selected = selected[0] - 1 # - 1 because rn starts from 1 (filters line)
|
||||
if selected >= 0 and selected < len(self.crud.data_index):
|
||||
selected = self.crud.data_index[selected]
|
||||
self.selected = self.crud.db.table_model.get_by_id(selected)
|
||||
self.print_b.setEnabled(True)
|
||||
return
|
||||
self.selected = None
|
||||
self.print_b.setEnabled(False)
|
||||
|
||||
def print_label(self):
|
||||
self.check()
|
||||
if self.selected is not None and self.printer is not None:
|
||||
self.printer.print_autotest_label(self.selected)
|
||||
37
src/ui/autotests_archive/autotests_archive.ui
Executable file
37
src/ui/autotests_archive/autotests_archive.ui
Executable file
|
|
@ -0,0 +1,37 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Autotests archive</class>
|
||||
<widget class="QWidget" name="Autotests archive">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>98</width>
|
||||
<height>61</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="1" column="0">
|
||||
<widget class="QPushButton" name="print_b">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Stampa</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QWidget" name="crud_w" native="true"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<tabstops>
|
||||
<tabstop>print_b</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
|
@ -70,7 +70,7 @@ class Cell:
|
|||
value = self.parse()
|
||||
fail = False
|
||||
except Exception:
|
||||
traceback.print_exc()
|
||||
self.log.exception(traceback.format_exc())
|
||||
value = None
|
||||
fail = True
|
||||
if fail or value != self.value:
|
||||
|
|
@ -316,7 +316,7 @@ class Crud(Widget):
|
|||
try:
|
||||
r[fn] = w.parse(row_number=rn, crud=self)
|
||||
except Exception:
|
||||
traceback.print_exc()
|
||||
self.log.exception(traceback.format_exc())
|
||||
self.set_row_color(rn, "red")
|
||||
fail = True
|
||||
add_row, r, filter_fail = self.row_filter(r, rn, self)
|
||||
|
|
@ -332,7 +332,7 @@ class Crud(Widget):
|
|||
try:
|
||||
self.db.commit(data, self.deleted_rows)
|
||||
except Exception as e:
|
||||
traceback.print_exc()
|
||||
self.log.exception(traceback.format_exc())
|
||||
QMessageBox.critical(None, "Errore Salvataggio DB", str(e))
|
||||
return False
|
||||
# GET DATA
|
||||
|
|
|
|||
|
|
@ -22,9 +22,6 @@
|
|||
<height>600</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Crud</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="0" column="0">
|
||||
<widget class="QGroupBox" name="db_gb">
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
import logging
|
||||
|
||||
from PyQt5 import uic
|
||||
from PyQt5.QtCore import Qt, pyqtSignal
|
||||
from PyQt5.QtGui import QIcon
|
||||
from PyQt5.QtWidgets import QDialog
|
||||
from ui.helpers import replace_widget
|
||||
|
||||
dialogs = {}
|
||||
|
||||
|
|
@ -18,17 +21,11 @@ class Dialog(QDialog):
|
|||
self.ui = uic.loadUi(u, self)
|
||||
# LOGO
|
||||
self.setWindowIcon(QIcon("src/ui/imgs/neo.ico"))
|
||||
self.log = logging.getLogger(f"{self.__class__.__name__} ({id(self)})")
|
||||
|
||||
def setCentralWidget(self, widget):
|
||||
widget.setParent(self)
|
||||
i = self.layout().replaceWidget(self.centralwidget, widget, options=Qt.FindDirectChildrenOnly)
|
||||
if i is None:
|
||||
raise AssertionError(
|
||||
"{}.centralwidget is missing, cannot replace it. Maybe check dialog.ui file".format(__name__))
|
||||
self.centralwidget.hide()
|
||||
self.centralwidget.deleteLater()
|
||||
del i
|
||||
self.centralwidget = widget
|
||||
replace_widget(self, "centralWidget", widget)
|
||||
|
||||
def centralWidget(self):
|
||||
return self.centralwidget
|
||||
|
|
|
|||
|
|
@ -16,12 +16,9 @@
|
|||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string></string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QWidget" name="centralwidget" native="true">
|
||||
<widget class="QWidget" name="centralWidget" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
|
|
@ -34,4 +31,4 @@
|
|||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
</ui>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
def replace_widget(parent, name, new, delete=False):
|
||||
old = getattr(parent, name)
|
||||
old.parentWidget().layout().replaceWidget(old, new)
|
||||
replaced = old.parentWidget().layout().replaceWidget(old, new)
|
||||
if replaced is None:
|
||||
raise AssertionError(f"{name} not found, cannot replace it.")
|
||||
old.hide()
|
||||
setattr(parent, name, new)
|
||||
new.show()
|
||||
|
|
|
|||
|
|
@ -10,9 +10,6 @@
|
|||
<height>294</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Login</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="2" column="0">
|
||||
<spacer name="horizontalSpacer">
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@
|
|||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>320</width>
|
||||
<height>180</height>
|
||||
<width>94</width>
|
||||
<height>40</height>
|
||||
</rect>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget"/>
|
||||
|
|
@ -16,7 +16,7 @@
|
|||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>320</width>
|
||||
<width>94</width>
|
||||
<height>24</height>
|
||||
</rect>
|
||||
</property>
|
||||
|
|
@ -30,8 +30,16 @@
|
|||
<property name="title">
|
||||
<string>Amministrazione</string>
|
||||
</property>
|
||||
<addaction name="login_management_a"/>
|
||||
<addaction name="users_management_a"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuStrumenti">
|
||||
<property name="title">
|
||||
<string>Strumenti</string>
|
||||
</property>
|
||||
<addaction name="archive_a"/>
|
||||
<addaction name="autotests_archive_a"/>
|
||||
</widget>
|
||||
<addaction name="menuStrumenti"/>
|
||||
<addaction name="admin_m"/>
|
||||
<addaction name="menuAbout"/>
|
||||
</widget>
|
||||
|
|
@ -40,11 +48,21 @@
|
|||
<string>Powered by</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="login_management_a">
|
||||
<action name="users_management_a">
|
||||
<property name="text">
|
||||
<string>Gestione utenti</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="archive_a">
|
||||
<property name="text">
|
||||
<string>Archivio</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="autotests_archive_a">
|
||||
<property name="text">
|
||||
<string>Archivio autotest</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
import sys
|
||||
|
||||
from lib.db import Recipes
|
||||
from PyQt5.QtCore import Qt, QTimer, pyqtSignal
|
||||
from PyQt5.QtCore import QTimer, pyqtSignal
|
||||
from PyQt5.QtGui import QKeySequence
|
||||
from PyQt5.QtWidgets import QShortcut
|
||||
from ui.crud import Crud
|
||||
from ui.helpers import replace_widget
|
||||
from ui.widget import Widget
|
||||
|
||||
|
||||
|
|
@ -27,9 +28,9 @@ def recipes_row_filter(row, row_number, crud):
|
|||
class Recipe_Selection(Widget):
|
||||
ok = pyqtSignal(Recipes)
|
||||
|
||||
def __init__(self, session=None):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.crud_aliases = {
|
||||
crud_aliases = {
|
||||
"name": "Ricetta",
|
||||
"client": "Cliente",
|
||||
"part_number": "N° disegno",
|
||||
|
|
@ -40,20 +41,14 @@ class Recipe_Selection(Widget):
|
|||
"recipes",
|
||||
display_name="SELEZIONE RICETTA",
|
||||
readonly=True,
|
||||
select=list(self.crud_aliases.keys()),
|
||||
select=list(crud_aliases.keys()),
|
||||
filters={"archived": False},
|
||||
fields_aliases=self.crud_aliases,
|
||||
fields_aliases=crud_aliases,
|
||||
autocomplete={"archived": False},
|
||||
row_upgrader=recipes_row_upgrader,
|
||||
row_filter=recipes_row_filter,
|
||||
)
|
||||
i = self.layout().replaceWidget(self.crud_w, self.crud, options=Qt.FindDirectChildrenOnly)
|
||||
if i is None:
|
||||
raise AssertionError("{}.crud_w is missing, cannot replace it. Maybe check dialog.ui file".format(__name__))
|
||||
self.crud_w.hide()
|
||||
self.crud_w.deleteLater()
|
||||
del i
|
||||
self.crud_w = self.crud
|
||||
replace_widget(self, "crud_w", self.crud)
|
||||
self.selected = None
|
||||
self.select_b.setEnabled(False)
|
||||
QShortcut(QKeySequence("Return"), self).activated.connect(self.select_b.click)
|
||||
|
|
|
|||
|
|
@ -10,9 +10,6 @@
|
|||
<height>600</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="1" column="0">
|
||||
<widget class="QPushButton" name="select_b">
|
||||
|
|
|
|||
|
|
@ -10,9 +10,6 @@
|
|||
<height>85</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Test</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="1" column="0">
|
||||
<widget class="QWidget" name="centralWidget" native="true">
|
||||
|
|
|
|||
|
|
@ -10,9 +10,6 @@
|
|||
<height>108</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="img_l">
|
||||
|
|
|
|||
|
|
@ -2,9 +2,6 @@
|
|||
<ui version="4.0">
|
||||
<class>Autotest</class>
|
||||
<widget class="QWidget" name="Autotest">
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout"/>
|
||||
</widget>
|
||||
<resources/>
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ class Users_Management(Widget):
|
|||
def parse(self, row_number=None, crud=None):
|
||||
return Users.parse_roles(self.text())
|
||||
|
||||
self.crud_aliases = {
|
||||
crud_aliases = {
|
||||
"id": "Id",
|
||||
"username": "Nome utente",
|
||||
"password": "Password",
|
||||
|
|
@ -48,8 +48,8 @@ class Users_Management(Widget):
|
|||
"users",
|
||||
display_name="GESTIONE UTENTI",
|
||||
readonly=["id"],
|
||||
select=list(self.crud_aliases.keys()),
|
||||
fields_aliases=self.crud_aliases,
|
||||
select=list(crud_aliases.keys()),
|
||||
fields_aliases=crud_aliases,
|
||||
autocomplete={"archived": False},
|
||||
widget_classes={
|
||||
"username": Username_Line_Edit_Cell_Widget,
|
||||
|
|
@ -68,7 +68,7 @@ class Users_Management(Widget):
|
|||
try:
|
||||
user = Users.generate(username=row["username"], password=row["password"], roles=row["roles"])
|
||||
except AssertionError as e:
|
||||
traceback.print_exc()
|
||||
self.log.exception(traceback.format_exc())
|
||||
crud.set_row_color(row_number, "red")
|
||||
QMessageBox.critical(None, "Errore Salvataggio DB", f"Errore alla riga {row_number}:\n{str(e)}")
|
||||
return False, None, True
|
||||
|
|
|
|||
|
|
@ -10,9 +10,6 @@
|
|||
<height>18</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout"/>
|
||||
</widget>
|
||||
<resources/>
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import logging
|
||||
|
||||
from lib.helpers import get_resource
|
||||
from PyQt5 import uic
|
||||
from PyQt5.QtCore import Qt
|
||||
|
|
@ -13,6 +15,7 @@ class Widget(QWidget):
|
|||
u = get_resource("ui/{0}/{0}.ui".format(me.lower()))
|
||||
self.ui = uic.loadUi(u, self)
|
||||
self.setWindowTitle(me)
|
||||
self.log = logging.getLogger(f"{self.__class__.__name__} ({id(self)})")
|
||||
|
||||
def setParent(self, parent):
|
||||
parent._closing.connect(self._parent_closing)
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import logging
|
||||
|
||||
from lib.helpers import get_resource
|
||||
from PyQt5 import uic
|
||||
from PyQt5.QtCore import Qt, pyqtSignal
|
||||
|
|
@ -18,6 +20,7 @@ class Window(QMainWindow):
|
|||
self.ui = uic.loadUi(u, self)
|
||||
# LOGO
|
||||
self.setWindowIcon(QIcon(get_resource("ui/imgs/neo.ico")))
|
||||
self.log = logging.getLogger(f"{self.__class__.__name__} ({id(self)})")
|
||||
|
||||
def setCentralWidget(self, widget):
|
||||
widget.setParent(self)
|
||||
|
|
|
|||
|
|
@ -20,42 +20,8 @@
|
|||
<height>24</height>
|
||||
</rect>
|
||||
</property>
|
||||
<widget class="QMenu" name="menuAbout">
|
||||
<property name="title">
|
||||
<string>About</string>
|
||||
</property>
|
||||
<addaction name="powered_by_m"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuRecords">
|
||||
<property name="title">
|
||||
<string>Records</string>
|
||||
</property>
|
||||
<addaction name="record_exporter_m"/>
|
||||
</widget>
|
||||
<addaction name="menuRecords"/>
|
||||
<addaction name="menuAbout"/>
|
||||
</widget>
|
||||
<action name="powered_by_m">
|
||||
<property name="text">
|
||||
<string>Powered by</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="diagnostic_panel_m">
|
||||
<property name="text">
|
||||
<string>Panel</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionExit">
|
||||
<property name="text">
|
||||
<string>Exit</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="record_exporter_m">
|
||||
<property name="text">
|
||||
<string>Exporter</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
</ui>
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user