From 8baee6b2253b9a954a62baf3af724c29d6427312 Mon Sep 17 00:00:00 2001 From: "Christopher T. Johnson" Date: Fri, 7 Feb 2025 17:22:24 -0500 Subject: [PATCH] Working code. Need to populate --- docketModel.py | 66 ++++++++++++++++++++++++++++++++++++++++ lib/__init__.py | 0 lib/utils.py | 18 +++++++++++ scotus-pull.py | 78 +++++++++++++++++------------------------------- ui/MainWindow.py | 60 ++++++++++++++++++++++++++++++++----- ui/MainWindow.ui | 63 +++++++++++++++++++++++++++++++++++--- 6 files changed, 222 insertions(+), 63 deletions(-) create mode 100644 docketModel.py create mode 100644 lib/__init__.py create mode 100644 lib/utils.py diff --git a/docketModel.py b/docketModel.py new file mode 100644 index 0000000..184539c --- /dev/null +++ b/docketModel.py @@ -0,0 +1,66 @@ +import datetime +from PySide6.QtCore import QAbstractTableModel, QModelIndex, Qt +from PySide6.QtGui import QColor, QFont +from PySide6.QtSql import QSqlQuery + +from lib.utils import query_error + +class docketModel(QAbstractTableModel): + entries = [] + def __init__(self, case_id:int|None = None) -> None: + super(docketModel,self).__init__() + if case_id == None: + return + query = QSqlQuery() + query.prepare("SELECT * FROM entries WHERE case_id = :cid " + "ORDER BY entry_id") + q2 = QSqlQuery() + q2.prepare("SELECT * FROM documents WHERE entry_id = :eid") + + query.bindValue(":cid", case_id) + if not query.exec(): + query_error(query) + while query.next(): + date = datetime.date.fromtimestamp(query.value(2)) + assert isinstance(date, datetime.date) + row = [ + date.strftime("%B %-d, %Y"), + query.value(3) + ] + self.entries.append(row) + q2.bindValue(":eid", query.value("entry_id")) + if not q2.exec(): + query_error(q2) + row = [] + while q2.next(): + row.append(q2.value('name')) + if len(row) > 0: + self.entries.append([None, " ".join(row)]) + return + + def rowCount(self, parent:QModelIndex|None = None) -> int: + return len(self.entries) + + def columnCount(self, parent:QModelIndex|None = None) -> int: + return 2 + + def data(self, index: QModelIndex, role:int): + if not index.isValid(): + return '' + if role == Qt.ItemDataRole.DisplayRole: + return self.entries[index.row()][index.column()] + if role == Qt.ItemDataRole.TextAlignmentRole and index.column() == 0: + return Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignTop + return + + + def headerData(self, section:int, orientation:Qt.Orientation, role:int): + if orientation == Qt.Orientation.Vertical: + return + if role == Qt.ItemDataRole.FontRole: + font = QFont() + font.setBold(True) + return font + if role != Qt.ItemDataRole.DisplayRole: + return + return ['Date', 'Proceedings and Orders'][section] diff --git a/lib/__init__.py b/lib/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/lib/utils.py b/lib/utils.py new file mode 100644 index 0000000..3d4514e --- /dev/null +++ b/lib/utils.py @@ -0,0 +1,18 @@ +from typing import NoReturn +from PySide6.QtCore import QCoreApplication +from PySide6.QtSql import QSqlQuery + + +translate = QCoreApplication.translate +def query_error(query: QSqlQuery) -> NoReturn: + """Standarized query error reporter.""" + print( + translate("MainWindow", "SQL Error:\n") + + "{}\n{}\n{}:{}".format( + query.executedQuery(), + query.boundValues(), + query.lastError().type(), + query.lastError().text(), + ) + ) + raise Exception(translate("MainWindow", "SQL Error")) diff --git a/scotus-pull.py b/scotus-pull.py index 0b4f5cd..691b97f 100755 --- a/scotus-pull.py +++ b/scotus-pull.py @@ -5,27 +5,16 @@ import sys import dateparser import requests from typing import NoReturn -from PySide6.QtCore import QCoreApplication, QModelIndex, Signal, Qt +from PySide6.QtCore import QCoreApplication, QModelIndex, Signal, Qt, Slot from PySide6.QtSql import QSqlDatabase, QSqlQuery, QSqlQueryModel from PySide6.QtWidgets import QAbstractItemView, QApplication, QHeaderView, QMainWindow, QStyledItemDelegate, QTableWidgetItem from bs4 import BeautifulSoup, Tag +from docketModel import docketModel from ui.MainWindow import Ui_MainWindow - +from lib.utils import query_error translate = QCoreApplication.translate -def query_error(query: QSqlQuery) -> NoReturn: - """Standarized query error reporter.""" - print( - translate("MainWindow", "SQL Error:\n") - + "{}\n{}\n{}:{}".format( - query.executedQuery(), - query.boundValues(), - query.lastError().type(), - query.lastError().text(), - ) - ) - raise Exception(translate("MainWindow", "SQL Error")) class dateDelegate(QStyledItemDelegate): def displayText(self, value, locale) -> str: @@ -64,40 +53,29 @@ class MainWindow(QMainWindow, Ui_MainWindow): self.casesView.doubleClicked.connect(self.rowClicked) self.casesView.clicked.connect(self.rowClicked) - self.docketWidget.setColumnCount(2) - self.docketWidget.setHorizontalHeaderLabels([ - 'Date','Proceedings and Orders', - ]) - self.docketWidget.resizeColumnToContents(0) - self.docketWidget.horizontalHeader().setSectionResizeMode(1, QHeaderView.ResizeMode.Stretch) + self.docketView.setModel(docketModel()) + self.docketView.horizontalHeader().setSectionResizeMode(1, QHeaderView.ResizeMode.Stretch) + self.docketView.resizeRowsToContents() return - def populateDocket(self, case_id:int) -> None: - query = QSqlQuery() - query.prepare("SELECT * FROM entries WHERE case_id=:cid ORDER BY entry_id") - query.bindValue(":cid", case_id) - if not query.exec(): - query_error(query) - self.docketWidget.clearContents() - row = 0 - while query.next(): - print(query.value(0), query.value(1), query.value(2), query.value(3)) - item = QTableWidgetItem() - item.setData(Qt.ItemDataRole.DisplayRole, query.value(2)) - self.docketWidget.setItem(row,0, item) - item = QTableWidgetItem() - item.setData(Qt.ItemDataRole.DisplayRole, query.value(3)) - self.docketWidget.setItem(row, 1, item) - row += 1 - self.docketWidget.setRowCount(row) - - return - + @Slot(QModelIndex) def rowClicked(self, index:QModelIndex) -> None: + if not index.isValid(): + raise Exception("Bad index") docket = index.siblingAtColumn(1).data() - print(docket) + self.docketLabel.setText(docket) self.show_entries.emit(index.siblingAtColumn(0).data()) - self.populateDocket(index.siblingAtColumn(0).data()) + model = docketModel(index.siblingAtColumn(0).data()) + self.docketView.setModel(model) + self.docketView.resizeColumnToContents(0) + self.docketView.resizeRowsToContents() + return + + @Slot() + def on_updateButton_clicked(self): + text = self.docketInput.toPlainText() + update_db(text) + self.update() return SQL_CMDS = [ @@ -107,6 +85,7 @@ SQL_CMDS = [ "docket_id TEXT, " "linked INTEGER, " "petitioners TEXT, respondents TEXT, date INTEGER, " + "active INTEGER, " "FOREIGN KEY(linked) REFERENCES cases(case_id))", # "CREATE TABLE IF NOT EXISTS entries (" @@ -141,7 +120,9 @@ def schema_update(db: QSqlDatabase) -> None: table_name = matches.group(2) create_cmd = ( matches.group(1) + + '"' + matches.group(2) + + '"' + matches.group(3) ) else: @@ -173,7 +154,6 @@ def schema_update(db: QSqlDatabase) -> None: # Step 4 create new table new_table_name = table_name + "_new" sql = matches.group(1) + new_table_name + matches.group(3) - print(sql) if not query.exec(sql): query_error(query) # step 5 transfer content @@ -228,7 +208,7 @@ def update_proceedings(case_id: int, bs: BeautifulSoup) -> None: if not query.next(): query.prepare("INSERT INTO entries (case_id, date, text) VALUES (:cid,:date,:text)") query.bindValue(':cid', case_id) - query.bindValue(':date', date.timestamp) + query.bindValue(':date', date.timestamp()) query.bindValue(':text', text) if not query.exec(): query_error(query) @@ -238,7 +218,6 @@ def update_proceedings(case_id: int, bs: BeautifulSoup) -> None: tr = trs.pop(0) assert isinstance(tr, Tag) assert isinstance(tr.contents[1], Tag) - print(tr.contents[1]) for a in tr.contents[1]: assert isinstance(a, Tag) url = a.attrs['href'] @@ -256,7 +235,6 @@ def update_proceedings(case_id: int, bs: BeautifulSoup) -> None: query.bindValue(":url", url) if not query.exec(): query_error(query) - break return def update_db(case_id) -> int: @@ -315,8 +293,6 @@ def update_db(case_id) -> int: assert isinstance(tr, Tag) and isinstance(tr.contents[0], Tag) linked = tr.contents[0].string - print(docket_id, petitioners, respondent, date, linked) - # # See if this case already exists. # @@ -376,8 +352,8 @@ def main() -> int: db.setDatabaseName("scotus.db") db.open() schema_update(db) - #update_db('24-203') - #update_db('23A1058') + update_db('24-203') + update_db('23A1058') window = MainWindow() return app.exec() diff --git a/ui/MainWindow.py b/ui/MainWindow.py index 73c7700..699d258 100644 --- a/ui/MainWindow.py +++ b/ui/MainWindow.py @@ -15,15 +15,16 @@ from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor, QFont, QFontDatabase, QGradient, QIcon, QImage, QKeySequence, QLinearGradient, QPainter, QPalette, QPixmap, QRadialGradient, QTransform) -from PySide6.QtWidgets import (QApplication, QHeaderView, QMainWindow, QMenuBar, - QSizePolicy, QStatusBar, QTableView, QTableWidget, - QTableWidgetItem, QVBoxLayout, QWidget) +from PySide6.QtWidgets import (QApplication, QFrame, QHBoxLayout, QHeaderView, + QLabel, QMainWindow, QMenuBar, QPlainTextEdit, + QPushButton, QSizePolicy, QStatusBar, QTableView, + QVBoxLayout, QWidget) class Ui_MainWindow(object): def setupUi(self, MainWindow): if not MainWindow.objectName(): MainWindow.setObjectName(u"MainWindow") - MainWindow.resize(800, 600) + MainWindow.resize(929, 710) self.centralwidget = QWidget(MainWindow) self.centralwidget.setObjectName(u"centralwidget") self.verticalLayout = QVBoxLayout(self.centralwidget) @@ -33,15 +34,55 @@ class Ui_MainWindow(object): self.verticalLayout.addWidget(self.casesView) - self.docketWidget = QTableWidget(self.centralwidget) - self.docketWidget.setObjectName(u"docketWidget") + self.docketLabel = QLabel(self.centralwidget) + self.docketLabel.setObjectName(u"docketLabel") - self.verticalLayout.addWidget(self.docketWidget) + self.verticalLayout.addWidget(self.docketLabel) + + self.docketView = QTableView(self.centralwidget) + self.docketView.setObjectName(u"docketView") + + self.verticalLayout.addWidget(self.docketView) + + self.frame = QFrame(self.centralwidget) + self.frame.setObjectName(u"frame") + sizePolicy = QSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Minimum) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.frame.sizePolicy().hasHeightForWidth()) + self.frame.setSizePolicy(sizePolicy) + self.frame.setFrameShape(QFrame.Shape.StyledPanel) + self.frame.setFrameShadow(QFrame.Shadow.Raised) + self.horizontalLayout = QHBoxLayout(self.frame) + self.horizontalLayout.setObjectName(u"horizontalLayout") + self.docketInput = QPlainTextEdit(self.frame) + self.docketInput.setObjectName(u"docketInput") + sizePolicy1 = QSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed) + sizePolicy1.setHorizontalStretch(0) + sizePolicy1.setVerticalStretch(0) + sizePolicy1.setHeightForWidth(self.docketInput.sizePolicy().hasHeightForWidth()) + self.docketInput.setSizePolicy(sizePolicy1) + self.docketInput.setMaximumSize(QSize(16777215, 27)) + + self.horizontalLayout.addWidget(self.docketInput) + + self.updateButton = QPushButton(self.frame) + self.updateButton.setObjectName(u"updateButton") + + self.horizontalLayout.addWidget(self.updateButton) + + self.searchButton = QPushButton(self.frame) + self.searchButton.setObjectName(u"searchButton") + + self.horizontalLayout.addWidget(self.searchButton) + + + self.verticalLayout.addWidget(self.frame) MainWindow.setCentralWidget(self.centralwidget) self.menubar = QMenuBar(MainWindow) self.menubar.setObjectName(u"menubar") - self.menubar.setGeometry(QRect(0, 0, 800, 24)) + self.menubar.setGeometry(QRect(0, 0, 929, 24)) MainWindow.setMenuBar(self.menubar) self.statusbar = QStatusBar(MainWindow) self.statusbar.setObjectName(u"statusbar") @@ -54,5 +95,8 @@ class Ui_MainWindow(object): def retranslateUi(self, MainWindow): MainWindow.setWindowTitle(QCoreApplication.translate("MainWindow", u"MainWindow", None)) + self.docketLabel.setText(QCoreApplication.translate("MainWindow", u"TextLabel", None)) + self.updateButton.setText(QCoreApplication.translate("MainWindow", u"Update Docket", None)) + self.searchButton.setText(QCoreApplication.translate("MainWindow", u"Search", None)) # retranslateUi diff --git a/ui/MainWindow.ui b/ui/MainWindow.ui index de469dd..462ad84 100644 --- a/ui/MainWindow.ui +++ b/ui/MainWindow.ui @@ -6,8 +6,8 @@ 0 0 - 800 - 600 + 929 + 710 @@ -19,7 +19,62 @@ - + + + TextLabel + + + + + + + + + + + 0 + 0 + + + + QFrame::Shape::StyledPanel + + + QFrame::Shadow::Raised + + + + + + + 0 + 0 + + + + + 16777215 + 27 + + + + + + + + Update Docket + + + + + + + Search + + + + + @@ -28,7 +83,7 @@ 0 0 - 800 + 929 24