205 lines
5.7 KiB
Python
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()
|