diff --git a/batch_processing/batch_creation.py b/batch_processing/batch_creation.py index 1f10bb9..ccacef5 100644 --- a/batch_processing/batch_creation.py +++ b/batch_processing/batch_creation.py @@ -11,7 +11,6 @@ from enum import Enum from io import BytesIO from typing import Optional, List -import tkinter as tk from pydantic import BaseModel, ConfigDict, Field from file_handling.data_conversion import make_str_enum @@ -108,7 +107,7 @@ def generate_multi_label_batch(labels, quotes) -> BytesIO | None: labels_enum = make_str_enum("Label", labels) class LabeledQuoteMulti(BaseModel): - label: List[labels_enum] = Field(..., min_items=1) + label: List[labels_enum] = Field(..., min_length=1) model_config = ConfigDict(use_enum_values=True, extra='forbid') SCHEMA = LabeledQuoteMulti.model_json_schema() @@ -156,7 +155,7 @@ def generate_keyword_extraction_batch(texts) -> BytesIO | None: Returns a BytesIO whose .name is set to 'batchinput.jsonl'. """ class KeywordExtraction(BaseModel): - keywords: list[str] = Field(..., min_items=1) + keywords: list[str] = Field(..., min_length=1) model_config = ConfigDict(extra='forbid') SCHEMA = KeywordExtraction.model_json_schema() diff --git a/file_handling/data_import.py b/file_handling/data_import.py index a2b44b1..3651303 100644 --- a/file_handling/data_import.py +++ b/file_handling/data_import.py @@ -360,17 +360,6 @@ def _initial_blank_preview(): _fill_preview_rows([[""] * 5 for _ in range(5)]) _update_dataset_name_preview() - def _current_header_labels() -> list[str]: - # Return the labels currently shown atop the preview - return [tree.heading(cid)["text"] for cid in tree["columns"]] - - def _selected_header_label() -> str: - labels = _current_header_labels() - idx = selected_col.get() - if 0 <= idx < len(labels): - return str(labels[idx]) - return "" - def _update_dataset_name_controls_enabled(): # Enable/disable "selected column header" radio based on has_headers + data present has_data = bool(_loaded_rows) diff --git a/live_processing/keyword_extraction_live.py b/live_processing/keyword_extraction_live.py index 68ad06c..109681f 100644 --- a/live_processing/keyword_extraction_live.py +++ b/live_processing/keyword_extraction_live.py @@ -8,29 +8,28 @@ from typing import Optional import tkinter as tk +from tkinter import messagebox from pydantic import BaseModel, ValidationError, Field, ConfigDict -from openai import OpenAI from file_handling.data_import import import_data from file_handling.data_conversion import save_as_csv, to_long_df -from settings import secrets_store, config +from settings import config +from batch_processing.batch_method import get_client # Progress UI lives in a separate module from ui.progress_ui import ProgressController -# Initialize OpenAI client with stored API key -try: - OPENAI_API_KEY = secrets_store.load_api_key() - client = OpenAI(api_key=OPENAI_API_KEY) if OPENAI_API_KEY else None -except Exception: - OPENAI_API_KEY = None - client = None - def keyword_extraction_pipeline(parent: Optional[tk.Misc] = None): """ Prompt for quotes CSVs, extract keywords from each quote, show progress, then save results to CSV. """ + try: + client = get_client() + except Exception as e: + messagebox.showerror("API Key Required", str(e)) + return + # Get quotes data from_import = import_data(parent, "Select the quotes data") if from_import is None: @@ -40,7 +39,7 @@ def keyword_extraction_pipeline(parent: Optional[tk.Misc] = None): class KeywordExtraction(BaseModel): id: int | None = None quote: str - keywords: list[str] = Field(..., min_items=1) + keywords: list[str] = Field(..., min_length=1) model_config = ConfigDict() total = len(quotes) diff --git a/live_processing/multi_label_live.py b/live_processing/multi_label_live.py index a16a335..e40b8f0 100644 --- a/live_processing/multi_label_live.py +++ b/live_processing/multi_label_live.py @@ -8,29 +8,28 @@ from typing import Optional, List import tkinter as tk +from tkinter import messagebox from pydantic import BaseModel, ValidationError, Field, ConfigDict -from openai import OpenAI from file_handling.data_import import import_data from file_handling.data_conversion import make_str_enum, save_as_csv, to_long_df -from settings import secrets_store, config +from settings import config +from batch_processing.batch_method import get_client # Progress UI lives in a separate module from ui.progress_ui import ProgressController -# Initialize OpenAI client with stored API key -try: - OPENAI_API_KEY = secrets_store.load_api_key() - client = OpenAI(api_key=OPENAI_API_KEY) if OPENAI_API_KEY else None -except Exception: - OPENAI_API_KEY = None - client = None - def multi_label_pipeline(parent: Optional[tk.Misc] = None): """ Prompt for labels/quotes CSVs, classify each quote with 1+ labels, show progress, then save results to CSV. """ + try: + client = get_client() + except Exception as e: + messagebox.showerror("API Key Required", str(e)) + return + # Get labels data from_import = import_data(parent, "Select the labels data") if from_import is None: @@ -47,7 +46,7 @@ def multi_label_pipeline(parent: Optional[tk.Misc] = None): class LabeledQuoteMulti(BaseModel): id: int | None = None quote: str - label: List[labels] = Field(..., min_items=1) + label: List[labels] = Field(..., min_length=1) model_config = ConfigDict(use_enum_values=True, extra='forbid') total = len(quotes) diff --git a/live_processing/single_label_live.py b/live_processing/single_label_live.py index ee804c3..896232c 100644 --- a/live_processing/single_label_live.py +++ b/live_processing/single_label_live.py @@ -8,29 +8,28 @@ from typing import Optional import tkinter as tk +from tkinter import messagebox from pydantic import BaseModel, ValidationError, Field, ConfigDict -from openai import OpenAI from file_handling.data_import import import_data from file_handling.data_conversion import make_str_enum, save_as_csv, to_long_df -from settings import secrets_store, config +from settings import config +from batch_processing.batch_method import get_client # Progress UI lives in a separate module from ui.progress_ui import ProgressController -# Initialize OpenAI client with stored API key -try: - OPENAI_API_KEY = secrets_store.load_api_key() - client = OpenAI(api_key=OPENAI_API_KEY) if OPENAI_API_KEY else None -except Exception: - OPENAI_API_KEY = None - client = None - def single_label_pipeline(parent: Optional[tk.Misc] = None): """ Prompt for labels/quotes CSVs, classify each quote with exactly one label, show progress, then save results to CSV. """ + try: + client = get_client() + except Exception as e: + messagebox.showerror("API Key Required", str(e)) + return + # Get labels data from_import = import_data(parent, "Select the labels data") if from_import is None: diff --git a/ui/main_window.py b/ui/main_window.py index 80efc85..9f898d4 100644 --- a/ui/main_window.py +++ b/ui/main_window.py @@ -92,7 +92,12 @@ def build_ui(root: tk.Tk) -> None: root: The main Tkinter window to build the UI in """ root.title(APP_TITLE) - root.iconbitmap(asset_path("app.ico")) + try: + # .ico files are only natively supported by iconbitmap on Windows; + # this raises TclError on Linux/macOS Tk builds. + root.iconbitmap(asset_path("app.ico")) + except tk.TclError: + pass # ===== Top-level grid: header, spacer, table area ===== root.columnconfigure(0, weight=1)