Introdotto versioning visibile ver 1.0
This commit is contained in:
@@ -9,6 +9,10 @@ import asyncio
|
|||||||
import threading
|
import threading
|
||||||
import contextlib
|
import contextlib
|
||||||
|
|
||||||
|
from version_info import module_version
|
||||||
|
|
||||||
|
__version__ = module_version(__name__)
|
||||||
|
|
||||||
|
|
||||||
class _LoopHolder:
|
class _LoopHolder:
|
||||||
"""Store the global loop instance and its worker thread."""
|
"""Store the global loop instance and its worker thread."""
|
||||||
|
|||||||
@@ -18,6 +18,9 @@ from typing import Any, Dict, Optional
|
|||||||
from sqlalchemy import text
|
from sqlalchemy import text
|
||||||
from sqlalchemy.ext.asyncio import create_async_engine
|
from sqlalchemy.ext.asyncio import create_async_engine
|
||||||
from sqlalchemy.pool import NullPool
|
from sqlalchemy.pool import NullPool
|
||||||
|
from version_info import module_version
|
||||||
|
|
||||||
|
__version__ = module_version(__name__)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import pyodbc
|
import pyodbc
|
||||||
|
|||||||
@@ -8,7 +8,9 @@ from pathlib import Path
|
|||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from user_session import UserSession
|
from user_session import UserSession
|
||||||
|
from version_info import module_version
|
||||||
|
|
||||||
|
__version__ = module_version(__name__)
|
||||||
|
|
||||||
AUDIT_LOG_PATH = Path(__file__).with_name("warehouse_audit.log")
|
AUDIT_LOG_PATH = Path(__file__).with_name("warehouse_audit.log")
|
||||||
_LOGGER = logging.getLogger("warehouse_audit")
|
_LOGGER = logging.getLogger("warehouse_audit")
|
||||||
|
|||||||
@@ -1,3 +1,9 @@
|
|||||||
|
from pathlib import Path
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
os.chdir(Path(__file__).resolve().parent)
|
||||||
|
|
||||||
from runtime_support import ensure_stdio, run_with_fatal_log
|
from runtime_support import ensure_stdio, run_with_fatal_log
|
||||||
|
|
||||||
ensure_stdio("warehouse_barcode")
|
ensure_stdio("warehouse_barcode")
|
||||||
|
|||||||
@@ -19,6 +19,9 @@ from barcode_repository import BarcodeRepository
|
|||||||
from barcode_service import BarcodeActionResult, BarcodeService, BarcodeViewState
|
from barcode_service import BarcodeActionResult, BarcodeService, BarcodeViewState
|
||||||
from db_config import build_dsn_from_config, ensure_db_config
|
from db_config import build_dsn_from_config, ensure_db_config
|
||||||
from login_window import prompt_login_compact
|
from login_window import prompt_login_compact
|
||||||
|
from version_info import module_version, versioned_title
|
||||||
|
|
||||||
|
__version__ = module_version(__name__)
|
||||||
|
|
||||||
|
|
||||||
class BarcodeClientApp:
|
class BarcodeClientApp:
|
||||||
@@ -59,9 +62,11 @@ class BarcodeClientApp:
|
|||||||
self._apply_state(self.service.state)
|
self._apply_state(self.service.state)
|
||||||
self._bind_keys()
|
self._bind_keys()
|
||||||
self.root.protocol("WM_DELETE_WINDOW", self._shutdown)
|
self.root.protocol("WM_DELETE_WINDOW", self._shutdown)
|
||||||
|
self.root.after(80, self._finalize_window_placement)
|
||||||
|
self.root.after(250, self._finalize_window_placement)
|
||||||
|
|
||||||
def _build_ui(self) -> None:
|
def _build_ui(self) -> None:
|
||||||
self.root.title("WMS")
|
self.root.title(versioned_title("WMS", __name__))
|
||||||
self.root.configure(bg="#f1f1f1")
|
self.root.configure(bg="#f1f1f1")
|
||||||
self._apply_responsive_geometry()
|
self._apply_responsive_geometry()
|
||||||
|
|
||||||
@@ -254,6 +259,18 @@ class BarcodeClientApp:
|
|||||||
|
|
||||||
self.root.geometry(f"{width}x{height}+{x}+{y}")
|
self.root.geometry(f"{width}x{height}+{x}+{y}")
|
||||||
|
|
||||||
|
def _finalize_window_placement(self) -> None:
|
||||||
|
"""Let Windows finish frame sizing, then snap the barcode window in place."""
|
||||||
|
|
||||||
|
try:
|
||||||
|
if self._is_barcode_desktop:
|
||||||
|
self.root.state("zoomed")
|
||||||
|
else:
|
||||||
|
self.root.geometry("+0+0")
|
||||||
|
self.root.update_idletasks()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
def _bind_keys(self) -> None:
|
def _bind_keys(self) -> None:
|
||||||
self.root.bind("<F1>", lambda _e: self._start_queue(1))
|
self.root.bind("<F1>", lambda _e: self._start_queue(1))
|
||||||
self.root.bind("<F2>", lambda _e: self._start_queue(0))
|
self.root.bind("<F2>", lambda _e: self._start_queue(0))
|
||||||
|
|||||||
@@ -9,6 +9,10 @@ from __future__ import annotations
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
|
from version_info import module_version
|
||||||
|
|
||||||
|
__version__ = module_version(__name__)
|
||||||
|
|
||||||
|
|
||||||
SQL_NEXT_PICKING = """
|
SQL_NEXT_PICKING = """
|
||||||
SELECT TOP (1)
|
SELECT TOP (1)
|
||||||
|
|||||||
@@ -6,7 +6,9 @@ from dataclasses import dataclass
|
|||||||
from typing import Literal
|
from typing import Literal
|
||||||
|
|
||||||
from barcode_repository import BarcodeRepository, LegacyMoveResult
|
from barcode_repository import BarcodeRepository, LegacyMoveResult
|
||||||
|
from version_info import module_version
|
||||||
|
|
||||||
|
__version__ = module_version(__name__)
|
||||||
|
|
||||||
ModeName = Literal["idle", "priority_high", "priority_low", "manual_load", "manual_unload", "confirm"]
|
ModeName = Literal["idle", "priority_high", "priority_low", "manual_load", "manual_unload", "confirm"]
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,9 @@ import tkinter as tk
|
|||||||
import customtkinter as ctk
|
import customtkinter as ctk
|
||||||
|
|
||||||
from ui_theme import theme_color, theme_font, theme_padding, theme_value
|
from ui_theme import theme_color, theme_font, theme_padding, theme_value
|
||||||
|
from version_info import module_version
|
||||||
|
|
||||||
|
__version__ = module_version(__name__)
|
||||||
|
|
||||||
|
|
||||||
class InlineBusyOverlay:
|
class InlineBusyOverlay:
|
||||||
|
|||||||
@@ -13,6 +13,9 @@ from async_msssql_query import AsyncMSSQLClient, make_mssql_dsn
|
|||||||
from locale_text import load_locale_catalog, text as loc_text
|
from locale_text import load_locale_catalog, text as loc_text
|
||||||
from tooltips import WidgetToolTip, load_tooltip_catalog, tooltip_text
|
from tooltips import WidgetToolTip, load_tooltip_catalog, tooltip_text
|
||||||
from ui_theme import theme_section, theme_value
|
from ui_theme import theme_section, theme_value
|
||||||
|
from version_info import module_version, versioned_title
|
||||||
|
|
||||||
|
__version__ = module_version(__name__)
|
||||||
|
|
||||||
CONFIG_PATH = Path(__file__).with_name("db_connection.json")
|
CONFIG_PATH = Path(__file__).with_name("db_connection.json")
|
||||||
DEFAULT_DB_CONFIG: dict[str, Any] = {
|
DEFAULT_DB_CONFIG: dict[str, Any] = {
|
||||||
@@ -101,7 +104,7 @@ class DatabaseConfigWindow(tk.Toplevel):
|
|||||||
self.result_config: dict[str, Any] | None = None
|
self.result_config: dict[str, Any] | None = None
|
||||||
merged = {**DEFAULT_DB_CONFIG, **(initial or {})}
|
merged = {**DEFAULT_DB_CONFIG, **(initial or {})}
|
||||||
|
|
||||||
self.title(loc_text("dbconfig.title", catalog=self._locale_catalog, default="Configurazione Database"))
|
self.title(versioned_title(loc_text("dbconfig.title", catalog=self._locale_catalog, default="Configurazione Database"), __name__))
|
||||||
self.geometry(str(theme_value(self._theme, "window_geometry", "520x360")))
|
self.geometry(str(theme_value(self._theme, "window_geometry", "520x360")))
|
||||||
self.resizable(False, False)
|
self.resizable(False, False)
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -19,8 +19,9 @@ from typing import Any, Callable, Optional
|
|||||||
import customtkinter as ctk
|
import customtkinter as ctk
|
||||||
|
|
||||||
from async_loop_singleton import get_global_loop
|
from async_loop_singleton import get_global_loop
|
||||||
|
from version_info import module_version
|
||||||
|
|
||||||
__VERSION__ = "GestioneAreeFrame v3.3.0-singleloop"
|
__version__ = module_version(__name__)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from async_msssql_query import AsyncMSSQLClient # noqa: F401
|
from async_msssql_query import AsyncMSSQLClient # noqa: F401
|
||||||
|
|||||||
@@ -26,8 +26,11 @@ from tksheet import Sheet, natural_sort_key
|
|||||||
from tooltips import WidgetToolTip, load_tooltip_catalog, tooltip_text
|
from tooltips import WidgetToolTip, load_tooltip_catalog, tooltip_text
|
||||||
from ui_theme import theme_color, theme_font, theme_section, theme_value
|
from ui_theme import theme_color, theme_font, theme_section, theme_value
|
||||||
from user_session import UserSession
|
from user_session import UserSession
|
||||||
|
from version_info import module_version, versioned_title
|
||||||
from window_placement import place_window_fullsize_below_parent_later
|
from window_placement import place_window_fullsize_below_parent_later
|
||||||
|
|
||||||
|
__version__ = module_version(__name__)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
except Exception: # pragma: no cover - fallback used only when Loguru is not available
|
except Exception: # pragma: no cover - fallback used only when Loguru is not available
|
||||||
@@ -212,7 +215,7 @@ class LayoutWindow(ctk.CTkToplevel):
|
|||||||
self._theme = theme_section("layout_window", {})
|
self._theme = theme_section("layout_window", {})
|
||||||
self._locale_catalog = load_locale_catalog()
|
self._locale_catalog = load_locale_catalog()
|
||||||
self._tooltip_catalog = load_tooltip_catalog()
|
self._tooltip_catalog = load_tooltip_catalog()
|
||||||
self.title(loc_text("layout.title", catalog=self._locale_catalog, default="Layout corsie"))
|
self.title(versioned_title(loc_text("layout.title", catalog=self._locale_catalog, default="Layout corsie"), __name__))
|
||||||
self.geometry(str(theme_value(self._theme, "window_geometry", "1200x740")))
|
self.geometry(str(theme_value(self._theme, "window_geometry", "1200x740")))
|
||||||
minsize = theme_value(self._theme, "window_minsize", [980, 560])
|
minsize = theme_value(self._theme, "window_minsize", [980, 560])
|
||||||
self.minsize(int(minsize[0]), int(minsize[1]))
|
self.minsize(int(minsize[0]), int(minsize[1]))
|
||||||
|
|||||||
@@ -71,8 +71,11 @@ from gestione_aree import AsyncRunner
|
|||||||
from locale_text import load_locale_catalog, text as loc_text
|
from locale_text import load_locale_catalog, text as loc_text
|
||||||
from ui_theme import theme_color, theme_font, theme_section, theme_value
|
from ui_theme import theme_color, theme_font, theme_section, theme_value
|
||||||
from user_session import UserSession
|
from user_session import UserSession
|
||||||
|
from version_info import module_version, versioned_title
|
||||||
from window_placement import place_window_fullsize_below_parent_later
|
from window_placement import place_window_fullsize_below_parent_later
|
||||||
|
|
||||||
|
__version__ = module_version(__name__)
|
||||||
|
|
||||||
# === IMPORT procedura async prenota/s-prenota (no pyodbc qui) ===
|
# === IMPORT procedura async prenota/s-prenota (no pyodbc qui) ===
|
||||||
import asyncio
|
import asyncio
|
||||||
try:
|
try:
|
||||||
@@ -1240,7 +1243,7 @@ def open_pickinglist_window(parent: tk.Misc, db_client, session: UserSession | N
|
|||||||
|
|
||||||
win = ctk.CTkToplevel(parent)
|
win = ctk.CTkToplevel(parent)
|
||||||
locale_catalog = load_locale_catalog()
|
locale_catalog = load_locale_catalog()
|
||||||
win.title(loc_text("picking.title", catalog=locale_catalog, default="Gestione Picking List"))
|
win.title(versioned_title(loc_text("picking.title", catalog=locale_catalog, default="Gestione Picking List"), __name__))
|
||||||
theme = theme_section("pickinglist_window", {})
|
theme = theme_section("pickinglist_window", {})
|
||||||
win.geometry(str(theme_value(theme, "window_geometry", "1200x700")))
|
win.geometry(str(theme_value(theme, "window_geometry", "1200x700")))
|
||||||
minsize = theme_value(theme, "window_minsize", [1000, 560])
|
minsize = theme_value(theme, "window_minsize", [1000, 560])
|
||||||
|
|||||||
@@ -21,6 +21,9 @@ from busy_overlay import InlineBusyOverlay
|
|||||||
from locale_text import load_locale_catalog, text as loc_text
|
from locale_text import load_locale_catalog, text as loc_text
|
||||||
from ui_theme import theme_color, theme_font, theme_section, theme_value
|
from ui_theme import theme_color, theme_font, theme_section, theme_value
|
||||||
from user_session import UserSession
|
from user_session import UserSession
|
||||||
|
from version_info import module_version, versioned_title
|
||||||
|
|
||||||
|
__version__ = module_version(__name__)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
@@ -457,7 +460,7 @@ class ScaricoDialog(ctk.CTkToplevel):
|
|||||||
self._async = AsyncRunner(self)
|
self._async = AsyncRunner(self)
|
||||||
self.rows_tree: ttk.Treeview | None = None
|
self.rows_tree: ttk.Treeview | None = None
|
||||||
|
|
||||||
self.title(loc_text("scarico.title", catalog=self._locale_catalog, default="Scarica {ubicazione}").format(ubicazione=ubicazione))
|
self.title(versioned_title(loc_text("scarico.title", catalog=self._locale_catalog, default="Scarica {ubicazione}").format(ubicazione=ubicazione), __name__))
|
||||||
self.resizable(False, False)
|
self.resizable(False, False)
|
||||||
self.transient(parent)
|
self.transient(parent)
|
||||||
self.protocol("WM_DELETE_WINDOW", self._close)
|
self.protocol("WM_DELETE_WINDOW", self._close)
|
||||||
|
|||||||
@@ -6,6 +6,9 @@ import json
|
|||||||
from functools import lru_cache
|
from functools import lru_cache
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
from version_info import module_version
|
||||||
|
|
||||||
|
__version__ = module_version(__name__)
|
||||||
|
|
||||||
_LOCALE_FILE = Path(__file__).with_name("locale.json")
|
_LOCALE_FILE = Path(__file__).with_name("locale.json")
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,9 @@ from gestione_aree import AsyncRunner
|
|||||||
from locale_text import load_locale_catalog, text as loc_text
|
from locale_text import load_locale_catalog, text as loc_text
|
||||||
from ui_theme import theme_section, theme_value
|
from ui_theme import theme_section, theme_value
|
||||||
from user_session import UserSession, create_user_session
|
from user_session import UserSession, create_user_session
|
||||||
|
from version_info import module_version, versioned_title
|
||||||
|
|
||||||
|
__version__ = module_version(__name__)
|
||||||
|
|
||||||
|
|
||||||
SQL_LOGIN = """
|
SQL_LOGIN = """
|
||||||
@@ -64,7 +67,7 @@ class LoginWindow(tk.Toplevel):
|
|||||||
self._show_ready_after_id: str | None = None
|
self._show_ready_after_id: str | None = None
|
||||||
self._clear_topmost_after_id: str | None = None
|
self._clear_topmost_after_id: str | None = None
|
||||||
|
|
||||||
self.title(loc_text("login.msg.title", catalog=self._locale_catalog, default="Login"))
|
self.title(versioned_title(loc_text("login.msg.title", catalog=self._locale_catalog, default="Login"), __name__))
|
||||||
self.geometry("170x145+0+0" if self.compact else str(theme_value(self._theme, "window_geometry", "165x155+0+0")))
|
self.geometry("170x145+0+0" if self.compact else str(theme_value(self._theme, "window_geometry", "165x155+0+0")))
|
||||||
self.resizable(False, False)
|
self.resizable(False, False)
|
||||||
try:
|
try:
|
||||||
|
|||||||
4
main.py
4
main.py
@@ -33,6 +33,7 @@ from storico_udc import open_storico_udc_window
|
|||||||
from tooltips import WidgetToolTip, load_tooltip_catalog, tooltip_text
|
from tooltips import WidgetToolTip, load_tooltip_catalog, tooltip_text
|
||||||
from ui_theme import theme_font, theme_section, theme_value
|
from ui_theme import theme_font, theme_section, theme_value
|
||||||
from user_session import UserSession, create_user_session
|
from user_session import UserSession, create_user_session
|
||||||
|
from version_info import module_version, versioned_title
|
||||||
from view_celle_multi_udc import open_celle_multiple_window
|
from view_celle_multi_udc import open_celle_multiple_window
|
||||||
from window_placement import (
|
from window_placement import (
|
||||||
cascade_children_below_parent,
|
cascade_children_below_parent,
|
||||||
@@ -43,6 +44,7 @@ from window_placement import (
|
|||||||
# Development shortcut: skip the login dialog and boot directly as MAG1.
|
# Development shortcut: skip the login dialog and boot directly as MAG1.
|
||||||
# Set to False when you want to restore normal authentication.
|
# Set to False when you want to restore normal authentication.
|
||||||
BYPASS_LOGIN = False
|
BYPASS_LOGIN = False
|
||||||
|
__version__ = module_version(__name__)
|
||||||
BYPASS_LOGIN_USER = {
|
BYPASS_LOGIN_USER = {
|
||||||
"operator_id": 4,
|
"operator_id": 4,
|
||||||
"login": "MAG1",
|
"login": "MAG1",
|
||||||
@@ -176,7 +178,7 @@ class Launcher(ctk.CTk):
|
|||||||
color=str(theme_value(self._theme, "exit_icon_color", "#ca3d3d"))
|
color=str(theme_value(self._theme, "exit_icon_color", "#ca3d3d"))
|
||||||
)
|
)
|
||||||
self.title(
|
self.title(
|
||||||
f"{loc_text('launcher.window_title', catalog=self._locale_catalog, default='Warehouse 1.0.0')} - {self.session.display_name}"
|
f"{versioned_title(loc_text('launcher.window_title', catalog=self._locale_catalog, default='Warehouse'), __name__)} - {self.session.display_name}"
|
||||||
)
|
)
|
||||||
self._apply_dynamic_geometry()
|
self._apply_dynamic_geometry()
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,10 @@ from functools import wraps
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Dict, List, Optional
|
from typing import Any, Dict, List, Optional
|
||||||
|
|
||||||
|
from version_info import module_version
|
||||||
|
|
||||||
|
__version__ = module_version(__name__)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
except Exception: # pragma: no cover - safety fallback if dependency is missing locally
|
except Exception: # pragma: no cover - safety fallback if dependency is missing locally
|
||||||
|
|||||||
@@ -24,8 +24,11 @@ from gestione_scarico import move_pallet_async
|
|||||||
from locale_text import load_locale_catalog, text as loc_text
|
from locale_text import load_locale_catalog, text as loc_text
|
||||||
from tooltips import WidgetToolTip, load_tooltip_catalog, tooltip_text
|
from tooltips import WidgetToolTip, load_tooltip_catalog, tooltip_text
|
||||||
from ui_theme import theme_color, theme_font, theme_padding, theme_section, theme_value
|
from ui_theme import theme_color, theme_font, theme_padding, theme_section, theme_value
|
||||||
|
from version_info import module_version, versioned_title
|
||||||
from window_placement import place_window_fullsize_below_parent_later
|
from window_placement import place_window_fullsize_below_parent_later
|
||||||
|
|
||||||
|
__version__ = module_version(__name__)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
except Exception: # pragma: no cover - fallback used only when Loguru is not available
|
except Exception: # pragma: no cover - fallback used only when Loguru is not available
|
||||||
@@ -275,7 +278,7 @@ class ResetCorsieWindow(ctk.CTkToplevel):
|
|||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
self._theme = theme_section("reset_corsie", {})
|
self._theme = theme_section("reset_corsie", {})
|
||||||
self._locale_catalog = load_locale_catalog()
|
self._locale_catalog = load_locale_catalog()
|
||||||
self.title(loc_text("reset.title", catalog=self._locale_catalog, default="Gestione Corsie - svuotamento celle per corsia"))
|
self.title(versioned_title(loc_text("reset.title", catalog=self._locale_catalog, default="Gestione Corsie - svuotamento celle per corsia"), __name__))
|
||||||
self.geometry(str(theme_value(self._theme, "window_geometry", "1000x680")))
|
self.geometry(str(theme_value(self._theme, "window_geometry", "1000x680")))
|
||||||
minsize = theme_value(self._theme, "window_minsize", [880, 560])
|
minsize = theme_value(self._theme, "window_minsize", [880, 560])
|
||||||
self.minsize(int(minsize[0]), int(minsize[1]))
|
self.minsize(int(minsize[0]), int(minsize[1]))
|
||||||
|
|||||||
@@ -6,25 +6,42 @@ import sys
|
|||||||
import traceback
|
import traceback
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
import tempfile
|
||||||
from typing import Callable, TypeVar
|
from typing import Callable, TypeVar
|
||||||
|
|
||||||
|
from version_info import module_version
|
||||||
|
|
||||||
|
__version__ = module_version(__name__)
|
||||||
|
|
||||||
BASE_DIR = Path(__file__).resolve().parent
|
BASE_DIR = Path(__file__).resolve().parent
|
||||||
FATAL_LOG = BASE_DIR / "warehouse_fatal.log"
|
FATAL_LOG = BASE_DIR / "warehouse_fatal.log"
|
||||||
|
TEMP_DIR = Path(tempfile.gettempdir())
|
||||||
_STDIO_HANDLES = []
|
_STDIO_HANDLES = []
|
||||||
T = TypeVar("T")
|
T = TypeVar("T")
|
||||||
|
|
||||||
|
|
||||||
|
def _open_log_file(path: Path):
|
||||||
|
"""Open a log path, falling back to the user temp directory when needed."""
|
||||||
|
|
||||||
|
try:
|
||||||
|
path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
return path.open("a", encoding="utf-8", buffering=1)
|
||||||
|
except Exception:
|
||||||
|
fallback = TEMP_DIR / path.name
|
||||||
|
fallback.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
return fallback.open("a", encoding="utf-8", buffering=1)
|
||||||
|
|
||||||
|
|
||||||
def ensure_stdio(app_name: str) -> None:
|
def ensure_stdio(app_name: str) -> None:
|
||||||
"""Give ``pythonw`` a real stdout/stderr target before loggers are imported."""
|
"""Give ``pythonw`` a real stdout/stderr target before loggers are imported."""
|
||||||
|
|
||||||
stamp = datetime.now().strftime("%Y%m%d")
|
stamp = datetime.now().strftime("%Y%m%d")
|
||||||
if sys.stdout is None:
|
if sys.stdout is None:
|
||||||
handle = (BASE_DIR / f"{app_name}_stdout_{stamp}.log").open("a", encoding="utf-8", buffering=1)
|
handle = _open_log_file(BASE_DIR / f"{app_name}_stdout_{stamp}.log")
|
||||||
_STDIO_HANDLES.append(handle)
|
_STDIO_HANDLES.append(handle)
|
||||||
sys.stdout = handle
|
sys.stdout = handle
|
||||||
if sys.stderr is None:
|
if sys.stderr is None:
|
||||||
handle = (BASE_DIR / f"{app_name}_stderr_{stamp}.log").open("a", encoding="utf-8", buffering=1)
|
handle = _open_log_file(BASE_DIR / f"{app_name}_stderr_{stamp}.log")
|
||||||
_STDIO_HANDLES.append(handle)
|
_STDIO_HANDLES.append(handle)
|
||||||
sys.stderr = handle
|
sys.stderr = handle
|
||||||
|
|
||||||
@@ -32,12 +49,21 @@ def ensure_stdio(app_name: str) -> None:
|
|||||||
def log_fatal(app_name: str, exc: BaseException) -> None:
|
def log_fatal(app_name: str, exc: BaseException) -> None:
|
||||||
"""Write one startup/runtime crash to a persistent log file."""
|
"""Write one startup/runtime crash to a persistent log file."""
|
||||||
|
|
||||||
with FATAL_LOG.open("a", encoding="utf-8") as handle:
|
with _open_log_file(FATAL_LOG) as handle:
|
||||||
handle.write("\n" + "=" * 80 + "\n")
|
handle.write("\n" + "=" * 80 + "\n")
|
||||||
handle.write(f"{datetime.now():%Y-%m-%d %H:%M:%S} | {app_name}\n")
|
handle.write(f"{datetime.now():%Y-%m-%d %H:%M:%S} | {app_name}\n")
|
||||||
handle.write("".join(traceback.format_exception(type(exc), exc, exc.__traceback__)))
|
handle.write("".join(traceback.format_exception(type(exc), exc, exc.__traceback__)))
|
||||||
|
|
||||||
|
|
||||||
|
def log_runtime_event(app_name: str, message: str) -> None:
|
||||||
|
"""Write a lightweight launch/shutdown trace for console-less starts."""
|
||||||
|
|
||||||
|
safe_name = "".join(ch if ch.isalnum() or ch in ("_", "-") else "_" for ch in app_name.lower())
|
||||||
|
path = BASE_DIR / f"{safe_name}_launch.log"
|
||||||
|
with _open_log_file(path) as handle:
|
||||||
|
handle.write(f"{datetime.now():%Y-%m-%d %H:%M:%S} | {message}\n")
|
||||||
|
|
||||||
|
|
||||||
def show_fatal_message(app_name: str, exc: BaseException) -> None:
|
def show_fatal_message(app_name: str, exc: BaseException) -> None:
|
||||||
"""Show a best-effort message box for console-less launches."""
|
"""Show a best-effort message box for console-less launches."""
|
||||||
|
|
||||||
@@ -49,7 +75,10 @@ def show_fatal_message(app_name: str, exc: BaseException) -> None:
|
|||||||
root.withdraw()
|
root.withdraw()
|
||||||
messagebox.showerror(
|
messagebox.showerror(
|
||||||
app_name,
|
app_name,
|
||||||
f"Avvio non riuscito.\n\nDettaglio salvato in:\n{FATAL_LOG}\n\n{exc}",
|
"Avvio non riuscito.\n\n"
|
||||||
|
f"Controlla i log in:\n{BASE_DIR}\n\n"
|
||||||
|
f"Se non ci sono, controlla anche:\n{TEMP_DIR}\n\n"
|
||||||
|
f"{exc}",
|
||||||
parent=root,
|
parent=root,
|
||||||
)
|
)
|
||||||
root.destroy()
|
root.destroy()
|
||||||
@@ -61,8 +90,15 @@ def run_with_fatal_log(app_name: str, func: Callable[[], T]) -> T:
|
|||||||
"""Run an app entry point and persist otherwise invisible ``pythonw`` crashes."""
|
"""Run an app entry point and persist otherwise invisible ``pythonw`` crashes."""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return func()
|
log_runtime_event(
|
||||||
|
app_name,
|
||||||
|
f"START exe={sys.executable!r} version={sys.version.split()[0]} cwd={Path.cwd()} base={BASE_DIR} argv={sys.argv!r}",
|
||||||
|
)
|
||||||
|
result = func()
|
||||||
|
log_runtime_event(app_name, f"RETURN result={result!r}")
|
||||||
|
return result
|
||||||
except BaseException as exc:
|
except BaseException as exc:
|
||||||
|
log_runtime_event(app_name, f"EXCEPTION type={type(exc).__name__} value={exc!r}")
|
||||||
log_fatal(app_name, exc)
|
log_fatal(app_name, exc)
|
||||||
show_fatal_message(app_name, exc)
|
show_fatal_message(app_name, exc)
|
||||||
raise
|
raise
|
||||||
|
|||||||
@@ -11,9 +11,12 @@ from busy_overlay import InlineBusyOverlay
|
|||||||
from gestione_aree import AsyncRunner
|
from gestione_aree import AsyncRunner
|
||||||
from locale_text import load_locale_catalog, text as loc_text
|
from locale_text import load_locale_catalog, text as loc_text
|
||||||
from ui_theme import theme_color, theme_font, theme_section, theme_value
|
from ui_theme import theme_color, theme_font, theme_section, theme_value
|
||||||
|
from version_info import module_version, versioned_title
|
||||||
from window_placement import place_window_fullsize_below_parent_later
|
from window_placement import place_window_fullsize_below_parent_later
|
||||||
from tooltips import WidgetToolTip, load_tooltip_catalog, tooltip_text
|
from tooltips import WidgetToolTip, load_tooltip_catalog, tooltip_text
|
||||||
|
|
||||||
|
__version__ = module_version(__name__)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from openpyxl import Workbook
|
from openpyxl import Workbook
|
||||||
from openpyxl.styles import Alignment, Font
|
from openpyxl.styles import Alignment, Font
|
||||||
@@ -85,7 +88,7 @@ class SearchWindow(ctk.CTkToplevel):
|
|||||||
self._theme = theme_section("search_window", {})
|
self._theme = theme_section("search_window", {})
|
||||||
self._locale_catalog = load_locale_catalog()
|
self._locale_catalog = load_locale_catalog()
|
||||||
self._tooltip_catalog = load_tooltip_catalog()
|
self._tooltip_catalog = load_tooltip_catalog()
|
||||||
self.title(loc_text("search.title", catalog=self._locale_catalog, default="Ricerca UDC/Lotto/Codice"))
|
self.title(versioned_title(loc_text("search.title", catalog=self._locale_catalog, default="Ricerca UDC/Lotto/Codice"), __name__))
|
||||||
self.geometry(str(theme_value(self._theme, "window_geometry", "1100x720")))
|
self.geometry(str(theme_value(self._theme, "window_geometry", "1100x720")))
|
||||||
minsize = theme_value(self._theme, "window_minsize", [900, 560])
|
minsize = theme_value(self._theme, "window_minsize", [900, 560])
|
||||||
self.minsize(int(minsize[0]), int(minsize[1]))
|
self.minsize(int(minsize[0]), int(minsize[1]))
|
||||||
|
|||||||
@@ -13,8 +13,11 @@ from busy_overlay import InlineBusyOverlay
|
|||||||
from gestione_aree import AsyncRunner
|
from gestione_aree import AsyncRunner
|
||||||
from locale_text import load_locale_catalog, text as loc_text
|
from locale_text import load_locale_catalog, text as loc_text
|
||||||
from ui_theme import theme_color, theme_font, theme_section, theme_value
|
from ui_theme import theme_color, theme_font, theme_section, theme_value
|
||||||
|
from version_info import module_version, versioned_title
|
||||||
from window_placement import place_window_fullsize_below_parent_later
|
from window_placement import place_window_fullsize_below_parent_later
|
||||||
|
|
||||||
|
__version__ = module_version(__name__)
|
||||||
|
|
||||||
|
|
||||||
SQL_STORICO_PL = """
|
SQL_STORICO_PL = """
|
||||||
WITH base AS (
|
WITH base AS (
|
||||||
@@ -136,7 +139,7 @@ class StoricoPickingListWindow(ctk.CTkToplevel):
|
|||||||
self._busy = InlineBusyOverlay(self, self._theme)
|
self._busy = InlineBusyOverlay(self, self._theme)
|
||||||
self.var_documento = tk.StringVar()
|
self.var_documento = tk.StringVar()
|
||||||
|
|
||||||
self.title(loc_text("history.picking.title", catalog=self._locale_catalog, default="Storico Picking List"))
|
self.title(versioned_title(loc_text("history.picking.title", catalog=self._locale_catalog, default="Storico Picking List"), __name__))
|
||||||
self.geometry(str(theme_value(self._theme, "window_geometry", "1200x720")))
|
self.geometry(str(theme_value(self._theme, "window_geometry", "1200x720")))
|
||||||
minsize = theme_value(self._theme, "window_minsize", [980, 560])
|
minsize = theme_value(self._theme, "window_minsize", [980, 560])
|
||||||
self.minsize(int(minsize[0]), int(minsize[1]))
|
self.minsize(int(minsize[0]), int(minsize[1]))
|
||||||
|
|||||||
@@ -12,8 +12,11 @@ from busy_overlay import InlineBusyOverlay
|
|||||||
from gestione_aree import AsyncRunner
|
from gestione_aree import AsyncRunner
|
||||||
from locale_text import load_locale_catalog, text as loc_text
|
from locale_text import load_locale_catalog, text as loc_text
|
||||||
from ui_theme import theme_color, theme_font, theme_section, theme_value
|
from ui_theme import theme_color, theme_font, theme_section, theme_value
|
||||||
|
from version_info import module_version, versioned_title
|
||||||
from window_placement import place_window_fullsize_below_parent_later
|
from window_placement import place_window_fullsize_below_parent_later
|
||||||
|
|
||||||
|
__version__ = module_version(__name__)
|
||||||
|
|
||||||
|
|
||||||
SQL_STORICO_UDC = """
|
SQL_STORICO_UDC = """
|
||||||
WITH direct AS (
|
WITH direct AS (
|
||||||
@@ -155,7 +158,7 @@ class StoricoUDCWindow(ctk.CTkToplevel):
|
|||||||
self._busy = InlineBusyOverlay(self, self._theme)
|
self._busy = InlineBusyOverlay(self, self._theme)
|
||||||
self.var_udc = tk.StringVar(value=str(initial_udc or ""))
|
self.var_udc = tk.StringVar(value=str(initial_udc or ""))
|
||||||
|
|
||||||
self.title(loc_text("history.udc.title", catalog=self._locale_catalog, default="Storico movimenti UDC"))
|
self.title(versioned_title(loc_text("history.udc.title", catalog=self._locale_catalog, default="Storico movimenti UDC"), __name__))
|
||||||
self.geometry(str(theme_value(self._theme, "window_geometry", "1100x720")))
|
self.geometry(str(theme_value(self._theme, "window_geometry", "1100x720")))
|
||||||
minsize = theme_value(self._theme, "window_minsize", [900, 560])
|
minsize = theme_value(self._theme, "window_minsize", [900, 560])
|
||||||
self.minsize(int(minsize[0]), int(minsize[1]))
|
self.minsize(int(minsize[0]), int(minsize[1]))
|
||||||
|
|||||||
@@ -6,6 +6,9 @@ import json
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import tkinter as tk
|
import tkinter as tk
|
||||||
|
|
||||||
|
from version_info import module_version
|
||||||
|
|
||||||
|
__version__ = module_version(__name__)
|
||||||
|
|
||||||
_TOOLTIP_FILE = Path(__file__).with_name("tooltip.json")
|
_TOOLTIP_FILE = Path(__file__).with_name("tooltip.json")
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,10 @@ from functools import lru_cache
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
|
from version_info import module_version
|
||||||
|
|
||||||
|
__version__ = module_version(__name__)
|
||||||
|
|
||||||
THEME_PATH = Path(__file__).with_name("ui_theme.json")
|
THEME_PATH = Path(__file__).with_name("ui_theme.json")
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,9 @@ from dataclasses import dataclass, field
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import FrozenSet
|
from typing import FrozenSet
|
||||||
|
|
||||||
|
from version_info import module_version
|
||||||
|
|
||||||
|
__version__ = module_version(__name__)
|
||||||
|
|
||||||
ALL_ACTIONS: FrozenSet[str] = frozenset(
|
ALL_ACTIONS: FrozenSet[str] = frozenset(
|
||||||
{
|
{
|
||||||
|
|||||||
71
version_info.py
Normal file
71
version_info.py
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
"""Application and module version registry for Warehouse/FlyWMS bridge."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
APP_VERSION = "1.0.0"
|
||||||
|
|
||||||
|
MODULE_VERSIONS: dict[str, str] = {
|
||||||
|
"app": APP_VERSION,
|
||||||
|
"async_loop_singleton": "1.0.0",
|
||||||
|
"async_msssql_query": "1.0.0",
|
||||||
|
"audit_log": "1.0.0",
|
||||||
|
"main": "1.0.0",
|
||||||
|
"barcode_client": "1.0.0",
|
||||||
|
"barcode_repository": "1.0.0",
|
||||||
|
"barcode_service": "1.0.0",
|
||||||
|
"busy_overlay": "1.0.0",
|
||||||
|
"db_config": "1.0.0",
|
||||||
|
"gestione_aree": "1.0.0",
|
||||||
|
"gestione_layout": "1.0.0",
|
||||||
|
"gestione_pickinglist": "1.0.0",
|
||||||
|
"gestione_scarico": "1.0.0",
|
||||||
|
"locale_text": "1.0.0",
|
||||||
|
"login_window": "1.0.0",
|
||||||
|
"prenota_sprenota_sql": "1.0.0",
|
||||||
|
"reset_corsie": "1.0.0",
|
||||||
|
"search_pallets": "1.0.0",
|
||||||
|
"storico_pickinglist": "1.0.0",
|
||||||
|
"storico_udc": "1.0.0",
|
||||||
|
"tooltips": "1.0.0",
|
||||||
|
"ui_theme": "1.0.0",
|
||||||
|
"user_session": "1.0.0",
|
||||||
|
"view_celle_multi_udc": "1.0.0",
|
||||||
|
"window_placement": "1.0.0",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def module_key(module_name: str) -> str:
|
||||||
|
"""Return the stable registry key for a module name or path."""
|
||||||
|
|
||||||
|
name = str(module_name or "").replace("\\", "/")
|
||||||
|
stem = Path(name).stem if "/" in name or "." not in name else name.rsplit(".", 1)[-1]
|
||||||
|
return stem or "app"
|
||||||
|
|
||||||
|
|
||||||
|
def module_version(module_name: str | None = None) -> str:
|
||||||
|
"""Return the version for a module, falling back to the app version."""
|
||||||
|
|
||||||
|
if not module_name:
|
||||||
|
return APP_VERSION
|
||||||
|
return MODULE_VERSIONS.get(module_key(module_name), APP_VERSION)
|
||||||
|
|
||||||
|
|
||||||
|
def version_label(module_name: str | None = None) -> str:
|
||||||
|
"""Return the standard visual label used in window titles."""
|
||||||
|
|
||||||
|
return f"ver. {module_version(module_name)}"
|
||||||
|
|
||||||
|
|
||||||
|
def versioned_title(title: str, module_name: str | None = None) -> str:
|
||||||
|
"""Append the standard version label to a window title."""
|
||||||
|
|
||||||
|
clean_title = str(title or "").strip()
|
||||||
|
label = version_label(module_name)
|
||||||
|
if not clean_title:
|
||||||
|
return label
|
||||||
|
if label.lower() in clean_title.lower():
|
||||||
|
return clean_title
|
||||||
|
return f"{clean_title} - {label}"
|
||||||
@@ -22,8 +22,11 @@ from gestione_scarico import move_pallet_async
|
|||||||
from locale_text import load_locale_catalog, text as loc_text
|
from locale_text import load_locale_catalog, text as loc_text
|
||||||
from tooltips import WidgetToolTip, load_tooltip_catalog, tooltip_text
|
from tooltips import WidgetToolTip, load_tooltip_catalog, tooltip_text
|
||||||
from ui_theme import theme_color, theme_font, theme_section, theme_value
|
from ui_theme import theme_color, theme_font, theme_section, theme_value
|
||||||
|
from version_info import module_version, versioned_title
|
||||||
from window_placement import place_window_fullsize_below_parent_later
|
from window_placement import place_window_fullsize_below_parent_later
|
||||||
|
|
||||||
|
__version__ = module_version(__name__)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
except Exception: # pragma: no cover - fallback used only when Loguru is not available
|
except Exception: # pragma: no cover - fallback used only when Loguru is not available
|
||||||
@@ -436,7 +439,7 @@ class CelleMultipleWindow(ctk.CTkToplevel):
|
|||||||
self._theme = theme_section("multi_udc", {})
|
self._theme = theme_section("multi_udc", {})
|
||||||
self._locale_catalog = load_locale_catalog()
|
self._locale_catalog = load_locale_catalog()
|
||||||
self._tooltip_catalog = load_tooltip_catalog()
|
self._tooltip_catalog = load_tooltip_catalog()
|
||||||
self.title(loc_text("multi.title", catalog=self._locale_catalog, default="Celle con piu' pallet"))
|
self.title(versioned_title(loc_text("multi.title", catalog=self._locale_catalog, default="Celle con piu' pallet"), __name__))
|
||||||
self.session = session
|
self.session = session
|
||||||
self.geometry(str(theme_value(self._theme, "window_geometry", "1100x700")))
|
self.geometry(str(theme_value(self._theme, "window_geometry", "1100x700")))
|
||||||
minsize = theme_value(self._theme, "window_minsize", [900, 550])
|
minsize = theme_value(self._theme, "window_minsize", [900, 550])
|
||||||
|
|||||||
@@ -1,3 +1,9 @@
|
|||||||
|
from pathlib import Path
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
os.chdir(Path(__file__).resolve().parent)
|
||||||
|
|
||||||
from runtime_support import ensure_stdio, run_with_fatal_log
|
from runtime_support import ensure_stdio, run_with_fatal_log
|
||||||
|
|
||||||
ensure_stdio("warehouse_main")
|
ensure_stdio("warehouse_main")
|
||||||
|
|||||||
@@ -8,6 +8,9 @@ import math
|
|||||||
import tkinter as tk
|
import tkinter as tk
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
from version_info import module_version
|
||||||
|
|
||||||
|
__version__ = module_version(__name__)
|
||||||
|
|
||||||
MODULE_LOG_NAME = "window_placement"
|
MODULE_LOG_NAME = "window_placement"
|
||||||
MODULE_LOG_PATH = Path(__file__).with_name("window_placement.log")
|
MODULE_LOG_PATH = Path(__file__).with_name("window_placement.log")
|
||||||
|
|||||||
Reference in New Issue
Block a user