Working checkpoint
This commit is contained in:
2
lib/__init__.py
Normal file
2
lib/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
||||
from .books import Book
|
||||
from .read import EditDialog
|
||||
67
lib/books.py
Normal file
67
lib/books.py
Normal 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
277
lib/read.py
Normal 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
|
||||
Reference in New Issue
Block a user