Working checkpoint

This commit is contained in:
Christopher T. Johnson
2023-11-10 21:48:15 -05:00
parent e9dbadb5e3
commit 598201425c
16 changed files with 1132 additions and 0 deletions

2
lib/__init__.py Normal file
View File

@@ -0,0 +1,2 @@
from .books import Book
from .read import EditDialog

67
lib/books.py Normal file
View File

@@ -0,0 +1,67 @@
import json
import os
import xml.dom.minidom
class Book:
sections = []
metadata = {}
def __init__(self, src: str) -> None:
super(Book, self).__init__()
self.parse_book(src)
return
def parse_book(self, src: str) -> None:
with open(f"{src}/content.opf", "r") as f:
dom = xml.dom.minidom.parse(f)
metadata = dom.getElementsByTagName("metadata")[0]
for meta in metadata.childNodes:
if meta.nodeType != xml.dom.Node.ELEMENT_NODE:
continue
if meta.prefix == "dc":
self.metadata[meta.localName] = meta.firstChild.data
#
# The manifest contains a list of all the files contained in this
# EPUB
#
manifest = dom.getElementsByTagName("manifest")[0]
#
# The spine contains the documents in order they are read
#
spine = dom.getElementsByTagName("spine")[0]
for itemref in spine.childNodes:
if itemref.nodeType != xml.dom.Node.ELEMENT_NODE:
continue
# If linear == "no" skip
if (
itemref.hasAttribute("linear")
and itemref.getAttribute("linear") == "no"
):
continue
idref = itemref.getAttribute("idref")
for item in manifest.childNodes:
if item.nodeType != xml.dom.Node.ELEMENT_NODE:
continue
id = item.getAttribute("id")
if id == idref:
break
#
# A properly created *.opf will always have a correct
# spin/manifest.
#
href = item.getAttribute("href")
print(f"{idref}: {href}")
self.parse_section(src, href)
return
def parse_section(self, src: str, href: str) -> None:
with open(f"{src}/{href}") as f:
dom = xml.dom.minidom.parse(f)
title = dom.getElementsByTagName("title")[0].firstChild.data
body = dom.getElementsByTagName("body")[0]
paragraphs = []
for p in body.getElementsByTagName("p"):
paragraphs.append(p.toxml())
self.sections.append({"title": title, "paragraphs": paragraphs})
return

277
lib/read.py Normal file
View File

@@ -0,0 +1,277 @@
import json
from PyDictionary import PyDictionary # type: ignore[import-untyped]
from PyQt6.QtCore import Qt, pyqtSlot
from PyQt6.QtGui import (
QFont,
QTextCharFormat,
QTextCursor,
QTextDocument,
QTextListFormat,
)
from PyQt6.QtSql import QSqlDatabase, QSqlQuery, QSqlQueryModel
from PyQt6.QtWidgets import QDialog
from main import query_error
from ui.EditDialog import Ui_Dialog
class EditDialog(QDialog, Ui_Dialog):
def __init__(self, book_id: int, person_id: int) -> None:
super(EditDialog, self).__init__()
self.book_id = book_id
self.person_id = person_id
self.setupUi(self)
self.current_paragraph(self.person_id)
self.paraEdit.setReadOnly(True)
self.defEdit.setReadOnly(True)
self.stackedWidget.setCurrentIndex(0)
self.defineBtn.clicked.connect(self.defineAction)
self.showBtn.clicked.connect(self.showAction)
self.nextBtn.clicked.connect(self.nextAction)
self.prevBtn.clicked.connect(self.prevAction)
return
def current_paragraph(self, person_id: int) -> None:
query = QSqlQuery()
query.prepare("SELECT * FROM people WHERE person_id = :person_id")
query.bindValue(":person_id", person_id)
query.exec()
query.next()
paragraph_id = query.value("paragraph_id")
self.display_paragraph(paragraph_id)
return
def display_paragraph(self, paragraph_id: int) -> None:
self.paragraph_id = paragraph_id
query = QSqlQuery()
query.prepare("SELECT * FROM paragraphs WHERE paragraph_id = :paragraph_id")
query.bindValue(":paragraph_id", paragraph_id)
query.exec()
query.next()
self.section_id = query.value("section_id")
self.paraSequence = query.value("sequence")
cursor = self.paraEdit.textCursor()
cursor.movePosition(QTextCursor.MoveOperation.Start)
cursor.movePosition(
QTextCursor.MoveOperation.End, QTextCursor.MoveMode.KeepAnchor
)
cursor.removeSelectedText()
cursor.insertHtml(query.value("content"))
query.prepare(
"SELECT * FROM word_paragraph " "WHERE paragraph_id = :paragraph_id"
)
query.bindValue(":paragraph_id", self.paragraph_id)
if not query.exec():
query_error(query)
def_format = QTextCharFormat()
def_format.setFontUnderline(True)
cursor = QTextCursor(self.paraEdit.document())
while query.next():
start = query.value("start")
end = query.value("end")
cursor.setPosition(start, QTextCursor.MoveMode.MoveAnchor)
cursor.setPosition(end, QTextCursor.MoveMode.KeepAnchor)
cursor.setCharFormat(def_format)
cursor.setPosition(0)
self.paraEdit.setTextCursor(cursor)
return
@pyqtSlot()
def defineAction(self) -> None:
cursor = self.paraEdit.textCursor()
word = cursor.selectedText()
start = cursor.selectionStart()
end = cursor.selectionEnd()
if start != end:
word = word.strip()
if len(word) == 0 or word.find(" ") >= 0:
return
if start > end:
tmp = start
start = end
end = tmp
query = QSqlQuery()
query.prepare("SELECT * FROM words WHERE word = :word")
query.bindValue(":word", word)
if not query.exec():
query_error(query)
if query.next(): # we have something
self.defined(query.value("word_id"), start, end)
return
dictionary = PyDictionary()
meaning = json.dumps(dictionary.meaning(word))
por = dictionary.translate(word, "pt")
query.prepare(
"INSERT INTO words (word, definition, translation) "
"VALUES (:word, :definition, :translation)"
)
query.bindValue(":word", word)
query.bindValue(":definition", meaning)
query.bindValue(":translation", por)
if not query.exec():
query_error(query)
self.defined(query.lastInsertId(), start, end)
return
def defined(self, word_id: int, start: int, end: int) -> None:
query = QSqlQuery()
query.prepare(
"SELECT * FROM word_paragraph "
"WHERE word_id = :word_id "
"AND paragraph_id = :paragraph_id "
"AND start = :start "
"AND end = :end"
)
query.bindValue(":word_id", word_id)
query.bindValue(":paragraph_id", self.paragraph_id)
query.bindValue(":start", start)
query.bindValue(":end", end)
if not query.exec():
query_error(query)
if query.next():
return
query.prepare(
"INSERT INTO word_paragraph VALUES "
"( :word_id, :paragraph_id, :start, :end)"
)
query.bindValue(":word_id", word_id)
query.bindValue(":paragraph_id", self.paragraph_id)
query.bindValue(":start", start)
query.bindValue(":end", end)
if not query.exec():
query_error(query)
def_format = QTextCharFormat()
def_format.setFontUnderline(True)
cursor = QTextCursor(self.paraEdit.document())
cursor.setPosition(start, QTextCursor.MoveMode.MoveAnchor)
cursor.setPosition(end, QTextCursor.MoveMode.KeepAnchor)
cursor.setCharFormat(def_format)
return
def display_definition(self) -> None:
cursor = self.paraEdit.textCursor()
query = QSqlQuery()
query.prepare(
"SELECT w.* FROM word_paragraph wp "
"LEFT JOIN words w "
"ON (w.word_id = wp.word_id) "
"WHERE :position BETWEEN wp.start AND wp.end "
"AND wp.paragraph_id = :paragraph_id"
)
query.bindValue(":position", cursor.position())
query.bindValue(":paragraph_id", self.paragraph_id)
print("display_definition()", cursor.position())
if not query.exec():
query_error(query)
if not query.next():
return
word = query.value("word")
definition = json.loads(query.value("definition"))
self.defEdit.document().clear()
cursor = self.defEdit.textCursor()
word_format = QTextCharFormat()
word_format.setFontPointSize(48)
word_format.setFontWeight(QFont.Weight.Bold)
cursor.insertText(word, word_format)
typeFormat = QTextListFormat()
typeFormat.setStyle(QTextListFormat.Style.ListDisc)
typeFormat.setIndent(1)
defFormat = QTextListFormat()
defFormat.setStyle(QTextListFormat.Style.ListCircle)
defFormat.setIndent(2)
word_format.setFontWeight(QFont.Weight.Normal)
word_format.setFontPointSize(16)
cursor.setCharFormat(word_format)
for key in definition.keys():
cursor.insertList(typeFormat)
cursor.insertText(key)
cursor.insertList(defFormat)
first = True
for a_def in definition[key]:
if not first:
cursor.insertBlock()
cursor.insertText(a_def)
first = False
return
@pyqtSlot()
def showAction(self) -> None:
idx = self.stackedWidget.currentIndex()
if idx == 0:
self.display_definition()
self.stackedWidget.setCurrentIndex(1 - idx)
return
def nextDefinition(self):
cursor = self.paraEdit.textCursor()
position = cursor.position()
print(position)
formats = cursor.block().textFormats()
found = None
for f in formats:
wc = QTextCursor(cursor)
wc.setPosition(f.start)
wc.movePosition(
QTextCursor.MoveOperation.Right,
QTextCursor.MoveMode.KeepAnchor,
f.length,
)
word = wc.selectedText()
cf = wc.charFormat()
if f.start <= position:
continue
if not cf.fontUnderline():
continue
if not found:
found = f
elif f.start < found.start:
found = f
if found:
cursor.setPosition(found.start)
self.paraEdit.setTextCursor(cursor)
self.display_definition()
return
@pyqtSlot()
def nextAction(self) -> None:
if self.stackedWidget.currentIndex() == 1:
self.nextDefinition()
return
paraQuery = QSqlQuery()
paraQuery.prepare(
"SELECT * FROM paragraphs WHERE "
"section_id=:section_id AND sequence = :sequence"
)
paraQuery.bindValue(":section_id", self.section_id)
paraQuery.bindValue(":sequence", self.paraSequence + 1)
if not paraQuery.exec():
query_error(paraQuery)
if not paraQuery.next():
secQuery = QSqlQuery()
secQuery.prepare(
"SELECT * FROM sections WHERE book_id=:book_id "
"AND sequence = "
"(SELECT sequence FROM sections WHERE "
"section_id = :section_id)+1"
)
secQuery.bindValue(":book_id", self.book_id)
secQuery.bindValue(":section_id", self.section_id)
if not secQuery.exec():
query_error(secQuery)
if not secQuery.next():
return
self.paraSequence = 0
self.section_id = secQuery.value("section_id")
paraQuery.bindValue(":section_id", self.section_id)
paraQuery.bindValue(":sequence", self.paraSequence)
if not paraQuery.exec():
query_error(paraQuery)
paraQuery.next()
self.display_paragraph(paraQuery.value("paragraph_id"))
return
@pyqtSlot()
def prevAction(self) -> None:
return