refactor: reorganize backend modules into sub-packages for models, data, visualization, wafer, and controllers

This commit is contained in:
jack
2026-06-11 12:15:00 -07:00
parent b9f8032203
commit 72334795da
47 changed files with 155 additions and 60 deletions
+6 -6
View File
@@ -5,13 +5,13 @@ from PySide6.QtQml import QQmlApplicationEngine
from PySide6.QtQuickControls2 import QQuickStyle from PySide6.QtQuickControls2 import QQuickStyle
from PySide6.QtWidgets import QApplication from PySide6.QtWidgets import QApplication
from pygui.backend.device_controller import DeviceController from pygui.backend.controllers.device_controller import DeviceController
from pygui.backend.local_settings import LocalSettings from pygui.backend.data.local_settings import LocalSettings
from pygui.backend.local_settings_model import LocalSettingsModel from pygui.backend.data.local_settings_model import LocalSettingsModel
from pygui.backend.file_browser import FileBrowser from pygui.backend.data.file_browser import FileBrowser
from pygui.backend.session_controller import SessionController from pygui.backend.controllers.session_controller import SessionController
# Importing wafer_map_item registers the @QmlElement WaferMapItem (QML: import ISC.Wafer). # Importing wafer_map_item registers the @QmlElement WaferMapItem (QML: import ISC.Wafer).
from pygui.backend.wafer_map_item import WaferMapItem from pygui.backend.visualization.wafer_map_item import WaferMapItem
# ===== Application Entry Point ===== # ===== Application Entry Point =====
def main() -> int: def main() -> int:
+32 -1
View File
@@ -1 +1,32 @@
# ===== Backend Package Marker ===== # ===== Backend Package =====
# Backward-compatible re-exports so existing callers keep working
# while imports are migrated to the new sub-package paths.
from pygui.backend.controllers.device_controller import DeviceController # noqa: F401
from pygui.backend.controllers.session_controller import SessionController # noqa: F401
from pygui.backend.models.frame import Frame # noqa: F401
from pygui.backend.models.frame_player import FramePlayer, frames_from_wafer_data # noqa: F401
from pygui.backend.models.frame_stats import Stats, compute_stats # noqa: F401
from pygui.backend.models.session_model import SessionModel # noqa: F401
from pygui.backend.models.data_model import TemperatureTableModel # noqa: F401
from pygui.backend.models.sensor_editor import SensorEditor # noqa: F401
from pygui.backend.models.threshold_classifier import ThresholdConfig # noqa: F401
from pygui.backend.models.stability_detector import StabilityDetector # noqa: F401
from pygui.backend.data.csv_recorder import CsvRecorder # noqa: F401
from pygui.backend.data.csv_file_metadata import CSVFileMetadata # noqa: F401
from pygui.backend.data.data_records import read_data_records # noqa: F401
from pygui.backend.data.data_segment import DataSegment # noqa: F401
from pygui.backend.data.file_browser import FileBrowser # noqa: F401
from pygui.backend.data.local_settings import LocalSettings # noqa: F401
from pygui.backend.data.local_settings_model import LocalSettingsModel # noqa: F401
from pygui.backend.visualization.wafer_map_item import WaferMapItem # noqa: F401
from pygui.backend.visualization.graph_view import GraphView # noqa: F401
from pygui.backend.visualization.rbf_heatmap import interpolate_field # noqa: F401
from pygui.backend.visualization.contour_models import ContourLine, ContourSegment # noqa: F401
from pygui.backend.wafer.zwafer_models import ZWaferData, Sensor, DataRecord # noqa: F401
from pygui.backend.wafer.zwafer_parser import ZWaferParser # noqa: F401
from pygui.backend.wafer.wafer_layouts import load_layout, available_families # noqa: F401
@@ -0,0 +1,6 @@
# ===== Controllers Sub-package =====
from pygui.backend.controllers.device_controller import DeviceController
from pygui.backend.controllers.session_controller import SessionController
__all__ = ["DeviceController", "SessionController"]
@@ -15,9 +15,9 @@ from typing import Any, Optional
from PySide6.QtCore import QObject, Property, Qt, Signal, Slot from PySide6.QtCore import QObject, Property, Qt, Signal, Slot
from pygui.backend.data_model import TemperatureTableModel from pygui.backend.models.data_model import TemperatureTableModel
from pygui.backend.graph_view import GraphView from pygui.backend.visualization.graph_view import GraphView
from pygui.backend.local_settings import LocalSettings from pygui.backend.data.local_settings import LocalSettings
from pygui.serialcomm.data_parser import ( from pygui.serialcomm.data_parser import (
convert_to_temperatures, convert_to_temperatures,
parse_binary_data, parse_binary_data,
@@ -26,7 +26,7 @@ from pygui.serialcomm.data_parser import (
) )
from pygui.serialcomm.device_service import DeviceService from pygui.serialcomm.device_service import DeviceService
from pygui.serialcomm.serial_port import WaferInfo from pygui.serialcomm.serial_port import WaferInfo
from pygui.backend.session_controller import SessionController from pygui.backend.controllers.session_controller import SessionController
# import pygui.backend.wafer_map_item # import pygui.backend.wafer_map_item
stream_controller = SessionController() stream_controller = SessionController()
@@ -5,15 +5,15 @@ from typing import Any, Optional
from PySide6.QtCore import QObject, Property, QTimer, Qt, Signal, Slot from PySide6.QtCore import QObject, Property, QTimer, Qt, Signal, Slot
from pygui.backend.csv_recorder import CsvRecorder from pygui.backend.data.csv_recorder import CsvRecorder
from pygui.backend.frame import Frame from pygui.backend.models.frame import Frame
from pygui.backend.frame_player import FramePlayer, frames_from_wafer_data from pygui.backend.models.frame_player import FramePlayer, frames_from_wafer_data
from pygui.backend.session_model import SessionModel from pygui.backend.models.session_model import SessionModel
from pygui.backend.threshold_classifier import ThresholdConfig from pygui.backend.models.threshold_classifier import ThresholdConfig
from pygui.backend.zwafer_models import Sensor from pygui.backend.wafer.zwafer_models import Sensor
from pygui.backend.zwafer_parser import ZWaferParser from pygui.backend.wafer.zwafer_parser import ZWaferParser
from pygui.serialcomm.stream_reader import StreamReader from pygui.serialcomm.stream_reader import StreamReader
from pygui.backend.sensor_editor import SensorEditor from pygui.backend.models.sensor_editor import SensorEditor
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@@ -155,7 +155,7 @@ class SessionController(QObject):
# ---- review: file load + playback ---- # ---- review: file load + playback ----
@Slot(str) @Slot(str)
def loadFile(self, file_path: str) -> None: def loadFile(self, file_path: str) -> None:
from pygui.backend.data_records import is_official_csv, read_data_records, read_official_csv from pygui.backend.data.data_records import is_official_csv, read_data_records, read_official_csv
sensors: list[Sensor] = [] sensors: list[Sensor] = []
frames = [] frames = []
+2
View File
@@ -0,0 +1,2 @@
# ===== Crypto Sub-package =====
from pygui.backend.crypto.crypto_helper import * # noqa: F403
+16
View File
@@ -0,0 +1,16 @@
# ===== Data Sub-package =====
from pygui.backend.data.csv_recorder import CsvRecorder
from pygui.backend.data.csv_file_metadata import CSVFileMetadata
from pygui.backend.data.data_records import read_data_records, is_official_csv, read_official_csv
from pygui.backend.data.data_segment import DataSegment
from pygui.backend.data.file_browser import FileBrowser
from pygui.backend.data.local_settings import LocalSettings
from pygui.backend.data.local_settings_model import LocalSettingsModel
__all__ = [
"CsvRecorder", "CSVFileMetadata",
"read_data_records", "is_official_csv", "read_official_csv",
"DataSegment",
"FileBrowser",
"LocalSettings", "LocalSettingsModel",
]
@@ -5,8 +5,8 @@ from pathlib import Path
from typing import Optional, TextIO from typing import Optional, TextIO
from pygui.backend.frame import Frame from pygui.backend.models.frame import Frame
from pygui.backend.zwafer_models import Sensor from pygui.backend.wafer.zwafer_models import Sensor
class CsvRecorder: class CsvRecorder:
def __init__(self) -> None: def __init__(self) -> None:
@@ -2,7 +2,7 @@
from __future__ import annotations from __future__ import annotations
from pathlib import Path from pathlib import Path
from pygui.backend.zwafer_models import DataRecord, Sensor from pygui.backend.wafer.zwafer_models import DataRecord, Sensor
def read_data_records(file_path: str) -> list[DataRecord]: def read_data_records(file_path: str) -> list[DataRecord]:
records: list[DataRecord] = [] records: list[DataRecord] = []
@@ -40,7 +40,7 @@ def is_official_csv(file_path: str) -> bool:
def read_official_csv(file_path: str) -> tuple[list[Sensor], list[DataRecord]]: def read_official_csv(file_path: str) -> tuple[list[Sensor], list[DataRecord]]:
"""Parse the headerless official CSV: row 0 = sensor names, rows 1+ = values (no timestamp).""" """Parse the headerless official CSV: row 0 = sensor names, rows 1+ = values (no timestamp)."""
from pygui.backend.wafer_layouts import load_layout_for_wafer_id from pygui.backend.wafer.wafer_layouts import load_layout_for_wafer_id
stem = Path(file_path).stem stem = Path(file_path).stem
wafer_id = stem.split("-")[0] if "-" in stem else stem wafer_id = stem.split("-")[0] if "-" in stem else stem
@@ -8,8 +8,8 @@ from pathlib import Path
from PySide6.QtCore import QObject, Property, QStandardPaths, Signal, Slot from PySide6.QtCore import QObject, Property, QStandardPaths, Signal, Slot
from PySide6.QtWidgets import QFileDialog, QMessageBox from PySide6.QtWidgets import QFileDialog, QMessageBox
from pygui.backend.csv_file_metadata import CSVFileMetadata from pygui.backend.data.csv_file_metadata import CSVFileMetadata
from pygui.backend.zwafer_parser import ZWaferParser from pygui.backend.wafer.zwafer_parser import ZWaferParser
# ===== File Browser Model ===== # ===== File Browser Model =====
@@ -6,7 +6,7 @@ from typing import Any
from PySide6.QtCore import QObject, Property, QDateTime, QStandardPaths, Signal, Slot from PySide6.QtCore import QObject, Property, QDateTime, QStandardPaths, Signal, Slot
from pygui.backend.local_settings import LocalSettings from pygui.backend.data.local_settings import LocalSettings
MASTER_FAMILIES = ("A", "B", "C", "D", "E", "F", "P", "X", "Z") MASTER_FAMILIES = ("A", "B", "C", "D", "E", "F", "P", "X", "Z")
+19
View File
@@ -0,0 +1,19 @@
# ===== Models Sub-package =====
from pygui.backend.models.frame import Frame
from pygui.backend.models.frame_player import FramePlayer, frames_from_wafer_data
from pygui.backend.models.frame_stats import Stats, compute_stats
from pygui.backend.models.session_model import SessionModel, SessionUpdate
from pygui.backend.models.data_model import TemperatureTableModel
from pygui.backend.models.sensor_editor import SensorEditor
from pygui.backend.models.threshold_classifier import ThresholdConfig, classify, resolve_bounds
from pygui.backend.models.stability_detector import StabilityDetector
__all__ = [
"Frame", "FramePlayer", "frames_from_wafer_data",
"Stats", "compute_stats",
"SessionModel", "SessionUpdate",
"TemperatureTableModel",
"SensorEditor",
"ThresholdConfig", "classify", "resolve_bounds",
"StabilityDetector",
]
@@ -1,6 +1,6 @@
from __future__ import annotations from __future__ import annotations
from pygui.backend.frame import Frame from pygui.backend.models.frame import Frame
from pygui.backend.zwafer_models import ZWaferData from pygui.backend.wafer.zwafer_models import ZWaferData
def frames_from_wafer_data(data: ZWaferData, records) -> list[Frame]: def frames_from_wafer_data(data: ZWaferData, records) -> list[Frame]:
"""Build Frames from parsed DataRecords (records = list[DataRecord].)""" """Build Frames from parsed DataRecords (records = list[DataRecord].)"""
@@ -1,10 +1,10 @@
from __future__ import annotations from __future__ import annotations
from dataclasses import dataclass from dataclasses import dataclass
from pygui.backend.frame import Frame from pygui.backend.models.frame import Frame
from pygui.backend.frame_stats import Stats, compute_stats from pygui.backend.models.frame_stats import Stats, compute_stats
from pygui.backend.stability_detector import StabilityDetector from pygui.backend.models.stability_detector import StabilityDetector
from pygui.backend.threshold_classifier import ThresholdConfig, classify, resolve_bounds from pygui.backend.models.threshold_classifier import ThresholdConfig, classify, resolve_bounds
@dataclass(frozen=True) @dataclass(frozen=True)
class SessionUpdate: class SessionUpdate:
@@ -0,0 +1,11 @@
# ===== Visualization Sub-package =====
from pygui.backend.visualization.wafer_map_item import WaferMapItem
from pygui.backend.visualization.graph_view import GraphView
from pygui.backend.visualization.rbf_heatmap import interpolate_field
from pygui.backend.visualization.contour_models import ContourLine, ContourSegment
__all__ = [
"WaferMapItem", "GraphView",
"interpolate_field",
"ContourLine", "ContourSegment",
]
@@ -2,7 +2,7 @@ from typing import List, Tuple, Optional
import numpy as np import numpy as np
from pygui.backend.contour_models import ContourLine, ContourSegment from pygui.backend.visualization.contour_models import ContourLine, ContourSegment
# ===== Contour Generation ===== # ===== Contour Generation =====
@@ -20,8 +20,8 @@ from PySide6.QtGui import (
from PySide6.QtQml import QmlElement from PySide6.QtQml import QmlElement
from PySide6.QtQuick import QQuickPaintedItem from PySide6.QtQuick import QQuickPaintedItem
from pygui.backend.rbf_heatmap import interpolate_field from pygui.backend.visualization.rbf_heatmap import interpolate_field
from pygui.backend.zwafer_models import Sensor from pygui.backend.wafer.zwafer_models import Sensor
QML_IMPORT_NAME = "ISC.Wafer" QML_IMPORT_NAME = "ISC.Wafer"
QML_IMPORT_MAJOR_VERSION = 1 QML_IMPORT_MAJOR_VERSION = 1
+10
View File
@@ -0,0 +1,10 @@
# ===== Wafer Sub-package =====
from pygui.backend.wafer.zwafer_models import ZWaferData, Sensor, DataRecord
from pygui.backend.wafer.zwafer_parser import ZWaferParser
from pygui.backend.wafer.wafer_layouts import load_layout, available_families
__all__ = [
"ZWaferData", "Sensor", "DataRecord",
"ZWaferParser",
"load_layout", "available_families",
]
@@ -16,9 +16,9 @@ from pathlib import Path
import yaml import yaml
from pygui.backend.zwafer_models import Sensor from pygui.backend.wafer.zwafer_models import Sensor
_LAYOUTS_DIR = Path(__file__).parent.parent / "assets" / "layouts" _LAYOUTS_DIR = Path(__file__).parent.parent.parent / "assets" / "layouts"
def _family_name(raw_name: str) -> str: def _family_name(raw_name: str) -> str:
@@ -2,7 +2,7 @@ from pathlib import Path
from typing import Tuple, Optional from typing import Tuple, Optional
from datetime import datetime, timedelta from datetime import datetime, timedelta
from pygui.backend.zwafer_models import ZWaferData, Sensor from pygui.backend.wafer.zwafer_models import ZWaferData, Sensor
# ===== Z-Wafer CSV Parser ===== # ===== Z-Wafer CSV Parser =====
+1 -1
View File
@@ -11,7 +11,7 @@ from typing import Optional
import serial.tools.list_ports import serial.tools.list_ports
from pygui.backend.local_settings import LocalSettings from pygui.backend.data.local_settings import LocalSettings
from pygui.serialcomm.serial_port import SerialPort, WaferInfo from pygui.serialcomm.serial_port import SerialPort, WaferInfo
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
+1 -1
View File
@@ -4,7 +4,7 @@ import logging
import threading import threading
from typing import Callable from typing import Callable
from pygui.backend.frame import Frame from pygui.backend.models.frame import Frame
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
+5 -5
View File
@@ -1,8 +1,8 @@
from pygui.backend.csv_recorder import CsvRecorder from pygui.backend.data.csv_recorder import CsvRecorder
from pygui.backend.data_records import read_data_records from pygui.backend.data.data_records import read_data_records
from pygui.backend.frame import Frame from pygui.backend.models.frame import Frame
from pygui.backend.zwafer_models import Sensor from pygui.backend.wafer.zwafer_models import Sensor
from pygui.backend.zwafer_parser import ZWaferParser from pygui.backend.wafer.zwafer_parser import ZWaferParser
def test_recording_roundtrips_through_parser(tmp_path): def test_recording_roundtrips_through_parser(tmp_path):
+1 -1
View File
@@ -1,5 +1,5 @@
"""Tests for src/pygui/backend/data_records.py.""" """Tests for src/pygui/backend/data_records.py."""
from pygui.backend.data_records import read_data_records from pygui.backend.data.data_records import read_data_records
def test_reads_rows_after_data_marker(tmp_path): def test_reads_rows_after_data_marker(tmp_path):
+2 -2
View File
@@ -1,5 +1,5 @@
from pygui.backend.frame import Frame from pygui.backend.models.frame import Frame
from pygui.backend.frame_player import FramePlayer from pygui.backend.models.frame_player import FramePlayer
def frames(n): def frames(n):
return [Frame(seq=i, time=float(i), values=[149.0 + i])for i in range(n)] return [Frame(seq=i, time=float(i), values=[149.0 + i])for i in range(n)]
+1 -1
View File
@@ -1,6 +1,6 @@
import math import math
import pytest import pytest
from pygui.backend.frame_stats import compute_stats, Stats from pygui.backend.models.frame_stats import compute_stats, Stats
def test_basic_stat(): def test_basic_stat():
+1 -1
View File
@@ -1,6 +1,6 @@
"""Tests for src/pygui/backend/rbf_heatmap.py.""" """Tests for src/pygui/backend/rbf_heatmap.py."""
import numpy as np import numpy as np
from pygui.backend.rbf_heatmap import interpolate_field, BACKEND from pygui.backend.visualization.rbf_heatmap import interpolate_field, BACKEND
def test_field_shape_and_interpolates_at_sites(): def test_field_shape_and_interpolates_at_sites():
+1 -1
View File
@@ -1,6 +1,6 @@
"""Tests for src/pygui/backend/sensor_editor.py.""" """Tests for src/pygui/backend/sensor_editor.py."""
import pytest import pytest
from pygui.backend.sensor_editor import SensorEditor from pygui.backend.models.sensor_editor import SensorEditor
def test_replace_overrides_raw_value(): def test_replace_overrides_raw_value():
+3 -3
View File
@@ -1,6 +1,6 @@
from pygui.backend.frame import Frame from pygui.backend.models.frame import Frame
from pygui.backend.threshold_classifier import ThresholdConfig, BAND_HIGH, BAND_IN, BAND_LOW from pygui.backend.models.threshold_classifier import ThresholdConfig, BAND_HIGH, BAND_IN, BAND_LOW
from pygui.backend.session_model import SessionModel, SessionUpdate from pygui.backend.models.session_model import SessionModel, SessionUpdate
+1 -1
View File
@@ -1,4 +1,4 @@
from pygui.backend.stability_detector import ( from pygui.backend.models.stability_detector import (
StabilityDetector, STATE_IDLE, STATE_RAMP, STATE_SET, StabilityDetector, STATE_IDLE, STATE_RAMP, STATE_SET,
) )
+1 -1
View File
@@ -1,5 +1,5 @@
import time import time
from pygui.backend.frame import Frame from pygui.backend.models.frame import Frame
from pygui.serialcomm.stream_reader import StreamReader from pygui.serialcomm.stream_reader import StreamReader
class FakeTransport: class FakeTransport:
+1 -1
View File
@@ -1,6 +1,6 @@
import math import math
from pygui.backend.threshold_classifier import ( from pygui.backend.models.threshold_classifier import (
ThresholdConfig, classify, classify_all, resolve_bounds, ThresholdConfig, classify, classify_all, resolve_bounds,
BAND_IN, BAND_HIGH, BAND_LOW BAND_IN, BAND_HIGH, BAND_LOW
) )
+2 -2
View File
@@ -1,7 +1,7 @@
"""Tests for src/pygui/backend/wafer_layouts.py.""" """Tests for src/pygui/backend/wafer_layouts.py."""
import pytest import pytest
from pygui.backend.wafer_layouts import load_layout, available_families from pygui.backend.wafer.wafer_layouts import load_layout, available_families
from pygui.backend.zwafer_models import Sensor from pygui.backend.wafer.zwafer_models import Sensor
def test_lists_bundled_families(): def test_lists_bundled_families():