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

286
main.py Executable file
View File

@@ -0,0 +1,286 @@
#!/usr/bin/env python3
import os
import re
import sys
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 QApplication, QFileDialog, QMainWindow
from lib import *
from ui.MainWindow import Ui_MainWindow
# from ui.testWindow import Ui_MainWindow
def query_error(query: QSqlQuery) -> None:
print(
"SQL Error:\n{}\n{}\n{}:{}".format(
query.executedQuery(),
query.boundValues(),
query.lastError().type(),
query.lastError().text(),
)
)
raise Exception("SQL Error")
class MainWindow(QMainWindow, Ui_MainWindow):
book_id = 0
def __init__(self) -> None:
super(MainWindow, self).__init__()
self.setupUi(self)
model = QSqlQueryModel()
query = QSqlQuery("SELECT * FROM people ORDER BY name")
model.setQuery(query)
self.peopleView.setModel(model)
self.peopleView.setModelColumn(1)
# self.load_definition(word, PyDictionary.meaning(word))
# self.wordButton.clicked.connect(self.wordAction)
self.ReadButton.clicked.connect(self.readAction)
self.bookBtn.clicked.connect(self.bookAction)
self.show()
return
@pyqtSlot()
def bookAction(self) -> None:
directory = QFileDialog.getExistingDirectory()
book = Book(directory)
self.book_id = self.store_book(book)
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:
document = None # self.textEdit.document()
myCursor = QTextCursor(document)
myCursor.movePosition(QTextCursor.MoveOperation.Start)
myCursor.movePosition(
QTextCursor.MoveOperation.End, QTextCursor.MoveMode.KeepAnchor
)
myCursor.removeSelectedText()
word_format = QTextCharFormat()
# word_format.setFontFamily("Caveat")
word_format.setFontPointSize(48)
word_format.setFontWeight(QFont.Weight.Bold)
myCursor.insertText(word, word_format)
# word_format.setFont(document.defaultFont())
typeFormat = QTextListFormat()
typeFormat.setStyle(QTextListFormat.Style.ListDisc)
typeFormat.setIndent(1)
defFormat = QTextListFormat()
defFormat.setStyle(QTextListFormat.Style.ListCircle)
defFormat.setIndent(2)
myCursor.setCharFormat(word_format)
for key in definition.keys():
myCursor.insertList(typeFormat)
myCursor.insertText(key)
myCursor.insertList(defFormat)
first = True
for a_def in definition[key]:
if not first:
myCursor.insertBlock()
myCursor.insertText(a_def)
first = False
return
@pyqtSlot()
def readAction(self) -> None:
indexes = self.peopleView.selectedIndexes()
if len(indexes) < 1:
return
person_id = indexes[0].siblingAtColumn(0).data()
name = indexes[0].data()
print(person_id, name)
dlg = EditDialog(self.book_id, person_id)
dlg.exec()
return
SQL_CMDS = [
"PRAGMA foreign_keys=ON",
"CREATE TABLE IF NOT EXISTS words "
"(word_id INTEGER PRIMARY KEY AUTOINCREMENT, word TEXT, definition TEXT, "
"translation TEXT)",
"CREATE TABLE IF NOT EXISTS books "
"(book_id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT, author TEXT, "
"uuid TEXT, level INTEGER)",
"CREATE TABLE IF NOT EXISTS sections "
"(section_id INTEGER PRIMARY KEY AUTOINCREMENT, "
"title TEXT, sequence INTEGER, "
"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 "
"(person_id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, "
"organization TEXT, "
"paragraph_id INTEGER REFERENCES paragraphs ON DELETE CASCADE)",
"CREATE TABLE IF NOT EXISTS person_book "
"(person_id INTEGER REFERENCES people ON DELETE CASCADE, "
"book_id INTEGER REFERENCES books ON DELETE CASCADE)",
"CREATE TABLE IF NOT EXISTS word_paragraph "
"(word_id INTEGER REFERENCES words ON DELETE CASCADE, "
"paragraph_id INTEGER REFERENCES paragraphs ON DELETE CASCADE, "
"start INTEGER NOT NULL, end INTEGER NOT NULL)",
]
def schema_update(db: QSqlDatabase) -> None:
query = QSqlQuery()
for sql in SQL_CMDS:
print(sql)
inlower = sql.lower()
if not inlower.startswith("create table "):
if not query.exec(sql):
query_error(query)
continue
create_cmd = re.sub(r"IF NOT EXISTS ", "", sql)
create_cmd = re.sub(r"\s\s*", " ", create_cmd)
matches = re.search(r"^(CREATE TABLE )([^ ]+)( \(.+)$", create_cmd)
if matches:
table_name = matches.group(2)
create_cmd = (
matches.group(1) + '"' + matches.group(2) + '"' + matches.group(3)
)
else:
raise AttributeError(f"No match found: {create_cmd}")
query.prepare("SELECT sql FROM sqlite_schema WHERE tbl_name = :tbl")
query.bindValue(":tbl", table_name)
if not query.exec():
query_error(query)
query.next()
old = query.value(0)
if not old:
if not query.exec(sql):
query_error(query)
continue
if old.lower() == create_cmd.lower():
continue
print(old.lower())
print(create_cmd.lower())
print(f"Updating: {table_name}")
# Step 1 turn off foreign key constraints
if not query.exec("PRAGMA foreign_keys=OFF"):
query_error(query)
# Step 2 start a transaction
db.transaction()
# Step 3 remember old indexes, triggers, and views
# Step 4 create new table
new_table_name = table_name + "_new"
if not query.exec(matches.group(1) + new_table_name + matches.group(3)):
query_error(query)
# step 5 transfer content
coldefs = re.search(r"\((.+)\)", old).group(1).split(", ")
cols = [x.split(" ")[0] for x in coldefs]
if not query.exec(
"INSERT INTO "
+ new_table_name
+ "("
+ ", ".join(cols)
+ ") SELECT "
+ ", ".join(cols)
+ " FROM "
+ table_name
):
query_error(query)
# step 6 Drop old table
if not query.exec("DROP TABLE " + table_name):
query_error(query)
# step 6 rename new table to old table
if not query.exec("ALTER TABLE " + new_table_name + " RENAME TO " + table_name):
query_error(query)
# step 8 create indexes, triggers, and views
# step 9 rebuild affected views
# step 10 turn foreign key constrants back on
if not query.exec("PRAGMA foreign_keys=ON"):
query_error(query)
# step 11 commit the changes
db.commit()
return
def main() -> int:
db = QSqlDatabase()
db = db.addDatabase("QSQLITE")
db.setDatabaseName("twel.db")
db.open()
app = QApplication(sys.argv)
schema_update(db)
window: QMainWindow = MainWindow()
return app.exec()
if __name__ == "__main__":
outOfDate = []
for fileName in os.listdir("ui"):
if not fileName.endswith(".py"):
continue
uiName = fileName[:-3] + ".ui"
if os.path.getmtime("ui/" + uiName) > os.path.getmtime("ui/" + fileName):
outOfDate.append(fileName)
if len(outOfDate) > 0:
print(f"UI out of date: {', '.join(outOfDate)}")
sys.exit(1)
sys.exit(main())