199 lines
7.5 KiB
Python
199 lines
7.5 KiB
Python
import random
|
|
import sys
|
|
from typing import Any, Union
|
|
|
|
|
|
import loguru
|
|
from PySide6 import QtWidgets, QtCore, QtGui
|
|
from PySide6.QtCore import Qt
|
|
from PySide6.QtGui import QPainter, QPen, QColor
|
|
from PySide6.QtCharts import QChart, QChartView, QLineSeries, QValueAxis, QCategoryAxis
|
|
|
|
from src import LOG_DIR
|
|
from src.backend.semester import Semester
|
|
|
|
log = loguru.logger
|
|
log.remove()
|
|
log.add(sys.stdout, level="INFO")
|
|
log.add(f"{LOG_DIR}/application.log", rotation="1 MB", retention="10 days")
|
|
|
|
|
|
def mergedicts(d1: dict[str, Any], d2: dict[str, Any]):
|
|
res: dict[str, Any] = {}
|
|
d1_data = list(d1.items())
|
|
d2_data = list(d2.items())
|
|
for i in range(len(d1)):
|
|
# _data is a list of tuples
|
|
d1_data_slice = d1_data[i]
|
|
d2_data_slice = d2_data[i]
|
|
# convert the tuples to dicts
|
|
d1_dict = dict([d1_data_slice])
|
|
d2_dict = dict([d2_data_slice])
|
|
# merge the dicts
|
|
res.update(d1_dict) # type: ignore
|
|
res.update(d2_dict) # type: ignore
|
|
return res
|
|
|
|
class DataQtGraph(QtWidgets.QWidget):
|
|
def __init__(
|
|
self,
|
|
title: str,
|
|
data: dict[str, Union[list[str], dict[str, list[int]]]],
|
|
generateMissing: bool,
|
|
y_label: str,
|
|
x_rotation: int = 90,
|
|
):
|
|
super().__init__()
|
|
self.series = QLineSeries()
|
|
self.chart = QChart()
|
|
# scale the chart to fit the data
|
|
self.chart.setTitle(title)
|
|
self.chart.legend().setVisible(True)
|
|
|
|
layout = QtWidgets.QVBoxLayout()
|
|
|
|
lst = []
|
|
if generateMissing:
|
|
s_start = Semester.from_string(data["x"][0])
|
|
s_end = Semester.from_string(data["x"][-1])
|
|
# generate all semesters from start to end
|
|
missing_semesters = Semester.generate_missing(s_start, s_end)
|
|
x_data = data["x"]
|
|
y_data = data["y"]
|
|
if not isinstance(y_data, list):
|
|
for key in y_data:
|
|
data = {"x": [], "y": []}
|
|
data["y-label"] = key
|
|
for semester in missing_semesters:
|
|
if semester not in x_data:
|
|
data["x"].append(semester)
|
|
data["y"].append(0)
|
|
data["x"].append(semester)
|
|
# get the index of the semester in x_data
|
|
if semester in x_data:
|
|
index = x_data.index(semester)
|
|
print("index:", index)
|
|
print(key, y_data[key])
|
|
data["y"].append(y_data[key][index])
|
|
lst.append(data)
|
|
# for key in y_data:
|
|
# data = {"x": x_data, "y": y_data[key]}
|
|
# data = self.generateMissingSemesters(data)
|
|
# data["y-label"] = key
|
|
else:
|
|
# data = self.generateMissingSemesters(data)
|
|
data = {"x": [], "y": []}
|
|
for semester in missing_semesters:
|
|
# if semester not in x_data, set y to 0
|
|
if semester not in x_data:
|
|
data["x"].append(semester)
|
|
data["y"].append(0)
|
|
data["x"].append(semester)
|
|
# get the index of the semester in x_data
|
|
if semester in x_data:
|
|
index = x_data.index(semester)
|
|
data["y"].append(y_data[index])
|
|
data["y-label"] = y_label
|
|
lst.append(data)
|
|
|
|
else:
|
|
x_data = data["x"]
|
|
y_data = data["y"]
|
|
if not isinstance(y_data, list):
|
|
for key in y_data:
|
|
data = {"x": x_data, "y": y_data[key]}
|
|
data["y-label"] = key
|
|
lst.append(data)
|
|
else:
|
|
lst.append(data)
|
|
x_data = lst[0]["x"] #
|
|
|
|
self.chart.createDefaultAxes()
|
|
for entry in lst:
|
|
print("entry:", entry)
|
|
entryseries = QLineSeries()
|
|
for x_val, y_val in zip(entry["x"], entry["y"]):
|
|
#
|
|
entryseries.append(entry["x"].index(x_val), y_val)
|
|
entryseries.setName(entry["y-label"] if "y-label" in entry else y_label)
|
|
entryseries.setPen(
|
|
QPen(
|
|
QColor(
|
|
random.randint(0, 255),
|
|
random.randint(0, 255),
|
|
random.randint(0, 255),
|
|
),
|
|
2,
|
|
)
|
|
)
|
|
|
|
self.chart.addSeries(entryseries)
|
|
|
|
x_axis = QCategoryAxis()
|
|
for index, semester in enumerate(lst[0]["x"]):
|
|
x_axis.append(semester, index)
|
|
x_axis.setLabelsPosition(
|
|
QCategoryAxis.AxisLabelsPosition.AxisLabelsPositionOnValue
|
|
)
|
|
# rotare the label by 45 degrees
|
|
x_axis.setLabelsAngle(x_rotation)
|
|
x_axis.setLabelsFont(QtGui.QFont("Arial", 8, QtGui.QFont.Weight.Normal, False))
|
|
self.chart.setAxisX(x_axis, entryseries)
|
|
|
|
# entryseries.append(
|
|
# str()
|
|
# )
|
|
self.chart.legend().setVisible(True)
|
|
self.chart.legend().setAlignment(QtCore.Qt.AlignmentFlag.AlignTop)
|
|
# set legend labels
|
|
|
|
self.chart.setAxisY(QValueAxis(self.chart), entryseries)
|
|
# the chart's x axis labels are not being displayed, as the chart only has limited space. scale down the x axis font
|
|
|
|
chartview = QChartView(self.chart)
|
|
chartview.setRenderHint(QPainter.RenderHint.Antialiasing)
|
|
layout.addWidget(chartview)
|
|
self.setLayout(layout)
|
|
|
|
def generateMissingSemesters(self, data: dict[list]):
|
|
# join the data into a single dict with x values as key and y values as value
|
|
tmp_data = dict(zip(data["x"], data["y"], strict=False))
|
|
# split into dicts based on SoSe and WiSe
|
|
SoSe_data = {k: v for k, v in tmp_data.items() if "SoSe" in k}
|
|
WiSe_data = {k: v for k, v in tmp_data.items() if "WiSe" in k}
|
|
SoSe_years = [int(sose.split("SoSe")[1]) for sose in SoSe_data]
|
|
WiSe_years = [int(wise.split("WiSe")[1].split("/")[0]) for wise in WiSe_data]
|
|
years = SoSe_years + WiSe_years
|
|
years = [
|
|
year for year in range(min(list(set(years))), max(list(set(years))) + 1)
|
|
]
|
|
years.sort()
|
|
for year in years:
|
|
SoSe_year = f"SoSe{year}"
|
|
WiSe_year = f"WiSe{year}/{year + 1}"
|
|
if SoSe_year not in SoSe_data.keys():
|
|
SoSe_data[SoSe_year] = 0
|
|
if WiSe_year not in WiSe_data.keys():
|
|
WiSe_data[WiSe_year] = 0
|
|
|
|
# sort WiSe_data to have same order as SoSe_data
|
|
WiSe_data = dict(sorted(WiSe_data.items(), key=lambda x: x[0]))
|
|
SoSe_data = dict(sorted(SoSe_data.items(), key=lambda x: x[0]))
|
|
data = mergedicts(SoSe_data, WiSe_data)
|
|
# split the data back into x and y
|
|
data = {"x": list(data.keys()), "y": list(data.values())}
|
|
return data
|
|
|
|
|
|
if __name__ == "__main__":
|
|
import sys
|
|
|
|
app = QtWidgets.QApplication(sys.argv)
|
|
|
|
graph_data = {"x": ["WiSe 25/26", "WiSe 24/25", "SoSe 25"], "y": [1, 2, 1]}
|
|
widget = DataGraph(
|
|
"ELSA Apparate pro Semester", graph_data, True, "Anzahl der Apparate"
|
|
)
|
|
widget.show()
|
|
sys.exit(app.exec())
|