# -*- coding: utf-8 -*-
"""
:File: EuljiroBible/gui/ui/tab_keyword_logic.py
:Author: Benjamin Jaedon Choi - https://github.com/saintbenjamin
:Affiliated Church: The Eulji-ro Presbyterian Church [대한예수교장로회(통합) 을지로교회]
:Address: The Eulji-ro Presbyterian Church, 24-10, Eulji-ro 20-gil, Jung-gu, Seoul 04549, South Korea
:Telephone: +82-2-2266-3070
:E-mail: euljirochurch [at] G.M.A.I.L. (replace [at] with @ and G.M.A.I.L as you understood.)
:License: MIT License with Attribution Requirement (see LICENSE file for details); Copyright (c) 2025 The Eulji-ro Presbyterian Church.
Handles the keyword search, result display, and verse saving logic for the :class:`gui.ui.tab_keyword.TabKeyword` UI.
"""
from PySide6.QtCore import Qt
from PySide6.QtWidgets import QMessageBox
from core.utils.bible_keyword_searcher import BibleKeywordSearcher
from core.utils.bible_parser import resolve_book_name
from core.utils.logger import log_debug
from core.utils.utils_output import format_output, save_to_files
from gui.utils.keyword_result_model import KeywordResultTableModel
from gui.utils.keyword_highlight_delegate import KeywordHighlightDelegate
[docs]
class TabKeywordLogic:
"""
Backend logic for keyword-based Bible search and verse export.
This class implements the non-UI logic used by the TabKeyword GUI tab,
including executing keyword searches, formatting results for display,
and exporting a selected verse to the configured overlay output file.
Attributes:
settings (dict): Application-level settings dictionary used for output paths
and persistence behavior.
tr (Callable[[str], str]): Translation function used to render localized
UI strings and message box text.
"""
[docs]
def __init__(self, settings, tr_func):
"""
Initialize the logic handler with settings and a translation function.
Args:
settings (dict): Global application settings.
tr_func (Callable[[str], str]): Translation function for localized strings.
"""
self.settings = settings
self.tr = tr_func
[docs]
def run_search(self, parent):
"""
Run a keyword search and update the UI with results and summary.
This reads the selected Bible version and keyword input from the parent UI,
executes the search via BibleKeywordSearcher, and then updates the table and
summary views through the parent's update methods. User-facing warnings are
shown when input is missing or no results are found.
Args:
parent (QWidget): The TabKeyword UI instance providing the search inputs
and output widgets.
"""
log_debug("[TabKeyword] run_search started")
version = parent.version_box.currentData() or parent.version_box.currentText()
keywords = parent.keyword_input.text().strip().split()
if not keywords or all(k == "" for k in keywords):
QMessageBox.warning(parent, self.tr("warn_input_title"), self.tr("warn_input_msg"))
return
# Determine search mode from radio buttons
mode = "compact" if parent.radio_compact.isChecked() else "and"
# Execute search
searcher = BibleKeywordSearcher(version)
results = searcher.search(" ".join(keywords), mode=mode)
counts = searcher.count_keywords(results, keywords)
parent.update_table(results)
parent.update_summary(counts)
log_debug(f"[TabKeyword] search results: {len(results)} found")
parent.summary_box.append("") # add spacing line
parent.summary_box.append(f"{self.tr('total_results_label')} {len(results)}")
if not results:
QMessageBox.information(parent, self.tr("info_no_results_title"), self.tr("info_no_results_msg"))
[docs]
def save_selected_verse(self, parent):
"""
Save the currently selected verse from the result table to the overlay output.
This extracts the selected reference from the result table, resolves/normalizes
the book name, retrieves the verse text from the selected version, formats the
output text, and writes it using the project's output-saving helper.
Args:
parent (QWidget): The TabKeyword UI instance providing table selection,
Bible data access, language context, and settings.
Raises:
Exception: Any error raised by the output writer is caught and presented
to the user via a critical dialog.
"""
log_debug("[TabKeyword] save_selected_verse called")
index = parent.table.currentIndex()
row = index.row()
if row < 0:
QMessageBox.warning(parent, self.tr("warn_selection_title"), self.tr("warn_selection_msg"))
return
model = parent.table.model()
ref = model.index(row, 1).data() # Get reference string like "John 3:16"
try:
book_str, chap_verse = ref.rsplit(' ', 1)
book = resolve_book_name(book_str) or book_str
chapter, verse = chap_verse.split(':')
chapter = int(chapter)
verse = int(verse)
except Exception as e:
log_debug(f"[TabKeyword] Failed to parse reference: {ref}")
QMessageBox.warning(parent, self.tr("warn_selection_title"), self.tr("warn_selection_msg"))
return
normalized = book.replace(" ", "")
version = parent.version_box.currentData() or parent.version_box.currentText()
verses = parent.bible_data.get_verses(version)
# Normalize book name if needed
if normalized not in verses:
for key in verses:
if key.lower().replace(" ", "") == normalized.lower():
normalized = key
break
else:
log_debug(f"[TabKeyword] Book '{book}' not found in version {version}")
QMessageBox.warning(
parent,
self.tr("warn_no_chapter_title"),
self.tr("warn_no_chapter_msg").format(normalized, chapter)
)
return
verse_range = (verse, verse)
book_alias = parent.bible_data.get_book_alias(parent.current_language)
version_alias = parent.bible_data.get_version_alias(parent.current_language)
merged = format_output(
[version],
normalized,
str(chapter),
verse_range,
{version: verses},
self.tr,
for_whitebox=True,
lang_code=parent.current_language,
bible_data=parent.bible_data,
book_alias=book_alias,
version_alias=version_alias
)
try:
save_to_files(merged, parent.settings)
log_debug("[TabKeyword] selected verse saved successfully")
except Exception as e:
QMessageBox.critical(parent, self.tr("error_saving_title"), self.tr("error_saving_msg").format(e))
[docs]
def clear_outputs(self, parent):
"""
Clear the verse output destination.
This writes an empty string to the configured output path(s), effectively
clearing any overlay text that is being polled or displayed.
Args:
parent (QWidget): The TabKeyword UI instance providing settings.
"""
save_to_files("", parent.settings)
[docs]
def update_table(self, parent, results):
"""
Update the result table model and install keyword highlighting.
This constructs a KeywordResultTableModel from the search results, attaches it
to the UI table, and applies a KeywordHighlightDelegate for visual emphasis of
matched terms.
Args:
parent (QWidget): The TabKeyword UI instance containing the table widget.
results (List[dict]): List of verse entry dictionaries returned by the searcher.
"""
model = KeywordResultTableModel(results, parent.bible_data, parent.current_language, parent.tr)
parent.table.setModel(model)
keywords = parent.keyword_input.text().strip().split()
delegate = KeywordHighlightDelegate(keywords)
parent.table.setItemDelegateForColumn(2, delegate)
parent.table.resizeColumnsToContents()
parent.table.resizeRowsToContents()
parent.table.setStyleSheet("QTableView::item { padding: 6px; }")
[docs]
def update_summary(self, parent, counts):
"""
Update the summary box with per-keyword occurrence counts.
Args:
parent (QWidget): The TabKeyword UI instance containing the summary widget.
counts (Dict[str, int]): Mapping from keyword to occurrence count.
"""
parent.summary_box.setPlainText("\n".join(f"{k}: {v}" for k, v in counts.items()))