Add new changes to main #7

Merged
WorldTeacher merged 4 commits from dev into main 2025-05-14 13:58:47 +01:00
11 changed files with 198 additions and 126 deletions

View File

@@ -0,0 +1,62 @@
on:
workflow_dispatch:
inputs:
release_notes:
description: Release notes (use \n for newlines)
type: string
required: false
github_release:
description: 'Create Gitea Release'
default: true
type: boolean
bump:
description: 'Bump type'
required: false
default: 'patch'
type: choice
options:
- 'major'
- 'minor'
- 'patch'
jobs:
bump:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install UV
uses: astral-sh/setup-uv@v5
- name: Set up Python
run: uv python install
with:
python-version-file: "pyproject.toml"
- name: Install dependencies
run: uv sync --locked --all-extras --dev
- name: Install Bump tool
run: uv tool install bump-my-version
- name: Bump version
id: bump_version
run: |
uv tool run bump-my-version --bump ${{ github.event.inputs.bump }} --tag --allow-dirty
- name: Add release notes
id: add_release_notes
run: |
echo "RELEASE_NOTES<<EOF" >> $GITHUB_ENV
echo "${{ github.event.inputs.release_notes }}" >> $GITHUB_ENV
echo "EOF" >> $GITHUB_ENV
- name: Create Gitea Release
if: ${{ github.event.inputs.github_release == 'true' }}
uses: softprops/action-gh-release@v1
with:
tag_name: ${{ github.sha }}
release_name: Release ${{ github.sha }}
body: ${{ env.RELEASE_NOTES }}
draft: false
prerelease: false
env:
GITHUB_TOKEN: ${{ secrets.TOKEN }}
GITHUB_REPOSITORY: ${{ github.repository }}

2
.gitignore vendored
View File

@@ -228,3 +228,5 @@ config.yaml
**/tempCodeRunnerFile.py
uv.lock
logs/
*.pdf
*.docx

View File

@@ -583,25 +583,28 @@ class Database:
return self.query_db("SELECT * FROM subjects")
# Messages
def addMessage(self, message: dict, user: str, app_id: Union[str, int]):
def addMessage(
self, messages: list[dict[str, Any]], user: str, app_id: Union[str, int]
):
"""add a Message to the database
Args:
message (dict): the message to be added
user (str): the user who added the message
messages (list[dict[str, Any]]): the messages to be added
user (str): the user who added the messages
app_id (Union[str,int]): the id of the apparat
"""
def __getUserId(user):
def __getUserId(user: str):
return self.query_db(
"SELECT id FROM user WHERE username=?", (user,), one=True
)[0]
user_id = __getUserId(user)
self.query_db(
"INSERT INTO messages (message, user_id, remind_at,appnr) VALUES (?,?,?,?)",
(message["message"], user_id, message["remind_at"], app_id),
)
for message in messages:
self.query_db(
"INSERT INTO messages (message, user_id, remind_at,appnr) VALUES (?,?,?,?)",
(message["message"], user_id, message["remind_at"], app_id),
)
def getAllMessages(self) -> list[dict[str, str, str, str]]:
"""Get all the messages in the database
@@ -610,7 +613,7 @@ class Database:
list[dict[str, str, str, str]]: a list of dictionaries containing the message, the user who added the message, the apparat id and the id of the message
"""
def __get_user_name(user_id):
def __get_user_name(user_id: int):
return self.query_db(
"SELECT username FROM user WHERE id=?", (user_id,), one=True
)[0]
@@ -628,17 +631,17 @@ class Database:
]
return ret
def getMessages(self, date: str) -> list[dict[str, str, str, str]]:
def getMessages(self, date: str) -> list[dict[str, str]]:
"""Get all the messages for a specific date
Args:
date (str): a date.datetime object formatted as a string in the format "YYYY-MM-DD"
Returns:
list[dict[str, str, str, str]]: a list of dictionaries containing the message, the user who added the message, the apparat id and the id of the message
list[dict[str, str]]: a list of dictionaries containing the message, the user who added the message, the apparat id and the id of the message
"""
def __get_user_name(user_id):
def __get_user_name(user_id: int):
return self.query_db(
"SELECT username FROM user WHERE id=?", (user_id,), one=True
)[0]
@@ -650,7 +653,7 @@ class Database:
]
return ret
def deleteMessage(self, message_id):
def deleteMessage(self, message_id: int):
"""Delete a message from the database
Args:
@@ -692,7 +695,9 @@ class Database:
)[0]
return f"{title} " if title is not None else ""
def getSpecificProfData(self, prof_id: Union[str, int], fields: List[str]) -> tuple:
def getSpecificProfData(
self, prof_id: Union[str, int], fields: List[str]
) -> tuple[Any, ...]:
"""A customisable function to get specific data of a professor based on the id
Args:
@@ -761,7 +766,7 @@ class Database:
return [Prof().from_tuple(prof) for prof in profs]
# Apparat
def getAllAparats(self, deleted=0) -> list[tuple]:
def getAllAparats(self, deleted: int = 0) -> list[Apparat]:
"""Get all the apparats in the database
Args:
@@ -770,9 +775,13 @@ class Database:
Returns:
list[tuple]: a list of tuples containing the apparats
"""
return self.query_db(
apparats = self.query_db(
"SELECT * FROM semesterapparat WHERE deletion_status=?", (deleted,)
)
ret: list[Apparat] = []
for apparat in apparats:
ret.append(Apparat().from_tuple(apparat))
return ret
def getApparatData(self, appnr, appname) -> ApparatData:
"""Get the Apparat data based on the apparat number and the name
@@ -1595,7 +1604,7 @@ class Database:
else:
return Prof()
def getProfIDByApparat(self, apprarat_id):
def getProfIDByApparat(self, apprarat_id: int) -> Optional[int]:
"""Get the prof id based on the semesterapparat id from the database
Args:
@@ -1613,7 +1622,7 @@ class Database:
else:
return None
def copyBookToApparat(self, book_id, apparat):
def copyBookToApparat(self, book_id: int, apparat: int):
# get book data
new_apparat_id = apparat
new_prof_id = self.getProfIDByApparat(new_apparat_id)
@@ -1634,7 +1643,7 @@ class Database:
connection.commit()
connection.close()
def moveBookToApparat(self, book_id, appratat):
def moveBookToApparat(self, book_id: int, appratat: int):
"""Move the book to the new apparat
Args:
@@ -1649,7 +1658,7 @@ class Database:
connection.commit()
connection.close()
def getApparatNameByAppNr(self, appnr):
def getApparatNameByAppNr(self, appnr: int):
query = f"SELECT name FROM semesterapparat WHERE appnr = '{appnr}' and deletion_status = 0"
data = self.query_db(query)
if data:

View File

@@ -1,38 +1,38 @@
from dataclasses import dataclass, field
from enum import Enum
import json
from typing import Union, Any, Optional
@dataclass
class Prof:
id: int = None
_title: str = None
firstname: str = None
lastname: str = None
fullname: str = None
mail: str = None
telnr: str = None
id: Optional[int] = None
_title: Optional[str] = None
firstname: Optional[str] = None
lastname: Optional[str] = None
fullname: Optional[str] = None
mail: Optional[str] = None
telnr: Optional[str] = None
# add function that sets the data based on a dict
def from_dict(self, data: dict):
def from_dict(self, data: dict[str, Union[str, int]]):
for key, value in data.items():
if hasattr(self, key):
setattr(self, key, value)
return self
@property
def title(self):
def title(self) -> str:
if self._title is None or self._title == "None":
return ""
return self._title
@title.setter
def title(self, value):
def title(self, value: str):
self._title = value
# add function that sets the data from a tuple
def from_tuple(self, data: tuple):
def from_tuple(self, data: tuple[Union[str, int], ...]):
setattr(self, "id", data[0])
setattr(self, "_title", data[1])
setattr(self, "firstname", data[2])
@@ -42,7 +42,7 @@ class Prof:
setattr(self, "telnr", data[6])
return self
def name(self, comma=False):
def name(self, comma: bool = False) -> Optional[str]:
if self.firstname is None and self.lastname is None:
if "," in self.fullname:
self.firstname = self.fullname.split(",")[1].strip()
@@ -62,9 +62,9 @@ class BookData:
signature: str | None = None
edition: str | None = None
link: str | None = None
isbn: str | list | None = field(default_factory=list)
isbn: Union[str, list[str], None] = field(default_factory=list)
author: str | None = None
language: str | list | None = field(default_factory=list)
language: Union[str, list[str], None] = field(default_factory=list)
publisher: str | None = None
place: str | None = None
year: str | None = None
@@ -73,31 +73,33 @@ class BookData:
in_apparat: bool | None = False
adis_idn: str | None = None
def from_dict(self, data: dict):
def from_dict(self, data: dict) -> "BookData":
for key, value in data.items():
setattr(self, key, value)
return self
@property
def to_dict(self):
def to_dict(self) -> str:
"""Convert the dataclass to a dictionary."""
return json.dumps(self.__dict__, ensure_ascii=False)
def from_dataclass(self, dataclass):
def from_dataclass(self, dataclass: Optional[Any]) -> None:
if dataclass is None:
return
for key, value in dataclass.__dict__.items():
setattr(self, key, value)
def from_string(self, data: str):
data = json.loads(data)
return BookData(**data)
def from_string(self, data: str) -> "BookData":
ndata = json.loads(data)
return BookData(**ndata)
@dataclass
class MailData:
subject: str | None = None
body: str | None = None
mailto: str | None = None
prof: str | None = None
subject: Optional[str] = None
body: Optional[str] = None
mailto: Optional[str] = None
prof: Optional[str] = None
class Subjects(Enum):
@@ -127,15 +129,15 @@ class Subjects(Enum):
ECONOMICS = (24, "Wirtschaftslehre")
@property
def id(self):
def id(self) -> int:
return self.value[0]
@property
def name(self):
def name(self) -> str:
return self.value[1]
@classmethod
def get_index(cls, name):
def get_index(cls, name: str) -> Optional[int]:
for i in cls:
if i.name == name:
return i.id - 1
@@ -158,7 +160,7 @@ class Apparat:
prof_id_adis: str | None = None
konto: int | None = None
def from_tuple(self, data: tuple):
def from_tuple(self, data: tuple[Any, ...]) -> "Apparat":
self.id = data[0]
self.name = data[1]
self.prof_id = data[2]
@@ -176,7 +178,7 @@ class Apparat:
return self
@property
def get_semester(self):
def get_semester(self) -> Optional[str]:
if self.extend_until is not None:
return self.extend_until
else:
@@ -190,7 +192,7 @@ class ELSA:
semester: str | None = None
prof_id: int | None = None
def from_tuple(self, data):
def from_tuple(self, data: tuple[Any, ...]) -> "ELSA":
self.id = data[0]
self.date = data[1]
self.semester = data[2]

View File

@@ -1,6 +1,6 @@
# Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\SemesterapparatsManager\src\ui\dialogs\dialog_sources\settings.ui'
#
# Created by: PyQt6 UI code generator 6.8.0
# Created by: PyQt6 UI code generator 6.9.0
#
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
# run again. Do not edit this file unless you know what you are doing.

View File

@@ -1,6 +1,6 @@
# Form implementation generated from reading ui file '/home/alexander/GitHub/Semesterapparate/ui/dialogs/parsed_titles.ui'
# Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\SemesterapparatsManager\src\ui\dialogs\parsed_titles.ui'
#
# Created by: PyQt6 UI code generator 6.5.3
# Created by: PyQt6 UI code generator 6.9.0
#
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
# run again. Do not edit this file unless you know what you are doing.
@@ -38,12 +38,7 @@ class Ui_Form(object):
self.label_2 = QtWidgets.QLabel(parent=self.horizontalLayoutWidget)
self.label_2.setObjectName("label_2")
self.horizontalLayout.addWidget(self.label_2)
spacerItem = QtWidgets.QSpacerItem(
40,
20,
QtWidgets.QSizePolicy.Policy.Expanding,
QtWidgets.QSizePolicy.Policy.Minimum,
)
spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum)
self.horizontalLayout.addItem(spacerItem)
self.frame_2 = QtWidgets.QFrame(parent=Form)
self.frame_2.setGeometry(QtCore.QRect(10, 80, 381, 201))
@@ -59,9 +54,7 @@ class Ui_Form(object):
self.listWidget = QtWidgets.QListWidget(parent=self.horizontalLayoutWidget_2)
self.listWidget.setObjectName("listWidget")
self.horizontalLayout_2.addWidget(self.listWidget)
self.listWidget_done = QtWidgets.QListWidget(
parent=self.horizontalLayoutWidget_2
)
self.listWidget_done = QtWidgets.QListWidget(parent=self.horizontalLayoutWidget_2)
self.listWidget_done.setObjectName("listWidget_done")
self.horizontalLayout_2.addWidget(self.listWidget_done)
self.progressBar = QtWidgets.QProgressBar(parent=Form)
@@ -70,10 +63,7 @@ class Ui_Form(object):
self.progressBar.setObjectName("progressBar")
self.buttonBox = QtWidgets.QDialogButtonBox(parent=Form)
self.buttonBox.setGeometry(QtCore.QRect(230, 290, 156, 23))
self.buttonBox.setStandardButtons(
QtWidgets.QDialogButtonBox.StandardButton.Cancel
| QtWidgets.QDialogButtonBox.StandardButton.Ok
)
self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.StandardButton.Cancel|QtWidgets.QDialogButtonBox.StandardButton.Ok)
self.buttonBox.setObjectName("buttonBox")
self.toolButton = QtWidgets.QToolButton(parent=Form)
self.toolButton.setGeometry(QtCore.QRect(20, 290, 25, 19))

View File

@@ -3,7 +3,7 @@ from PyQt6 import QtWidgets
from .dialog_sources.reminder_ui import Ui_Erinnerung as Ui_Dialog
from src import Icon
import datetime as date
from typing import Any
class ReminderDialog(QtWidgets.QDialog, Ui_Dialog):
def __init__(self, parent=None):
@@ -13,8 +13,10 @@ class ReminderDialog(QtWidgets.QDialog, Ui_Dialog):
self.setWindowTitle("Erinnerung")
self.dateEdit.setDate(date.datetime.now())
def return_message(self) -> dict:
return {
"message": self.message_box.toPlainText(),
"remind_at": self.dateEdit.date().toString("yyyy-MM-dd"),
}
def return_message(self) -> list[dict[str, Any]]:
return [
{
"message": self.message_box.toPlainText(),
"remind_at": self.dateEdit.date().toString("yyyy-MM-dd"),
}
]

View File

@@ -1,6 +1,6 @@
# Form implementation generated from reading ui file 'c:\Users\aky547\GitHub\SemesterapparatsManager\src\ui\sounds\semesterapparat_ui.ui'
#
# Created by: PyQt6 UI code generator 6.8.0
# Created by: PyQt6 UI code generator 6.9.0
#
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
# run again. Do not edit this file unless you know what you are doing.

View File

@@ -1,7 +1,7 @@
# encoding: utf-8
import atexit
import os
import time
import sys
import tempfile
import webbrowser
@@ -260,7 +260,7 @@ class Ui(Ui_Semesterapparat):
def create_doc(self):
log.debug("Creating document")
# open DocumentPrintDialog
dialog = DocumentPrintDialog(self.MainWindow)
dialog = DocumentPrintDialog(self.MainWindow) # type: ignore
dialog.show()
def checkValidInput(self):
@@ -269,24 +269,24 @@ class Ui(Ui_Semesterapparat):
else:
self.check_file.setEnabled(False)
def setWidget(self, widget):
def setWidget(self, widget: QtWidgets.QWidget):
# remove all widgets from localwidget
self.hideWidget()
# add widget to localwidget
self.admin_action.layout().addWidget(widget)
self.admin_action.layout().addWidget(widget) # type: ignore
def hideWidget(self):
try:
widgets = [
self.admin_action.layout().itemAt(i).widget()
for i in range(self.admin_action.layout().count())
self.admin_action.layout().itemAt(i).widget() # type: ignore
for i in range(self.admin_action.layout().count()) # type: ignore
]
except AttributeError:
return
for widget in widgets:
self.admin_action.layout().removeWidget(widget)
widget.deleteLater()
self.admin_action.layout().removeWidget(widget) # type: ignore
widget.deleteLater() # type: ignore
def adminActions(self):
if self.select_action_box.currentText() == "Nutzer anlegen":
@@ -302,7 +302,7 @@ class Ui(Ui_Semesterapparat):
self.hideWidget()
self.admin_action.setTitle("")
def toggleButton(self, button):
def toggleButton(self, button: QtWidgets.QCheckBox):
if button.isChecked():
button.setChecked(False)
self.validate_semester()
@@ -317,7 +317,7 @@ class Ui(Ui_Semesterapparat):
def get_apparats(self):
alist = self.db.getAllAparats(deleted=0)
alist = natsorted(alist, key=lambda x: x[4], reverse=True)
alist = natsorted(alist, key=lambda x: x.appnr, reverse=True)
self.tableWidget_apparate.setRowCount(0)
for apparat in alist:
self.insert_apparat_into_table(apparat)
@@ -327,15 +327,16 @@ class Ui(Ui_Semesterapparat):
self.app_fach.clear()
self.app_fach.addItem("")
self.app_fach.setCurrentText("")
self.app_fach.addItems([subject[1] for subject in self.db.getSubjects()])
self.app_fach.addItems([subject[1] for subject in self.db.getSubjects()]) # type: ignore
def open_documentation(self):
log.info("Opening Documentation")
if not self.docu.isRunning():
self.docu.start()
time.sleep(5)
webbrowser.open("http://localhost:8000")
def update_calendar(self, data):
def update_calendar(self, data: list[dict[str, Any]]):
self.calendarWidget.setMessages([data])
self.calendarWidget.updateCells()
@@ -459,8 +460,7 @@ class Ui(Ui_Semesterapparat):
else self.sem_winter.text() + " " + self.sem_year.text()
)
app.prof_id_adis = self.prof_id_adis.text()
prof_id = self.db.getProfByName(prof.fullname).id
self.add_files(prof_id)
self.add_files()
app.apparat_id_adis = self.apparat_id_adis.text()
appdata = ApparatData(prof=prof, apparat=app)
self.db.updateApparat(appdata)
@@ -1305,7 +1305,7 @@ class Ui(Ui_Semesterapparat):
"prof_tel": self.prof_tel_nr.text(),
}
def add_files(self, prof_id=None):
def add_files(self):
"""
Add Files to the associated prof in the database
@@ -1346,24 +1346,28 @@ class Ui(Ui_Semesterapparat):
self.insert_apparat_into_table(apparat)
log.info("Inserted {} apparats into table".format(len(self.apparats)))
def insert_apparat_into_table(self, apparat):
def insert_apparat_into_table(self, apparat: Apparat):
# log.debug(apparat)
def __dauer_check(apparat):
return "Ja" if apparat[7] == 1 else "Nein"
def __dauer_check(apparat: Apparat):
return "Ja" if apparat.eternal == 1 else "Nein"
semester = apparat[8] if apparat[8] is not None else apparat[5]
semester = (
apparat.extend_until
if apparat.extend_until is not None
else apparat.created_semester
)
self.tableWidget_apparate.insertRow(0)
self.tableWidget_apparate.setItem(
0, 0, QtWidgets.QTableWidgetItem(str(apparat[4]))
0, 0, QtWidgets.QTableWidgetItem(str(apparat.appnr))
)
self.tableWidget_apparate.setItem(
0, 1, QtWidgets.QTableWidgetItem(str(apparat[1]))
0, 1, QtWidgets.QTableWidgetItem(str(apparat.name))
)
self.tableWidget_apparate.setItem(
0,
2,
QtWidgets.QTableWidgetItem(
self.db.getProfNameById(apparat[2], add_title=False)
self.db.getProfNameById(apparat.prof_id, add_title=False)
),
)
self.tableWidget_apparate.setItem(
@@ -1375,7 +1379,7 @@ class Ui(Ui_Semesterapparat):
0, 4, QtWidgets.QTableWidgetItem(__dauer_check(apparat))
)
self.tableWidget_apparate.setItem(
0, 5, QtWidgets.QTableWidgetItem(str(apparat[13]))
0, 5, QtWidgets.QTableWidgetItem(str(apparat.konto))
)
def open_context_menu(self, position):
@@ -1440,7 +1444,7 @@ class Ui(Ui_Semesterapparat):
dialog = CalendarEntry(messages=messages, date=selected_date)
# append dialog to self.frame_2
self.calendarlayout.addWidget(dialog)
dialog.repaintSignal.connect(lambda: self.calendarWidget.reload(selected_date))
dialog.repaintSignal.connect(lambda: self.calendarWidget.reload(selected_date)) # type: ignore
def open_settings(self):
# log.debug(settings.dict())
@@ -1463,7 +1467,7 @@ class Ui(Ui_Semesterapparat):
else:
pass
def media_context_menu(self, position):
def media_context_menu(self, position): # type: ignore
menu = QtWidgets.QMenu()
delete_action = QtGui.QAction("Löschen")
@@ -1475,36 +1479,36 @@ class Ui(Ui_Semesterapparat):
apparatmenu = menu.addMenu("Apparate")
generalmenu = menu.addMenu("Allgemeines")
apparatmenu.addActions(
apparatmenu.addActions( # type: ignore
[apparat_add_action, apparat_copy_action, apparat_move_action]
)
generalmenu.addActions([edit_action, delete_action, update_data_action])
generalmenu.addActions([edit_action, delete_action, update_data_action]) # type: ignore
# disable apparat_add_action
apparat_add_action.setEnabled(False)
delete_action.triggered.connect(self.delete_medium)
edit_action.triggered.connect(self.edit_medium)
apparat_add_action.triggered.connect(self.add_to_apparat)
apparat_copy_action.triggered.connect(self.copy_to_apparat)
apparat_move_action.triggered.connect(self.move_to_apparat)
update_data_action.triggered.connect(self.update_data)
menu.exec(self.tableWidget_apparat_media.mapToGlobal(position))
delete_action.triggered.connect(self.delete_medium) # type: ignore
edit_action.triggered.connect(self.edit_medium) # type: ignore
apparat_add_action.triggered.connect(self.add_to_apparat) # type: ignore
apparat_copy_action.triggered.connect(self.copy_to_apparat) # type: ignore
apparat_move_action.triggered.connect(self.move_to_apparat) # type: ignore
update_data_action.triggered.connect(self.update_data) # type: ignore
menu.exec(self.tableWidget_apparat_media.mapToGlobal(position)) # type: ignore
def update_data(self):
# TODO: use link in table, parse data and if needed, update location / signature
pass
def copy_to_apparat(self):
selected_rows = self.tableWidget_apparat_media.selectionModel().selectedRows()
signatures = []
selected_rows = self.tableWidget_apparat_media.selectionModel().selectedRows() # type: ignore
signatures: list[int] = []
for row in selected_rows:
signature = self.tableWidget_apparat_media.item(row.row(), 1).text()
signature = self.tableWidget_apparat_media.item(row.row(), 1).text() # type: ignore
book_id = self.db.getBookIdBasedOnSignature(
self.active_apparat,
self.db.getProfId(self.profdata),
self.db.getProfId(self.profdata), # type: ignore
signature,
)
signatures.append(book_id)
result, apparat = self.confirm_action_dialog(
signatures.append(book_id) # type: ignore
result, apparat = self.confirm_action_dialog( # type: ignore
"In welchen Apparat sollen die Medien kopiert werden?"
)
if result == 1:

View File

@@ -2,6 +2,7 @@ from PyQt6 import QtWidgets, QtCore
from PyQt6.QtCore import QDate
from PyQt6.QtGui import QColor, QPen
from src.backend import Database
from typing import Any
import darkdetect
import loguru
import sys
@@ -37,7 +38,7 @@ class MessageCalendar(QtWidgets.QCalendarWidget):
self.messages.remove(message)
self.updateCells()
def setMessages(self, messages):
def setMessages(self, messages: list[dict[str, Any]]):
# remove all drawn circles
for message in messages:

View File

@@ -1,5 +1,5 @@
import random
from typing import Union
from typing import Union, Any
import pyqtgraph as pg
from PyQt6 import QtWidgets
@@ -12,8 +12,8 @@ log.add(sys.stdout)
log.add("logs/application.log", rotation="1 MB", retention="10 days")
def mergedicts(d1, d2):
res = {}
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)):
@@ -24,18 +24,18 @@ def mergedicts(d1, d2):
d1_dict = dict([d1_data_slice])
d2_dict = dict([d2_data_slice])
# merge the dicts
res.update(d1_dict)
res.update(d2_dict)
res.update(d1_dict) # type: ignore
res.update(d2_dict) # type: ignore
return res
class DataGraph(QtWidgets.QWidget):
def __init__(
self,
title,
data=Union[dict[list, list] | dict[list[dict[str, list]]]],
generateMissing=False,
label=None,
title: str,
data=Union[dict[list, list], dict[list[dict[str, list[Any]]]]],
generateMissing: bool = False,
label: str = None,
):
super().__init__()
log.debug(