Compare commits

...

31 Commits

Author SHA1 Message Date
Christopher T. Johnson
86ccee18fb Checkpoint 2025-03-10 11:21:14 -04:00
Christopher T. Johnson
b2d67f7aea Case Load is working better. #22 2025-02-27 16:01:13 -05:00
Christopher T. Johnson
71b0a6a112 Lint 2025-02-27 13:44:45 -05:00
Christopher T. Johnson
69fa955be1 Switch from DocketEntry widget to ItemDelegate.
We were using a subclassed QTextEdit widget so that we could capture
clicks on anchors.

Moving to a delegate removed the widget per row issue.  Unfortunately,
there was no more click on anchor.

Adding a mouseEvent() to the TableView allowed us to translate raw
mouse presses to anchor triggers.

Fixes: #18
2025-02-27 13:40:28 -05:00
Christopher T. Johnson
db4716b21b Set text on init of Mainwindow. Fixes: #16 2025-02-27 10:29:32 -05:00
Christopher T. Johnson
7fd369be74 Indentation error. Fixes: #19 2025-02-27 10:26:56 -05:00
Christopher T. Johnson
30dd9b2bcd Process 404 returns correctly. Fixes: #20 2025-02-27 10:22:20 -05:00
Christopher T. Johnson
a6316f27f6 Lint 2025-02-26 16:49:38 -05:00
Christopher T. Johnson
6129258f1b Create names for columns
Addresses #17 but it feels like we should have these definitions
in the dbConfig.  Which leads to the question of moving from
QtSql to Pony ORM
2025-02-26 16:47:33 -05:00
Christopher T. Johnson
be07589f24 Lint 2025-02-26 11:02:04 -05:00
Christopher T. Johnson
f952879753 Adjust splitter when initially loading PDF Document
Closes: #4

After consideration, we don't want to change the size of the dialog
which is the only way to make it fit the PDF.

But the geometry is saved, so the user can resize and place it.

We did set the splitter to give more space to the PDF.
2025-02-26 11:00:00 -05:00
Christopher T. Johnson
e1d1946fa5 Move the page select spinner. Fixes: #3 2025-02-25 17:39:27 -05:00
Christopher T. Johnson
89486d7c97 Remove extra prints from workers. Fixes: #15 2025-02-25 17:30:07 -05:00
Christopher T. Johnson
dfa604e846 Lint 2025-02-25 15:33:20 -05:00
Christopher T. Johnson
7a02bbb262 Add StatusBar information
Updated workers.py to hold "current" values.
Status bar is three widgets, status label, progressbar and load button.

Fixes: #8, #9
2025-02-25 15:24:34 -05:00
Christopher T. Johnson
dd9f08aa5e More linting 2025-02-25 10:17:14 -05:00
Christopher T. Johnson
49de6b1f35 Lint 2025-02-25 10:14:44 -05:00
Christopher T. Johnson
d46281fda7 Add a new dialog to get database settings. Fixes: #5
Added a new dialog for database settings.
Updated dbConfig to use QSettings.
2025-02-25 09:56:53 -05:00
Christopher T. Johnson
9a60a5adb5 Stop trying to remember command lines. Make to the rescue 2025-02-24 18:18:25 -05:00
Christopher T. Johnson
92ad7efaad Move to QSettings for database credentials.
fixes: #1
2025-02-24 16:35:37 -05:00
Christopher T. Johnson
f68e6c65cc Remove unused lines 2025-02-24 16:30:27 -05:00
Christopher T. Johnson
1bdd6f0c1e Lint 2025-02-20 18:18:49 -05:00
Christopher T. Johnson
72a262c718 Clean up Cases Display
Subclass QTableModel to return checkbox indicators.
Fix column headers
2025-02-20 17:41:10 -05:00
Christopher T. Johnson
eaaa00890e Lint 2025-02-20 10:14:42 -05:00
Christopher T. Johnson
a48e39d9ac Ignore the example directory 2025-02-20 10:13:58 -05:00
Christopher T. Johnson
babff15a24 Add Python Faulthandler 2025-02-20 10:13:36 -05:00
Christopher T. Johnson
e682fba042 Remove debug and misc. cleanup 2025-02-20 10:12:58 -05:00
Christopher T. Johnson
4b4758b747 Cleanup MainWindow.py 2025-02-20 10:11:59 -05:00
Christopher T. Johnson
918510d5e6 Move extended class to library 2025-02-20 10:11:28 -05:00
Christopher T. Johnson
c2e0cbf9a1 Add ToDo list org file 2025-02-20 10:10:56 -05:00
Christopher T. Johnson
9f1c54c1e7 Implement a PDF Viewer Dialog
This is based on the example in pyside6 changed to use a QDialog and
not a QMainWindow.  The MenuBar is gone, the Status Bar is gone.
2025-02-20 10:09:32 -05:00
29 changed files with 5093 additions and 243 deletions

1
.gitignore vendored
View File

@@ -5,3 +5,4 @@ venv
*.db
__pycache__
DocketPDF
example

View File

@@ -1,51 +1,45 @@
from typing import Any, Self, cast
import enum
from typing import Any, cast
from PySide6.QtCore import (
QDate,
QEvent,
QModelIndex,
QObject,
QPersistentModelIndex,
QPoint,
QRect,
QSize,
Qt,
Signal,
Slot,
)
from PySide6.QtGui import (
QCloseEvent,
QColor,
QFont,
QPainter,
QTextDocument,
QTextDocumentFragment,
)
from PySide6.QtSql import QSqlTableModel
from PySide6.QtSql import QSqlQuery, QSqlTableModel
from PySide6.QtWidgets import (
QAbstractItemView,
QHBoxLayout,
QHeaderView,
QLabel,
QMainWindow,
QStyle,
QProgressBar,
QPushButton,
QSizePolicy,
QStyledItemDelegate,
QStyleOptionViewItem,
QTableView,
QWidget,
)
from docketModel import docketModel
from dockettableview import documentDelegate
from lib.utils import (
QStyleOptionViewItemInit,
query_error,
readGeometry,
writeGeometry,
)
from ui.MainWindow import Ui_MainWindow
from workers import loadCases, updateThread
class QStyleOptionViewItemInit(QStyleOptionViewItem):
backgroundBrush: QColor
rect: QRect
widget: QWidget
font: QFont
text: str
class dateDelegate(QStyledItemDelegate):
def displayText(self, value: QDate, _: Any) -> str:
return value.toString("MMMM d, yyyy")
@@ -59,7 +53,10 @@ class dateDelegate(QStyledItemDelegate):
options = cast(QStyleOptionViewItemInit, option)
super().initStyleOption(options, index)
assert isinstance(index, QModelIndex)
if index.siblingAtColumn(5).data() == 0:
if (
index.siblingAtColumn(5).data(Qt.ItemDataRole.CheckStateRole)
== Qt.CheckState.Unchecked
):
options.backgroundBrush = QColor(0x444444)
return
@@ -74,30 +71,107 @@ class activeDelegate(QStyledItemDelegate):
options = cast(QStyleOptionViewItemInit, option)
super().initStyleOption(options, index)
assert isinstance(index, QModelIndex)
if index.siblingAtColumn(5).data() == 0:
if (
index.siblingAtColumn(5).data(Qt.ItemDataRole.CheckStateRole)
== Qt.CheckState.Unchecked
):
options.backgroundBrush = QColor(0x444444)
return
class casesModel(QSqlTableModel):
class ColumnNames(enum.IntEnum):
case_id = 0
docket_id = 1
petitioners = 2
respondents = 3
date = 4
active = 5
def flags(self, index: QModelIndex | QPersistentModelIndex) -> Qt.ItemFlag:
if not index.isValid():
return Qt.ItemFlag.NoItemFlags
flags = super(casesModel, self).flags(index)
if index.column() == casesModel.ColumnNames.active:
flags = (
Qt.ItemFlag.ItemIsEnabled
| Qt.ItemFlag.ItemIsEditable
| Qt.ItemFlag.ItemIsUserCheckable
)
return flags
def data(
self,
index: QModelIndex | QPersistentModelIndex,
role: int = Qt.ItemDataRole.DisplayRole,
) -> Any:
if not index.isValid():
return None
if index.column() == casesModel.ColumnNames.active:
if role == Qt.ItemDataRole.CheckStateRole:
value = super(casesModel, self).data(index)
return (
Qt.CheckState.Checked
if value == 1
else Qt.CheckState.Unchecked
)
elif role == Qt.ItemDataRole.DisplayRole:
return ""
return super().data(index, role)
def setData(
self,
index: QModelIndex | QPersistentModelIndex,
value: Any,
role: int = Qt.ItemDataRole.DisplayRole,
) -> bool:
if (
role == Qt.ItemDataRole.CheckStateRole
and index.column() == casesModel.ColumnNames.active
):
super(casesModel, self).setData(index, 1 if value else 0)
return True
return super().setData(index, value, role)
class MainWindow(QMainWindow, Ui_MainWindow):
show_entries = Signal(int)
update_status = Signal()
loadThread = None
def __init__(self) -> None:
super(MainWindow, self).__init__()
self.setupUi(self)
# self.loadThread = loadCases()
# self.loadThread.finished.connect(self.updateDone)
# self.loadThread.start()
readGeometry(self)
model = QSqlTableModel()
model = casesModel()
model.setTable("cases")
model.sort(1, Qt.SortOrder.AscendingOrder)
model.setFilter(
"1=1 ORDER BY SUBSTRING(docket_id, 1, 3), CAST(SUBSTRING(docket_id,4) AS INTEGER)"
)
model.select()
model.setHeaderData(1, Qt.Orientation.Horizontal, "Docket")
model.setHeaderData(2, Qt.Orientation.Horizontal, "Petitioners")
model.setHeaderData(3, Qt.Orientation.Horizontal, "Respondents")
model.setHeaderData(
casesModel.ColumnNames.docket_id,
Qt.Orientation.Horizontal,
"Docket",
)
model.setHeaderData(
casesModel.ColumnNames.petitioners,
Qt.Orientation.Horizontal,
"Petitioners",
)
model.setHeaderData(
casesModel.ColumnNames.respondents,
Qt.Orientation.Horizontal,
"Respondents",
)
model.setHeaderData(
casesModel.ColumnNames.date, Qt.Orientation.Horizontal, "Date"
)
model.setHeaderData(
casesModel.ColumnNames.active, Qt.Orientation.Horizontal, "Active"
)
self.casesView.setModel(model)
self.casesView.setSelectionMode(
@@ -106,23 +180,33 @@ class MainWindow(QMainWindow, Ui_MainWindow):
self.casesView.setSelectionBehavior(
QAbstractItemView.SelectionBehavior.SelectRows
)
self.casesView.hideColumn(0)
self.casesView.hideColumn(casesModel.ColumnNames.case_id)
self.casesView.setItemDelegate(activeDelegate())
self.casesView.setItemDelegateForColumn(4, dateDelegate())
self.casesView.resizeColumnToContents(1)
self.casesView.resizeColumnToContents(4)
self.casesView.setItemDelegateForColumn(
casesModel.ColumnNames.date, dateDelegate()
)
self.casesView.resizeColumnToContents(casesModel.ColumnNames.docket_id)
self.casesView.resizeColumnToContents(casesModel.ColumnNames.date)
header = self.casesView.horizontalHeader()
header.setSectionResizeMode(2, QHeaderView.ResizeMode.Fixed)
header.setSectionResizeMode(3, QHeaderView.ResizeMode.Fixed)
header.setSectionResizeMode(
casesModel.ColumnNames.petitioners, QHeaderView.ResizeMode.Fixed
)
header.setSectionResizeMode(
casesModel.ColumnNames.respondents, QHeaderView.ResizeMode.Fixed
)
self.show()
remaining = (
self.casesView.width()
- header.sectionSize(1)
- header.sectionSize(4)
- header.sectionSize(casesModel.ColumnNames.docket_id)
- header.sectionSize(casesModel.ColumnNames.date)
- 5
)
self.casesView.setColumnWidth(2, int(remaining * 0.5))
self.casesView.setColumnWidth(3, int(remaining * 0.5))
self.casesView.setColumnWidth(
casesModel.ColumnNames.petitioners, int(remaining * 0.5)
)
self.casesView.setColumnWidth(
casesModel.ColumnNames.respondents, int(remaining * 0.5)
)
self.casesView.verticalHeader().hide()
self.casesView.resizeRowsToContents()
self.casesView.doubleClicked.connect(self.rowClicked)
@@ -133,8 +217,83 @@ class MainWindow(QMainWindow, Ui_MainWindow):
self.docketView.horizontalHeader().setSectionResizeMode(
1, QHeaderView.ResizeMode.Stretch
)
self.docketView.resizeRowsToContents()
# self.docketView.setItemDelegateForColumn(1, documentDelegate())
widget = QWidget()
layout = QHBoxLayout(widget)
self.status = QLabel("Status")
layout.addWidget(self.status)
layout.addStretch()
self.progress = QProgressBar()
self.progress.setRange(0, 100)
self.progress.setValue(0)
self.progress.setMinimumWidth(150)
self.progress.setMaximumWidth(150)
self.progress.setSizePolicy(
QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Preferred
)
layout.addWidget(self.progress)
self.loadButton = QPushButton()
self.loadButton.setObjectName("loadButton")
self.loadButton.setText("Load Cases")
layout.addWidget(self.loadButton)
self.statusbar.addWidget(widget, 10)
self.loadButton.clicked.connect(self.loadCases)
self.update_status.connect(self.statusBarUpdate)
self.update_status.emit()
self.docketLabel.setText("")
return
def closeEvent(self, event: QCloseEvent) -> None:
writeGeometry(self)
super().closeEvent(event)
return
@Slot()
def loadCases(self) -> None:
if self.loadThread is None:
self.loadThread = loadCases()
self.loadThread.caseLoaded.connect(self.progress.setValue)
self.loadThread.finished.connect(self.loadCasesDone)
self.loadButton.setEnabled(False)
self.loadThread.start()
return
@Slot()
def loadCasesDone(self) -> None:
self.loadButton.setEnabled(True)
self.update_status.emit()
return
@Slot()
def statusBarUpdate(self) -> None:
if self.loadThread is None:
year = ""
max = "unknown"
else:
year = self.loadThread.year
max = str(self.loadThread.number)
query = QSqlQuery()
query.prepare(
"SELECT COUNT(*) AS number_active FROM cases WHERE active=1"
)
if not query.exec():
query_error(query)
if query.next():
active = query.value("number_active")
query.prepare(
"select SUBSTRING(docket_id,1,2) AS year, "
"SUBSTRING(docket_id,3,1) AS type, "
"MAX(CAST(SUBSTRING(docket_id,4) AS INTEGER)) AS number "
"FROM cases "
"GROUP BY year, type "
"ORDER BY type, year, number"
"LIMIT 2"
)
# if not query.exec():
# query_error(query)
msg = f"Oldest: {year}, Active: {active}, Max. Case: {max}"
self.status.setText(msg)
return
@Slot(QModelIndex) # type: ignore
@@ -161,6 +320,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
assert isinstance(self.updateThread, updateThread)
self.updateThread.finished.connect(self.updateDone)
self.updateThread.setDocketId(text)
self.updateThread.setDocketId(text)
self.updateThread.start()
return

View File

@@ -1,10 +1,9 @@
import datetime
from typing import Any, cast
import enum
from typing import Any
from PySide6.QtCore import (
QAbstractTableModel,
QDate,
QDateTime,
QModelIndex,
QPersistentModelIndex,
Qt,
@@ -18,6 +17,10 @@ from lib.utils import query_error
class docketModel(QAbstractTableModel):
entries: list[list[str | None]] = []
class ColumnNames(enum.IntEnum):
date = 0
text = 1
def __init__(self, case_id: int | None = None) -> None:
super(docketModel, self).__init__()
if case_id == None:

View File

@@ -1,13 +1,12 @@
from typing import cast
from PySide6.QtCore import (
QAbstractItemModel,
QDir,
QFile,
QModelIndex,
QObject,
QPersistentModelIndex,
QPoint,
QPointF,
QRect,
QSize,
Qt,
@@ -16,12 +15,8 @@ from PySide6.QtCore import (
Slot,
)
from PySide6.QtGui import (
QColor,
QFont,
QMouseEvent,
QPaintEvent,
QPainter,
QPalette,
QTextDocument,
)
from PySide6.QtNetwork import (
@@ -30,7 +25,7 @@ from PySide6.QtNetwork import (
QNetworkRequest,
)
from PySide6.QtWidgets import (
QSizePolicy,
QAbstractItemView,
QStyle,
QStyledItemDelegate,
QStyleOptionViewItem,
@@ -39,53 +34,16 @@ from PySide6.QtWidgets import (
QWidget,
)
from lib.utils import QStyleOptionViewItemInit
from pdfView import PDFViewer
class QStyleOptionViewItemInit(QStyleOptionViewItem):
backgroundBrush: QColor
rect: QRect
widget: QWidget
font: QFont
text: str
class documentDelegate(QStyledItemDelegate):
def initStyleOption(
self,
option: QStyleOptionViewItem,
index: QModelIndex | QPersistentModelIndex,
/,
class docketEntryDelegate(QStyledItemDelegate):
def __init__(
self, view: QAbstractItemView, parent: QWidget | None = None
) -> None:
options = cast(QStyleOptionViewItemInit, option)
super().initStyleOption(options, index)
assert index.isValid() and isinstance(index, QModelIndex)
if index.siblingAtColumn(6).data() == 0:
options.backgroundBrush = QColor(0x444444)
return
def paint(
self,
painter: QPainter,
option: QStyleOptionViewItem,
index: QModelIndex | QPersistentModelIndex,
) -> None:
options = cast(QStyleOptionViewItemInit, option)
self.initStyleOption(options, index)
painter.save()
doc = QTextDocument()
doc.setTextWidth(options.rect.width())
doc.setHtml(options.text)
options.text = ""
options.widget.style().drawControl(
QStyle.ControlElement.CE_ItemViewItem,
options,
painter,
)
painter.translate(options.rect.left(), options.rect.top())
clip = QRect(0, 0, options.rect.width(), options.rect.height())
doc.drawContents(painter, clip)
painter.restore()
super(docketEntryDelegate, self).__init__(parent)
self.view = view
return
def sizeHint(
@@ -96,66 +54,50 @@ class documentDelegate(QStyledItemDelegate):
options = cast(QStyleOptionViewItemInit, option)
self.initStyleOption(options, index)
doc = QTextDocument()
doc.setTextWidth(options.rect.width())
doc.setHtml(options.text)
doc.setTextWidth(options.rect.width())
print(f"sizeHint: {doc.idealWidth()}, {doc.size().height()}")
return QSize(int(doc.idealWidth()), int(doc.size().height()))
def paint(
self,
painter: QPainter,
option: QStyleOptionViewItem,
index: QModelIndex | QPersistentModelIndex,
) -> None:
options = cast(QStyleOptionViewItemInit, option)
self.initStyleOption(options, index)
painter.save()
class docketEntry(QTextEdit):
def __init__(self, parent: QWidget|None = None) -> None:
super(docketEntry, self).__init__(parent)
self.setSizePolicy(QSizePolicy.Policy.Preferred ,
QSizePolicy.Policy.Fixed)
return
def mousePressEvent(self, e: QMouseEvent) -> None:
super().mousePressEvent(e)
anchor = self.anchorAt(e.pos())
print(f"self.size(): {self.size()}")
if anchor:
print(f"Anchors away: {anchor}")
obj = cast(QObject, self)
while not isinstance(obj, docketTableView) and obj is not None:
obj = obj.parent()
assert obj is not None
index = obj.indexAt(obj.mapFromGlobal(self.mapToGlobal(e.pos())))
obj.anchorSignal.emit(index, anchor)
return
def sizeHint(self) -> QSize:
size = self.size()
doc = QTextDocument()
doc.setPlainText(self.document().toPlainText())
doc.setTextWidth(size.width())
docSize = doc.size()
return QSize(size.width(), int(docSize.height()))
doc.setHtml(options.text)
doc.setTextWidth(options.rect.width())
def paintEvent(self, e: QPaintEvent) -> None:
print(f"event.rect: {e.rect()}, size: {self.size()}")
print(self.document().toPlainText())
return super().paintEvent(e)
options.text = ""
options.widget.style().drawControl(
QStyle.ControlElement.CE_ItemViewItem, options, painter
)
painter.translate(options.rect.left(), options.rect.top())
clip = QRect(0, 0, options.rect.width(), options.rect.height())
doc.drawContents(painter, clip)
painter.restore()
return
class docketTableView(QTableView):
manager = QNetworkAccessManager()
manager: QNetworkAccessManager
clickedEvent = Signal(QPoint)
anchorSignal = Signal(QModelIndex, str)
pdf: PDFViewer
def __init__(self, parent: QWidget | None = None) -> None:
super(docketTableView, self).__init__(parent)
self.setItemDelegateForColumn(1, docketEntryDelegate(self))
self.anchorSignal.connect(self.doAnchor)
self.manager = QNetworkAccessManager()
self.manager.finished.connect(self.getDone)
return
def setModel(self, model: QAbstractItemModel | None) -> None:
assert model is not None
super().setModel(model)
self.model().modelReset.connect(self.modelReset)
print("Setting Model")
return
@Slot(QNetworkReply) # type: ignore
def getDone(self, reply: QNetworkReply) -> None:
dest = QFile("." + reply.url().path())
@@ -163,53 +105,60 @@ class docketTableView(QTableView):
dest.write(reply.readAll())
dest.close()
reply.deleteLater()
self.pdf = PDFViewer(self)
self.pdf.load_pdf(dest)
self.pdf.open(dest)
self.pdf.show()
return
@Slot(QModelIndex, str) # type: ignore
def doAnchor(self, index: QModelIndex, anchor: str) -> None:
def doAnchor(self, _: QModelIndex, anchor: str) -> None:
url = QUrl(anchor)
print(f"{index.row()}, {index.column()} -> {url.path()}")
dirs = url.path().split("/")
dirs.pop()
dirs.pop(0)
currentDir = QDir()
path = "/".join(dirs)
currentDir.mkpath(path)
if QDir("." + url.path()).exists():
file = QFile("." + url.path())
print(f"Checking for .{url.path()} existance.")
if not hasattr(self, "pdf"):
self.pdf = PDFViewer(self)
self.pdf.load_pdf(file)
if QFile("." + url.path()).exists():
self.pdf.open(QFile("." + url.path()))
self.pdf.show()
else:
print(f"Fetching {url}")
self.manager.get(QNetworkRequest(url))
return
def modelReset(self) -> None:
model = self.model()
red = QPalette()
red.setColor(QPalette.ColorRole.Base, Qt.GlobalColor.red)
for row in range(0, model.rowCount()):
index = model.index(row, 1)
widget = docketEntry()
widget.setHtml(model.data(index, Qt.ItemDataRole.DisplayRole))
widget.setAutoFillBackground(False)
widget.setReadOnly(True)
widget.setPalette(red)
self.setIndexWidget(index, widget)
def mousePressEvent(self, event: QMouseEvent) -> None:
#
# The mouse has been pressed somewere in our rect. We need to translate that to a click
# within the cell with the document. This will allow the document to find anchors.
#
index = self.indexAt(event.pos())
if not index.isValid():
return
def paintEvent(self, e: QPaintEvent) -> None:
for row in range(0, self.model().rowCount()):
index = self.model().index(row, 1)
widget = cast(docketEntry, self.indexWidget(index))
print(f"{row}: {widget.toPlainText()}")
print(f"{row}: {widget.sizeHint()}")
print(f"{row}: {widget.size()}")
print()
widget.resize(75,27)
super().paintEvent(e)
if index.column() != 1:
return
doc = QTextDocument()
doc.setHtml(index.data(Qt.ItemDataRole.DisplayRole))
doc.setTextWidth(self.columnWidth(index.column()))
#
# We need to map our click position to a position within the cell.
#
pos = event.position()
new_pos = QPointF(
pos.x() - self.horizontalScrollBar().value(),
pos.y() - self.verticalScrollBar().value(),
)
rowHeight = 0
for row in range(0, index.row()):
rowHeight += self.rowHeight(row)
cell_pos = QPointF(
new_pos.x() - self.columnWidth(0), new_pos.y() - rowHeight
)
te = QTextEdit()
te.setDocument(doc)
anchor = te.anchorAt(cell_pos.toPoint())
if anchor:
self.anchorSignal.emit(index, anchor)
return

View File

@@ -1,5 +1,4 @@
from datetime import date, datetime
from decimal import Decimal
from datetime import date
from pony.orm import ( # type: ignore[import-untyped]
Database,
@@ -10,8 +9,7 @@ from pony.orm import ( # type: ignore[import-untyped]
Set,
set_sql_debug,
)
db = Database
from PySide6.QtCore import QSettings
db = Database()
@@ -50,13 +48,18 @@ class History(db.Entity): # type: ignore[name-defined]
number = Required(int)
def updateDatabase(settings: QSettings) -> None:
set_sql_debug(True)
engine = settings.value("engine")
if engine not in ["QMYSQL", "MARIADB"]:
raise Exception(f"Unknown database engine: {engine}")
db.bind(
provider="mysql",
user="scotus",
host="ceph5",
database="scotus",
password="lechOtvirf8Om/",
user=settings.value("user"),
host=settings.value("hostname"),
database=settings.value("databasename"),
password=settings.value("password"),
)
db.generate_mapping(create_tables=True)
db.disconnect()
return

View File

@@ -1,7 +1,14 @@
from typing import NoReturn
from PySide6.QtCore import QCoreApplication
from PySide6.QtCore import (
QByteArray,
QCoreApplication,
QRect,
QSettings,
)
from PySide6.QtGui import QColor, QFont
from PySide6.QtSql import QSqlQuery
from PySide6.QtWidgets import QStyleOptionViewItem, QWidget
translate = QCoreApplication.translate
@@ -18,3 +25,35 @@ def query_error(query: QSqlQuery) -> NoReturn:
)
)
raise Exception(translate("MainWindow", "SQL Error"))
class QStyleOptionViewItemInit(QStyleOptionViewItem):
backgroundBrush: QColor
rect: QRect
widget: QWidget
font: QFont
text: str
def openSettings(group: str | None = None) -> QSettings:
settings = QSettings("Troglodite Services", "SCOTUS Watch")
if group is not None:
settings.beginGroup(group)
return settings
def readGeometry(widget: QWidget) -> None:
settings = openSettings(widget.objectName())
geometry = settings.value("geometry", QByteArray())
assert isinstance(geometry, QByteArray)
if not geometry.isEmpty():
widget.restoreGeometry(geometry)
settings.endGroup()
return
def writeGeometry(widget: QWidget) -> None:
settings = openSettings(widget.objectName())
settings.setValue("geometry", widget.saveGeometry())
settings.endGroup()
return

View File

@@ -1,31 +1,216 @@
from PySide6.QtCore import QFile
from PySide6.QtPdf import QPdfDocument
import math
from PySide6.QtCore import QFile, QModelIndex, QPoint, QSize, Signal, Slot
from PySide6.QtGui import QCloseEvent, QGuiApplication
from PySide6.QtPdf import QPdfBookmarkModel, QPdfDocument
from PySide6.QtPdfWidgets import QPdfView
from PySide6.QtWidgets import QDialog, QVBoxLayout, QWidget
from PySide6.QtWidgets import QComboBox, QDialog, QMenuBar, QSpinBox, QWidget
from lib.utils import readGeometry, writeGeometry
from ui.pdfViewer import Ui_pdfViewer
ZOOM_MULTIPLIER = math.sqrt(2.0)
class PDFViewer(QDialog):
pdf_view: QPdfView
pdf_document: QPdfDocument
class ZoomSelector(QComboBox):
zoom_mode_changed = Signal(QPdfView.ZoomMode)
zoom_factor_changed = Signal(float)
def __init__(self, parent: QWidget) -> None:
def __init__(self, parent: QWidget | None = None) -> None:
super(ZoomSelector, self).__init__(parent)
self.setEditable(True)
for text in [
"Fit Width",
"Fit Page",
"12%",
"25%",
"33%",
"50%",
"66%",
"75%",
"100%",
"125%",
"150%",
"200%",
"400%",
]:
self.addItem(text)
self.currentTextChanged.connect(self.on_current_text_changed)
lineEdit = self.lineEdit()
assert lineEdit is not None
lineEdit.editingFinished.connect(self._editing_finished)
return
@Slot(float) # type: ignore
def set_zoom_factor(self, zoomFactor: float) -> None:
percent = int(zoomFactor * 100)
self.setCurrentText(f"{percent}%")
return
@Slot()
def reset(self) -> None:
self.setCurrentIndex(8) # 100%
return
@Slot(str) # type: ignore
def on_current_text_changed(self, text: str) -> None:
if text == "Fit Width":
self.zoom_mode_changed.emit(QPdfView.ZoomMode.FitToWidth)
elif text == "Fit Page":
self.zoom_mode_changed.emit(QPdfView.ZoomMode.FitInView)
elif text.endswith("%"):
zoom_level = int(text[:-1])
factor = zoom_level / 100.0
self.zoom_mode_changed.emit(QPdfView.ZoomMode.Custom)
self.zoom_factor_changed.emit(factor)
return
@Slot()
def _editing_finished(self) -> None:
lineEdit = self.lineEdit()
assert lineEdit is not None
self.on_current_text_changed(lineEdit.text())
return
class PDFViewer(QDialog, Ui_pdfViewer):
menubar: QMenuBar
zoomSelector: ZoomSelector
pageSelector: QSpinBox
document: QPdfDocument
def __init__(self, parent: QWidget | None) -> None:
super(PDFViewer, self).__init__(parent)
self.setupUi(self)
self.setObjectName("PDFViewer")
readGeometry(self)
print(self.objectName())
self.zoomSelector = ZoomSelector(self)
self.pageSelector = QSpinBox(self)
self.document = QPdfDocument(self)
self.pdf_view = QPdfView()
self.pdf_document = QPdfDocument()
self.zoomSelector.setMaximumWidth(150)
self.mainToolBar.insertWidget(self.actionZoom_In, self.zoomSelector)
self.mainToolBar.insertWidget(self.actionNext_Page, self.pageSelector)
self.pageSelector.valueChanged.connect(self.page_selected)
nav = self.pdfView.pageNavigator()
nav.currentPageChanged.connect(self.pageSelector.setValue)
nav.backAvailableChanged.connect(self.actionBack.setEnabled)
nav.forwardAvailableChanged.connect(self.actionForward.setEnabled)
self.zoomSelector.zoom_mode_changed.connect(self.pdfView.setZoomMode)
self.zoomSelector.zoom_factor_changed.connect(
self.pdfView.setZoomFactor
)
self.zoomSelector.reset()
layout = QVBoxLayout(self)
layout.addWidget(self.pdf_view)
self.setLayout(layout)
bookmark_model = QPdfBookmarkModel(self)
bookmark_model.setDocument(self.document)
self.bookmarkView.setModel(bookmark_model)
self.bookmarkView.activated.connect(self.bookmark_selected)
self.tabWidget.setTabEnabled(1, False) # disable pages tabwidget
self.pdfView.setDocument(self.document)
self.pdfView.zoomFactorChanged.connect(
self.zoomSelector.set_zoom_factor
)
return
def load_pdf(self, file: QFile) -> None:
if not file.isOpen():
file.open(file.OpenModeFlag.ReadOnly)
self.pdf_document.load(file)
self.pdf_view.setDocument(self.pdf_document)
self.pdf_view.setPageMode(QPdfView.PageMode.MultiPage)
size = self.pdf_document.pagePointSize(1)
self.resize(size.toSize())
print(size)
def closeEvent(self, event: QCloseEvent) -> None:
print("closeEvent")
writeGeometry(self)
super().closeEvent(event)
return
@Slot(QFile) # type: ignore
def open(self, file: QFile) -> None:
self.document.load(file.fileName())
document_title = self.document.metaData(
QPdfDocument.MetaDataField.Title
)
self.setWindowTitle(document_title if document_title else "PDF Viewer")
self.page_selected(0)
self.pageSelector.setMaximum(self.document.pageCount() - 1)
if self.zoomSelector.currentIndex() == 8:
pageSize = self.document.pagePointSize(0)
dpi = QGuiApplication.primaryScreen().physicalDotsPerInch()
size = QSize(
int(pageSize.width() / 72 * dpi), self.pdfView.height()
)
self.pdfView.resize(size)
self.splitter.setSizes([100, size.width()])
return
@Slot(QModelIndex) # type: ignore
def bookmark_selected(self, index: QModelIndex) -> None:
if not index.isValid():
return
page = index.data(int(QPdfBookmarkModel.Role.Page))
zoom_level = index.data(int(QPdfBookmarkModel.Role.Level))
nav = self.pdfView.pageNavigator()
assert nav is not None
nav.jump(page, QPoint(), zoom_level)
return
@Slot(int) # type: ignore
def page_selected(self, page: int) -> None:
nav = self.pdfView.pageNavigator()
nav.jump(page, QPoint(), nav.currentZoom())
return
@Slot()
def on_actionOpen_triggered(self) -> None:
print("What are you doing in actionOpen_triggered?")
return
@Slot()
def on_actionQuit_triggered(self) -> None:
self.close()
return
@Slot()
def on_actionZoom_In_triggered(self) -> None:
factor = self.pdfView.zoomFactor() * ZOOM_MULTIPLIER
self.pdfView.setZoomFactor(factor)
return
@Slot()
def on_actionZoom_Out_triggered(self) -> None:
factor = self.pdfView.zoomFactor() / ZOOM_MULTIPLIER
self.pdfView.setZoomFactor(factor)
return
@Slot()
def on_actionPrevious_Page_triggered(self) -> None:
nav = self.pdfView.pageNavigator()
nav.jump(nav.currentPage() - 1, QPoint(), nav.currentZoom())
return
@Slot()
def on_actionNext_Page_triggered(self) -> None:
nav = self.pdfView.pageNavigator()
nav.jump(nav.currentPage() + 1, QPoint(), nav.currentZoom())
return
@Slot()
def on_actionContinuous_triggered(self) -> None:
cont_checked = self.actionContinuous.isChecked()
mode = (
QPdfView.PageMode.MultiPage
if cont_checked
else QPdfView.PageMode.SinglePage
)
self.pdfView.setPageMode(mode)
return
@Slot()
def on_actionBack_triggered(self) -> None:
self.pdfView.pageNavigator().back()
return
@Slot()
def on_actionForward_triggered(self) -> None:
self.pdfView.pageNavigator().forward()
return

View File

@@ -1,28 +1,51 @@
#!venv/bin/python3
import faulthandler
import sys
from typing import cast
from PySide6.QtCore import QFile
from PySide6.QtSql import (
QSqlDatabase,
)
from PySide6.QtWidgets import (
QApplication,
QDialog,
QMainWindow,
)
from lib.dbConfig import updateDatabase
from lib.utils import openSettings
from setupDialog import setupDialog
faulthandler.enable()
from MainWindow import MainWindow
def main() -> int:
#
app = QApplication(sys.argv)
settings = openSettings("database")
file = QFile(settings.fileName())
if not file.exists():
result = setupDialog().exec()
if result == QDialog.DialogCode.Rejected:
print(f"We require database credentials")
return 2
updateDatabase(settings)
db = QSqlDatabase.addDatabase("QMYSQL")
db.setHostName("ceph5")
db.setDatabaseName("scotus")
db.setUserName("scotus")
db.setPassword("lechOtvirf8Om/")
db.setHostName(settings.value("hostname"))
port = settings.value("port", "3306")
assert isinstance(port, str)
db.setPort(int(port))
db.setDatabaseName(settings.value("databasename", "scotus")) # type: ignore
db.setUserName(settings.value("user", "scotus")) # type: ignore
db.setPassword(settings.value("password"))
settings.endGroup()
db.open()
import lib.dbConfig
window = MainWindow()
assert isinstance(window, QMainWindow)
return app.exec()

87
scotusPdfView.py Normal file
View File

@@ -0,0 +1,87 @@
from PySide6.QtCore import QEvent, QObject, QPointF, QRect, QRectF, Qt
from PySide6.QtGui import QColor, QMouseEvent, QPaintEvent, QPainter, QPen
from PySide6.QtPdfWidgets import QPdfView
from PySide6.QtWidgets import QApplication, QWidget
def printParentHierarchy(obj: QObject, indent: int = 0):
if obj:
indentStr = ' ' * indent * 2
print(f"{indentStr}{obj.metaObject().className()}:{obj.objectName()}")
printParentHierarchy(obj.parent(), indent + 1)
return
class scotusPdfView(QPdfView):
drawing: bool
start_point: QPointF
end_point: QPointF
def formatRect(self, rect: QRectF|QRect) -> str:
return f"({rect.left()},{rect.top()}):({rect.right()},{rect.bottom()})"
def __init__(self, parent: QWidget) -> None:
self.drawing = False
super(scotusPdfView, self).__init__(parent)
print(self.viewport())
return
def paintEvent(self, event: QPaintEvent) -> None:
super(scotusPdfView,self).paintEvent(event)
if self.drawing:
page = self.pageNavigator().currentPage()
viewport = self.viewport()
painter = QPainter(viewport)
doc = self.document()
dpi = QApplication.primaryScreen().logicalDotsPerInch()
#
# XXX: Our mouse events are storing coordinates in the "self"
# coordinate system. We need them in the viewport system
#
rect = QRectF(viewport.mapFrom(self, self.start_point),
viewport.mapFrom(self, self.end_point))
redSolid = QPen(Qt.GlobalColor.red, 2, Qt.PenStyle.SolidLine)
blueDash = QPen(Qt.GlobalColor.blue, 2, Qt.PenStyle.DashLine)
greenDash = QPen(Qt.GlobalColor.green, 2, Qt.PenStyle.DashLine)
painter.setPen(redSolid)
painter.drawRect(rect) # Mouse drag box
selectRect = QRectF(rect.left()/dpi*72,
rect.top()/dpi*72,
rect.right()/dpi*72,
rect.bottom()/dpi*72)
painter.setPen(blueDash)
selection = doc.getSelectionAtIndex(page, 10, 50)
selection = doc.getSelection(page, selectRect.topLeft(), selectRect.bottomRight())
bb = selection.boundingRectangle()
painter.drawRect(bb) # Selection bounding box
print(selection.text())
print()
painter.setPen(greenDash)
bb.setLeft(bb.left()/72*dpi)
bb.setTop(bb.top()/72*dpi)
bb.setRight(bb.right()/72*dpi)
bb.setBottom(bb.bottom()/72*dpi)
painter.drawRect(bb) # Selection translated into viewport coordinates?
return
def mousePressEvent(self, event: QMouseEvent) -> None:
if event.button() == Qt.MouseButton.LeftButton:
self.start_point = event.pos()
self.drawing = True
return
def mouseMoveEvent(self, event: QMouseEvent) -> None:
if self.drawing:
pos = event.pos()
self.end_point = pos
viewport = self.viewport()
viewport.update()
return
def mouseReleaseEvent(self, event: QMouseEvent) -> None:
if event.button() == Qt.MouseButton.LeftButton:
self.drawing = False
self.viewport().update()
return

68
setupDialog.py Normal file
View File

@@ -0,0 +1,68 @@
from PySide6.QtCore import Qt, Slot
from PySide6.QtGui import QPalette
from PySide6.QtWidgets import QDialog, QStatusBar, QVBoxLayout, QWidget
from lib.utils import openSettings
from ui.dbSetup import Ui_setupDialog
class setupDialog(QDialog, Ui_setupDialog):
def __init__(self, parent: QWidget | None = None) -> None:
super(setupDialog, self).__init__(parent)
self.setupUi(self)
self.sqliteEdit.setEnabled(False)
layout = self.layout()
assert isinstance(layout, QVBoxLayout)
self.statusBar = QStatusBar(self)
self.statusBar.setObjectName("statusBar")
where = layout.indexOf(self.buttonBox)
layout.insertWidget(where, self.statusBar)
self.pwEdit.editingFinished.connect(self.pwDone)
self.pwConfirmEdit.editingFinished.connect(self.pwDone)
return
@Slot()
def pwDone(self) -> None:
pw = self.pwEdit.text()
confirm = self.pwConfirmEdit.text()
if pw != confirm:
palette = self.statusBar.palette()
palette.setColor(QPalette.ColorRole.WindowText, Qt.GlobalColor.red)
self.statusBar.setPalette(palette)
self.statusBar.showMessage("Password doesn't match")
self.pwGood = False
else:
self.statusBar.clearMessage()
self.pwGood = True
return
@Slot(int) # type: ignore
def done(self, r: int) -> None:
if r == QDialog.DialogCode.Rejected:
super(setupDialog, self).done(r)
return
self.pwDone()
if not self.pwGood:
return
super(setupDialog, self).done(r)
return
@Slot()
def accept(self) -> None:
settings = openSettings("database")
settings.setValue("hostname", self.hostEdit.text())
settings.setValue("databasename", self.dbEdit.text())
settings.setValue("user", self.userEdit.text())
settings.setValue("password", self.pwEdit.text())
settings.setValue("port", self.portEdit.text())
if self.comboBox.currentIndex() == 0:
settings.setValue("engine", "QMYSQL")
elif self.comboBox.currentIndex() == 1:
settings.setValue("engine", "QSQLITE")
# Other types: QDB2, QIBASE, QOCI, QODBC, QPSQL, QMIMER
else:
print("Bad Database Type")
settings.sync()
return super().accept()

14
ui/Makefile Normal file
View File

@@ -0,0 +1,14 @@
depfiles := $(shell awk '/file/{gsub(/<\/?file>/,"");print}' resources.qrc)
SRC := $(wildcard *.ui)
PY := $(patsubst %.ui, %.py, ${SRC})
all: resources.rcc ${PY}
%.py: %.ui
pyside6-uic $< >$@
resources.rcc: resources.qrc
pyside6-rcc --binary resources.qrc -o resources.rcc
resources.qrc: ${depfiles}
touch resources.qrc
%.qm:%.ts
pyside6-lrelease $<
%ts: *.ui ../main.py ../lib/*.py
pyside6-lupdate *.ui ../main.py ../lib/*.py -ts $@

176
ui/dbSetup.py Normal file
View File

@@ -0,0 +1,176 @@
# -*- coding: utf-8 -*-
################################################################################
## Form generated from reading UI file 'dbSetup.ui'
##
## Created by: Qt User Interface Compiler version 6.8.2
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
QMetaObject, QObject, QPoint, QRect,
QSize, QTime, QUrl, Qt)
from PySide6.QtGui import (QAction, QBrush, QColor, QConicalGradient,
QCursor, QFont, QFontDatabase, QGradient,
QIcon, QImage, QKeySequence, QLinearGradient,
QPainter, QPalette, QPixmap, QRadialGradient,
QTransform)
from PySide6.QtWidgets import (QAbstractButton, QApplication, QComboBox, QDialog,
QDialogButtonBox, QFormLayout, QLabel, QLineEdit,
QSizePolicy, QStackedWidget, QVBoxLayout, QWidget)
class Ui_setupDialog(object):
def setupUi(self, setupDialog):
if not setupDialog.objectName():
setupDialog.setObjectName(u"setupDialog")
setupDialog.resize(400, 300)
self.actionBrowse = QAction(setupDialog)
self.actionBrowse.setObjectName(u"actionBrowse")
icon = QIcon(QIcon.fromTheme(u"folder-open"))
self.actionBrowse.setIcon(icon)
self.actionBrowse.setMenuRole(QAction.MenuRole.NoRole)
self.verticalLayout = QVBoxLayout(setupDialog)
self.verticalLayout.setObjectName(u"verticalLayout")
self.comboBox = QComboBox(setupDialog)
self.comboBox.addItem("")
self.comboBox.addItem("")
self.comboBox.setObjectName(u"comboBox")
self.verticalLayout.addWidget(self.comboBox)
self.stackedWidget = QStackedWidget(setupDialog)
self.stackedWidget.setObjectName(u"stackedWidget")
self.mariadb = QWidget()
self.mariadb.setObjectName(u"mariadb")
self.formLayout = QFormLayout(self.mariadb)
self.formLayout.setObjectName(u"formLayout")
self.label = QLabel(self.mariadb)
self.label.setObjectName(u"label")
self.formLayout.setWidget(0, QFormLayout.LabelRole, self.label)
self.hostEdit = QLineEdit(self.mariadb)
self.hostEdit.setObjectName(u"hostEdit")
self.formLayout.setWidget(0, QFormLayout.FieldRole, self.hostEdit)
self.label_2 = QLabel(self.mariadb)
self.label_2.setObjectName(u"label_2")
self.formLayout.setWidget(1, QFormLayout.LabelRole, self.label_2)
self.portEdit = QLineEdit(self.mariadb)
self.portEdit.setObjectName(u"portEdit")
self.formLayout.setWidget(1, QFormLayout.FieldRole, self.portEdit)
self.label_3 = QLabel(self.mariadb)
self.label_3.setObjectName(u"label_3")
self.formLayout.setWidget(2, QFormLayout.LabelRole, self.label_3)
self.dbEdit = QLineEdit(self.mariadb)
self.dbEdit.setObjectName(u"dbEdit")
self.formLayout.setWidget(2, QFormLayout.FieldRole, self.dbEdit)
self.label_4 = QLabel(self.mariadb)
self.label_4.setObjectName(u"label_4")
self.formLayout.setWidget(3, QFormLayout.LabelRole, self.label_4)
self.userEdit = QLineEdit(self.mariadb)
self.userEdit.setObjectName(u"userEdit")
self.formLayout.setWidget(3, QFormLayout.FieldRole, self.userEdit)
self.label_5 = QLabel(self.mariadb)
self.label_5.setObjectName(u"label_5")
self.formLayout.setWidget(4, QFormLayout.LabelRole, self.label_5)
self.pwEdit = QLineEdit(self.mariadb)
self.pwEdit.setObjectName(u"pwEdit")
self.pwEdit.setEchoMode(QLineEdit.EchoMode.Password)
self.formLayout.setWidget(4, QFormLayout.FieldRole, self.pwEdit)
self.label_7 = QLabel(self.mariadb)
self.label_7.setObjectName(u"label_7")
self.formLayout.setWidget(5, QFormLayout.LabelRole, self.label_7)
self.pwConfirmEdit = QLineEdit(self.mariadb)
self.pwConfirmEdit.setObjectName(u"pwConfirmEdit")
self.pwConfirmEdit.setEchoMode(QLineEdit.EchoMode.Password)
self.formLayout.setWidget(5, QFormLayout.FieldRole, self.pwConfirmEdit)
self.stackedWidget.addWidget(self.mariadb)
self.sqlite = QWidget()
self.sqlite.setObjectName(u"sqlite")
self.formLayout_2 = QFormLayout(self.sqlite)
self.formLayout_2.setObjectName(u"formLayout_2")
self.label_6 = QLabel(self.sqlite)
self.label_6.setObjectName(u"label_6")
self.formLayout_2.setWidget(0, QFormLayout.LabelRole, self.label_6)
self.sqliteEdit = QLineEdit(self.sqlite)
self.sqliteEdit.setObjectName(u"sqliteEdit")
self.formLayout_2.setWidget(0, QFormLayout.FieldRole, self.sqliteEdit)
self.stackedWidget.addWidget(self.sqlite)
self.verticalLayout.addWidget(self.stackedWidget)
self.buttonBox = QDialogButtonBox(setupDialog)
self.buttonBox.setObjectName(u"buttonBox")
self.buttonBox.setOrientation(Qt.Orientation.Horizontal)
self.buttonBox.setStandardButtons(QDialogButtonBox.StandardButton.Cancel|QDialogButtonBox.StandardButton.Ok)
self.verticalLayout.addWidget(self.buttonBox)
#if QT_CONFIG(shortcut)
self.label.setBuddy(self.hostEdit)
self.label_2.setBuddy(self.portEdit)
self.label_3.setBuddy(self.dbEdit)
self.label_4.setBuddy(self.userEdit)
self.label_5.setBuddy(self.pwEdit)
self.label_7.setBuddy(self.pwConfirmEdit)
#endif // QT_CONFIG(shortcut)
self.retranslateUi(setupDialog)
self.buttonBox.accepted.connect(setupDialog.accept)
self.buttonBox.rejected.connect(setupDialog.reject)
self.comboBox.currentIndexChanged.connect(self.stackedWidget.setCurrentIndex)
self.stackedWidget.setCurrentIndex(0)
QMetaObject.connectSlotsByName(setupDialog)
# setupUi
def retranslateUi(self, setupDialog):
setupDialog.setWindowTitle(QCoreApplication.translate("setupDialog", u"setupDialog", None))
self.actionBrowse.setText("")
#if QT_CONFIG(tooltip)
self.actionBrowse.setToolTip(QCoreApplication.translate("setupDialog", u"Browse for database file", None))
#endif // QT_CONFIG(tooltip)
self.comboBox.setItemText(0, QCoreApplication.translate("setupDialog", u"MariaDB/MySQL", None))
self.comboBox.setItemText(1, QCoreApplication.translate("setupDialog", u"SQLite", None))
self.label.setText(QCoreApplication.translate("setupDialog", u"Host", None))
self.label_2.setText(QCoreApplication.translate("setupDialog", u"Port", None))
self.portEdit.setText(QCoreApplication.translate("setupDialog", u"3306", None))
self.label_3.setText(QCoreApplication.translate("setupDialog", u"Database Name", None))
self.dbEdit.setText(QCoreApplication.translate("setupDialog", u"scotus", None))
self.label_4.setText(QCoreApplication.translate("setupDialog", u"User Name", None))
self.userEdit.setText(QCoreApplication.translate("setupDialog", u"scotus", None))
self.label_5.setText(QCoreApplication.translate("setupDialog", u"Password", None))
self.label_7.setText(QCoreApplication.translate("setupDialog", u"Confirm", None))
self.label_6.setText(QCoreApplication.translate("setupDialog", u"Datbase File", None))
# retranslateUi

231
ui/dbSetup.ui Normal file
View File

@@ -0,0 +1,231 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>setupDialog</class>
<widget class="QDialog" name="setupDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>setupDialog</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QComboBox" name="comboBox">
<item>
<property name="text">
<string>MariaDB/MySQL</string>
</property>
</item>
<item>
<property name="text">
<string>SQLite</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QStackedWidget" name="stackedWidget">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="mariadb">
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Host</string>
</property>
<property name="buddy">
<cstring>hostEdit</cstring>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="hostEdit"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Port</string>
</property>
<property name="buddy">
<cstring>portEdit</cstring>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="portEdit">
<property name="text">
<string>3306</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Database Name</string>
</property>
<property name="buddy">
<cstring>dbEdit</cstring>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="dbEdit">
<property name="text">
<string>scotus</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>User Name</string>
</property>
<property name="buddy">
<cstring>userEdit</cstring>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="userEdit">
<property name="text">
<string>scotus</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Password</string>
</property>
<property name="buddy">
<cstring>pwEdit</cstring>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLineEdit" name="pwEdit">
<property name="echoMode">
<enum>QLineEdit::EchoMode::Password</enum>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Confirm</string>
</property>
<property name="buddy">
<cstring>pwConfirmEdit</cstring>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QLineEdit" name="pwConfirmEdit">
<property name="echoMode">
<enum>QLineEdit::EchoMode::Password</enum>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="sqlite">
<layout class="QFormLayout" name="formLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Datbase File</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="sqliteEdit"/>
</item>
</layout>
</widget>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok</set>
</property>
</widget>
</item>
</layout>
<action name="actionBrowse">
<property name="icon">
<iconset theme="folder-open"/>
</property>
<property name="text">
<string/>
</property>
<property name="toolTip">
<string>Browse for database file</string>
</property>
<property name="menuRole">
<enum>QAction::MenuRole::NoRole</enum>
</property>
</action>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>setupDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>227</x>
<y>278</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>setupDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>295</x>
<y>284</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>comboBox</sender>
<signal>currentIndexChanged(int)</signal>
<receiver>stackedWidget</receiver>
<slot>setCurrentIndex(int)</slot>
<hints>
<hint type="sourcelabel">
<x>199</x>
<y>22</y>
</hint>
<hint type="destinationlabel">
<x>199</x>
<y>149</y>
</hint>
</hints>
</connection>
</connections>
</ui>

Binary file not shown.

Binary file not shown.

BIN
ui/images/go-next-view.svgz Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
ui/images/zoom-in.svgz Normal file

Binary file not shown.

Binary file not shown.

BIN
ui/images/zoom-out.svgz Normal file

Binary file not shown.

Binary file not shown.

172
ui/pdfViewer.py Normal file
View File

@@ -0,0 +1,172 @@
# -*- coding: utf-8 -*-
################################################################################
## Form generated from reading UI file 'pdfViewer.ui'
##
## Created by: Qt User Interface Compiler version 6.8.2
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
QMetaObject, QObject, QPoint, QRect,
QSize, QTime, QUrl, Qt)
from PySide6.QtGui import (QAction, QBrush, QColor, QConicalGradient,
QCursor, QFont, QFontDatabase, QGradient,
QIcon, QImage, QKeySequence, QLinearGradient,
QPainter, QPalette, QPixmap, QRadialGradient,
QTransform)
from PySide6.QtWidgets import (QApplication, QDialog, QHeaderView, QSizePolicy,
QSplitter, QTabWidget, QToolBar, QTreeView,
QVBoxLayout, QWidget)
from scotusPdfView import scotusPdfView
import resources_rc
class Ui_pdfViewer(object):
def setupUi(self, pdfViewer):
if not pdfViewer.objectName():
pdfViewer.setObjectName(u"pdfViewer")
pdfViewer.resize(700, 513)
self.actionZoom_In = QAction(pdfViewer)
self.actionZoom_In.setObjectName(u"actionZoom_In")
icon = QIcon(QIcon.fromTheme(QIcon.ThemeIcon.ZoomIn))
self.actionZoom_In.setIcon(icon)
self.actionZoom_In.setMenuRole(QAction.MenuRole.NoRole)
self.actionZoom_Out = QAction(pdfViewer)
self.actionZoom_Out.setObjectName(u"actionZoom_Out")
icon1 = QIcon(QIcon.fromTheme(QIcon.ThemeIcon.ZoomOut))
self.actionZoom_Out.setIcon(icon1)
self.actionZoom_Out.setMenuRole(QAction.MenuRole.NoRole)
self.actionPrevious_Page = QAction(pdfViewer)
self.actionPrevious_Page.setObjectName(u"actionPrevious_Page")
icon2 = QIcon()
icon2.addFile(u":/icons/images/go-previous-view-page.svgz", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionPrevious_Page.setIcon(icon2)
self.actionPrevious_Page.setMenuRole(QAction.MenuRole.NoRole)
self.actionNext_Page = QAction(pdfViewer)
self.actionNext_Page.setObjectName(u"actionNext_Page")
icon3 = QIcon()
icon3.addFile(u":/icons/images/go-next-view-page.svgz", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionNext_Page.setIcon(icon3)
self.actionNext_Page.setMenuRole(QAction.MenuRole.NoRole)
self.actionContinuous = QAction(pdfViewer)
self.actionContinuous.setObjectName(u"actionContinuous")
self.actionContinuous.setCheckable(True)
self.actionContinuous.setMenuRole(QAction.MenuRole.NoRole)
self.actionBack = QAction(pdfViewer)
self.actionBack.setObjectName(u"actionBack")
icon4 = QIcon()
icon4.addFile(u":/icons/images/go-previous-view.svgz", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionBack.setIcon(icon4)
self.actionBack.setMenuRole(QAction.MenuRole.NoRole)
self.actionForward = QAction(pdfViewer)
self.actionForward.setObjectName(u"actionForward")
icon5 = QIcon()
icon5.addFile(u":/icons/images/go-next-view.svgz", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionForward.setIcon(icon5)
self.actionForward.setMenuRole(QAction.MenuRole.NoRole)
self.verticalLayout = QVBoxLayout(pdfViewer)
self.verticalLayout.setObjectName(u"verticalLayout")
self.mainToolBar = QToolBar(pdfViewer)
self.mainToolBar.setObjectName(u"mainToolBar")
self.verticalLayout.addWidget(self.mainToolBar)
self.widget = QWidget(pdfViewer)
self.widget.setObjectName(u"widget")
self.verticalLayout_2 = QVBoxLayout(self.widget)
self.verticalLayout_2.setSpacing(0)
self.verticalLayout_2.setObjectName(u"verticalLayout_2")
self.verticalLayout_2.setContentsMargins(0, 0, 0, 0)
self.splitter = QSplitter(self.widget)
self.splitter.setObjectName(u"splitter")
self.splitter.setOrientation(Qt.Orientation.Horizontal)
self.tabWidget = QTabWidget(self.splitter)
self.tabWidget.setObjectName(u"tabWidget")
sizePolicy = QSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.tabWidget.sizePolicy().hasHeightForWidth())
self.tabWidget.setSizePolicy(sizePolicy)
self.tabWidget.setTabPosition(QTabWidget.TabPosition.West)
self.bookmarkTab = QWidget()
self.bookmarkTab.setObjectName(u"bookmarkTab")
self.verticalLayout_3 = QVBoxLayout(self.bookmarkTab)
self.verticalLayout_3.setSpacing(0)
self.verticalLayout_3.setObjectName(u"verticalLayout_3")
self.verticalLayout_3.setContentsMargins(2, 2, 2, 2)
self.bookmarkView = QTreeView(self.bookmarkTab)
self.bookmarkView.setObjectName(u"bookmarkView")
sizePolicy.setHeightForWidth(self.bookmarkView.sizePolicy().hasHeightForWidth())
self.bookmarkView.setSizePolicy(sizePolicy)
self.verticalLayout_3.addWidget(self.bookmarkView)
self.tabWidget.addTab(self.bookmarkTab, "")
self.pagesTab = QWidget()
self.pagesTab.setObjectName(u"pagesTab")
self.tabWidget.addTab(self.pagesTab, "")
self.splitter.addWidget(self.tabWidget)
self.pdfView = scotusPdfView(self.splitter)
self.pdfView.setObjectName(u"pdfView")
sizePolicy1 = QSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
sizePolicy1.setHorizontalStretch(10)
sizePolicy1.setVerticalStretch(0)
sizePolicy1.setHeightForWidth(self.pdfView.sizePolicy().hasHeightForWidth())
self.pdfView.setSizePolicy(sizePolicy1)
self.splitter.addWidget(self.pdfView)
self.verticalLayout_2.addWidget(self.splitter)
self.verticalLayout.addWidget(self.widget)
self.mainToolBar.addAction(self.actionZoom_Out)
self.mainToolBar.addAction(self.actionZoom_In)
self.mainToolBar.addSeparator()
self.mainToolBar.addAction(self.actionBack)
self.mainToolBar.addAction(self.actionForward)
self.mainToolBar.addAction(self.actionPrevious_Page)
self.mainToolBar.addAction(self.actionNext_Page)
self.retranslateUi(pdfViewer)
self.tabWidget.setCurrentIndex(0)
QMetaObject.connectSlotsByName(pdfViewer)
# setupUi
def retranslateUi(self, pdfViewer):
pdfViewer.setWindowTitle(QCoreApplication.translate("pdfViewer", u"PDF Viewer", None))
self.actionZoom_In.setText(QCoreApplication.translate("pdfViewer", u"Zoom In", None))
#if QT_CONFIG(tooltip)
self.actionZoom_In.setToolTip(QCoreApplication.translate("pdfViewer", u"Zoom In", None))
#endif // QT_CONFIG(tooltip)
#if QT_CONFIG(shortcut)
self.actionZoom_In.setShortcut(QCoreApplication.translate("pdfViewer", u"Ctrl+=", None))
#endif // QT_CONFIG(shortcut)
self.actionZoom_Out.setText(QCoreApplication.translate("pdfViewer", u"Zoom Out", None))
#if QT_CONFIG(shortcut)
self.actionZoom_Out.setShortcut(QCoreApplication.translate("pdfViewer", u"Ctrl+-", None))
#endif // QT_CONFIG(shortcut)
self.actionPrevious_Page.setText(QCoreApplication.translate("pdfViewer", u"Previous Page", None))
#if QT_CONFIG(shortcut)
self.actionPrevious_Page.setShortcut(QCoreApplication.translate("pdfViewer", u"PgUp", None))
#endif // QT_CONFIG(shortcut)
self.actionNext_Page.setText(QCoreApplication.translate("pdfViewer", u"Next Page", None))
#if QT_CONFIG(shortcut)
self.actionNext_Page.setShortcut(QCoreApplication.translate("pdfViewer", u"PgDown", None))
#endif // QT_CONFIG(shortcut)
self.actionContinuous.setText(QCoreApplication.translate("pdfViewer", u"Continuous", None))
#if QT_CONFIG(tooltip)
self.actionContinuous.setToolTip(QCoreApplication.translate("pdfViewer", u"Continuous", None))
#endif // QT_CONFIG(tooltip)
self.actionBack.setText(QCoreApplication.translate("pdfViewer", u"Back", None))
self.actionForward.setText(QCoreApplication.translate("pdfViewer", u"Forward", None))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.bookmarkTab), QCoreApplication.translate("pdfViewer", u"Bookmarks", None))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.pagesTab), QCoreApplication.translate("pdfViewer", u"Pages", None))
# retranslateUi

227
ui/pdfViewer.ui Normal file
View File

@@ -0,0 +1,227 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>pdfViewer</class>
<widget class="QDialog" name="pdfViewer">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>700</width>
<height>513</height>
</rect>
</property>
<property name="windowTitle">
<string>PDF Viewer</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QToolBar" name="mainToolBar">
<addaction name="actionZoom_Out"/>
<addaction name="actionZoom_In"/>
<addaction name="separator"/>
<addaction name="actionBack"/>
<addaction name="actionForward"/>
<addaction name="actionPrevious_Page"/>
<addaction name="actionNext_Page"/>
</widget>
</item>
<item>
<widget class="QWidget" name="widget" native="true">
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QSplitter" name="splitter">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<widget class="QTabWidget" name="tabWidget">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="tabPosition">
<enum>QTabWidget::TabPosition::West</enum>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="bookmarkTab">
<attribute name="title">
<string>Bookmarks</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_3">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>2</number>
</property>
<property name="topMargin">
<number>2</number>
</property>
<property name="rightMargin">
<number>2</number>
</property>
<property name="bottomMargin">
<number>2</number>
</property>
<item>
<widget class="QTreeView" name="bookmarkView">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="pagesTab">
<attribute name="title">
<string>Pages</string>
</attribute>
</widget>
</widget>
<widget class="scotusPdfView" name="pdfView" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>10</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
<action name="actionZoom_In">
<property name="icon">
<iconset theme="QIcon::ThemeIcon::ZoomIn"/>
</property>
<property name="text">
<string>Zoom In</string>
</property>
<property name="toolTip">
<string>Zoom In</string>
</property>
<property name="shortcut">
<string>Ctrl+=</string>
</property>
<property name="menuRole">
<enum>QAction::MenuRole::NoRole</enum>
</property>
</action>
<action name="actionZoom_Out">
<property name="icon">
<iconset theme="QIcon::ThemeIcon::ZoomOut"/>
</property>
<property name="text">
<string>Zoom Out</string>
</property>
<property name="shortcut">
<string>Ctrl+-</string>
</property>
<property name="menuRole">
<enum>QAction::MenuRole::NoRole</enum>
</property>
</action>
<action name="actionPrevious_Page">
<property name="icon">
<iconset resource="resources.qrc">
<normaloff>:/icons/images/go-previous-view-page.svgz</normaloff>:/icons/images/go-previous-view-page.svgz</iconset>
</property>
<property name="text">
<string>Previous Page</string>
</property>
<property name="shortcut">
<string>PgUp</string>
</property>
<property name="menuRole">
<enum>QAction::MenuRole::NoRole</enum>
</property>
</action>
<action name="actionNext_Page">
<property name="icon">
<iconset resource="resources.qrc">
<normaloff>:/icons/images/go-next-view-page.svgz</normaloff>:/icons/images/go-next-view-page.svgz</iconset>
</property>
<property name="text">
<string>Next Page</string>
</property>
<property name="shortcut">
<string>PgDown</string>
</property>
<property name="menuRole">
<enum>QAction::MenuRole::NoRole</enum>
</property>
</action>
<action name="actionContinuous">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Continuous</string>
</property>
<property name="toolTip">
<string>Continuous</string>
</property>
<property name="menuRole">
<enum>QAction::MenuRole::NoRole</enum>
</property>
</action>
<action name="actionBack">
<property name="icon">
<iconset resource="resources.qrc">
<normaloff>:/icons/images/go-previous-view.svgz</normaloff>:/icons/images/go-previous-view.svgz</iconset>
</property>
<property name="text">
<string>Back</string>
</property>
<property name="menuRole">
<enum>QAction::MenuRole::NoRole</enum>
</property>
</action>
<action name="actionForward">
<property name="icon">
<iconset resource="resources.qrc">
<normaloff>:/icons/images/go-next-view.svgz</normaloff>:/icons/images/go-next-view.svgz</iconset>
</property>
<property name="text">
<string>Forward</string>
</property>
<property name="menuRole">
<enum>QAction::MenuRole::NoRole</enum>
</property>
</action>
</widget>
<customwidgets>
<customwidget>
<class>scotusPdfView</class>
<extends>QWidget</extends>
<header>scotusPdfView.h</header>
</customwidget>
</customwidgets>
<resources>
<include location="resources.qrc"/>
</resources>
<connections/>
</ui>

11
ui/resources.qrc Normal file
View File

@@ -0,0 +1,11 @@
<RCC>
<qresource prefix="/icons">
<file>images/document-open.svgz</file>
<file>images/go-next-view.svgz</file>
<file>images/go-previous-view.svgz</file>
<file>images/go-next-view-page.svgz</file>
<file>images/go-previous-view-page.svgz</file>
<file>images/zoom-in.svgz</file>
<file>images/zoom-out.svgz</file>
</qresource>
</RCC>

3468
ui/resources_rc.py Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -4,7 +4,7 @@ import re
import dateparser
import requests
from bs4 import BeautifulSoup, Tag
from PySide6.QtCore import QDateTime, QThread
from PySide6.QtCore import QDateTime, QThread, Signal
from PySide6.QtSql import QSqlDatabase, QSqlQuery
from lib.utils import query_error
@@ -80,7 +80,6 @@ def update_proceedings(
if not query.exec():
query_error(query)
assert isinstance(text, str)
print(f"text: {text.lower()}")
#
# If cert is denied, a petion for rehearing can be requested.
# The petitioner has 40 days to file for a rehearing.
@@ -107,7 +106,6 @@ def update_db(case_id: str, db: QSqlDatabase) -> int:
#
# We assume that case_id == docket_id at this point. If it does not,
# then we will build out from the request we get
print(f"Updating {case_id}")
matches = re.match(r"(\d\d)[-A](\d+)(.*)$", case_id)
if matches is None:
raise Exception(f"Not a match {case_id}")
@@ -130,12 +128,16 @@ def update_db(case_id: str, db: QSqlDatabase) -> int:
r = requests.get(
f"https://www.supremecourt.gov/docket/docketfiles/html/public/{case_id}.html"
)
if r.status_code == 404:
return -1
if r.status_code != 200:
print(r.status_code)
exit(1)
bs = BeautifulSoup(r.text, "lxml")
#
# SCOTUS does not return 404 for page not found.
# Feb 27: SCOTUS is returning 404s but I don't trust them.
#
title = bs.find("title")
assert isinstance(title, Tag) and isinstance(title.string, str)
@@ -156,9 +158,7 @@ def update_db(case_id: str, db: QSqlDatabase) -> int:
assert isinstance(tmp, str)
matches = re.match(r"(No.)?\s*(\d+[-A]\d+).*$", tmp)
assert matches is not None
print(matches, matches.groups())
docket_id = matches.group(2)
print(f"Found {docket_id}")
#
# Title is second row, first column
@@ -223,9 +223,17 @@ def update_db(case_id: str, db: QSqlDatabase) -> int:
#
# If there is a linked case, we need to get the ID for that case.
if linked is not None:
#
# If this case is on the Emergency Docket and it is linked to
# a case on the regular docket, then this case is no longer active
#
deactivate = False
linked = linked.replace("Linked with ", "")
for did in linked.split(","):
did = did.strip()
if re.match(r"\d+-\d+$", did):
deactivate = True
query.prepare("SELECT * FROM cases WHERE docket_id = :did")
query.bindValue(":did", linked)
if not query.exec():
@@ -255,6 +263,14 @@ def update_db(case_id: str, db: QSqlDatabase) -> int:
query.bindValue(":rhs", linked_id)
if not query.exec():
query_error(query)
if re.match(r"\d+-\d+$", docket_id):
deactivate = False
if deactivate:
query.prepare("UPDATE cases SET active=0 WHERE case_id = :cid")
query.bindValue(":cid", case_id)
if not query.exec():
query_error(query)
#
# XXX - Process lower courts
#
@@ -272,7 +288,6 @@ class updateThread(QThread):
def __init__(self) -> None:
super(updateThread, self).__init__()
print("updateThread: __init__(docket_id)")
return
def setDocketId(self, docket_id: str) -> None:
@@ -280,30 +295,32 @@ class updateThread(QThread):
return
def run(self) -> None:
print(f"updateThread: running on {self.currentThread()}")
db = QSqlDatabase.cloneDatabase("qt_sql_default_connection", "update")
if not db.open():
print(db.lastError())
raise Exception("db.open()")
case_id = update_db(str(self.docket_id), db)
update_db(str(self.docket_id), db)
db.close()
del db
QSqlDatabase.removeDatabase("update")
print(f"updateThread: run() returns {case_id}")
return
class loadCases(QThread):
caseLoaded = Signal(int)
year = QDateTime.currentDateTime().toString("yy")
number = 0
edocket = 0
def run(self) -> None:
db = QSqlDatabase.cloneDatabase("qt_sql_default_connection", "load")
if not db.open():
raise Exception("db.open()")
year = QDateTime.currentDateTime().toString("yy")
query = QSqlQuery(db)
query.prepare("SELECT * FROM history WHERE year = :year")
print(f"year = {year}")
query.bindValue(":year", year)
query.bindValue(":year", self.year)
if not query.exec():
query_error(query)
@@ -312,48 +329,62 @@ class loadCases(QThread):
"INSERT INTO history (year, edocket, number) "
"VALUES (:year, 0, 1)"
)
query.bindValue(":year", year)
query.bindValue(":year", self.year)
if not query.exec():
query_error(query)
edocket = 0
number = 1
self.number = 1
history_id = query.lastInsertId()
else:
history_id = query.value("history_id")
edocket = query.value("edocket")
number = query.value("number")
self.number = query.value("number")
count = 0
while year > "00" and count < 100:
while self.year > "00" and count < 100:
self.caseLoaded.emit(count)
query.prepare("SELECT * FROM cases WHERE docket_id = :did")
if edocket == 1:
docket_id = f"{year}A{number}"
docket_id = f"{self.year}A{self.number}"
else:
docket_id = f"{year}-{number}"
docket_id = f"{self.year}-{self.number}"
print(f"Updating: {docket_id} ", end='')
query.bindValue(":did", docket_id)
if not query.exec():
query_error(query)
if query.next():
if query.value("active") == 0:
print("Already exists and is inactive")
number += 1
self.number += 1
print("INACTIVE")
continue
print()
result = update_db(docket_id, db)
print(f"result: {result}")
if result < 0:
year = f"{int(year) - 1:02d}"
if number > 1:
if edocket == 0:
edocket = 1
self.number = 1
query.prepare("UPDATE history SET number = :number, "
"edocket=:edocket "
"WHERE history_id=:hid")
query.bindValue(':number', 1)
query.bindValue(':edocket', 1)
query.bindValue(':hid', history_id)
if not query.exec():
query_error(query)
continue
edocket = 0
self.year = f"{int(self.year) - 1:02d}"
if self.number > 1:
query.prepare(
"UPDATE history set number = :number WHERE history_id=:hid"
)
query.bindValue(":number", number - 1)
query.bindValue(":number", self.number - 1)
query.bindValue(":hid", history_id)
if not query.exec():
query_error(query)
query.prepare("SELECT * FROM history WHERE year = :year")
print(f"year = {year}")
query.bindValue(":year", year)
query.bindValue(":year", self.year)
if not query.exec():
query_error(query)
@@ -362,29 +393,31 @@ class loadCases(QThread):
"INSERT INTO history (year, edocket, number) "
"VALUES (:year, 0, 1)"
)
query.bindValue(":year", year)
query.bindValue(":year", self.year)
if not query.exec():
query_error(query)
edocket = 0
number = 1
self.number = 1
history_id = query.lastInsertId()
else:
history_id = query.value("history_id")
edocket = query.value("edocket")
number = query.value("number")
self.number = query.value("number")
continue
number += 1
self.number += 1
count += 1
if number > 1:
self.number -= 1
if self.number > 1:
query.prepare(
"UPDATE history SET number= :number WHERE year = :year"
)
query.bindValue(":number", number)
query.bindValue(":year", year)
query.bindValue(":number", self.number)
query.bindValue(":year", self.year)
if not query.exec():
query_error(query)
db.close()
del db
QSqlDatabase.removeDatabase("load")
self.caseLoaded.emit(0)
return