diff --git a/backend/csv_file_metadata.py b/backend/csv_file_metadata.py index 2b2c1c0..bcebb3d 100644 --- a/backend/csv_file_metadata.py +++ b/backend/csv_file_metadata.py @@ -4,11 +4,12 @@ from datetime import datetime # ===== CSV Metadata Model ===== class CSVFileMetadata: # ===== Lifecycle ===== - def __init__(self, wafer="", date="", chamber="", notes="", filename="", columns=0): + def __init__(self, wafer="", date="", chamber="", notes="", master_type="", filename="", columns=0): self.wafer = wafer self.date = date self.chamber = chamber self.notes = notes + self.master_type = master_type self.filename = filename self.columns = columns @@ -42,6 +43,7 @@ class CSVFileMetadata: "date": self.string_date_format(), # Ensure we save as string "chamber": self.chamber, "notes": self.notes, + "masterType": self.master_type, } def string_date_format(self) -> str: diff --git a/backend/file_browser.py b/backend/file_browser.py index d057558..9d1035b 100644 --- a/backend/file_browser.py +++ b/backend/file_browser.py @@ -54,7 +54,7 @@ class FileBrowser(QObject): self._refresh_files(show_empty_message=False) # ===== Metadata Persistence ===== - @Slot(str, str, str, str, str, bool) + @Slot(str, str, str, str, str, bool, str) def saveMetadata( self, file_path: str, @@ -63,6 +63,7 @@ class FileBrowser(QObject): chamber: str, notes: str, selected: bool, + master_type: str = "", ) -> None: csv_path = Path(file_path) if not csv_path.exists(): @@ -79,12 +80,14 @@ class FileBrowser(QObject): row["chamber"] = chamber row["notes"] = notes row["selected"] = selected + row["masterType"] = master_type payload = { "wafer": wafer, "date": date, "chamber": chamber, "notes": notes, + "masterType": master_type, } try: @@ -103,6 +106,10 @@ class FileBrowser(QObject): str(row.get("fileName", "")): bool(row.get("selected", False)) for row in self._files } + master_state = { + str(row.get("fileName", "")): row.get("masterType", "") or "" + for row in self._files + } self._files = [] @@ -120,6 +127,7 @@ class FileBrowser(QObject): "date": metadata.string_date_format(), "chamber": metadata.chamber, "notes": metadata.notes, + "masterType": master_state.get(str(csv_path), "") or metadata.master_type, "fileName": str(csv_path), "highlight": metadata.get_wafer_type() in {"A", "B", "C"}, } @@ -132,6 +140,7 @@ class FileBrowser(QObject): "date": "", "chamber": "", "notes": "Unable to parse metadata", + "masterType": master_state.get(str(csv_path), ""), "fileName": str(csv_path), "highlight": False, } @@ -152,6 +161,7 @@ class FileBrowser(QObject): date=str(payload.get("date", "")), chamber=str(payload.get("chamber", "")), notes=str(payload.get("notes", "")), + master_type=str(payload.get("masterType", "")), filename=str(csv_path), ) except Exception: @@ -222,6 +232,37 @@ class FileBrowser(QObject): def _show_error(self, message: str) -> None: QMessageBox.critical(None, "iSenseCloud", message) + @Slot(str) + def showInfo(self, message: str) -> None: + self._show_info(message) + + @Slot(str, result="QVariantMap") + def parseCsvMetadata(self, file_path: str) -> dict: + from pathlib import Path as _Path + + csv_path = _Path(file_path) + if not csv_path.exists(): + return {"success": False, "error": "File not found"} + + parser_data, _ = self._parser.parse(file_path) + if parser_data is None: + return {"success": False, "error": "Failed to parse CSV"} + + wafer = parser_data.serial or "" + date_text = "" + if parser_data.date != datetime.min: + date_text = CSVFileMetadata.format_date(parser_data.date) + columns = len(parser_data.csv_headers) if parser_data.csv_headers else 0 + family_code = wafer[0].upper() if wafer else "" + + return { + "success": True, + "wafer": wafer, + "date": date_text, + "columns": columns, + "familyCode": family_code, + } + def _show_info(self, message: str) -> None: QMessageBox.information(None, "iSenseCloud", message) diff --git a/backend/local_settings_model.py b/backend/local_settings_model.py index f58ca7f..2c221a9 100644 --- a/backend/local_settings_model.py +++ b/backend/local_settings_model.py @@ -48,14 +48,16 @@ class LocalSettingsModel(QObject): self._recompute_derived() def _resolve_data_dir(self) -> Path: - documents_dir = QStandardPaths.writableLocation(QStandardPaths.DocumentsLocation) + documents_dir = QStandardPaths.writableLocation( + QStandardPaths.DocumentsLocation + ) base_dir = Path(documents_dir) if documents_dir else (Path.home() / "Documents") return base_dir / "isc_data" def _new_defaults(self) -> dict[str, Any]: return { "chamberId": "2", - "reverseZWafer": True, + "reverseZWafer": False, "debugMode": False, "waferReadTimeout": 120000, "waferDetectTimeout": 5000, @@ -250,7 +252,9 @@ class LocalSettingsModel(QObject): @Slot() def loadSettings(self) -> None: loaded = LocalSettings.read_settings(str(self._data_dir)) - self._chamber_id = str(loaded.chamber_id).strip() or str(self._defaults["chamberId"]) + self._chamber_id = str(loaded.chamber_id).strip() or str( + self._defaults["chamberId"] + ) self._reverse_z_wafer = bool(loaded.reverse_z_wafer) self._debug_mode = bool(loaded.debug) self._wafer_read_timeout = int(loaded.wafer_read_timeout) diff --git a/requirements.txt b/requirements.txt index acc0c78..4e03671 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,4 @@ # ===== Runtime Dependencies ===== -PySide6 +PySide6>=6.6.0 +pyqtgraph>=0.13.0 +numpy>=1.24.0