from html.parser import HTMLParser from io import StringIO from PyQt6.QtCore import QSize, Qt, pyqtSlot from PyQt6.QtGui import QStandardItem, QStandardItemModel from PyQt6.QtSql import QSqlQuery, QSqlQueryModel from PyQt6.QtWidgets import QDialog, QDialogButtonBox, QStyledItemDelegate from main import query_error from ui.PeopleDialog import Ui_Dialog class MLStripper(HTMLParser): def __init__(self): super().__init__() self.reset() return def reset(self): super().reset() self.strict = False self.convert_charrefs = True self.text = StringIO() self.first = True return def handle_data(self, d): if self.first: self.text.write(d) self.first = False return def get_data(self): return self.text.getvalue() class PersonDialog(QDialog, Ui_Dialog): SectionIdRole = Qt.ItemDataRole.UserRole SectionSequenceRole = Qt.ItemDataRole.UserRole + 1 BookIdRole = Qt.ItemDataRole.UserRole + 2 person_id = 0 def __init__(self, *args, **kwargs): self.person_id = kwargs.pop("person_id", 0) super(PersonDialog, self).__init__(*args, **kwargs) self.setupUi(self) self.show() model = QSqlQueryModel() query = QSqlQuery() query.prepare("SELECT book_id, title " "FROM books " "ORDER BY title") if not query.exec(): query_error(query) model.setQuery(query) self.bookCombo.setPlaceholderText("Select A Book") self.bookCombo.setModel(model) self.bookCombo.setModelColumn(1) self.bookCombo.setCurrentIndex(-1) model = QStandardItemModel() self.sectionCombo.setPlaceholderText("Select A Section") self.sectionCombo.setModel(model) self.sectionCombo.setEnabled(False) self.sectionCombo.setCurrentIndex(-1) button = self.buttonBox.button(QDialogButtonBox.StandardButton.Ok) button.setEnabled(False) # # Connections # self.bookCombo.currentIndexChanged.connect(self.bookSelected) self.sectionCombo.currentIndexChanged.connect(self.sectionSelected) self.nameEdit.editingFinished.connect(self.checkLineEdits) self.orgEdit.editingFinished.connect(self.checkLineEdits) if self.person_id > 0: self.setPerson(self.person_id) return def setPerson(self, person_id: int) -> None: query = QSqlQuery() query.prepare( "SELECT p.name, p.organization, p.book_id, s.sequence " "FROM people p " "LEFT JOIN person_book pb " "ON (p.book_id = pb.book_id " "AND p.person_id = pb.person_id) " "LEFT JOIN sections s " "ON (s.section_id = pb.section_id) " "WHERE p.person_id = :person_id" ) query.bindValue(":person_id", person_id) if not query.exec(): query_error(query) if not query.next(): raise Exception(f"No person record for {person_id}") self.person_id = person_id self.nameEdit.setText(query.value("name")) self.orgEdit.setText(query.value("organization")) model = self.bookCombo.model() matches = model.match( model.createIndex(0, 0), Qt.ItemDataRole.DisplayRole, query.value("book_id"), 1, Qt.MatchFlag.MatchExactly, ) if len(matches) != 1: raise Excpetion( f"Match failed looking for book_id: {query.value('book_id')}" ) row = int(matches[0].row()) self.bookCombo.setCurrentIndex(row) self.sectionCombo.setCurrentIndex(query.value("sequence")) return @pyqtSlot(int) def bookSelected(self, index: int) -> None: book_index = self.bookCombo.model().createIndex(index, 0) book_id = book_index.data() model = self.sectionCombo.model() query = QSqlQuery() query.prepare( "SELECT section_id, sequence, content " "FROM sections " "WHERE book_id = :book_id " "ORDER BY sequence" ) query.bindValue(":book_id", book_id) if not query.exec(): query_error(query) model.clear() stripper = MLStripper() while query.next(): stripper.feed(query.value("content")) content = stripper.get_data() stripper.reset() item = QStandardItem() item.setData(content[:40], Qt.ItemDataRole.DisplayRole) item.setData( query.value("sequence"), PersonDialog.SectionSequenceRole ) item.setData(query.value("section_id"), PersonDialog.SectionIdRole) model.appendRow(item) self.sectionCombo.setEnabled(True) return @pyqtSlot(int) def sectionSelected(self, row: int) -> None: self.checkLineEdits() return @pyqtSlot() def accept(self) -> None: query = QSqlQuery() if self.person_id > 0: query.prepare( "UPDATE people SET " "name = :name, " "organization = :org, " "email = :email, " "book_id = :book_id " "WHERE person_id = :person_id" ) query.bindValue(":person_id", self.person_id) else: query.prepare( "INSERT INTO people " "(name, organization, email, book_id) " "VALUES (:name, :org, :email, :book_id)" ) query.bindValue(":name", self.nameEdit.text().strip()) query.bindValue(":org", self.nameEdit.text().strip()) query.bindValue(":email", self.emailEdit.text().strip()) row = self.bookCombo.currentIndex() model = self.bookCombo.model() book_id = model.data(model.createIndex(row, 0)) query.bindValue(":book_id", book_id) section_id = self.sectionCombo.currentData(PersonDialog.SectionIdRole) print(f"section_id: {section_id}") if not section_id: raise Exception(f"Section id is null") if not query.exec(): query_error(query) if self.person_id <= 0: self.person_id = query.lastInsertId() query.prepare( "SELECT * FROM person_book pb " "WHERE pb.person_id = :person_id " "AND pb.book_id = :book_id" ) query.bindValue(":person_id", self.person_id) query.bindValue(":book_id", book_id) if not query.exec(): query_error(query) if query.next(): query.prepare( "UPDATE person_book SET " "section_id = :section_id " "WHERE person_id = :person_id " "AND book_id = :book_id" ) else: query.prepare( "INSERT INTO person_book " "(person_id, book_id, section_id, block) " "VALUES (:person_id, :book_id, :section_id, 0 )" ) query.bindValue(":person_id", self.person_id) query.bindValue(":book_id", book_id) query.bindValue(":section_id", section_id) if not query.exec(): query_error(query) super().accept() return @pyqtSlot() def checkLineEdits(self): name = self.nameEdit.text().strip() org = self.orgEdit.text().strip() button = self.buttonBox.button(QDialogButtonBox.StandardButton.Ok) if name and org: button.setEnabled(True) else: button.setEnabled(False) return