studia/jezyki-skryptowe/lista10/stats.py
2024-06-14 16:53:58 +02:00

205 lines
5.7 KiB
Python

from datetime import datetime, timedelta
import sys
from PyQt6.QtWidgets import (
QApplication,
QWidget,
QVBoxLayout,
QDateEdit,
QGridLayout,
QHBoxLayout,
QPushButton,
QLabel,
QListWidget,
QListWidgetItem,
QFrame,
QFileDialog,
QMessageBox
)
from database import init_database, get_database, close_database
from models.station import Station
from models.rental import Rental
from peewee import fn, SQL
DATA_ROLE = 1001
LABELS = [
"Avg. rent duration when starting there",
"Avg. rent duration when stopping there",
"Count of different bikes stopped there",
"Most popular destination from there"
]
class DatabaseViewer(QWidget):
def __init__(self):
self.value_labels = {}
super().__init__()
self.init()
self.stations = []
def init(self):
self.setWindowTitle("Database stats")
self.setGeometry(100, 100, 900, 500)
mainLayout = QVBoxLayout()
topLayout = QHBoxLayout()
self.pathLabel = QLabel("Select database file...")
self.openButton = QPushButton("Open")
self.openButton.clicked.connect(self.open_file_dialog)
topLayout.addWidget(self.pathLabel)
topLayout.addWidget(self.openButton)
middleLayout = QHBoxLayout()
self.stationsListWidget = QListWidget()
self.stationsListWidget.itemClicked.connect(self.select_station)
middleLayout.addWidget(self.stationsListWidget, 2)
self.statsFrame = QFrame()
statsLayout = QVBoxLayout(self.statsFrame)
self.create_stats_widget(statsLayout)
middleLayout.addWidget(self.statsFrame, 2)
mainLayout.addLayout(topLayout)
mainLayout.addLayout(middleLayout)
self.setLayout(mainLayout)
def reset_ui(self):
self.pathLabel.setText("Select database file...")
self.stationsListWidget.clear()
for label in LABELS:
self.value_labels[label].setText("")
def select_station(self, item):
station_id = item.data(DATA_ROLE)
try:
avg_time_start = (Rental
.select(fn.AVG(Rental.duration))
.where(Rental.rent_station == station_id)
.scalar()
) or 0.0
avg_time_stopped = (Rental
.select(fn.AVG(Rental.duration))
.where(Rental.ret_station == station_id)
.scalar()
) or 0.0
bikes_count = (Rental
.select(fn.COUNT(fn.DISTINCT(Rental.bike_id)))
.where(Rental.ret_station == station_id)
.scalar()
) or 0
(most_popular, count) = (Rental
.select(Rental.ret_station, fn.COUNT(Rental.ret_station).alias("count"))
.where(Rental.rent_station == station_id)
.group_by(Rental.ret_station)
.order_by(SQL("count").desc())
.scalar(as_tuple=True)
) or (0,0)
most_popular_station = (Station
.select()
.where(Station.id == most_popular)
.get()
) if count != 0 else None
self.value_labels[LABELS[0]].setText(f"{avg_time_start:.2f} minutes")
self.value_labels[LABELS[1]].setText(f"{avg_time_stopped:.2f} minutes")
self.value_labels[LABELS[2]].setText(str(bikes_count))
self.value_labels[LABELS[3]].setText(f"{most_popular_station.name if most_popular_station is not None else '-'} ({count} trips)")
except Exception as ex:
self.show_error(f"Failed when executing database queries: {str(ex)}")
def create_stats_widget(self, layout):
gridLayout = QGridLayout()
for index, label in enumerate(LABELS):
labelWidget = QLabel(f"{label}:")
valueLabel = QLabel()
gridLayout.addWidget(labelWidget, index, 0)
gridLayout.addWidget(valueLabel, index, 1)
self.value_labels[label] = valueLabel
layout.addLayout(gridLayout)
def open_file_dialog(self):
if len(self.stations) > 0:
self.openButton.setText("Open")
self.stations = []
self.reset_ui()
try:
close_database()
except:
self.show_error("Failed to close database!")
else:
fileName, _ = QFileDialog.getOpenFileName(
self,
"Open Database File",
"",
"Sqlite Files (*.sqlite3);;All Files (*)",
)
if fileName:
self.load_database(fileName)
def load_database(self, file_path):
self.reset_ui()
self.openButton.setText("Close")
self.pathLabel.setText(file_path)
try:
init_database(file_path)
self.stations = Station.select().order_by(Station.name)
self.update_list()
except:
self.show_error("Failed to load database!")
def update_list(self):
self.stationsListWidget.clear()
for station in self.stations:
item = QListWidgetItem(station.name)
item.setData(DATA_ROLE, station.id)
self.stationsListWidget.addItem(item)
def show_error(self, text):
message_box = QMessageBox()
message_box.setIcon(QMessageBox.Icon.Critical)
message_box.setText("Error")
message_box.setWindowTitle("Error")
message_box.setInformativeText(text)
message_box.exec()
def main():
app = QApplication(sys.argv)
ex = DatabaseViewer()
ex.show()
sys.exit(app.exec())
if __name__ == "__main__":
main()