Mostly working Reader
This commit is contained in:
136
lib/books.py
136
lib/books.py
@@ -1,15 +1,49 @@
|
|||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import xml.dom.minidom
|
import xml.dom.minidom
|
||||||
|
from typing import Dict, List, cast
|
||||||
|
|
||||||
|
from PyQt6.QtSql import QSqlQuery
|
||||||
|
|
||||||
|
from main import query_error
|
||||||
|
|
||||||
|
|
||||||
class Book:
|
class Book:
|
||||||
sections = []
|
sections: List[str] = []
|
||||||
metadata = {}
|
metadata: Dict[str, str] = {}
|
||||||
|
words = {}
|
||||||
|
|
||||||
def __init__(self, src: str) -> None:
|
def __init__(self, src: str) -> None:
|
||||||
super(Book, self).__init__()
|
super(Book, self).__init__()
|
||||||
self.parse_book(src)
|
self.parse_book(src)
|
||||||
|
book_id = self.store() # Does nothing if already in database
|
||||||
|
self.load(book_id)
|
||||||
|
return
|
||||||
|
|
||||||
|
def load(self, book_id: int) -> None:
|
||||||
|
query = QSqlQuery()
|
||||||
|
query.prepare("SELECT * FROM books where book_id = :book_id")
|
||||||
|
query.bindValue(":book_id", book_id)
|
||||||
|
if not query.exec():
|
||||||
|
query_error(query)
|
||||||
|
if not query.next():
|
||||||
|
raise Exception(f"Missing book? book_id={book_id}")
|
||||||
|
self.metadata = {
|
||||||
|
"title": query.value("title"),
|
||||||
|
"creator": query.value("author"),
|
||||||
|
"identifier": query.value("uuid"),
|
||||||
|
"level": query.value("level"),
|
||||||
|
}
|
||||||
|
|
||||||
|
self.sections = []
|
||||||
|
query.prepare(
|
||||||
|
"SELECT * FORM sections WHERE book_id = :book_id " "ORDER BY sequence"
|
||||||
|
)
|
||||||
|
while query.next():
|
||||||
|
self.sections.append(query.value("contents"))
|
||||||
|
#
|
||||||
|
# Load words!
|
||||||
|
#
|
||||||
return
|
return
|
||||||
|
|
||||||
def parse_book(self, src: str) -> None:
|
def parse_book(self, src: str) -> None:
|
||||||
@@ -53,15 +87,105 @@ class Book:
|
|||||||
href = item.getAttribute("href")
|
href = item.getAttribute("href")
|
||||||
print(f"{idref}: {href}")
|
print(f"{idref}: {href}")
|
||||||
self.parse_section(src, href)
|
self.parse_section(src, href)
|
||||||
|
#
|
||||||
|
# "sections" is now loaded
|
||||||
|
#
|
||||||
return
|
return
|
||||||
|
|
||||||
|
def store(self) -> int:
|
||||||
|
uuid = self.metadata["identifier"]
|
||||||
|
query = QSqlQuery()
|
||||||
|
query.prepare(
|
||||||
|
"SELECT COUNT(*) AS number, book_id FROM books b " "WHERE b.uuid = :uuid"
|
||||||
|
)
|
||||||
|
query.bindValue(":uuid", uuid)
|
||||||
|
if not query.exec():
|
||||||
|
query_error(query)
|
||||||
|
query.next()
|
||||||
|
if query.value("number") > 0:
|
||||||
|
book_id: int = query.value("book_id")
|
||||||
|
return book_id
|
||||||
|
query.prepare(
|
||||||
|
"INSERT INTO books (title, author, uuid, level) VALUES ("
|
||||||
|
":title, :author, :uuid, 0)"
|
||||||
|
)
|
||||||
|
query.bindValue(":title", self.metadata["title"])
|
||||||
|
query.bindValue(":author", self.metadata["creator"])
|
||||||
|
query.bindValue(":uuid", uuid)
|
||||||
|
if not query.exec():
|
||||||
|
query_error(query)
|
||||||
|
book_id = query.lastInsertId()
|
||||||
|
query.prepare(
|
||||||
|
"INSERT INTO sections (sequence, book_id, content) "
|
||||||
|
"VALUES (:sequence, :book_id, :content)"
|
||||||
|
)
|
||||||
|
query.bindValue(":book_id", book_id)
|
||||||
|
for seq, section in enumerate(self.sections):
|
||||||
|
query.bindValue(":sequence", seq)
|
||||||
|
query.bindValue(":content", section)
|
||||||
|
if not query.exec():
|
||||||
|
query_error(query)
|
||||||
|
section_id = query.lastInsertId()
|
||||||
|
return book_id
|
||||||
|
|
||||||
def parse_section(self, src: str, href: str) -> None:
|
def parse_section(self, src: str, href: str) -> None:
|
||||||
|
newdom = xml.dom.getDOMImplementation().createDocument("", "html", None)
|
||||||
|
|
||||||
|
def strip_node(elm: xml.dom.minidom.Element) -> xml.dom.minidom.Node:
|
||||||
|
if elm.nodeType == xml.dom.Node.TEXT_NODE:
|
||||||
|
return cast(
|
||||||
|
xml.dom.minidom.Node,
|
||||||
|
newdom.createTextNode(cast(xml.dom.minidom.Text, elm).data),
|
||||||
|
)
|
||||||
|
|
||||||
|
newelm = newdom.createElement(elm.localName)
|
||||||
|
node = elm.firstChild
|
||||||
|
while node:
|
||||||
|
if node.nodeType == xml.dom.Node.TEXT_NODE:
|
||||||
|
text = node.data
|
||||||
|
if text:
|
||||||
|
text = text.strip()
|
||||||
|
if text and len(text) > 0:
|
||||||
|
newelm.appendChild(newdom.createTextNode(text))
|
||||||
|
elif node.localName == "img":
|
||||||
|
pass
|
||||||
|
elif node.localName == "a":
|
||||||
|
a_node = node.firstChild
|
||||||
|
while a_node:
|
||||||
|
if a_node.nodeType == xml.dom.Node.TEXT_NODE:
|
||||||
|
newelm.appendChild(newdom.createTextNode(a_node.data))
|
||||||
|
else:
|
||||||
|
newelm.appendChild(strip_node(a_node))
|
||||||
|
a_node = a_node.nextSibling
|
||||||
|
else:
|
||||||
|
newelm.appendChild(strip_node(node))
|
||||||
|
node = node.nextSibling
|
||||||
|
return newelm
|
||||||
|
|
||||||
|
def parse_node(parent: xml.dom.Node, elm: xml.dom.Node) -> None:
|
||||||
|
if elm.nodeType == xml.dom.Node.ELEMENT_NODE:
|
||||||
|
if elm.localName.startswith("h"):
|
||||||
|
clone = strip_node(elm)
|
||||||
|
parent.appendChild(clone)
|
||||||
|
elif elm.localName == "p":
|
||||||
|
clone = strip_node(elm)
|
||||||
|
clone.normalize()
|
||||||
|
parent.appendChild(clone)
|
||||||
|
else:
|
||||||
|
node = elm.firstChild
|
||||||
|
while node:
|
||||||
|
parse_node(parent, node)
|
||||||
|
node = node.nextSibling
|
||||||
|
return
|
||||||
|
|
||||||
with open(f"{src}/{href}") as f:
|
with open(f"{src}/{href}") as f:
|
||||||
dom = xml.dom.minidom.parse(f)
|
dom = xml.dom.minidom.parse(f)
|
||||||
title = dom.getElementsByTagName("title")[0].firstChild.data
|
title = dom.getElementsByTagName("title")[0].firstChild.data
|
||||||
body = dom.getElementsByTagName("body")[0]
|
body = dom.getElementsByTagName("body")[0]
|
||||||
paragraphs = []
|
section = newdom.createElement("body")
|
||||||
for p in body.getElementsByTagName("p"):
|
node = body.firstChild
|
||||||
paragraphs.append(p.toxml())
|
while node:
|
||||||
self.sections.append({"title": title, "paragraphs": paragraphs})
|
parse_node(section, node)
|
||||||
|
node = node.nextSibling
|
||||||
|
self.sections.append(section.toxml())
|
||||||
return
|
return
|
||||||
|
|||||||
207
lib/read.py
207
lib/read.py
@@ -1,80 +1,133 @@
|
|||||||
import json
|
import json
|
||||||
|
|
||||||
from PyDictionary import PyDictionary # type: ignore[import-untyped]
|
from PyDictionary import PyDictionary # type: ignore[import-untyped]
|
||||||
from PyQt6.QtCore import Qt, pyqtSlot
|
from PyQt6.QtCore import QRect, Qt, pyqtSlot
|
||||||
from PyQt6.QtGui import (
|
from PyQt6.QtGui import (
|
||||||
|
QBrush,
|
||||||
|
QColor,
|
||||||
QFont,
|
QFont,
|
||||||
|
QPainter,
|
||||||
|
QPainterPath,
|
||||||
QTextCharFormat,
|
QTextCharFormat,
|
||||||
QTextCursor,
|
QTextCursor,
|
||||||
QTextDocument,
|
QTextDocument,
|
||||||
QTextListFormat,
|
QTextListFormat,
|
||||||
)
|
)
|
||||||
from PyQt6.QtSql import QSqlDatabase, QSqlQuery, QSqlQueryModel
|
from PyQt6.QtSql import QSqlDatabase, QSqlQuery, QSqlQueryModel
|
||||||
from PyQt6.QtWidgets import QDialog
|
from PyQt6.QtWidgets import QDialog, QPushButton
|
||||||
|
|
||||||
from main import query_error
|
from main import query_error
|
||||||
from ui.EditDialog import Ui_Dialog
|
from ui.EditDialog import Ui_Dialog
|
||||||
|
|
||||||
|
|
||||||
class EditDialog(QDialog, Ui_Dialog):
|
class EditDialog(QDialog, Ui_Dialog):
|
||||||
def __init__(self, book_id: int, person_id: int) -> None:
|
def __init__(self, person_id: int) -> None:
|
||||||
super(EditDialog, self).__init__()
|
super(EditDialog, self).__init__()
|
||||||
self.book_id = book_id
|
|
||||||
self.person_id = person_id
|
self.person_id = person_id
|
||||||
self.setupUi(self)
|
self.setupUi(self)
|
||||||
self.current_paragraph(self.person_id)
|
self.load_book(self.person_id)
|
||||||
|
blockNumber = self.block
|
||||||
self.paraEdit.setReadOnly(True)
|
self.paraEdit.setReadOnly(True)
|
||||||
self.defEdit.setReadOnly(True)
|
self.defEdit.setReadOnly(True)
|
||||||
|
self.show_section(self.section_id)
|
||||||
|
self.block = blockNumber
|
||||||
|
self.savePosition()
|
||||||
self.stackedWidget.setCurrentIndex(0)
|
self.stackedWidget.setCurrentIndex(0)
|
||||||
|
self.nextParaBtn = QPushButton(parent=self.widget)
|
||||||
|
self.nextParaBtn.setObjectName("nextParaBtn")
|
||||||
|
self.verticalLayout.addWidget(self.nextParaBtn)
|
||||||
|
self.nextParaBtn.setText("Next Paragraph")
|
||||||
self.defineBtn.clicked.connect(self.defineAction)
|
self.defineBtn.clicked.connect(self.defineAction)
|
||||||
self.showBtn.clicked.connect(self.showAction)
|
self.showBtn.clicked.connect(self.showAction)
|
||||||
self.nextBtn.clicked.connect(self.nextAction)
|
self.nextBtn.clicked.connect(self.nextAction)
|
||||||
self.prevBtn.clicked.connect(self.prevAction)
|
self.prevBtn.clicked.connect(self.prevAction)
|
||||||
|
self.nextParaBtn.clicked.connect(self.nextParaAction)
|
||||||
|
self.paraEdit.verticalScrollBar().valueChanged.connect(self.scrollSlot)
|
||||||
return
|
return
|
||||||
|
|
||||||
def current_paragraph(self, person_id: int) -> None:
|
@pyqtSlot(int)
|
||||||
query = QSqlQuery()
|
def scrollSlot(self, value):
|
||||||
query.prepare("SELECT * FROM people WHERE person_id = :person_id")
|
self.update()
|
||||||
query.bindValue(":person_id", person_id)
|
|
||||||
query.exec()
|
|
||||||
query.next()
|
|
||||||
paragraph_id = query.value("paragraph_id")
|
|
||||||
self.display_paragraph(paragraph_id)
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def display_paragraph(self, paragraph_id: int) -> None:
|
def load_book(self, person_id: int) -> None:
|
||||||
self.paragraph_id = paragraph_id
|
|
||||||
query = QSqlQuery()
|
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(
|
query.prepare(
|
||||||
"SELECT * FROM word_paragraph " "WHERE paragraph_id = :paragraph_id"
|
"SELECT pb.* FROM people p "
|
||||||
|
"LEFT JOIN person_book pb "
|
||||||
|
"ON (p.book_id = pb.book_id "
|
||||||
|
"AND p.person_id = pb.person_id) "
|
||||||
|
"WHERE p.person_id = :person_id"
|
||||||
)
|
)
|
||||||
query.bindValue(":paragraph_id", self.paragraph_id)
|
query.bindValue(":person_id", person_id)
|
||||||
|
if not query.exec():
|
||||||
|
query_error(query)
|
||||||
|
if not query.next():
|
||||||
|
self.done(0)
|
||||||
|
self.book_id = query.value("book_id")
|
||||||
|
self.section_id = query.value("section_id")
|
||||||
|
self.block = query.value("block")
|
||||||
|
self.sections = []
|
||||||
|
self.section_map = {}
|
||||||
|
self.sequence_map = {}
|
||||||
|
query.prepare(
|
||||||
|
"SELECT * FROM sections " "WHERE book_id = :book_id " "ORDER BY sequence"
|
||||||
|
)
|
||||||
|
query.bindValue(":book_id", self.book_id)
|
||||||
if not query.exec():
|
if not query.exec():
|
||||||
query_error(query)
|
query_error(query)
|
||||||
def_format = QTextCharFormat()
|
|
||||||
def_format.setFontUnderline(True)
|
|
||||||
cursor = QTextCursor(self.paraEdit.document())
|
|
||||||
while query.next():
|
while query.next():
|
||||||
start = query.value("start")
|
self.sections.append(query.value("content"))
|
||||||
end = query.value("end")
|
self.section_map[query.value("section_id")] = query.value("sequence")
|
||||||
cursor.setPosition(start, QTextCursor.MoveMode.MoveAnchor)
|
self.sequence_map[query.value("sequence")] = query.value("section_id")
|
||||||
cursor.setPosition(end, QTextCursor.MoveMode.KeepAnchor)
|
return
|
||||||
cursor.setCharFormat(def_format)
|
|
||||||
cursor.setPosition(0)
|
def show_section(self, section_id, start=True):
|
||||||
|
sequence = self.section_map[section_id]
|
||||||
|
self.setWindowTitle(f"Section {sequence}")
|
||||||
|
self.paraEdit.clear()
|
||||||
|
cursor = self.paraEdit.textCursor()
|
||||||
|
cursor.insertHtml(self.sections[sequence])
|
||||||
|
if start:
|
||||||
|
self.block = 0
|
||||||
|
else:
|
||||||
|
self.block = self.paraEdit.document().blockCount() - 1
|
||||||
|
textBlock = self.paraEdit.document().findBlockByNumber(self.block)
|
||||||
|
cursor.setPosition(textBlock.position())
|
||||||
self.paraEdit.setTextCursor(cursor)
|
self.paraEdit.setTextCursor(cursor)
|
||||||
|
self.paraEdit.ensureCursorVisible()
|
||||||
|
return
|
||||||
|
|
||||||
|
def mousePressEvent(self, event):
|
||||||
|
return
|
||||||
|
|
||||||
|
def paintEvent(self, e):
|
||||||
|
position = self.paraEdit.document().findBlockByNumber(self.block).position()
|
||||||
|
cursor = self.paraEdit.textCursor()
|
||||||
|
cursor.setPosition(position)
|
||||||
|
rect = self.paraEdit.cursorRect(cursor)
|
||||||
|
# print(rect)
|
||||||
|
pos = self.paraEdit.mapToParent(self.paraEdit.pos())
|
||||||
|
painter = QPainter(self)
|
||||||
|
brush = QBrush()
|
||||||
|
brush.setColor(QColor("green"))
|
||||||
|
brush.setStyle(Qt.BrushStyle.SolidPattern)
|
||||||
|
path = QPainterPath()
|
||||||
|
path.moveTo(0, 0)
|
||||||
|
path.lineTo(pos.x() - 1.0, pos.y() / 2.0)
|
||||||
|
path.lineTo(0, pos.y())
|
||||||
|
path.lineTo(0, 0)
|
||||||
|
# XXX - Replace the guess with a calculated value
|
||||||
|
painter.translate(1.0, pos.y() + rect.y() + 12)
|
||||||
|
painter.fillPath(path, brush)
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
|
def nextParaAction(self) -> None:
|
||||||
|
self.block += 1
|
||||||
|
if self.block >= self.paraEdit.document().blockCount():
|
||||||
|
self.nextAction()
|
||||||
|
return
|
||||||
|
self.savePosition()
|
||||||
return
|
return
|
||||||
|
|
||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
@@ -234,44 +287,58 @@ class EditDialog(QDialog, Ui_Dialog):
|
|||||||
self.display_definition()
|
self.display_definition()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
def scrollTo(self, position):
|
||||||
|
cursor = self.paraEdit.textCursor()
|
||||||
|
cursor.setPosition(position)
|
||||||
|
rect = self.paraEdit.cursorRect(cursor)
|
||||||
|
print(rect)
|
||||||
|
return
|
||||||
|
|
||||||
|
def savePosition(self) -> None:
|
||||||
|
cursor = self.paraEdit.textCursor()
|
||||||
|
cursor.setPosition(
|
||||||
|
self.paraEdit.document().findBlockByNumber(self.block).position()
|
||||||
|
)
|
||||||
|
self.paraEdit.setTextCursor(cursor)
|
||||||
|
self.scrollTo(cursor.position())
|
||||||
|
self.paraEdit.ensureCursorVisible()
|
||||||
|
self.update()
|
||||||
|
query = QSqlQuery()
|
||||||
|
query.prepare(
|
||||||
|
"UPDATE person_book SET section_id = :section_id, "
|
||||||
|
"block = :block "
|
||||||
|
"WHERE book_id = :book_id "
|
||||||
|
"AND person_id = :person_id"
|
||||||
|
)
|
||||||
|
query.bindValue(":section_id", self.section_id)
|
||||||
|
query.bindValue(":block", self.block)
|
||||||
|
query.bindValue(":book_id", self.book_id)
|
||||||
|
query.bindValue(":person_id", self.person_id)
|
||||||
|
if not query.exec():
|
||||||
|
query_error(query)
|
||||||
|
return
|
||||||
|
|
||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
def nextAction(self) -> None:
|
def nextAction(self) -> None:
|
||||||
if self.stackedWidget.currentIndex() == 1:
|
if self.stackedWidget.currentIndex() == 1:
|
||||||
self.nextDefinition()
|
self.nextDefinition()
|
||||||
return
|
return
|
||||||
paraQuery = QSqlQuery()
|
sequence = self.section_map[self.section_id]
|
||||||
paraQuery.prepare(
|
sequence += 1
|
||||||
"SELECT * FROM paragraphs WHERE "
|
self.section_id = self.sequence_map[sequence]
|
||||||
"section_id=:section_id AND sequence = :sequence"
|
self.show_section(self.section_id)
|
||||||
)
|
self.savePosition()
|
||||||
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
|
return
|
||||||
|
|
||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
def prevAction(self) -> None:
|
def prevAction(self) -> None:
|
||||||
|
if self.stackedWidget.currentIndex() == 1:
|
||||||
|
return
|
||||||
|
sequence = self.section_map[self.section_id]
|
||||||
|
if sequence < 1:
|
||||||
|
return
|
||||||
|
sequence -= 1
|
||||||
|
self.section_id = self.sequence_map[sequence]
|
||||||
|
self.show_section(self.section_id, False)
|
||||||
|
self.savePosition()
|
||||||
return
|
return
|
||||||
|
|||||||
140
main.py
140
main.py
@@ -5,6 +5,7 @@ import sys
|
|||||||
|
|
||||||
from PyQt6.QtCore import Qt, pyqtSlot
|
from PyQt6.QtCore import Qt, pyqtSlot
|
||||||
from PyQt6.QtGui import (
|
from PyQt6.QtGui import (
|
||||||
|
QAction,
|
||||||
QFont,
|
QFont,
|
||||||
QTextCharFormat,
|
QTextCharFormat,
|
||||||
QTextCursor,
|
QTextCursor,
|
||||||
@@ -33,8 +34,6 @@ def query_error(query: QSqlQuery) -> None:
|
|||||||
|
|
||||||
|
|
||||||
class MainWindow(QMainWindow, Ui_MainWindow):
|
class MainWindow(QMainWindow, Ui_MainWindow):
|
||||||
book_id = 0
|
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
super(MainWindow, self).__init__()
|
super(MainWindow, self).__init__()
|
||||||
self.setupUi(self)
|
self.setupUi(self)
|
||||||
@@ -47,64 +46,77 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|||||||
# self.wordButton.clicked.connect(self.wordAction)
|
# self.wordButton.clicked.connect(self.wordAction)
|
||||||
self.ReadButton.clicked.connect(self.readAction)
|
self.ReadButton.clicked.connect(self.readAction)
|
||||||
self.bookBtn.clicked.connect(self.bookAction)
|
self.bookBtn.clicked.connect(self.bookAction)
|
||||||
|
self.createActions()
|
||||||
self.show()
|
self.show()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
def createActions(self):
|
||||||
|
query = QSqlQuery()
|
||||||
|
query.prepare("SELECT * FROM books ORDER BY title")
|
||||||
|
if not query.exec():
|
||||||
|
query_error(query)
|
||||||
|
while query.next():
|
||||||
|
action = QAction(query.value("title"), self)
|
||||||
|
action.setData(query.value("book_id"))
|
||||||
|
action.triggered.connect(self.setBookAction)
|
||||||
|
self.menuBooks.addAction(action)
|
||||||
|
return
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
|
def setBookAction(self):
|
||||||
|
action = self.sender()
|
||||||
|
print(action)
|
||||||
|
book_id = action.data()
|
||||||
|
print(book_id)
|
||||||
|
indexes = self.peopleView.selectedIndexes()
|
||||||
|
if len(indexes) < 1:
|
||||||
|
return
|
||||||
|
person_id = indexes[0].siblingAtColumn(0).data()
|
||||||
|
print(person_id)
|
||||||
|
query = QSqlQuery()
|
||||||
|
query.prepare(
|
||||||
|
"UPDATE people SET book_id = :book_id " "WHERE person_id = :person_id"
|
||||||
|
)
|
||||||
|
query.bindValue(":book_id", book_id)
|
||||||
|
query.bindValue(":person_id", person_id)
|
||||||
|
if not query.exec():
|
||||||
|
query_error(query)
|
||||||
|
query.prepare(
|
||||||
|
"SELECT * FROM person_book "
|
||||||
|
"WHERE person_id = :person_id "
|
||||||
|
"AND book_id = :book_id"
|
||||||
|
)
|
||||||
|
query.bindValue(":person_id", person_id)
|
||||||
|
query.bindValue(":book_id", book_id)
|
||||||
|
if not query.exec():
|
||||||
|
query_error(query)
|
||||||
|
if query.next():
|
||||||
|
return
|
||||||
|
query.prepare(
|
||||||
|
"SELECT * FROM sections WHERE sequence = 0 " "AND book_id = :book_id"
|
||||||
|
)
|
||||||
|
query.bindValue(":book_id", book_id)
|
||||||
|
if not query.exec():
|
||||||
|
query_error(query)
|
||||||
|
if not query.next():
|
||||||
|
raise Exception(f"book_id: {book_id} has no section 0!")
|
||||||
|
section_id = query.value("section_id")
|
||||||
|
query.prepare(
|
||||||
|
"INSERT INTO person_book " "VALUES (:person_id, :book_id, :section_id, 0)"
|
||||||
|
)
|
||||||
|
query.bindValue(":person_id", person_id)
|
||||||
|
query.bindValue(":book_id", book_id)
|
||||||
|
query.bindValue(":section_id", section_id)
|
||||||
|
if not query.exec():
|
||||||
|
query_error(query)
|
||||||
|
return
|
||||||
|
|
||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
def bookAction(self) -> None:
|
def bookAction(self) -> None:
|
||||||
directory = QFileDialog.getExistingDirectory()
|
directory = QFileDialog.getExistingDirectory()
|
||||||
book = Book(directory)
|
self.book = Book(directory)
|
||||||
self.book_id = self.store_book(book)
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def store_book(self, book: Book) -> int:
|
|
||||||
uuid = book.metadata["identifier"]
|
|
||||||
query = QSqlQuery()
|
|
||||||
query.prepare(
|
|
||||||
"SELECT COUNT(*) AS number, book_id FROM books b " "WHERE b.uuid = :uuid"
|
|
||||||
)
|
|
||||||
query.bindValue(":uuid", uuid)
|
|
||||||
if not query.exec():
|
|
||||||
query_error(query)
|
|
||||||
query.next()
|
|
||||||
if query.value("number") > 0:
|
|
||||||
book_id: int = query.value("book_id")
|
|
||||||
return book_id
|
|
||||||
query.prepare(
|
|
||||||
"INSERT INTO books (title, author, uuid, level) VALUES ("
|
|
||||||
":title, :author, :uuid, 0)"
|
|
||||||
)
|
|
||||||
query.bindValue(":title", book.metadata["title"])
|
|
||||||
query.bindValue(":author", book.metadata["creator"])
|
|
||||||
query.bindValue(":uuid", uuid)
|
|
||||||
if not query.exec():
|
|
||||||
query_error(query)
|
|
||||||
book_id = query.lastInsertId()
|
|
||||||
section_query = QSqlQuery()
|
|
||||||
section_query.prepare(
|
|
||||||
"INSERT INTO sections (title, sequence, book_id) "
|
|
||||||
"VALUES (:title, :sequence, :book_id)"
|
|
||||||
)
|
|
||||||
section_query.bindValue(":book_id", book_id)
|
|
||||||
para_query = QSqlQuery()
|
|
||||||
para_query.prepare(
|
|
||||||
"INSERT INTO paragraphs (section_id, sequence, content) "
|
|
||||||
"VALUES (:section_id, :sequence, :content)"
|
|
||||||
)
|
|
||||||
for seq, section in enumerate(book.sections):
|
|
||||||
section_query.bindValue(":title", section["title"])
|
|
||||||
section_query.bindValue(":sequence", seq)
|
|
||||||
if not section_query.exec():
|
|
||||||
query_error(section_query)
|
|
||||||
section_id = query.lastInsertId()
|
|
||||||
para_query.bindValue(":section_id", section_id)
|
|
||||||
for ps, paragraph in enumerate(section["paragraphs"]):
|
|
||||||
para_query.bindValue(":sequence", ps)
|
|
||||||
para_query.bindValue(":content", paragraph)
|
|
||||||
if not para_query.exec():
|
|
||||||
query_error(para_query)
|
|
||||||
return book_id
|
|
||||||
|
|
||||||
def load_definition(self, word: str, definition: dict) -> None:
|
def load_definition(self, word: str, definition: dict) -> None:
|
||||||
document = None # self.textEdit.document()
|
document = None # self.textEdit.document()
|
||||||
myCursor = QTextCursor(document)
|
myCursor = QTextCursor(document)
|
||||||
@@ -144,9 +156,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|||||||
if len(indexes) < 1:
|
if len(indexes) < 1:
|
||||||
return
|
return
|
||||||
person_id = indexes[0].siblingAtColumn(0).data()
|
person_id = indexes[0].siblingAtColumn(0).data()
|
||||||
name = indexes[0].data()
|
dlg = EditDialog(person_id)
|
||||||
print(person_id, name)
|
|
||||||
dlg = EditDialog(self.book_id, person_id)
|
|
||||||
dlg.exec()
|
dlg.exec()
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -161,24 +171,20 @@ SQL_CMDS = [
|
|||||||
"uuid TEXT, level INTEGER)",
|
"uuid TEXT, level INTEGER)",
|
||||||
"CREATE TABLE IF NOT EXISTS sections "
|
"CREATE TABLE IF NOT EXISTS sections "
|
||||||
"(section_id INTEGER PRIMARY KEY AUTOINCREMENT, "
|
"(section_id INTEGER PRIMARY KEY AUTOINCREMENT, "
|
||||||
"title TEXT, sequence INTEGER, "
|
"sequence INTEGER, content TEXT, "
|
||||||
"book_id INTEGER REFERENCES books ON DELETE CASCADE) ",
|
"book_id INTEGER REFERENCES books ON DELETE CASCADE) ",
|
||||||
"CREATE TABLE IF NOT EXISTS paragraphs "
|
|
||||||
"(paragraph_id INTEGER PRIMARY KEY AUTOINCREMENT, "
|
|
||||||
"section_id INTEGER REFERENCES sections ON DELETE CASCADE, "
|
|
||||||
"sequence INTEGER NOT NULL DEFAULT 0, "
|
|
||||||
"content TEXT)",
|
|
||||||
"CREATE TABLE IF NOT EXISTS people "
|
"CREATE TABLE IF NOT EXISTS people "
|
||||||
"(person_id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, "
|
"(person_id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, "
|
||||||
"organization TEXT, "
|
"organization TEXT, book_id INTEGER REFERENCES books ON DELETE CASCADE) ",
|
||||||
"paragraph_id INTEGER REFERENCES paragraphs ON DELETE CASCADE)",
|
|
||||||
"CREATE TABLE IF NOT EXISTS person_book "
|
"CREATE TABLE IF NOT EXISTS person_book "
|
||||||
"(person_id INTEGER REFERENCES people ON DELETE CASCADE, "
|
"(person_id INTEGER REFERENCES people ON DELETE CASCADE, "
|
||||||
"book_id INTEGER REFERENCES books ON DELETE CASCADE)",
|
"book_id INTEGER REFERENCES books ON DELETE CASCADE, "
|
||||||
"CREATE TABLE IF NOT EXISTS word_paragraph "
|
"section_id INTEGER REFERENCES sections, "
|
||||||
|
"block INTEGER)",
|
||||||
|
"CREATE TABLE IF NOT EXISTS word_block "
|
||||||
"(word_id INTEGER REFERENCES words ON DELETE CASCADE, "
|
"(word_id INTEGER REFERENCES words ON DELETE CASCADE, "
|
||||||
"paragraph_id INTEGER REFERENCES paragraphs ON DELETE CASCADE, "
|
"section_id INTEGER REFERENCES sections ON DELETE CASCADE, "
|
||||||
"start INTEGER NOT NULL, end INTEGER NOT NULL)",
|
"block INTEGER NOT NULL, start INTEGER NOT NULL, end INTEGER NOT NULL)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -38,19 +38,31 @@ class Ui_MainWindow(object):
|
|||||||
self.horizontalLayout.addWidget(self.widget)
|
self.horizontalLayout.addWidget(self.widget)
|
||||||
MainWindow.setCentralWidget(self.centralwidget)
|
MainWindow.setCentralWidget(self.centralwidget)
|
||||||
self.menubar = QtWidgets.QMenuBar(parent=MainWindow)
|
self.menubar = QtWidgets.QMenuBar(parent=MainWindow)
|
||||||
self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 22))
|
self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 32))
|
||||||
self.menubar.setObjectName("menubar")
|
self.menubar.setObjectName("menubar")
|
||||||
|
self.menuFile = QtWidgets.QMenu(parent=self.menubar)
|
||||||
|
self.menuFile.setObjectName("menuFile")
|
||||||
|
self.menuBooks = QtWidgets.QMenu(parent=self.menubar)
|
||||||
|
self.menuBooks.setObjectName("menuBooks")
|
||||||
MainWindow.setMenuBar(self.menubar)
|
MainWindow.setMenuBar(self.menubar)
|
||||||
self.statusbar = QtWidgets.QStatusBar(parent=MainWindow)
|
self.statusbar = QtWidgets.QStatusBar(parent=MainWindow)
|
||||||
self.statusbar.setObjectName("statusbar")
|
self.statusbar.setObjectName("statusbar")
|
||||||
MainWindow.setStatusBar(self.statusbar)
|
MainWindow.setStatusBar(self.statusbar)
|
||||||
|
self.actionQuit = QtGui.QAction(parent=MainWindow)
|
||||||
|
self.actionQuit.setObjectName("actionQuit")
|
||||||
|
self.menuFile.addAction(self.actionQuit)
|
||||||
|
self.menubar.addAction(self.menuFile.menuAction())
|
||||||
|
self.menubar.addAction(self.menuBooks.menuAction())
|
||||||
|
|
||||||
self.retranslateUi(MainWindow)
|
self.retranslateUi(MainWindow)
|
||||||
QtCore.QMetaObject.connectSlotsByName(MainWindow)
|
QtCore.QMetaObject.connectSlotsByName(MainWindow)
|
||||||
|
|
||||||
def retranslateUi(self, MainWindow):
|
def retranslateUi(self, MainWindow):
|
||||||
_translate = QtCore.QCoreApplication.translate
|
_translate = QtCore.QCoreApplication.translate
|
||||||
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
|
MainWindow.setWindowTitle(_translate("MainWindow", "Reading Helper"))
|
||||||
self.WordButton.setText(_translate("MainWindow", "Words"))
|
self.WordButton.setText(_translate("MainWindow", "Words"))
|
||||||
self.ReadButton.setText(_translate("MainWindow", "Read"))
|
self.ReadButton.setText(_translate("MainWindow", "Read"))
|
||||||
self.bookBtn.setText(_translate("MainWindow", "Add Book"))
|
self.bookBtn.setText(_translate("MainWindow", "Add Book"))
|
||||||
|
self.menuFile.setTitle(_translate("MainWindow", "File"))
|
||||||
|
self.menuBooks.setTitle(_translate("MainWindow", "Books"))
|
||||||
|
self.actionQuit.setText(_translate("MainWindow", "Quit"))
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
<string>MainWindow</string>
|
<string>Reading Helper</string>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QWidget" name="centralwidget">
|
<widget class="QWidget" name="centralwidget">
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
@@ -66,11 +66,29 @@
|
|||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>800</width>
|
<width>800</width>
|
||||||
<height>22</height>
|
<height>32</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
|
<widget class="QMenu" name="menuFile">
|
||||||
|
<property name="title">
|
||||||
|
<string>File</string>
|
||||||
|
</property>
|
||||||
|
<addaction name="actionQuit"/>
|
||||||
|
</widget>
|
||||||
|
<widget class="QMenu" name="menuBooks">
|
||||||
|
<property name="title">
|
||||||
|
<string>Books</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<addaction name="menuFile"/>
|
||||||
|
<addaction name="menuBooks"/>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QStatusBar" name="statusbar"/>
|
<widget class="QStatusBar" name="statusbar"/>
|
||||||
|
<action name="actionQuit">
|
||||||
|
<property name="text">
|
||||||
|
<string>Quit</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
</widget>
|
</widget>
|
||||||
<resources/>
|
<resources/>
|
||||||
<connections/>
|
<connections/>
|
||||||
|
|||||||
Reference in New Issue
Block a user