st-ten-1/src/ui/recipe_selection/recipe_selection.py

542 lines
29 KiB
Python
Raw Normal View History

2022-09-20 15:42:59 +00:00
import csv
2023-01-05 11:16:04 +00:00
import locale
2022-07-27 12:54:19 +00:00
import os
2022-06-01 16:37:19 +00:00
import sys
2022-09-20 15:42:59 +00:00
import weakref
2022-07-27 12:54:19 +00:00
from glob import glob
2022-06-01 16:37:19 +00:00
2024-09-17 13:19:36 +00:00
from lib.db import Recipes, Users, db
2022-07-19 09:59:00 +00:00
from PyQt5.QtCore import QTimer, pyqtSignal
2022-06-01 16:37:19 +00:00
from PyQt5.QtGui import QKeySequence
2022-10-25 13:38:18 +00:00
from PyQt5.QtWidgets import QFileDialog, QMessageBox, QShortcut
2024-10-24 14:58:31 +00:00
import shutil
2024-09-18 12:18:07 +00:00
from lib.helpers.step import Step
2022-07-19 09:59:00 +00:00
from ui.crud import Crud, Json_External_Dialog_Editor_Cell_Widget
2022-06-01 16:37:19 +00:00
from ui.helpers import replace_widget
2022-07-25 09:16:14 +00:00
from ui.recipe_spec_and_step_editor import Recipe_Spec_And_Step_Editor
2022-06-01 16:37:19 +00:00
from ui.widget import Widget
2024-10-24 14:58:31 +00:00
from datetime import datetime
from src.components import ArchiveSynchronizer
2022-06-01 16:37:19 +00:00
2022-09-20 15:42:59 +00:00
class Noner:
def __getitem__(self, key):
return None
2022-11-09 16:18:11 +00:00
noner = Noner()
2022-06-01 16:37:19 +00:00
class Recipe_Selection(Widget):
ok = pyqtSignal(Recipes)
2022-09-20 15:42:59 +00:00
def __init__(self, config, unsupported_steps=None):
2022-11-09 16:18:11 +00:00
global noner
2022-06-01 16:37:19 +00:00
super().__init__()
2022-09-20 15:42:59 +00:00
self.config = config
2024-10-25 07:18:44 +00:00
self.archive_sync = ArchiveSynchronizer()
2024-02-23 15:01:43 +00:00
self.second_leak_test_enabled = self.config["hardware_config"]["second_leak_test"] == "present"
2023-01-31 16:42:37 +00:00
self.defaults = self.config.get("recipes_defaults", noner)
2022-09-22 19:19:19 +00:00
self.unsupported_steps = unsupported_steps
2022-06-01 16:37:19 +00:00
session = Users.get_session()
if session.is_admin:
2022-09-07 15:24:40 +00:00
readonly = False
2022-06-01 16:37:19 +00:00
crud_aliases = {
"name": "Ricetta",
2022-07-19 09:59:00 +00:00
"client": "Cliente",
"part_number": "N° disegno",
2022-06-01 16:37:19 +00:00
"spec": "Specifica",
2022-07-26 13:34:09 +00:00
"description": "Descrizione",
2022-06-01 16:37:19 +00:00
}
filters = None
else:
readonly = True
crud_aliases = {
"name": "Ricetta",
"client": "Cliente",
"part_number": "N° disegno",
"spec": "Specifica",
2022-07-26 13:34:09 +00:00
"description": "Descrizione",
2022-06-01 16:37:19 +00:00
}
filters = {"archived": False}
2022-11-09 16:18:11 +00:00
step_defaults = self.read_steps(self.config.get("recipes_defaults", noner), noner)
2024-02-23 15:01:43 +00:00
custom_label_folder = f"config/label_templates/{str(self.config.machine_id)}/"
standard_label_folder = f"config/label_templates/"
2023-10-13 15:52:41 +00:00
if os.path.exists(custom_label_folder):
label_folder = custom_label_folder
else:
label_folder = standard_label_folder
2022-09-22 19:19:19 +00:00
step_defaults.update({
"vision": {
# "recipe": sorted(glob("*.ini", root_dir="./config/vision/recipes/")), # only in python3.10
"recipe": sorted(map(os.path.basename, glob("./config/vision/recipes/*.ini"))),
},
"print": {
# "template": sorted(glob("*.prn", root_dir="./config/label_templates/")), # only in python3.10
2023-10-13 15:52:41 +00:00
"template": sorted(map(os.path.basename, glob(f"{label_folder}*.prn"))),
2022-09-22 19:19:19 +00:00
},
}),
2022-06-01 16:37:19 +00:00
self.crud = Crud(
"recipes",
display_name="SELEZIONE RICETTA",
readonly=readonly,
select=list(crud_aliases.keys()),
filters=filters,
fields_aliases=crud_aliases,
2022-09-22 19:19:19 +00:00
autocomplete={
2022-11-09 16:18:11 +00:00
"name": self.config.get("recipes_defaults", noner)["codice_ricetta"],
"client": self.config.get("recipes_defaults", noner)["cliente"],
"part_number": self.config.get("recipes_defaults", noner)["part_number"],
2022-09-22 19:19:19 +00:00
"spec": {
2022-11-09 16:18:11 +00:00
"count": len(self.config.get("recipes_defaults", noner)["dimensione_lotto_abilitata"]) and "count" not in self.unsupported_steps,
"connector": len(self.config.get("recipes_defaults", noner)["verifica_connettore_abilitata"]) and "connector" not in self.unsupported_steps,
2024-02-23 15:01:43 +00:00
"barcodes": len(
self.config.get("recipes_defaults", noner)["verifica_codice_a_barre_abilitata"]) and "barcodes" not in self.unsupported_steps,
"resistance": len(
self.config.get("recipes_defaults", noner)["verifica_resistenza_connettore_abilitata"]) and "resistance" not in self.unsupported_steps,
2022-11-09 16:18:11 +00:00
"screws": len(self.config.get("recipes_defaults", noner)["avvitatura_abilitata"]) and "screws" not in self.unsupported_steps,
2023-01-26 13:13:03 +00:00
"instruction": len(self.config.get("recipes_defaults", noner)["istruzione_abilitata"]) and "instruction" not in self.unsupported_steps,
2023-02-17 16:38:03 +00:00
"leak_1": len(self.config.get("recipes_defaults", noner)["prova_tenuta_abilitata"]) and "leak_1" not in self.unsupported_steps,
"leak_2": len(self.config.get("recipes_defaults", noner)["prova_tenuta_abilitata_2"]) and "leak_2" not in self.unsupported_steps,
2022-11-09 16:18:11 +00:00
"vision": len(self.config.get("recipes_defaults", noner)["test_visione_abilitato"]) and "vision" not in self.unsupported_steps,
"print": len(self.config.get("recipes_defaults", noner)["stampa_etichetta_abilitata"]) and "print" not in self.unsupported_steps,
2022-09-22 19:19:19 +00:00
"step_editors": step_defaults,
},
2022-11-09 16:18:11 +00:00
"description": self.config.get("recipes_defaults", noner)["descrizione"],
2022-09-22 19:19:19 +00:00
"archived": False,
},
2022-09-26 10:35:02 +00:00
sort={"name": True},
2022-09-20 15:42:59 +00:00
widget_classes={"spec": lambda *args, **kwargs: Json_External_Dialog_Editor_Cell_Widget(
Recipe_Spec_And_Step_Editor,
*args,
**kwargs,
2022-09-22 19:19:19 +00:00
unsupported_steps=self.unsupported_steps
2022-09-20 15:42:59 +00:00
), },
2022-09-13 10:39:09 +00:00
pagination=25,
2022-06-01 16:37:19 +00:00
)
replace_widget(self, "crud_w", self.crud)
self.crud_modified = None
self.selected = None
self.select_b.setEnabled(False)
self.select_b.clicked.connect(self.select)
QShortcut(QKeySequence("Return"), self).activated.connect(self.select_b.click)
2022-09-06 15:35:49 +00:00
QShortcut(QKeySequence("Enter"), self).activated.connect(self.select_b.click)
2022-06-01 16:37:19 +00:00
self.crud.modified.connect(self.check_modified)
self.crud.selected.connect(self.check_selected)
self.crud.emit()
2022-09-13 10:39:09 +00:00
self.crud.db_tw.setColumnWidth(0, 200)
2022-07-26 13:47:31 +00:00
self.crud.db_tw.setColumnWidth(1, 200)
self.crud.db_tw.setColumnWidth(2, 200)
self.crud.db_tw.setColumnWidth(3, 200)
self.crud.db_tw.setColumnWidth(4, 200)
self.crud.db_tw.setColumnWidth(5, 400)
2022-10-19 10:25:12 +00:00
if session.is_admin:
2024-02-23 15:01:43 +00:00
self.import_b.clicked.connect(lambda checked, selfi=weakref.ref(self): selfi().import_recipes())
self.export_b.clicked.connect(lambda checked, selfi=weakref.ref(self): selfi().export_recipes())
self.delete_all_b.clicked.connect(lambda checked, selfi=weakref.ref(self): selfi().delete_recipes())
2022-10-19 10:25:12 +00:00
else:
self.import_b.setVisible(False)
self.export_b.setVisible(False)
2022-10-25 13:38:18 +00:00
self.delete_all_b.setVisible(False)
2022-06-01 16:37:19 +00:00
# TESTING
2023-05-02 17:18:58 +00:00
if "--auto-select" in sys.argv:
2024-05-24 06:45:04 +00:00
recipe = "R56738/1"
2022-06-01 16:37:19 +00:00
cn = self.crud.select_index["name"]
2022-09-06 13:15:01 +00:00
self.crud.db_tw.clearSelection()
2022-06-01 16:37:19 +00:00
for rn in range(1, self.crud.db_tw.rowCount()):
2022-09-13 10:43:38 +00:00
if self.crud.db_tw.cellWidget(rn, cn).text() == recipe:
2022-06-01 16:37:19 +00:00
selection = self.crud.db_tw.model().index(rn, cn)
self.crud.db_tw.setCurrentIndex(selection)
break
self.test_timer = QTimer()
self.test_timer.setSingleShot(True)
self.test_timer.timeout.connect(self.select_b.clicked.emit)
self.test_timer.start(500)
# /TESTING
def check_modified(self, modified):
self.crud_modified = modified
self.check(self.crud_modified, self.selected)
2022-07-26 14:05:04 +00:00
def check_selected(self, selected=None):
if selected is not None and len(selected) == 1:
2024-02-23 15:01:43 +00:00
selected = selected[0] - 1 # - 1 because rn starts from 1 (filters line)
2022-06-01 16:37:19 +00:00
if selected >= 0 and selected < len(self.crud.data_index):
selected = self.crud.data_index[selected]
2022-07-06 13:54:22 +00:00
if selected is not None:
2022-07-26 14:05:04 +00:00
self.selected = selected
2022-07-06 13:54:22 +00:00
else:
self.selected = None
2022-06-01 16:37:19 +00:00
else:
self.selected = None
self.check(self.crud_modified, self.selected)
def check(self, modified, selected):
self.select_b.setEnabled(modified is False and selected is not None)
def select(self):
if self.selected is not None:
2022-07-26 14:05:04 +00:00
self.ok.emit(self.crud.db.table_model.get_by_id(self.selected))
2022-09-20 15:42:59 +00:00
2024-02-23 15:01:43 +00:00
def get_def(self, dict, key):
val = dict.get(key, self.defaults[key])
2023-01-31 16:42:37 +00:00
return val if val != "" else self.defaults[key]
2024-10-04 09:39:16 +00:00
# READ RECIPE STEPS FROM CSV ROW
2022-09-20 15:42:59 +00:00
def read_steps(self, row, defaults=None):
if defaults is None:
2022-11-09 16:18:11 +00:00
global noner
defaults = self.config.get("recipes_defaults", noner)
2022-09-26 11:08:34 +00:00
barcode_serial_field = self.config.get("recipe", {}).get("barcode_serial_field", "codice_a_barre").strip()
warning_image_field = self.config.get("recipe", {}).get("warning_image_field", "warning_img").strip()
2024-02-23 15:01:43 +00:00
decsep = locale.localeconv()["decimal_point"]
2023-01-05 11:16:04 +00:00
rcsv = row.get("r nominale", defaults["r nominale"]).replace(" ", "").replace(",", decsep).replace("Ω", "").replace("?", "")
2022-11-09 16:18:11 +00:00
if rcsv == "":
rcsv = "999"
2022-09-26 10:35:02 +00:00
print_template_field = self.config.get("recipe", {}).get("label_template_field", "modello_etichetta").strip()
2022-09-20 15:42:59 +00:00
return {
2022-10-18 09:57:08 +00:00
"count": {
"amount": row.get("dimensione_lotto", defaults["dimensione_lotto"]),
"warning_img": row.get(warning_image_field, defaults["warning_img"]),
2024-02-23 15:01:43 +00:00
"require_discard_piece": row.get("richiedi_inserimento_scarto", defaults["richiedi_inserimento_scarto"])
2022-10-18 09:57:08 +00:00
},
2022-09-20 15:42:59 +00:00
"connector": {
2022-09-22 19:19:19 +00:00
"connector": row.get("connettore", defaults["connettore"]),
2022-09-20 15:42:59 +00:00
},
"barcodes": {
2022-09-26 11:08:34 +00:00
"serial": row.get(barcode_serial_field, defaults["codice_a_barre"]),
2024-05-28 05:35:58 +00:00
"n_pieces": row.get("n_componenti") or defaults["n_componenti"],
2024-05-21 05:56:15 +00:00
"barcode_input_2": row.get("barcode_input_2", "-"),
"barcode_input_3": row.get("barcode_input_3", "-"),
2024-05-21 06:45:27 +00:00
"barcode_input_4": row.get("barcode_input_4", "-"),
2024-05-23 12:26:01 +00:00
"barcode_input_5": row.get("barcode_input_5", "-"),
2022-09-20 15:42:59 +00:00
},
"resistance": {
2023-02-14 16:03:17 +00:00
"scale": locale.atof(row.get("scala_resistenza", defaults["scala_resistenza"])),
"expected": locale.atof(rcsv),
2024-02-23 15:01:43 +00:00
"tolerance_pos": locale.atof(self.get_def(row, "tolleranza_resistenza_pos")),
"tolerance_neg": locale.atof(self.get_def(row, "tolleranza_resistenza_neg")),
2022-09-20 15:42:59 +00:00
},
2022-10-04 11:51:36 +00:00
"screws": {
"quantity": row.get("viti", defaults["viti"])
},
2023-08-25 16:51:03 +00:00
"instruction": {},
2022-09-20 15:42:59 +00:00
"leak_1": {
2022-09-21 14:15:04 +00:00
"pre_filling_time": int(row.get("tempo_pre_riempimento", defaults["tempo_pre_riempimento"])),
"pre_filling_pressure": int(row.get("pressione_pre_riempimento", defaults["pressione_pre_riempimento"])),
"filling_time": int(row.get("tempo_riempimento", defaults["tempo_riempimento"])),
2024-02-23 15:01:43 +00:00
"settling_time": int(self.get_def(row, "tempo_assestamento")),
"settling_pressure_min_percent": int(
row.get("percentuale_minima_pressione_assestamento", defaults["percentuale_minima_pressione_assestamento"])),
"settling_pressure_max_percent": int(
row.get("percentuale_massima_pressione_assestamento", defaults["percentuale_massima_pressione_assestamento"])),
2022-09-21 14:15:04 +00:00
"test_time": int(row.get("tempo_di_test", defaults["tempo_di_test"])),
2023-06-10 09:22:26 +00:00
"test_pressure_qneg": int(row.get("pressione_di_test_delta_minimo", defaults["pressione_di_test_delta_minimo"])),
2022-09-21 14:15:04 +00:00
"test_pressure": int(row.get("pressione_di_test", defaults["pressione_di_test"])),
2023-06-10 09:22:26 +00:00
"test_pressure_qpos": int(row.get("pressione_di_test_delta_massimo", defaults["pressione_di_test_delta_massimo"])),
2022-09-21 14:15:04 +00:00
"flush_time": int(row.get("tempo_svuotamento", defaults["tempo_svuotamento"])),
2023-02-17 10:46:30 +00:00
"flush_pressure": int(row.get("pressione_svuotamento", defaults["pressione_svuotamento"])),
2023-07-22 10:42:25 +00:00
"chan_sel": int(row.get("canale_di_prova", defaults["canale_di_prova"])),
"ext_flush_time": int(row.get("tempo_svuotamento_esterno", defaults["tempo_svuotamento_esterno"])),
"ext_blow_time": int(row.get("tempo_soffiaggio_esterno", defaults["tempo_soffiaggio_esterno"])),
2022-09-20 15:42:59 +00:00
},
"leak_2": {
2022-09-21 14:15:04 +00:00
"pre_filling_time": int(row.get("tempo_pre_riempimento_2", defaults["tempo_pre_riempimento_2"])),
"pre_filling_pressure": int(row.get("pressione_pre_riempimento_2", defaults["pressione_pre_riempimento_2"])),
"filling_time": int(row.get("tempo_riempimento_2", defaults["tempo_riempimento_2"])),
"settling_time": int(row.get("tempo_assestamento_2", defaults["tempo_assestamento_2"])),
2024-02-23 15:01:43 +00:00
"settling_pressure_min_percent": int(
row.get("percentuale_minima_pressione_assestamento_2", defaults["percentuale_minima_pressione_assestamento_2"])),
"settling_pressure_max_percent": int(
row.get("percentuale_massima_pressione_assestamento_2", defaults["percentuale_massima_pressione_assestamento_2"])),
2022-09-21 14:15:04 +00:00
"test_time": int(row.get("tempo_di_test_2", defaults["tempo_di_test_2"])),
2023-06-10 09:22:26 +00:00
"test_pressure_qneg": int(row.get("pressione_di_test_delta_minimo_2", defaults["pressione_di_test_delta_minimo_2"])),
2022-09-21 14:15:04 +00:00
"test_pressure": int(row.get("pressione_di_test_2", defaults["pressione_di_test_2"])),
2023-06-10 09:22:26 +00:00
"test_pressure_qpos": int(row.get("pressione_di_test_delta_massimo_2", defaults["pressione_di_test_delta_massimo_2"])),
2022-09-21 14:15:04 +00:00
"flush_time": int(row.get("tempo_svuotamento_2", defaults["tempo_svuotamento_2"])),
2023-02-17 10:46:30 +00:00
"flush_pressure": int(row.get("pressione_svuotamento_2", defaults["pressione_svuotamento_2"])),
2023-07-22 10:42:25 +00:00
"chan_sel": int(row.get("canale_di_prova_2", defaults["canale_di_prova_2"])),
"ext_flush_time": int(row.get("tempo_svuotamento_esterno_2", defaults["tempo_svuotamento_esterno"])),
"ext_blow_time": int(row.get("tempo_soffiaggio_esterno_2", defaults["tempo_soffiaggio_esterno"])),
2022-09-20 15:42:59 +00:00
},
"vision": {
2022-09-21 14:15:04 +00:00
"recipe": row.get("ricetta_visione", defaults["ricetta_visione"]),
2022-09-20 15:42:59 +00:00
},
"print": {
2022-09-26 10:35:02 +00:00
"template": row.get(print_template_field, defaults["modello_etichetta"]),
2024-02-23 15:01:43 +00:00
"labeltxt_1": row.get("testo_etich_1", ""),
2023-02-14 15:15:22 +00:00
"labeltxt_2": row.get("testo_etich_2", ""),
"labeltxt_3": row.get("testo_etich_3", ""),
"labeltxt_4": row.get("testo_etich_4", ""),
"labeltxt_5": row.get("barcode_input_finelinea", ""),
2023-02-17 10:46:30 +00:00
"extra_label": row.get("etichette_supplementari", ""),
2022-09-20 15:42:59 +00:00
},
}
2024-10-04 09:39:16 +00:00
# IMPORT RECIPES FROM CSV FILE TO DATABASE
2022-09-20 15:42:59 +00:00
def import_recipes(self, csv_path=None, defaults=None):
if defaults is None:
2022-11-09 16:18:11 +00:00
global noner
defaults = self.config.get("recipes_defaults", noner)
2022-09-20 15:42:59 +00:00
if csv_path is None:
2024-10-24 14:55:55 +00:00
options = QFileDialog.Options()
options |= QFileDialog.DontUseNativeDialog
2022-09-20 15:42:59 +00:00
csv_path, _ = QFileDialog.getOpenFileName(
2024-10-24 11:14:52 +00:00
self,
2024-10-24 14:58:31 +00:00
"Importazione ricette",
2022-09-20 15:42:59 +00:00
"ricette.csv",
"CSV data (*.csv);;All Files (*)",
2024-10-24 14:55:55 +00:00
options=options,
2022-09-20 15:42:59 +00:00
)
csv_path = str(csv_path)
if not len(csv_path):
return
self.log.info(f"recipes: importing recipes from {csv_path}")
2022-09-26 10:35:02 +00:00
recipe_name_field = self.config.get("recipe", {}).get("recipe_name_field", "codice_ricetta").strip()
2022-10-14 08:48:56 +00:00
part_number_field = self.config.get("recipe", {}).get("part_number_field", "part number").strip()
2022-11-04 15:45:42 +00:00
description_field = self.config.get("recipe", {}).get("description_field", "descrizione").strip()
2022-10-14 08:48:56 +00:00
barcode_enable_field = self.config.get("recipe", {}).get("barcode_enable_field", "verifica_codice_a_barre_abilitata").strip()
2022-10-13 18:20:04 +00:00
2024-02-23 15:01:43 +00:00
with open(csv_path, "r", encoding="utf-8-sig") as f:
2023-01-05 11:16:04 +00:00
reader = csv.DictReader(f)
2022-09-20 15:42:59 +00:00
count = 0
2023-01-05 11:16:04 +00:00
for ucrow in reader:
2024-02-23 15:01:43 +00:00
row = dict((k.lower(), v) for k, v in ucrow.items())
2022-09-26 10:35:02 +00:00
recipe_name = row.get(recipe_name_field, defaults["codice_ricetta"])
2022-09-26 15:43:59 +00:00
steps_specs = self.read_steps(row, defaults=defaults)
2024-09-18 12:18:07 +00:00
# create recipe or update existing one in DB
2022-09-20 15:42:59 +00:00
try:
recipe = Recipes.get_by_id(recipe_name)
recipe_is_new = False
except Recipes.DoesNotExist:
2022-09-26 15:43:59 +00:00
recipe = Recipes(name=recipe_name, part_number="TEMPORARY")
recipe_is_new = True
2024-09-18 12:18:07 +00:00
2022-09-21 14:15:04 +00:00
recipe.client = row.get("cliente", defaults["cliente"])
2022-10-14 08:48:56 +00:00
recipe.part_number = row.get(part_number_field, defaults["part_number"])
2022-11-04 14:17:32 +00:00
recipe.description = row.get(description_field, defaults["descrizione"])
2022-09-20 15:42:59 +00:00
recipe.spec = {
2022-10-18 09:57:08 +00:00
"count": len(row.get("dimensione_lotto_abilitata", defaults["dimensione_lotto_abilitata"])) and "count" not in self.unsupported_steps,
2024-02-23 15:01:43 +00:00
"connector": len(
row.get("verifica_connettore_abilitata", defaults["verifica_connettore_abilitata"])) and "connector" not in self.unsupported_steps,
2022-10-14 08:48:56 +00:00
"barcodes": len(row.get(barcode_enable_field, defaults["verifica_codice_a_barre_abilitata"])) and "barcodes" not in self.unsupported_steps,
2024-02-23 15:01:43 +00:00
"resistance": len(row.get("verifica_resistenza_connettore_abilitata",
defaults["verifica_resistenza_connettore_abilitata"])) and "resistance" not in self.unsupported_steps,
2022-10-04 11:51:36 +00:00
"screws": len(row.get("avvitatura_abilitata", defaults["avvitatura_abilitata"])) and "screws" not in self.unsupported_steps,
2023-01-26 13:13:03 +00:00
"instruction": len(row.get("istruzione_abilitata", defaults["istruzione_abilitata"])) and "instruction" not in self.unsupported_steps,
2023-02-17 16:38:03 +00:00
"leak_1": len(row.get("prova_tenuta_abilitata", defaults["prova_tenuta_abilitata"])) and "leak_1" not in self.unsupported_steps,
"leak_2": len(row.get("prova_tenuta_abilitata_2", defaults["prova_tenuta_abilitata_2"])) and "leak_2" not in self.unsupported_steps,
2022-09-22 19:19:19 +00:00
"vision": len(row.get("test_visione_abilitato", defaults["test_visione_abilitato"])) and "vision" not in self.unsupported_steps,
"print": len(row.get("stampa_etichetta_abilitata", defaults["stampa_etichetta_abilitata"])) and "print" not in self.unsupported_steps,
2022-09-20 15:42:59 +00:00
}
if recipe_is_new:
recipe.save(force_insert=True)
else:
recipe.save()
count += 1
2022-09-26 15:43:59 +00:00
db.commit()
2022-09-20 15:42:59 +00:00
self.log.info(f"recipes: imported {count} rows.")
self.crud.refresh()
2024-10-04 09:39:16 +00:00
# EXPORT RECIPES TABLE TO CSV FILE
2022-09-20 15:42:59 +00:00
def export_recipes(self, csv_path=None):
if csv_path is None:
2024-10-24 14:55:55 +00:00
options = QFileDialog.Options()
options |= QFileDialog.DontUseNativeDialog
2022-09-20 15:42:59 +00:00
csv_path, _ = QFileDialog.getSaveFileName(
2024-10-24 11:14:52 +00:00
self,
2022-09-20 15:42:59 +00:00
"Esportazione ricette",
"ricette.csv",
"CSV data (*.csv);;All Files (*)",
2024-10-24 14:55:55 +00:00
options=options,
2022-09-20 15:42:59 +00:00
)
csv_path = str(csv_path)
if not len(csv_path):
return
if not csv_path.lower().endswith(".csv"):
csv_path += ".csv"
csv_dir = os.path.dirname(csv_path)
if len(csv_dir):
os.makedirs(csv_dir, exist_ok=True)
2022-09-26 10:35:02 +00:00
recipe_name_field = self.config.get("recipe", {}).get("recipe_name_field", "codice_ricetta").strip()
2022-10-14 08:48:56 +00:00
barcode_enable_field = self.config.get("recipe", {}).get("barcode_enable_field", "verifica_codice_a_barre_abilitata").strip()
2022-09-26 11:08:34 +00:00
barcode_serial_field = self.config.get("recipe", {}).get("barcode_serial_field", "codice_a_barre").strip()
2022-09-26 10:35:02 +00:00
print_template_field = self.config.get("recipe", {}).get("label_template_field", "modello_etichetta").strip()
2022-09-20 15:42:59 +00:00
data = []
2024-10-25 09:34:25 +00:00
fieldnames = set() # Use a set to avoid duplicates
2022-11-07 15:26:57 +00:00
for recipe in list(Recipes.select()):
2022-09-20 15:42:59 +00:00
steps = recipe.get_steps_map()
exportable = {
2024-10-25 07:18:44 +00:00
# BASE SECTION
2022-09-26 10:35:02 +00:00
recipe_name_field: recipe.name,
2022-09-21 14:15:04 +00:00
"cliente": recipe.client,
2022-09-20 15:42:59 +00:00
"part_number": recipe.part_number,
}
2024-10-25 07:18:44 +00:00
2024-10-25 09:34:25 +00:00
# Add base fields to the fieldnames
fieldnames.update([recipe_name_field, "cliente", "part_number"])
2024-10-25 07:18:44 +00:00
2024-10-25 09:34:25 +00:00
# Check and add fields conditionally for each section
if "connector" in steps:
2024-10-25 07:18:44 +00:00
exportable.update({
"verifica_connettore_abilitata": "x",
"connettore": steps["connector"].spec["connector"]
})
2024-10-25 09:34:25 +00:00
fieldnames.update(["verifica_connettore_abilitata", "connettore"])
if "resistance" in steps:
2024-10-25 07:18:44 +00:00
exportable.update({
"verifica_resistenza_connettore_abilitata": "x",
"scala_resistenza": steps["resistance"].spec["scale"],
"r nominale": steps["resistance"].spec["expected"],
"tolleranza_resistenza_pos": steps["resistance"].spec["tolerance_pos"],
"tolleranza_resistenza_neg": steps["resistance"].spec["tolerance_neg"]
})
2024-10-25 09:34:25 +00:00
fieldnames.update(["verifica_resistenza_connettore_abilitata", "scala_resistenza", "r nominale",
"tolleranza_resistenza_pos", "tolleranza_resistenza_neg"])
2024-10-25 07:18:44 +00:00
2024-10-25 09:34:25 +00:00
if "barcodes" in steps:
2024-10-25 07:18:44 +00:00
exportable.update({
barcode_enable_field: "x",
barcode_serial_field: steps["barcodes"].spec["serial"]
})
2024-10-25 09:34:25 +00:00
fieldnames.update([barcode_enable_field, barcode_serial_field])
if recipe.spec.get("steps", {}).get("screws") and "screws" in steps:
2024-10-25 07:18:44 +00:00
exportable.update({
"avvitatura_abilitata": "x",
"viti": steps["screws"].spec["quantity"]
})
2024-10-25 09:34:25 +00:00
fieldnames.update(["avvitatura_abilitata", "viti"])
2024-10-25 07:18:44 +00:00
2024-10-25 09:34:25 +00:00
if "leak_1" in steps:
2024-10-25 07:18:44 +00:00
exportable.update({
"prova_tenuta_abilitata": "x",
"tempo_pre_riempimento": steps["leak_1"].spec["pre_filling_time"],
"pressione_pre_riempimento": steps["leak_1"].spec["pre_filling_pressure"],
"tempo_riempimento": steps["leak_1"].spec["filling_time"],
"tempo_assestamento": steps["leak_1"].spec["settling_time"],
"percentuale_minima_pressione_assestamento": steps["leak_1"].spec["settling_pressure_min_percent"],
"percentuale_massima_pressione_assestamento": steps["leak_1"].spec["settling_pressure_max_percent"],
"tempo_di_test": steps["leak_1"].spec["test_time"],
"pressione_di_test_delta_minimo": steps["leak_1"].spec["test_pressure_qneg"],
"pressione_di_test": steps["leak_1"].spec["test_pressure"],
"pressione_di_test_delta_massimo": steps["leak_1"].spec["test_pressure_qpos"],
"tempo_svuotamento": steps["leak_1"].spec["flush_time"],
"pressione_svuotamento": steps["leak_1"].spec["flush_pressure"],
})
2024-10-25 09:34:25 +00:00
fieldnames.update(["prova_tenuta_abilitata", "tempo_pre_riempimento", "pressione_pre_riempimento",
"tempo_riempimento", "tempo_assestamento",
"percentuale_minima_pressione_assestamento",
"percentuale_massima_pressione_assestamento", "tempo_di_test",
"pressione_di_test_delta_minimo",
"pressione_di_test", "pressione_di_test_delta_massimo", "tempo_svuotamento",
"pressione_svuotamento"])
if "leak_2" in steps:
2024-10-25 07:18:44 +00:00
exportable.update({
"prova_tenuta_abilitata_2": "x",
"tempo_pre_riempimento_2": steps["leak_2"].spec["pre_filling_time"],
"pressione_pre_riempimento_2": steps["leak_2"].spec["pre_filling_pressure"],
"tempo_riempimento_2": steps["leak_2"].spec["filling_time"],
"tempo_assestamento_2": steps["leak_2"].spec["settling_time"],
"percentuale_minima_pressione_assestamento_2": steps["leak_2"].spec[
"settling_pressure_min_percent"],
"percentuale_massima_pressione_assestamento_2": steps["leak_2"].spec[
"settling_pressure_max_percent"],
"tempo_di_test_2": steps["leak_2"].spec["test_time"],
"pressione_di_test_delta_minimo_2": steps["leak_2"].spec["test_pressure_qneg"],
"pressione_di_test_2": steps["leak_2"].spec["test_pressure"],
"pressione_di_test_delta_massimo_2": steps["leak_2"].spec["test_pressure_qpos"],
"tempo_svuotamento_2": steps["leak_2"].spec["flush_time"],
"pressione_svuotamento_2": steps["leak_2"].spec["flush_pressure"],
})
2024-10-25 09:34:25 +00:00
fieldnames.update(["prova_tenuta_abilitata_2", "tempo_pre_riempimento_2", "pressione_pre_riempimento_2",
"tempo_riempimento_2", "tempo_assestamento_2",
"percentuale_minima_pressione_assestamento_2",
"percentuale_massima_pressione_assestamento_2", "tempo_di_test_2",
"pressione_di_test_delta_minimo_2", "pressione_di_test_2",
"pressione_di_test_delta_massimo_2",
"tempo_svuotamento_2", "pressione_svuotamento_2"])
2024-10-25 07:18:44 +00:00
2024-10-25 09:34:25 +00:00
if "vision" in steps:
2024-10-25 07:18:44 +00:00
exportable.update({
"test_visione_abilitato": recipe.spec["vision"],
"ricetta_visione": steps["vision"].spec["recipe"]
})
2024-10-25 09:34:25 +00:00
fieldnames.update(["test_visione_abilitato", "ricetta_visione"])
2024-10-25 07:18:44 +00:00
2024-10-25 09:34:25 +00:00
if "print" in steps:
2024-10-25 07:18:44 +00:00
exportable.update({
"stampa_etichetta_abilitata": "x",
print_template_field: steps["print"].spec["template"],
"etichette_supplementari": steps["print"].spec["extra_label"]
})
2024-10-25 09:34:25 +00:00
fieldnames.update(["stampa_etichetta_abilitata", print_template_field, "etichette_supplementari"])
2024-10-25 07:18:44 +00:00
# Append the exportable dictionary to the data list
2022-09-20 15:42:59 +00:00
data.append(exportable)
2024-10-25 07:18:44 +00:00
2024-10-25 09:34:25 +00:00
# Convert the set to a list for CSV writing
fieldnames = list(fieldnames)
2024-10-25 07:18:44 +00:00
# Export to CSV if there is data
2022-09-20 15:42:59 +00:00
if len(data):
self.log.info(f"recipes: exporting recipes to {csv_path}")
with open(csv_path, "w", newline="") as f:
2024-10-25 09:34:25 +00:00
w = csv.DictWriter(f, fieldnames=fieldnames, extrasaction="ignore")
2022-09-20 15:42:59 +00:00
w.writeheader()
w.writerows(data)
self.log.info(f"recipes: exported {len(data)} rows.")
2022-10-25 13:38:18 +00:00
def delete_recipes(self):
ret = QMessageBox.warning(
None,
"Attenzione si sta cercando di cancellare tutte le ricette!",
"Si è sicuri di voler eliminare tutte le ricette?\nQuesta operazione non può essere annullata!",
buttons=QMessageBox.Ok | QMessageBox.Cancel,
defaultButton=QMessageBox.Cancel
)
if ret == QMessageBox.Ok:
Recipes.delete().execute()
self.crud.refresh()
2024-10-24 14:58:31 +00:00
def backup_current_recipes(self):
# Define the backup directory and file name
backup_dir = os.path.join('config', 'csv_import', 'backup_csv')
timestamp = datetime.now().strftime("%d%m%y%H%M%S")
2024-10-24 14:58:31 +00:00
backup_file = f"backup_{timestamp}.csv"
backup_path = os.path.join(backup_dir, backup_file)
# Ensure the backup directory exists
os.makedirs(backup_dir, exist_ok=True)
# Export current recipes to backup file
self.export_recipes(csv_path=backup_path)
def move_imported_csv(self, csv_path):
# Move the imported CSV to the 'imported_csv' directory
imported_dir = os.path.join('config', 'csv_import', 'imported_csv')
os.makedirs(imported_dir, exist_ok=True)
imported_path = os.path.join(imported_dir, os.path.basename(csv_path))
shutil.move(csv_path, imported_path)
self.log.info(f"Imported CSV moved to {imported_path}")
return imported_path
def check_and_import_auto_csv(self):
# Define the directory to check
auto_import_dir = os.path.join('config', 'csv_import', 'auto_csv_import')
# Check if the directory exists and is not empty
if os.path.exists(auto_import_dir) and os.listdir(auto_import_dir):
# Perform backup
self.backup_current_recipes()
# Move and import each CSV file in the directory
for csv_file in os.listdir(auto_import_dir):
csv_path = os.path.join(auto_import_dir, csv_file)
if os.path.isfile(csv_path) and csv_file.endswith(".csv"):
self.import_recipes(csv_path=csv_path)
self.move_imported_csv(csv_path)