merge main into dev #13
@@ -57,6 +57,3 @@ jobs:
|
|||||||
body: ${{ env.RELEASE_NOTES }}
|
body: ${{ env.RELEASE_NOTES }}
|
||||||
draft: false
|
draft: false
|
||||||
prerelease: false
|
prerelease: false
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.TOKEN }}
|
|
||||||
GITHUB_REPOSITORY: ${{ github.repository }}
|
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
Subject: Vorschläge für Neuauflagen - {Appname}
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Type: text/html; charset="UTF-8"
|
||||||
|
Content-Transfer-Encoding: 8bit
|
||||||
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
|
||||||
|
</head>
|
||||||
|
<body style=" font-family:'Segoe UI'; font-size:9pt; font-weight:400; font-style:normal;">
|
||||||
|
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">{greeting}</p>
|
||||||
|
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>
|
||||||
|
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">für Ihren Semesterapparat {AppNr} - {Appname} wurden folgende Neuauflagen gefunden:</p>
|
||||||
|
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>
|
||||||
|
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">{newEditions}</p>
|
||||||
|
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>
|
||||||
|
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Sollen wir die alte(n) Auflage(n) aus dem Apparat durch diese austauschen?</p>
|
||||||
|
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>
|
||||||
|
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><pre class="moz-signature" cols="72">-- </p>
|
||||||
|
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">{signature}</p>
|
||||||
|
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></pre></p></body></html>
|
||||||
@@ -1,17 +1,18 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "semesterapparatsmanager"
|
name = "semesterapparatsmanager"
|
||||||
version = "0.2.1"
|
version = "1.0.0"
|
||||||
description = "Add your description here"
|
description = "Add your description here"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.12"
|
requires-python = ">=3.13"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"appdirs>=1.4.4",
|
"appdirs>=1.4.4",
|
||||||
"beautifulsoup4>=4.12.3",
|
"beautifulsoup4>=4.13.5",
|
||||||
"bump-my-version>=0.29.0",
|
"bump-my-version>=0.29.0",
|
||||||
"chardet>=5.2.0",
|
"charset-normalizer>=3.4.3",
|
||||||
"comtypes>=1.4.9",
|
"comtypes>=1.4.9",
|
||||||
"darkdetect>=0.8.0",
|
"darkdetect>=0.8.0",
|
||||||
"docx2pdf>=0.1.8",
|
"docx2pdf>=0.1.8",
|
||||||
|
"httpx>=0.28.1",
|
||||||
"loguru>=0.7.3",
|
"loguru>=0.7.3",
|
||||||
"mkdocs>=1.6.1",
|
"mkdocs>=1.6.1",
|
||||||
"mkdocs-material>=9.5.49",
|
"mkdocs-material>=9.5.49",
|
||||||
@@ -35,9 +36,12 @@ dev = [
|
|||||||
"icecream>=2.1.4",
|
"icecream>=2.1.4",
|
||||||
"nuitka>=2.5.9",
|
"nuitka>=2.5.9",
|
||||||
]
|
]
|
||||||
|
swbtest = [
|
||||||
|
"alive-progress>=3.3.0",
|
||||||
|
]
|
||||||
|
|
||||||
[tool.bumpversion]
|
[tool.bumpversion]
|
||||||
current_version = "0.2.1"
|
current_version = "1.0.0"
|
||||||
parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)"
|
parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)"
|
||||||
serialize = ["{major}.{minor}.{patch}"]
|
serialize = ["{major}.{minor}.{patch}"]
|
||||||
search = "{current_version}"
|
search = "{current_version}"
|
||||||
@@ -61,3 +65,7 @@ post_commit_hooks = []
|
|||||||
filename = "src/__init__.py"
|
filename = "src/__init__.py"
|
||||||
[[tool.bumpversion.files]]
|
[[tool.bumpversion.files]]
|
||||||
filename = ".version"
|
filename = ".version"
|
||||||
|
|
||||||
|
[[tool.uv.index]]
|
||||||
|
url = "https://git.theprivateserver.de/api/packages/WorldTeacher/pypi/simple/"
|
||||||
|
default = false
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
__version__ = "0.2.1"
|
__version__ = "1.0.0"
|
||||||
__author__ = "Alexander Kirchner"
|
__author__ = "Alexander Kirchner"
|
||||||
__all__ = ["__version__", "__author__", "Icon", "settings"]
|
__all__ = ["__version__", "__author__", "Icon", "settings"]
|
||||||
|
|
||||||
@@ -8,25 +8,24 @@ from appdirs import AppDirs
|
|||||||
|
|
||||||
from config import Config
|
from config import Config
|
||||||
|
|
||||||
|
|
||||||
app = AppDirs("SemesterApparatsManager", "SAM")
|
app = AppDirs("SemesterApparatsManager", "SAM")
|
||||||
LOG_DIR = app.user_log_dir
|
LOG_DIR: str = app.user_log_dir # type: ignore
|
||||||
CONFIG_DIR = app.user_config_dir
|
CONFIG_DIR: str = app.user_config_dir # type: ignore
|
||||||
if not os.path.exists(LOG_DIR):
|
if not os.path.exists(LOG_DIR): # type: ignore
|
||||||
os.makedirs(LOG_DIR)
|
os.makedirs(LOG_DIR) # type: ignore
|
||||||
if not os.path.exists(CONFIG_DIR):
|
if not os.path.exists(CONFIG_DIR): # type: ignore
|
||||||
os.makedirs(CONFIG_DIR)
|
os.makedirs(CONFIG_DIR) # type: ignore
|
||||||
|
|
||||||
|
|
||||||
settings = Config(f"{CONFIG_DIR}/config.yaml")
|
settings = Config(f"{CONFIG_DIR}/config.yaml")
|
||||||
DATABASE_DIR = (
|
DATABASE_DIR = ( # type: ignore
|
||||||
app.user_config_dir if settings.database.path is None else settings.database.path
|
app.user_config_dir if settings.database.path is None else settings.database.path # type: ignore
|
||||||
)
|
)
|
||||||
if not os.path.exists(DATABASE_DIR):
|
if not os.path.exists(DATABASE_DIR): # type: ignore
|
||||||
os.makedirs(DATABASE_DIR)
|
os.makedirs(DATABASE_DIR) # type: ignore
|
||||||
first_launch = settings.exists
|
first_launch = settings.exists
|
||||||
if not os.path.exists(settings.database.temp.expanduser()):
|
if not os.path.exists(settings.database.temp.expanduser()): # type: ignore
|
||||||
settings.database.temp.expanduser().mkdir(parents=True, exist_ok=True)
|
settings.database.temp.expanduser().mkdir(parents=True, exist_ok=True) # type: ignore
|
||||||
from .utils.icon import Icon
|
from .utils.icon import Icon
|
||||||
|
|
||||||
if not os.path.exists("logs"):
|
if not os.path.exists("logs"):
|
||||||
|
|||||||
@@ -1,8 +1,24 @@
|
|||||||
from .semester import Semester
|
__all__ = [
|
||||||
from .database import Database
|
"AdminCommands",
|
||||||
|
"Semester",
|
||||||
|
"AutoAdder",
|
||||||
|
"AvailChecker",
|
||||||
|
"BookGrabber",
|
||||||
|
"Database",
|
||||||
|
"DocumentationThread",
|
||||||
|
"NewEditionCheckerThread",
|
||||||
|
"recreateElsaFile",
|
||||||
|
"recreateFile",
|
||||||
|
"Catalogue"
|
||||||
|
]
|
||||||
|
|
||||||
from .admin_console import AdminCommands
|
from .admin_console import AdminCommands
|
||||||
from .thread_bookgrabber import BookGrabber
|
from .create_file import recreateElsaFile, recreateFile
|
||||||
from .threads_availchecker import AvailChecker
|
from .database import Database
|
||||||
from .threads_autoadder import AutoAdder
|
|
||||||
from .documentation_thread import DocumentationThread
|
from .documentation_thread import DocumentationThread
|
||||||
from .create_file import recreateFile, recreateElsaFile
|
from .semester import Semester
|
||||||
|
from .thread_bookgrabber import BookGrabber
|
||||||
|
from .thread_neweditions import NewEditionCheckerThread
|
||||||
|
from .threads_autoadder import AutoAdder
|
||||||
|
from .threads_availchecker import AvailChecker
|
||||||
|
from .catalogue import Catalogue
|
||||||
|
|||||||
101
src/backend/catalogue.py
Normal file
101
src/backend/catalogue.py
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
import requests
|
||||||
|
from bs4 import BeautifulSoup
|
||||||
|
|
||||||
|
from src.logic import BookData as Book
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
import sys
|
||||||
|
import loguru
|
||||||
|
from src import LOG_DIR
|
||||||
|
URL = "https://rds.ibs-bw.de/phfreiburg/opac/RDSIndex/Search?type0%5B%5D=allfields&lookfor0%5B%5D={}&join=AND&bool0%5B%5D=AND&type0%5B%5D=au&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=ti&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=ct&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=isn&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=ta&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=co&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=py&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=pp&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=pu&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=si&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=zr&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=cc&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND"
|
||||||
|
BASE = "https://rds.ibs-bw.de"
|
||||||
|
|
||||||
|
log = loguru.logger
|
||||||
|
log.remove()
|
||||||
|
log.add(sys.stdout, level="INFO")
|
||||||
|
log.add(f"{LOG_DIR}/application.log", rotation="1 MB", retention="10 days")
|
||||||
|
|
||||||
|
log.add(
|
||||||
|
f"{LOG_DIR}/{datetime.now().strftime('%Y-%m-%d')}.log",
|
||||||
|
rotation="1 day",
|
||||||
|
retention="1 month",
|
||||||
|
)
|
||||||
|
class Catalogue:
|
||||||
|
def __init__(self, timeout=5):
|
||||||
|
self.timeout = timeout
|
||||||
|
reachable = self.check_connection()
|
||||||
|
if not reachable:
|
||||||
|
log.error("No internet connection available.")
|
||||||
|
raise ConnectionError("No internet connection available.")
|
||||||
|
|
||||||
|
def check_connection(self):
|
||||||
|
try:
|
||||||
|
response = requests.get("https://www.google.com", timeout=self.timeout)
|
||||||
|
if response.status_code == 200:
|
||||||
|
return True
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
log.error(f"Could not connect to google.com: {e}")
|
||||||
|
|
||||||
|
def search_book(self, searchterm: str):
|
||||||
|
response = requests.get(URL.format(searchterm), timeout=self.timeout)
|
||||||
|
return response.text
|
||||||
|
|
||||||
|
def search(self, link: str):
|
||||||
|
response = requests.get(link, timeout=self.timeout)
|
||||||
|
return response.text
|
||||||
|
|
||||||
|
def get_book_links(self, searchterm: str):
|
||||||
|
response = self.search_book(searchterm)
|
||||||
|
soup = BeautifulSoup(response, "html.parser")
|
||||||
|
links = soup.find_all("a", class_="title getFull")
|
||||||
|
res = []
|
||||||
|
for link in links:
|
||||||
|
res.append(BASE + link["href"])
|
||||||
|
return res
|
||||||
|
|
||||||
|
def get_book(self, searchterm: str):
|
||||||
|
log.info(f"Searching for term: {searchterm}")
|
||||||
|
|
||||||
|
links = self.get_book_links(searchterm)
|
||||||
|
for link in links:
|
||||||
|
result = self.search(link)
|
||||||
|
# in result search for class col-xs-12 rds-dl RDS_LOCATION
|
||||||
|
# if found, return text of href
|
||||||
|
soup = BeautifulSoup(result, "html.parser")
|
||||||
|
location = soup.find_all("div", class_="col-xs-12 rds-dl RDS_LOCATION")
|
||||||
|
for loc in location:
|
||||||
|
if f"1. OG Semesterapparat" in loc.text:
|
||||||
|
title = (
|
||||||
|
soup.find("div", class_="headline text")
|
||||||
|
.text.replace("\n", "")
|
||||||
|
.strip()
|
||||||
|
)
|
||||||
|
ppn = soup.find(
|
||||||
|
"div", class_="col-xs-12 col-md-5 col-lg-4 rds-dl-head RDS_PPN"
|
||||||
|
)
|
||||||
|
signature = soup.find(
|
||||||
|
"div", class_="col-xs-12 rds-dl RDS_SIGNATURE"
|
||||||
|
)
|
||||||
|
if signature:
|
||||||
|
signature = (
|
||||||
|
signature.find_next("div")
|
||||||
|
.find_next("div")
|
||||||
|
.text.replace("\n", "")
|
||||||
|
.strip()
|
||||||
|
)
|
||||||
|
# use ppn to find the next div and extract the text
|
||||||
|
if ppn:
|
||||||
|
ppn = ppn.find_next("div").text.replace("\n", "").strip()
|
||||||
|
else:
|
||||||
|
ppn = None
|
||||||
|
isbn = soup.find(
|
||||||
|
"div", class_="col-xs-12 col-md-5 col-lg-4 rds-dl-head RDS_ISBN"
|
||||||
|
)
|
||||||
|
if isbn:
|
||||||
|
isbn = isbn.find_next("div").find_next("div").text
|
||||||
|
else:
|
||||||
|
isbn = None
|
||||||
|
return Book(
|
||||||
|
title=title, ppn=ppn, signature=signature, isbn=isbn, link=link
|
||||||
|
)
|
||||||
|
return False
|
||||||
@@ -12,7 +12,7 @@ from typing import Any, List, Optional, Tuple, Union
|
|||||||
|
|
||||||
import loguru
|
import loguru
|
||||||
|
|
||||||
from src import LOG_DIR, settings, DATABASE_DIR
|
from src import DATABASE_DIR, LOG_DIR, settings
|
||||||
from src.backend.db import (
|
from src.backend.db import (
|
||||||
CREATE_ELSA_FILES_TABLE,
|
CREATE_ELSA_FILES_TABLE,
|
||||||
CREATE_ELSA_MEDIA_TABLE,
|
CREATE_ELSA_MEDIA_TABLE,
|
||||||
@@ -38,7 +38,6 @@ log.add(sys.stdout, level="INFO")
|
|||||||
log.add(f"{LOG_DIR}/application.log", rotation="1 MB", retention="10 days")
|
log.add(f"{LOG_DIR}/application.log", rotation="1 MB", retention="10 days")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
ascii_lowercase = lower + digits + punctuation
|
ascii_lowercase = lower + digits + punctuation
|
||||||
|
|
||||||
|
|
||||||
@@ -68,6 +67,57 @@ class Database:
|
|||||||
self.db_path = db_path
|
self.db_path = db_path
|
||||||
log.debug(f"Database path: {self.db_path}")
|
log.debug(f"Database path: {self.db_path}")
|
||||||
self.db_initialized = False
|
self.db_initialized = False
|
||||||
|
self.startup_check()
|
||||||
|
|
||||||
|
def startup_check(self):
|
||||||
|
# check existence of all tables. if any is missing, recreate the table
|
||||||
|
if not self.db_initialized:
|
||||||
|
self.initializeDatabase()
|
||||||
|
tables = self.get_db_contents()
|
||||||
|
tables = [t[1] for t in tables] if tables is not None else []
|
||||||
|
required_tables = [
|
||||||
|
"semesterapparat",
|
||||||
|
"messages",
|
||||||
|
"media",
|
||||||
|
"files",
|
||||||
|
"prof",
|
||||||
|
"user",
|
||||||
|
"subjects",
|
||||||
|
"elsa",
|
||||||
|
"elsa_files",
|
||||||
|
"elsa_media",
|
||||||
|
]
|
||||||
|
|
||||||
|
for table in required_tables:
|
||||||
|
if table not in tables:
|
||||||
|
log.critical(f"Table {table} is missing, recreating...")
|
||||||
|
self.create_table(table)
|
||||||
|
|
||||||
|
def create_table(self, table_name: str):
|
||||||
|
match table_name:
|
||||||
|
case "semesterapparat":
|
||||||
|
query = CREATE_TABLE_APPARAT
|
||||||
|
case "messages":
|
||||||
|
query = CREATE_TABLE_MESSAGES
|
||||||
|
case "media":
|
||||||
|
query = CREATE_TABLE_MEDIA
|
||||||
|
case "files":
|
||||||
|
query = CREATE_TABLE_FILES
|
||||||
|
case "prof":
|
||||||
|
query = CREATE_TABLE_PROF
|
||||||
|
case "user":
|
||||||
|
query = CREATE_TABLE_USER
|
||||||
|
case "subjects":
|
||||||
|
query = CREATE_TABLE_SUBJECTS
|
||||||
|
case "elsa":
|
||||||
|
query = CREATE_ELSA_TABLE
|
||||||
|
case "elsa_files":
|
||||||
|
query = CREATE_ELSA_FILES_TABLE
|
||||||
|
case "elsa_media":
|
||||||
|
query = CREATE_ELSA_MEDIA_TABLE
|
||||||
|
case _:
|
||||||
|
log.error(f"Table {table_name} is not a valid table name")
|
||||||
|
self.query_db(query)
|
||||||
|
|
||||||
def initializeDatabase(self):
|
def initializeDatabase(self):
|
||||||
if not self.db_initialized:
|
if not self.db_initialized:
|
||||||
@@ -201,12 +251,12 @@ class Database:
|
|||||||
logs_query = query
|
logs_query = query
|
||||||
|
|
||||||
logs_args = args
|
logs_args = args
|
||||||
if "fileblob" in query:
|
# if "fileblob" in query:
|
||||||
# set fileblob arg in logger to "too long"
|
# # set fileblob arg in logger to "too long"
|
||||||
logs_query = query
|
# logs_query = query
|
||||||
fileblob_location = query.find("fileblob")
|
# fileblob_location = query.find("fileblob")
|
||||||
# remove fileblob from query
|
# # remove fileblob from query
|
||||||
logs_query = query[:fileblob_location] + "fileblob = too long"
|
# logs_query = query[:fileblob_location] + "fileblob = too long"
|
||||||
|
|
||||||
log_message = f"Querying database with query {logs_query}, args: {logs_args}"
|
log_message = f"Querying database with query {logs_query}, args: {logs_args}"
|
||||||
# if "INSERT" in query:
|
# if "INSERT" in query:
|
||||||
@@ -435,6 +485,7 @@ class Database:
|
|||||||
deleted (int, optional): The state of the book. Set to 1 to include deleted ones. Defaults to 0.
|
deleted (int, optional): The state of the book. Set to 1 to include deleted ones. Defaults to 0.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
|
||||||
list[dict[int, BookData, int]]: A list of dictionaries containing the id, the metadata of the book and the availability of the book
|
list[dict[int, BookData, int]]: A list of dictionaries containing the id, the metadata of the book and the availability of the book
|
||||||
"""
|
"""
|
||||||
qdata = self.query_db(
|
qdata = self.query_db(
|
||||||
@@ -451,6 +502,46 @@ class Database:
|
|||||||
ret_result.append(data)
|
ret_result.append(data)
|
||||||
return ret_result
|
return ret_result
|
||||||
|
|
||||||
|
def getAllBooks(self):
|
||||||
|
# return all books in the database
|
||||||
|
qdata = self.query_db("SELECT id,bookdata FROM media WHERE deleted=0")
|
||||||
|
ret_result: list[dict[str, Any]] = []
|
||||||
|
if qdata is None:
|
||||||
|
return []
|
||||||
|
for result_a in qdata:
|
||||||
|
data: dict[str, Any] = {"id": int, "bookdata": BookData}
|
||||||
|
data["id"] = result_a[0]
|
||||||
|
data["bookdata"] = BookData().from_string(result_a[1])
|
||||||
|
|
||||||
|
ret_result.append(data)
|
||||||
|
return ret_result
|
||||||
|
|
||||||
|
def getBooksByProfId(self, prof_id: int, deleted: int = 0):
|
||||||
|
"""
|
||||||
|
Get the Books based on the professor id
|
||||||
|
|
||||||
|
Args:
|
||||||
|
prof_id (str): The ID of the professor
|
||||||
|
deleted (int, optional): The state of the book. Set to 1 to include deleted ones. Defaults to 0.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
list[dict[int, BookData, int]]: A list of dictionaries containing the id, the metadata of the book and the availability of the book
|
||||||
|
"""
|
||||||
|
qdata = self.query_db(
|
||||||
|
f"SELECT id,bookdata,available FROM media WHERE prof_id={prof_id} AND (deleted={deleted if deleted == 0 else '1 OR deleted=0'})"
|
||||||
|
)
|
||||||
|
ret_result = []
|
||||||
|
if qdata is None:
|
||||||
|
return []
|
||||||
|
for result_a in qdata:
|
||||||
|
data: dict[str, Any] = {"id": int, "bookdata": BookData, "available": int}
|
||||||
|
data["id"] = result_a[0]
|
||||||
|
data["bookdata"] = BookData().from_string(result_a[1])
|
||||||
|
data["available"] = result_a[2]
|
||||||
|
ret_result.append(data)
|
||||||
|
return ret_result
|
||||||
|
|
||||||
def updateBookdata(self, book_id: int, bookdata: BookData):
|
def updateBookdata(self, book_id: int, bookdata: BookData):
|
||||||
"""
|
"""
|
||||||
Update the bookdata in the database
|
Update the bookdata in the database
|
||||||
@@ -525,11 +616,12 @@ class Database:
|
|||||||
str: The filename of the recreated file
|
str: The filename of the recreated file
|
||||||
"""
|
"""
|
||||||
blob = self.getBlob(filename, app_id)
|
blob = self.getBlob(filename, app_id)
|
||||||
|
log.debug(blob)
|
||||||
tempdir = settings.database.temp.expanduser()
|
tempdir = settings.database.temp.expanduser()
|
||||||
if not tempdir.exists():
|
if not tempdir.exists():
|
||||||
tempdir.mkdir(parents=True, exist_ok=True)
|
tempdir.mkdir(parents=True, exist_ok=True)
|
||||||
file = tempfile.NamedTemporaryFile(
|
file = tempfile.NamedTemporaryFile(
|
||||||
delete=False, dir=tempdir_path, mode="wb", suffix=f".{filetype}"
|
delete=False, dir=tempdir, mode="wb", suffix=f".{filetype}"
|
||||||
)
|
)
|
||||||
file.write(blob)
|
file.write(blob)
|
||||||
# log.debug("file created")
|
# log.debug("file created")
|
||||||
@@ -701,6 +793,20 @@ class Database:
|
|||||||
else:
|
else:
|
||||||
return prof[0]
|
return prof[0]
|
||||||
|
|
||||||
|
def getProfMailById(self, prof_id: Union[str, int]) -> str:
|
||||||
|
"""get the mail of a professor based on the id
|
||||||
|
|
||||||
|
Args:
|
||||||
|
prof_id (Union[str,int]): the id of the professor
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: the mail of the professor
|
||||||
|
"""
|
||||||
|
mail = self.query_db("SELECT mail FROM prof WHERE id=?", (prof_id,), one=True)[
|
||||||
|
0
|
||||||
|
]
|
||||||
|
return mail if mail is not None else ""
|
||||||
|
|
||||||
def getTitleById(self, prof_id: Union[str, int]) -> str:
|
def getTitleById(self, prof_id: Union[str, int]) -> str:
|
||||||
"""get the title of a professor based on the id
|
"""get the title of a professor based on the id
|
||||||
|
|
||||||
@@ -877,6 +983,23 @@ class Database:
|
|||||||
(newDate, today, app_id),
|
(newDate, today, app_id),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def getId(self, apparat_name) -> Optional[int]:
|
||||||
|
"""get the id of an apparat based on the name
|
||||||
|
|
||||||
|
Args:
|
||||||
|
apparat_name (str): the name of the apparat e.g. "Semesterapparat 1"
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Optional[int]: the id of the apparat, if the apparat is not found, None is returned
|
||||||
|
"""
|
||||||
|
data = self.query_db(
|
||||||
|
"SELECT id FROM semesterapparat WHERE name=?", (apparat_name,), one=True
|
||||||
|
)
|
||||||
|
if data is None:
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
return data[0]
|
||||||
|
|
||||||
def getApparatId(self, apparat_name) -> Optional[int]:
|
def getApparatId(self, apparat_name) -> Optional[int]:
|
||||||
"""get the id of an apparat based on the name
|
"""get the id of an apparat based on the name
|
||||||
|
|
||||||
@@ -1014,22 +1137,22 @@ class Database:
|
|||||||
self.close_connection(conn)
|
self.close_connection(conn)
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def deleteApparat(self, app_id: Union[str, int], semester):
|
def deleteApparat(self, apparat: Apparat, semester: str):
|
||||||
"""Delete an apparat from the database
|
"""Delete an apparat from the database
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
app_id (Union[str, int]): the id of the apparat
|
apparat: (Apparat): the apparat to be deleted
|
||||||
semester (str): the semester the apparat should be deleted from
|
semester (str): the semester the apparat should be deleted from
|
||||||
"""
|
"""
|
||||||
log.info(f"Deleting apparat with id {app_id} in semester {semester}")
|
apparat_nr = apparat.appnr
|
||||||
|
app_id = self.getId(apparat.name)
|
||||||
self.query_db(
|
self.query_db(
|
||||||
"UPDATE semesterapparat SET deletion_status=1, deleted_date=? WHERE appnr=?",
|
"UPDATE semesterapparat SET deletion_status=1, deleted_date=? WHERE appnr=? AND name=?",
|
||||||
(semester, app_id),
|
(semester, apparat_nr, apparat.name),
|
||||||
)
|
|
||||||
self.query_db(
|
|
||||||
"UPDATE media SET deleted=1 WHERE app_id=?",
|
|
||||||
(app_id,),
|
|
||||||
)
|
)
|
||||||
|
# delete all books associated with the app_id
|
||||||
|
print(apparat_nr, app_id)
|
||||||
|
self.query_db("UPDATE media SET deleted=1 WHERE app_id=?", (app_id,))
|
||||||
|
|
||||||
def isEternal(self, id):
|
def isEternal(self, id):
|
||||||
"""check if the apparat is eternal (dauerapparat)
|
"""check if the apparat is eternal (dauerapparat)
|
||||||
|
|||||||
263
src/backend/thread_neweditions.py
Normal file
263
src/backend/thread_neweditions.py
Normal file
@@ -0,0 +1,263 @@
|
|||||||
|
import re
|
||||||
|
import sys
|
||||||
|
from concurrent.futures import ThreadPoolExecutor
|
||||||
|
from datetime import datetime
|
||||||
|
from math import ceil
|
||||||
|
from queue import Empty, Queue
|
||||||
|
from typing import List, Optional, Set, Union
|
||||||
|
|
||||||
|
import loguru
|
||||||
|
from PySide6.QtCore import QThread, Signal
|
||||||
|
|
||||||
|
from src import LOG_DIR
|
||||||
|
from src.logic import BookData
|
||||||
|
from src.logic.lehmannsapi import LehmannsClient
|
||||||
|
from src.logic.swb import SWB
|
||||||
|
|
||||||
|
log = loguru.logger
|
||||||
|
log.remove()
|
||||||
|
log.add(sys.stdout, level="INFO")
|
||||||
|
log.add(f"{LOG_DIR}/application.log", rotation="1 MB", retention="10 days")
|
||||||
|
|
||||||
|
log.add(
|
||||||
|
f"{LOG_DIR}/{datetime.now().strftime('%Y-%m-%d')}.log",
|
||||||
|
rotation="1 day",
|
||||||
|
retention="1 month",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _norm_text(s: Optional[str]) -> str:
|
||||||
|
if not s:
|
||||||
|
return ""
|
||||||
|
# lowercase, collapse whitespace, drop some punctuation
|
||||||
|
s = s.lower()
|
||||||
|
s = re.sub(r"[\s\-\u2013\u2014]+", " ", s) # spaces/dashes
|
||||||
|
s = re.sub(r"[\"'`:.,;!?()\[\]{}]", "", s)
|
||||||
|
return s.strip()
|
||||||
|
|
||||||
|
|
||||||
|
def _same_book(a: BookData, b: BookData) -> bool:
|
||||||
|
"""Heuristic: same if ISBNs intersect; fallback to (title, author, year) normalized."""
|
||||||
|
isbns_a = _norm_isbns(a.isbn)
|
||||||
|
isbns_b = _norm_isbns(b.isbn)
|
||||||
|
if isbns_a and isbns_b and (isbns_a & isbns_b):
|
||||||
|
return True
|
||||||
|
|
||||||
|
ta, tb = _norm_text(a.title), _norm_text(b.title)
|
||||||
|
aa, ab = _norm_text(a.author), _norm_text(b.author)
|
||||||
|
ya, yb = (a.year or "").strip(), (b.year or "").strip()
|
||||||
|
|
||||||
|
# strong title match required; then author if available; then year if available
|
||||||
|
if ta and tb and ta == tb:
|
||||||
|
# if both have authors, require match
|
||||||
|
if aa and ab and aa == ab:
|
||||||
|
# if both have year, require match
|
||||||
|
if ya and yb:
|
||||||
|
return ya == yb
|
||||||
|
return True
|
||||||
|
# if one/both authors missing, allow title (+year if both present)
|
||||||
|
if ya and yb:
|
||||||
|
return ya == yb
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def _norm_isbns(value: Union[str, List[str], None]) -> Set[str]:
|
||||||
|
"""Return a set of 10/13-digit ISBNs (digits only, keep X for ISBN-10 if present)."""
|
||||||
|
if value is None:
|
||||||
|
return set()
|
||||||
|
vals = value if isinstance(value, list) else [value]
|
||||||
|
out: Set[str] = set()
|
||||||
|
for v in vals:
|
||||||
|
s = str(v)
|
||||||
|
digits = re.sub(r"[^0-9Xx]", "", s)
|
||||||
|
# keep 13-digit or 10-digit tokens
|
||||||
|
m13 = re.findall(r"97[89]\d{10}", digits)
|
||||||
|
if m13:
|
||||||
|
out.update(m13)
|
||||||
|
else:
|
||||||
|
m10 = re.findall(r"\d{9}[0-9Xx]", digits)
|
||||||
|
out.update(x.upper() for x in m10)
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def filter_prefer_swb(records: List[BookData]) -> List[BookData]:
|
||||||
|
"""
|
||||||
|
If an SWB entry with a non-empty signature exists for a book, drop the HTTP(S) duplicate(s).
|
||||||
|
Returns a NEW list (does not mutate the input).
|
||||||
|
"""
|
||||||
|
swb_with_sig = [
|
||||||
|
r
|
||||||
|
for r in records
|
||||||
|
if (r.link == "SWB") and (r.signature and r.signature.strip())
|
||||||
|
]
|
||||||
|
if not swb_with_sig:
|
||||||
|
return list(records)
|
||||||
|
|
||||||
|
to_remove: Set[int] = set()
|
||||||
|
|
||||||
|
# For each URL entry, see if it matches any SWB-with-signature entry
|
||||||
|
for idx, rec in enumerate(records):
|
||||||
|
if not rec.link or not rec.link.lower().startswith("http"):
|
||||||
|
continue
|
||||||
|
for swb in swb_with_sig:
|
||||||
|
if _same_book(swb, rec):
|
||||||
|
to_remove.add(idx)
|
||||||
|
break
|
||||||
|
|
||||||
|
# Build filtered list
|
||||||
|
return [rec for i, rec in enumerate(records) if i not in to_remove]
|
||||||
|
|
||||||
|
|
||||||
|
class NewEditionCheckerThread(QThread):
|
||||||
|
updateSignal = Signal(int, int) # (processed, total)
|
||||||
|
updateProgress = Signal(int, int) # (processed, total)
|
||||||
|
total_entries_signal = Signal(int)
|
||||||
|
resultsSignal = Signal(list) # list[tuple[BookData, list[BookData]]]
|
||||||
|
|
||||||
|
def __init__(self, entries: Optional[list["BookData"]] = None, parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
self.entries: list["BookData"] = entries if entries is not None else []
|
||||||
|
self.results: list[tuple["BookData", list["BookData"]]] = []
|
||||||
|
|
||||||
|
def reset(self):
|
||||||
|
self.entries = []
|
||||||
|
self.results = []
|
||||||
|
|
||||||
|
# ---------- internal helpers ----------
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _split_evenly(items: list, parts: int) -> list[list]:
|
||||||
|
"""Split items as evenly as possible into `parts` chunks (no empty tails)."""
|
||||||
|
if parts <= 1 or len(items) <= 1:
|
||||||
|
return [items]
|
||||||
|
n = len(items)
|
||||||
|
base = n // parts
|
||||||
|
extra = n % parts
|
||||||
|
chunks = []
|
||||||
|
i = 0
|
||||||
|
for k in range(parts):
|
||||||
|
size = base + (1 if k < extra else 0)
|
||||||
|
if size == 0:
|
||||||
|
continue
|
||||||
|
chunks.append(items[i : i + size])
|
||||||
|
i += size
|
||||||
|
return chunks
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _clean_title(raw: str) -> str:
|
||||||
|
title = raw.rstrip(" .:,;!?")
|
||||||
|
title = re.sub(r"\s*\(.*\)", "", title)
|
||||||
|
return title.strip()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _process_book(
|
||||||
|
cls, book: "BookData"
|
||||||
|
) -> tuple["BookData", list["BookData"]] | None:
|
||||||
|
author = (
|
||||||
|
book.author.split(";")[0].replace(" ", "")
|
||||||
|
if (book.author and ";" in book.author)
|
||||||
|
else (book.author or "").replace(" ", "")
|
||||||
|
)
|
||||||
|
title = cls._clean_title(book.title or "")
|
||||||
|
|
||||||
|
# Query SWB
|
||||||
|
response: list[BookData] = SWB().getBooks(
|
||||||
|
[
|
||||||
|
"pica.bib=20735",
|
||||||
|
f"pica.tit={title.split(':')[0].strip()}",
|
||||||
|
# f"pica.per={author}",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
# Remove same PPN
|
||||||
|
response = [entry for entry in response if entry.ppn != book.ppn]
|
||||||
|
for respo in response:
|
||||||
|
respo.link = "SWB"
|
||||||
|
|
||||||
|
# Query Lehmanns
|
||||||
|
with LehmannsClient() as client:
|
||||||
|
results = client.search_by_title(title, strict=True)
|
||||||
|
if results:
|
||||||
|
for res in results:
|
||||||
|
response.append(BookData().from_LehmannsSearchResult(res))
|
||||||
|
|
||||||
|
if not response:
|
||||||
|
return None
|
||||||
|
|
||||||
|
response = filter_prefer_swb(response)
|
||||||
|
|
||||||
|
# Remove entries matching the same ISBN as the current book
|
||||||
|
response = [
|
||||||
|
entry
|
||||||
|
for entry in response
|
||||||
|
if not (_norm_isbns(entry.isbn) & _norm_isbns(book.isbn))
|
||||||
|
]
|
||||||
|
|
||||||
|
if not response:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return (book, response)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _worker(cls, items: list["BookData"], q: Queue) -> None:
|
||||||
|
"""Worker for one chunk; pushes ('result', ...), ('progress', 1), and ('done', None)."""
|
||||||
|
try:
|
||||||
|
for book in items:
|
||||||
|
try:
|
||||||
|
result = cls._process_book(book)
|
||||||
|
except Exception:
|
||||||
|
result = None
|
||||||
|
if result is not None:
|
||||||
|
q.put(("result", result))
|
||||||
|
q.put(("progress", 1))
|
||||||
|
finally:
|
||||||
|
q.put(("done", None))
|
||||||
|
|
||||||
|
# ---------- thread entry point ----------
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
total = len(self.entries)
|
||||||
|
self.total_entries_signal.emit(total)
|
||||||
|
|
||||||
|
if total == 0:
|
||||||
|
log.debug("No entries to process.")
|
||||||
|
self.resultsSignal.emit([])
|
||||||
|
return
|
||||||
|
|
||||||
|
# Up to 4 workers; ~20 items per worker
|
||||||
|
num_workers = min(4, max(1, ceil(total / 20)))
|
||||||
|
chunks = self._split_evenly(self.entries, num_workers)
|
||||||
|
sizes = [len(ch) for ch in chunks]
|
||||||
|
|
||||||
|
q: Queue = Queue()
|
||||||
|
processed = 0
|
||||||
|
finished_workers = 0
|
||||||
|
|
||||||
|
with ThreadPoolExecutor(max_workers=len(chunks)) as ex:
|
||||||
|
futures = [ex.submit(self._worker, ch, q) for ch in chunks]
|
||||||
|
|
||||||
|
log.info(
|
||||||
|
f"Launched {len(futures)} worker thread(s) for {total} entries: {sizes} entries per thread."
|
||||||
|
)
|
||||||
|
for idx, sz in enumerate(sizes, 1):
|
||||||
|
log.debug(f"Thread {idx}: {sz} entries")
|
||||||
|
|
||||||
|
# Aggregate progress/results
|
||||||
|
while finished_workers < len(chunks):
|
||||||
|
try:
|
||||||
|
kind, payload = q.get(timeout=0.1)
|
||||||
|
except Empty:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if kind == "progress":
|
||||||
|
processed += int(payload)
|
||||||
|
self.updateSignal.emit(processed, total)
|
||||||
|
self.updateProgress.emit(processed, total)
|
||||||
|
elif kind == "result":
|
||||||
|
self.results.append(payload)
|
||||||
|
elif kind == "done":
|
||||||
|
finished_workers += 1
|
||||||
|
|
||||||
|
self.resultsSignal.emit(self.results)
|
||||||
@@ -56,6 +56,9 @@ class AvailChecker(QThread):
|
|||||||
rds = transformer.get_data(data).return_data("rds_availability")
|
rds = transformer.get_data(data).return_data("rds_availability")
|
||||||
|
|
||||||
book_id = None
|
book_id = None
|
||||||
|
if not rds or not rds.items:
|
||||||
|
log.warning(f"No RDS data found for link {link}")
|
||||||
|
continue
|
||||||
for item in rds.items:
|
for item in rds.items:
|
||||||
sign = item.superlocation
|
sign = item.superlocation
|
||||||
loc = item.location
|
loc = item.location
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
import csv
|
import csv
|
||||||
|
from charset_normalizer import detect
|
||||||
import chardet
|
|
||||||
|
|
||||||
|
|
||||||
def csv_to_list(path: str) -> list[str]:
|
def csv_to_list(path: str) -> list[str]:
|
||||||
"""
|
"""
|
||||||
Extracts the data from a csv file and returns it as a pandas dataframe
|
Extracts the data from a csv file and returns it as a pandas dataframe
|
||||||
"""
|
"""
|
||||||
encoding = chardet.detect(open(path, "rb").read())["encoding"]
|
encoding = detect(open(path, "rb").read())["encoding"]
|
||||||
with open(path, newline="", encoding=encoding) as csvfile:
|
with open(path, newline="", encoding=encoding) as csvfile:
|
||||||
# if decoder fails to map, assign ""
|
# if decoder fails to map, assign ""
|
||||||
reader = csv.reader(csvfile, delimiter=";", quotechar="|")
|
reader = csv.reader(csvfile, delimiter=";", quotechar="|")
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
from dataclasses import dataclass, field
|
|
||||||
|
|
||||||
from enum import Enum
|
|
||||||
import json
|
import json
|
||||||
from typing import Union, Any, Optional
|
from dataclasses import dataclass, field
|
||||||
|
from enum import Enum
|
||||||
|
from typing import Any, Optional, Union
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Prof:
|
class Prof:
|
||||||
@@ -93,6 +93,24 @@ class BookData:
|
|||||||
ndata = json.loads(data)
|
ndata = json.loads(data)
|
||||||
return BookData(**ndata)
|
return BookData(**ndata)
|
||||||
|
|
||||||
|
def from_LehmannsSearchResult(self, result: Any) -> "BookData":
|
||||||
|
self.title = result.title
|
||||||
|
self.author = "; ".join(result.authors) if result.authors else None
|
||||||
|
self.edition = str(result.edition) if result.edition else None
|
||||||
|
self.link = result.url
|
||||||
|
self.isbn = (
|
||||||
|
result.isbn13
|
||||||
|
if isinstance(result.isbn13, list)
|
||||||
|
else [result.isbn13]
|
||||||
|
if result.isbn13
|
||||||
|
else []
|
||||||
|
)
|
||||||
|
self.pages = str(result.pages) if result.pages else None
|
||||||
|
self.publisher = result.publisher
|
||||||
|
self.year = str(result.year) if result.year else None
|
||||||
|
# self.pages = str(result.pages) if result.pages else None
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class MailData:
|
class MailData:
|
||||||
|
|||||||
280
src/logic/lehmannsapi.py
Normal file
280
src/logic/lehmannsapi.py
Normal file
@@ -0,0 +1,280 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import re
|
||||||
|
from dataclasses import dataclass, asdict, field
|
||||||
|
from typing import Optional, List, Iterable
|
||||||
|
from urllib.parse import urljoin, quote_plus
|
||||||
|
|
||||||
|
import httpx
|
||||||
|
from bs4 import BeautifulSoup
|
||||||
|
|
||||||
|
BASE = "https://www.lehmanns.de"
|
||||||
|
SEARCH_URL = "https://www.lehmanns.de/search/quick?mediatype_id=&q="
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class LehmannsSearchResult:
|
||||||
|
title: str
|
||||||
|
url: str
|
||||||
|
|
||||||
|
# Core fields from the listing card
|
||||||
|
year: Optional[int] = None
|
||||||
|
edition: Optional[int] = None
|
||||||
|
publisher: Optional[str] = None
|
||||||
|
isbn13: Optional[str] = None
|
||||||
|
|
||||||
|
# Extras from the listing card
|
||||||
|
description: Optional[str] = None
|
||||||
|
authors: list[str] = field(default_factory=list)
|
||||||
|
media_type: Optional[str] = None
|
||||||
|
book_format: Optional[str] = None
|
||||||
|
price_eur: Optional[float] = None
|
||||||
|
currency: str = "EUR"
|
||||||
|
image: Optional[str] = None
|
||||||
|
|
||||||
|
# From detail page:
|
||||||
|
pages: Optional[str] = None # "<N> Seiten"
|
||||||
|
buyable: bool = True # set in enrich_pages (detail page)
|
||||||
|
unavailable_hint: Optional[str] = None # e.g. "Titel ist leider vergriffen; keine Neuauflage"
|
||||||
|
|
||||||
|
def to_dict(self) -> dict:
|
||||||
|
return asdict(self)
|
||||||
|
|
||||||
|
|
||||||
|
class LehmannsClient:
|
||||||
|
"""Scrapes quick-search results, then enriches (and filters) via product pages."""
|
||||||
|
|
||||||
|
def __init__(self, timeout: float = 20.0):
|
||||||
|
self.client = httpx.Client(
|
||||||
|
headers={
|
||||||
|
"User-Agent": (
|
||||||
|
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 "
|
||||||
|
"(KHTML, like Gecko) Chrome/124.0 Safari/537.36"
|
||||||
|
),
|
||||||
|
"Accept-Language": "de-DE,de;q=0.9,en;q=0.8",
|
||||||
|
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
|
||||||
|
},
|
||||||
|
timeout=timeout,
|
||||||
|
follow_redirects=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
self.client.close()
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __exit__(self, *exc):
|
||||||
|
self.close()
|
||||||
|
|
||||||
|
# ------------------- Search (listing) -------------------
|
||||||
|
|
||||||
|
def build_search_url(self, title: str) -> str:
|
||||||
|
# spaces -> '+'
|
||||||
|
return SEARCH_URL + quote_plus(title)
|
||||||
|
|
||||||
|
def search_by_title(self, title: str, limit: Optional[int] = None, strict: bool = False) -> List[LehmannsSearchResult]:
|
||||||
|
"""
|
||||||
|
Parse the listing page only (no availability check here).
|
||||||
|
Use enrich_pages(...) afterwards to fetch detail pages, add 'pages',
|
||||||
|
and drop unbuyable items.
|
||||||
|
"""
|
||||||
|
url = self.build_search_url(title)
|
||||||
|
html = self._get(url)
|
||||||
|
if not html:
|
||||||
|
return []
|
||||||
|
results = self._parse_results(html)
|
||||||
|
self.enrich_pages(results)
|
||||||
|
if strict:
|
||||||
|
# filter results to only those with exact title match (case-insensitive)
|
||||||
|
title_lower = title.lower()
|
||||||
|
results = [r for r in results if r.title and r.title.lower() == title_lower]
|
||||||
|
results = [r for r in results if r.buyable]
|
||||||
|
return results
|
||||||
|
if limit is not None:
|
||||||
|
results = results[:max(0, limit)]
|
||||||
|
return results
|
||||||
|
|
||||||
|
# ------------------- Detail enrichment & filtering -------------------
|
||||||
|
|
||||||
|
def enrich_pages(self, results: Iterable[LehmannsSearchResult], drop_unbuyable: bool = True) -> List[LehmannsSearchResult]:
|
||||||
|
"""
|
||||||
|
Fetch each result.url, extract:
|
||||||
|
- pages: from <span class="book-meta meta-seiten" itemprop="numberOfPages">...</span>
|
||||||
|
- availability: from <li class="availability-3">...</li>
|
||||||
|
* if it contains "Titel ist leider vergriffen", mark buyable=False
|
||||||
|
* if it also contains "keine Neuauflage", set unavailable_hint accordingly
|
||||||
|
If drop_unbuyable=True, exclude non-buyable results from the returned list.
|
||||||
|
"""
|
||||||
|
enriched: List[LehmannsSearchResult] = []
|
||||||
|
for r in results:
|
||||||
|
try:
|
||||||
|
html = self._get(r.url)
|
||||||
|
if not html:
|
||||||
|
# Can't verify; keep as-is when not dropping, else skip
|
||||||
|
if not drop_unbuyable:
|
||||||
|
enriched.append(r)
|
||||||
|
continue
|
||||||
|
|
||||||
|
soup = BeautifulSoup(html, "html.parser")
|
||||||
|
|
||||||
|
# Pages
|
||||||
|
pages_node = soup.select_one(
|
||||||
|
"span.book-meta.meta-seiten[itemprop='numberOfPages'], "
|
||||||
|
"span.book-meta.meta-seiten[itemprop='numberofpages'], "
|
||||||
|
".meta-seiten [itemprop='numberOfPages'], "
|
||||||
|
".meta-seiten[itemprop='numberOfPages'], "
|
||||||
|
".book-meta.meta-seiten"
|
||||||
|
)
|
||||||
|
if pages_node:
|
||||||
|
text = pages_node.get_text(" ", strip=True)
|
||||||
|
m = re.search(r"\d+", text)
|
||||||
|
if m:
|
||||||
|
r.pages = f"{m.group(0)} Seiten"
|
||||||
|
|
||||||
|
# Availability via li.availability-3
|
||||||
|
avail_li = soup.select_one("li.availability-3")
|
||||||
|
if avail_li:
|
||||||
|
avail_text = " ".join(avail_li.get_text(" ", strip=True).split()).lower()
|
||||||
|
if "titel ist leider vergriffen" in avail_text:
|
||||||
|
r.buyable = False
|
||||||
|
if "keine neuauflage" in avail_text:
|
||||||
|
r.unavailable_hint = "Titel ist leider vergriffen; keine Neuauflage"
|
||||||
|
else:
|
||||||
|
r.unavailable_hint = "Titel ist leider vergriffen"
|
||||||
|
|
||||||
|
# Append or drop
|
||||||
|
if (not drop_unbuyable) or r.buyable:
|
||||||
|
enriched.append(r)
|
||||||
|
|
||||||
|
except Exception:
|
||||||
|
# On any per-item error, keep the record if not dropping; else skip
|
||||||
|
if not drop_unbuyable:
|
||||||
|
enriched.append(r)
|
||||||
|
continue
|
||||||
|
|
||||||
|
return enriched
|
||||||
|
|
||||||
|
# ------------------- Internals -------------------
|
||||||
|
|
||||||
|
def _get(self, url: str) -> Optional[str]:
|
||||||
|
try:
|
||||||
|
r = self.client.get(url)
|
||||||
|
r.encoding = "utf-8"
|
||||||
|
if r.status_code == 200 and "text/html" in (r.headers.get("content-type") or ""):
|
||||||
|
return r.text
|
||||||
|
except httpx.HTTPError:
|
||||||
|
pass
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _parse_results(self, html: str) -> List[LehmannsSearchResult]:
|
||||||
|
soup = BeautifulSoup(html, "html.parser")
|
||||||
|
results: list[LehmannsSearchResult] = []
|
||||||
|
|
||||||
|
for block in soup.select("div.info-block"):
|
||||||
|
a = block.select_one(".title a[href]")
|
||||||
|
if not a:
|
||||||
|
continue
|
||||||
|
url = urljoin(BASE, a["href"].strip())
|
||||||
|
base_title = (block.select_one(".title [itemprop='name']") or a).get_text(strip=True)
|
||||||
|
|
||||||
|
# Alternative headline => extend title
|
||||||
|
alt_tag = block.select_one(".description[itemprop='alternativeHeadline']")
|
||||||
|
alternative_headline = alt_tag.get_text(strip=True) if alt_tag else None
|
||||||
|
title = f"{base_title} : {alternative_headline}" if alternative_headline else base_title
|
||||||
|
description = alternative_headline
|
||||||
|
|
||||||
|
# Authors from .author
|
||||||
|
authors: list[str] = []
|
||||||
|
author_div = block.select_one("div.author")
|
||||||
|
if author_div:
|
||||||
|
t = author_div.get_text(" ", strip=True)
|
||||||
|
t = re.sub(r"^\s*von\s+", "", t, flags=re.I)
|
||||||
|
for part in re.split(r"\s*;\s*|\s*&\s*|\s+und\s+", t):
|
||||||
|
name = " ".join(part.split())
|
||||||
|
if name:
|
||||||
|
authors.append(name)
|
||||||
|
|
||||||
|
# Media + format
|
||||||
|
media_type = None
|
||||||
|
book_format = None
|
||||||
|
type_text = block.select_one(".type")
|
||||||
|
if type_text:
|
||||||
|
t = type_text.get_text(" ", strip=True)
|
||||||
|
m = re.search(r"\b(Buch|eBook|Hörbuch)\b", t)
|
||||||
|
if m:
|
||||||
|
media_type = m.group(1)
|
||||||
|
fm = re.search(r"\(([^)]+)\)", t)
|
||||||
|
if fm:
|
||||||
|
book_format = fm.group(1).strip().upper()
|
||||||
|
|
||||||
|
# Year
|
||||||
|
year = None
|
||||||
|
y = block.select_one("[itemprop='copyrightYear']")
|
||||||
|
if y:
|
||||||
|
try:
|
||||||
|
year = int(y.get_text(strip=True))
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Edition
|
||||||
|
edition = None
|
||||||
|
ed = block.select_one("[itemprop='bookEdition']")
|
||||||
|
if ed:
|
||||||
|
m = re.search(r"\d+", ed.get_text(strip=True))
|
||||||
|
if m:
|
||||||
|
edition = int(m.group())
|
||||||
|
|
||||||
|
# Publisher
|
||||||
|
publisher = None
|
||||||
|
pub = block.select_one(".publisherprop [itemprop='name']") or block.select_one(".publisher [itemprop='name']")
|
||||||
|
if pub:
|
||||||
|
publisher = pub.get_text(strip=True)
|
||||||
|
|
||||||
|
# ISBN-13
|
||||||
|
isbn13 = None
|
||||||
|
isbn_tag = block.select_one(".isbn [itemprop='isbn'], [itemprop='isbn']")
|
||||||
|
if isbn_tag:
|
||||||
|
digits = re.sub(r"[^0-9Xx]", "", isbn_tag.get_text(strip=True))
|
||||||
|
m = re.search(r"(97[89]\d{10})", digits)
|
||||||
|
if m:
|
||||||
|
isbn13 = m.group(1)
|
||||||
|
|
||||||
|
# Price (best effort)
|
||||||
|
price_eur = None
|
||||||
|
txt = block.get_text(" ", strip=True)
|
||||||
|
mprice = re.search(r"(\d{1,3}(?:\.\d{3})*,\d{2})\s*€", txt)
|
||||||
|
if not mprice and block.parent:
|
||||||
|
sib = block.parent.get_text(" ", strip=True)
|
||||||
|
mprice = re.search(r"(\d{1,3}(?:\.\d{3})*,\d{2})\s*€", sib)
|
||||||
|
if mprice:
|
||||||
|
num = mprice.group(1).replace(".", "").replace(",", ".")
|
||||||
|
try:
|
||||||
|
price_eur = float(num)
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Image (best-effort)
|
||||||
|
image = None
|
||||||
|
left_img = block.find_previous("img")
|
||||||
|
if left_img and left_img.get("src"):
|
||||||
|
image = urljoin(BASE, left_img["src"])
|
||||||
|
|
||||||
|
results.append(
|
||||||
|
LehmannsSearchResult(
|
||||||
|
title=title,
|
||||||
|
url=url,
|
||||||
|
description=description,
|
||||||
|
authors=authors,
|
||||||
|
media_type=media_type,
|
||||||
|
book_format=book_format,
|
||||||
|
year=year,
|
||||||
|
edition=edition,
|
||||||
|
publisher=publisher,
|
||||||
|
isbn13=isbn13,
|
||||||
|
price_eur=price_eur,
|
||||||
|
image=image,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return results
|
||||||
448
src/logic/swb.py
Normal file
448
src/logic/swb.py
Normal file
@@ -0,0 +1,448 @@
|
|||||||
|
import xml.etree.ElementTree as ET
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
from typing import Dict, Iterable, List, Optional, Tuple
|
||||||
|
|
||||||
|
import requests
|
||||||
|
|
||||||
|
from src.logic.dataclass import BookData
|
||||||
|
|
||||||
|
# -----------------------
|
||||||
|
# Dataclasses
|
||||||
|
# -----------------------
|
||||||
|
|
||||||
|
|
||||||
|
# --- MARC XML structures ---
|
||||||
|
@dataclass
|
||||||
|
class ControlField:
|
||||||
|
tag: str
|
||||||
|
value: str
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class SubField:
|
||||||
|
code: str
|
||||||
|
value: str
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class DataField:
|
||||||
|
tag: str
|
||||||
|
ind1: str = " "
|
||||||
|
ind2: str = " "
|
||||||
|
subfields: List[SubField] = field(default_factory=list)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class MarcRecord:
|
||||||
|
leader: str
|
||||||
|
controlfields: List[ControlField] = field(default_factory=list)
|
||||||
|
datafields: List[DataField] = field(default_factory=list)
|
||||||
|
|
||||||
|
|
||||||
|
# --- SRU record wrapper ---
|
||||||
|
@dataclass
|
||||||
|
class Record:
|
||||||
|
recordSchema: str
|
||||||
|
recordPacking: str
|
||||||
|
recordData: MarcRecord
|
||||||
|
recordPosition: int
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class EchoedSearchRequest:
|
||||||
|
version: str
|
||||||
|
query: str
|
||||||
|
maximumRecords: int
|
||||||
|
recordPacking: str
|
||||||
|
recordSchema: str
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class SearchRetrieveResponse:
|
||||||
|
version: str
|
||||||
|
numberOfRecords: int
|
||||||
|
records: List[Record] = field(default_factory=list)
|
||||||
|
echoedSearchRetrieveRequest: Optional[EchoedSearchRequest] = None
|
||||||
|
|
||||||
|
|
||||||
|
# -----------------------
|
||||||
|
# Parser
|
||||||
|
# -----------------------
|
||||||
|
|
||||||
|
ZS = "http://www.loc.gov/zing/srw/"
|
||||||
|
MARC = "http://www.loc.gov/MARC21/slim"
|
||||||
|
NS = {"zs": ZS, "marc": MARC}
|
||||||
|
|
||||||
|
|
||||||
|
def _text(elem: Optional[ET.Element]) -> str:
|
||||||
|
return (elem.text or "") if elem is not None else ""
|
||||||
|
|
||||||
|
|
||||||
|
def _req_text(parent: ET.Element, path: str) -> str:
|
||||||
|
el = parent.find(path, NS)
|
||||||
|
if el is None or el.text is None:
|
||||||
|
raise ValueError(f"Required element not found or empty: {path}")
|
||||||
|
return el.text
|
||||||
|
|
||||||
|
|
||||||
|
def parse_marc_record(record_el: ET.Element) -> MarcRecord:
|
||||||
|
"""
|
||||||
|
record_el is the <marc:record> element (default ns MARC in your sample)
|
||||||
|
"""
|
||||||
|
# leader
|
||||||
|
leader_text = _req_text(record_el, "marc:leader")
|
||||||
|
|
||||||
|
# controlfields
|
||||||
|
controlfields: List[ControlField] = []
|
||||||
|
for cf in record_el.findall("marc:controlfield", NS):
|
||||||
|
tag = cf.get("tag", "").strip()
|
||||||
|
controlfields.append(ControlField(tag=tag, value=_text(cf)))
|
||||||
|
|
||||||
|
# datafields
|
||||||
|
datafields: List[DataField] = []
|
||||||
|
for df in record_el.findall("marc:datafield", NS):
|
||||||
|
tag = df.get("tag", "").strip()
|
||||||
|
ind1 = df.get("ind1") or " "
|
||||||
|
ind2 = df.get("ind2") or " "
|
||||||
|
subfields: List[SubField] = []
|
||||||
|
for sf in df.findall("marc:subfield", NS):
|
||||||
|
code = sf.get("code", "")
|
||||||
|
subfields.append(SubField(code=code, value=_text(sf)))
|
||||||
|
datafields.append(DataField(tag=tag, ind1=ind1, ind2=ind2, subfields=subfields))
|
||||||
|
|
||||||
|
return MarcRecord(
|
||||||
|
leader=leader_text, controlfields=controlfields, datafields=datafields
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def parse_record(zs_record_el: ET.Element) -> Record:
|
||||||
|
recordSchema = _req_text(zs_record_el, "zs:recordSchema")
|
||||||
|
recordPacking = _req_text(zs_record_el, "zs:recordPacking")
|
||||||
|
|
||||||
|
# recordData contains a MARC <record> with default MARC namespace in your sample
|
||||||
|
recordData_el = zs_record_el.find("zs:recordData", NS)
|
||||||
|
if recordData_el is None:
|
||||||
|
raise ValueError("Missing zs:recordData")
|
||||||
|
|
||||||
|
marc_record_el = recordData_el.find("marc:record", NS)
|
||||||
|
if marc_record_el is None:
|
||||||
|
# If the MARC record uses default ns (xmlns="...") ElementTree still needs the ns-qualified name
|
||||||
|
# We already searched with prefix; this covers both default and prefixed cases.
|
||||||
|
raise ValueError("Missing MARC21 record inside zs:recordData")
|
||||||
|
|
||||||
|
marc_record = parse_marc_record(marc_record_el)
|
||||||
|
|
||||||
|
recordPosition = int(_req_text(zs_record_el, "zs:recordPosition"))
|
||||||
|
return Record(
|
||||||
|
recordSchema=recordSchema,
|
||||||
|
recordPacking=recordPacking,
|
||||||
|
recordData=marc_record,
|
||||||
|
recordPosition=recordPosition,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def parse_echoed_request(root: ET.Element) -> Optional[EchoedSearchRequest]:
|
||||||
|
el = root.find("zs:echoedSearchRetrieveRequest", NS)
|
||||||
|
if el is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Be permissive with missing fields
|
||||||
|
version = _text(el.find("zs:version", NS))
|
||||||
|
query = _text(el.find("zs:query", NS))
|
||||||
|
maximumRecords_text = _text(el.find("zs:maximumRecords", NS)) or "0"
|
||||||
|
recordPacking = _text(el.find("zs:recordPacking", NS))
|
||||||
|
recordSchema = _text(el.find("zs:recordSchema", NS))
|
||||||
|
|
||||||
|
try:
|
||||||
|
maximumRecords = int(maximumRecords_text)
|
||||||
|
except ValueError:
|
||||||
|
maximumRecords = 0
|
||||||
|
|
||||||
|
return EchoedSearchRequest(
|
||||||
|
version=version,
|
||||||
|
query=query,
|
||||||
|
maximumRecords=maximumRecords,
|
||||||
|
recordPacking=recordPacking,
|
||||||
|
recordSchema=recordSchema,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def parse_search_retrieve_response(xml_str: str) -> SearchRetrieveResponse:
|
||||||
|
root = ET.fromstring(xml_str)
|
||||||
|
|
||||||
|
# Root is zs:searchRetrieveResponse
|
||||||
|
version = _req_text(root, "zs:version")
|
||||||
|
numberOfRecords = int(_req_text(root, "zs:numberOfRecords"))
|
||||||
|
|
||||||
|
records_parent = root.find("zs:records", NS)
|
||||||
|
records: List[Record] = []
|
||||||
|
if records_parent is not None:
|
||||||
|
for r in records_parent.findall("zs:record", NS):
|
||||||
|
records.append(parse_record(r))
|
||||||
|
|
||||||
|
echoed = parse_echoed_request(root)
|
||||||
|
|
||||||
|
return SearchRetrieveResponse(
|
||||||
|
version=version,
|
||||||
|
numberOfRecords=numberOfRecords,
|
||||||
|
records=records,
|
||||||
|
echoedSearchRetrieveRequest=echoed,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# --- Query helpers over MarcRecord ---
|
||||||
|
|
||||||
|
|
||||||
|
def iter_datafields(
|
||||||
|
rec: MarcRecord,
|
||||||
|
tag: Optional[str] = None,
|
||||||
|
ind1: Optional[str] = None,
|
||||||
|
ind2: Optional[str] = None,
|
||||||
|
) -> Iterable[DataField]:
|
||||||
|
"""Yield datafields, optionally filtered by tag/indicators."""
|
||||||
|
for df in rec.datafields:
|
||||||
|
if tag is not None and df.tag != tag:
|
||||||
|
continue
|
||||||
|
if ind1 is not None and df.ind1 != ind1:
|
||||||
|
continue
|
||||||
|
if ind2 is not None and df.ind2 != ind2:
|
||||||
|
continue
|
||||||
|
yield df
|
||||||
|
|
||||||
|
|
||||||
|
def subfield_values(
|
||||||
|
rec: MarcRecord,
|
||||||
|
tag: str,
|
||||||
|
code: str,
|
||||||
|
*,
|
||||||
|
ind1: Optional[str] = None,
|
||||||
|
ind2: Optional[str] = None,
|
||||||
|
) -> List[str]:
|
||||||
|
"""All values for subfield `code` in every `tag` field (respecting indicators)."""
|
||||||
|
out: List[str] = []
|
||||||
|
for df in iter_datafields(rec, tag, ind1, ind2):
|
||||||
|
out.extend(sf.value for sf in df.subfields if sf.code == code)
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def first_subfield_value(
|
||||||
|
rec: MarcRecord,
|
||||||
|
tag: str,
|
||||||
|
code: str,
|
||||||
|
*,
|
||||||
|
ind1: Optional[str] = None,
|
||||||
|
ind2: Optional[str] = None,
|
||||||
|
default: Optional[str] = None,
|
||||||
|
) -> Optional[str]:
|
||||||
|
"""First value for subfield `code` in `tag` (respecting indicators)."""
|
||||||
|
for df in iter_datafields(rec, tag, ind1, ind2):
|
||||||
|
for sf in df.subfields:
|
||||||
|
if sf.code == code:
|
||||||
|
return sf.value
|
||||||
|
return default
|
||||||
|
|
||||||
|
|
||||||
|
def find_datafields_with_subfields(
|
||||||
|
rec: MarcRecord,
|
||||||
|
tag: str,
|
||||||
|
*,
|
||||||
|
where_all: Optional[Dict[str, str]] = None,
|
||||||
|
where_any: Optional[Dict[str, str]] = None,
|
||||||
|
casefold: bool = False,
|
||||||
|
ind1: Optional[str] = None,
|
||||||
|
ind2: Optional[str] = None,
|
||||||
|
) -> List[DataField]:
|
||||||
|
"""
|
||||||
|
Return datafields of `tag` whose subfields match constraints:
|
||||||
|
- where_all: every (code -> exact value) must be present
|
||||||
|
- where_any: at least one (code -> exact value) present
|
||||||
|
Set `casefold=True` for case-insensitive comparison.
|
||||||
|
"""
|
||||||
|
where_all = where_all or {}
|
||||||
|
where_any = where_any or {}
|
||||||
|
matched: List[DataField] = []
|
||||||
|
|
||||||
|
for df in iter_datafields(rec, tag, ind1, ind2):
|
||||||
|
# Map code -> list of values (with optional casefold applied)
|
||||||
|
vals: Dict[str, List[str]] = {}
|
||||||
|
for sf in df.subfields:
|
||||||
|
v = sf.value.casefold() if casefold else sf.value
|
||||||
|
vals.setdefault(sf.code, []).append(v)
|
||||||
|
|
||||||
|
ok = True
|
||||||
|
for c, v in where_all.items():
|
||||||
|
vv = v.casefold() if casefold else v
|
||||||
|
if c not in vals or vv not in vals[c]:
|
||||||
|
ok = False
|
||||||
|
break
|
||||||
|
|
||||||
|
if ok and where_any:
|
||||||
|
any_ok = any(
|
||||||
|
(c in vals) and ((v.casefold() if casefold else v) in vals[c])
|
||||||
|
for c, v in where_any.items()
|
||||||
|
)
|
||||||
|
if not any_ok:
|
||||||
|
ok = False
|
||||||
|
|
||||||
|
if ok:
|
||||||
|
matched.append(df)
|
||||||
|
|
||||||
|
return matched
|
||||||
|
|
||||||
|
|
||||||
|
def controlfield_value(
|
||||||
|
rec: MarcRecord, tag: str, default: Optional[str] = None
|
||||||
|
) -> Optional[str]:
|
||||||
|
"""Get the first controlfield value by tag (e.g., '001', '005')."""
|
||||||
|
for cf in rec.controlfields:
|
||||||
|
if cf.tag == tag:
|
||||||
|
return cf.value
|
||||||
|
return default
|
||||||
|
|
||||||
|
|
||||||
|
def datafields_value(
|
||||||
|
data: List[DataField], code: str, default: Optional[str] = None
|
||||||
|
) -> Optional[str]:
|
||||||
|
"""Get the first value for a specific subfield code in a list of datafields."""
|
||||||
|
for df in data:
|
||||||
|
for sf in df.subfields:
|
||||||
|
if sf.code == code:
|
||||||
|
return sf.value
|
||||||
|
return default
|
||||||
|
|
||||||
|
|
||||||
|
def datafield_value(
|
||||||
|
df: DataField, code: str, default: Optional[str] = None
|
||||||
|
) -> Optional[str]:
|
||||||
|
"""Get the first value for a specific subfield code in a datafield."""
|
||||||
|
for sf in df.subfields:
|
||||||
|
if sf.code == code:
|
||||||
|
return sf.value
|
||||||
|
return default
|
||||||
|
|
||||||
|
|
||||||
|
def _smart_join_title(a: str, b: Optional[str]) -> str:
|
||||||
|
"""
|
||||||
|
Join 245 $a and $b with MARC-style punctuation.
|
||||||
|
If $b is present, join with ' : ' unless either side already supplies punctuation.
|
||||||
|
"""
|
||||||
|
a = a.strip()
|
||||||
|
if not b:
|
||||||
|
return a
|
||||||
|
b = b.strip()
|
||||||
|
if a.endswith((":", ";", "/")) or b.startswith((":", ";", "/")):
|
||||||
|
return f"{a} {b}"
|
||||||
|
return f"{a} : {b}"
|
||||||
|
|
||||||
|
|
||||||
|
def subfield_values_from_fields(
|
||||||
|
fields: Iterable[DataField],
|
||||||
|
code: str,
|
||||||
|
) -> List[str]:
|
||||||
|
"""All subfield values with given `code` across a list of DataField."""
|
||||||
|
return [sf.value for df in fields for sf in df.subfields if sf.code == code]
|
||||||
|
|
||||||
|
|
||||||
|
def first_subfield_value_from_fields(
|
||||||
|
fields: Iterable[DataField],
|
||||||
|
code: str,
|
||||||
|
default: Optional[str] = None,
|
||||||
|
) -> Optional[str]:
|
||||||
|
"""First subfield value with given `code` across a list of DataField."""
|
||||||
|
for df in fields:
|
||||||
|
for sf in df.subfields:
|
||||||
|
if sf.code == code:
|
||||||
|
return sf.value
|
||||||
|
return default
|
||||||
|
|
||||||
|
|
||||||
|
def subfield_value_pairs_from_fields(
|
||||||
|
fields: Iterable[DataField],
|
||||||
|
code: str,
|
||||||
|
) -> List[Tuple[DataField, str]]:
|
||||||
|
"""
|
||||||
|
Return (DataField, value) pairs for all subfields with `code`.
|
||||||
|
Useful if you need to know which field a value came from.
|
||||||
|
"""
|
||||||
|
out: List[Tuple[DataField, str]] = []
|
||||||
|
for df in fields:
|
||||||
|
for sf in df.subfields:
|
||||||
|
if sf.code == code:
|
||||||
|
out.append((df, sf.value))
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def book_from_marc(rec: MarcRecord) -> BookData:
|
||||||
|
# PPN from controlfield 001
|
||||||
|
ppn = controlfield_value(rec, "001")
|
||||||
|
|
||||||
|
# Title = 245 $a + 245 $b (if present)
|
||||||
|
t_a = first_subfield_value(rec, "245", "a")
|
||||||
|
t_b = first_subfield_value(rec, "245", "b")
|
||||||
|
title = _smart_join_title(t_a, t_b) if t_a else None
|
||||||
|
|
||||||
|
# Signature = 924 where $9 == "Frei 129" → take that field's $g
|
||||||
|
frei_fields = find_datafields_with_subfields(
|
||||||
|
rec, "924", where_all={"9": "Frei 129"}
|
||||||
|
)
|
||||||
|
signature = first_subfield_value_from_fields(frei_fields, "g")
|
||||||
|
|
||||||
|
# Year = 264 $c (prefer ind2="1" publication; fallback to any 264)
|
||||||
|
year = first_subfield_value(rec, "264", "c", ind2="1") or first_subfield_value(
|
||||||
|
rec, "264", "c"
|
||||||
|
)
|
||||||
|
isbn = subfield_values(rec, "020", "a")
|
||||||
|
|
||||||
|
return BookData(
|
||||||
|
ppn=ppn,
|
||||||
|
title=title,
|
||||||
|
signature=signature,
|
||||||
|
edition=first_subfield_value(rec, "250", "a"),
|
||||||
|
year=year,
|
||||||
|
pages=first_subfield_value(rec, "300", "a"),
|
||||||
|
publisher=first_subfield_value(rec, "264", "b"),
|
||||||
|
isbn=isbn,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class SWB:
|
||||||
|
def __init__(self):
|
||||||
|
self.url = "https://sru.k10plus.de/opac-de-627!rec=1?version=1.1&operation=searchRetrieve&query={}&maximumRecords=10&recordSchema=marcxml"
|
||||||
|
self.bib_id = 20735
|
||||||
|
|
||||||
|
def get(self, query_args: Iterable[str]) -> List[Record]:
|
||||||
|
# if any query_arg ends with =, remove it
|
||||||
|
query_args = [arg for arg in query_args if not arg.endswith("=")]
|
||||||
|
query = "+and+".join(query_args)
|
||||||
|
query = query.replace(" ", "%20").replace("&", "%26")
|
||||||
|
|
||||||
|
url = self.url.format(query)
|
||||||
|
|
||||||
|
print("Fetching from SWB:", url)
|
||||||
|
headers = {
|
||||||
|
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
|
||||||
|
"Accept": "application/xml",
|
||||||
|
"Accept-Charset": "latin1,utf-8;q=0.7,*;q=0.3",
|
||||||
|
}
|
||||||
|
response = requests.get(url, headers=headers)
|
||||||
|
if response.status_code != 200:
|
||||||
|
raise Exception(f"Error fetching data from SWB: {response.status_code}")
|
||||||
|
# print(response.text)
|
||||||
|
data = response.content
|
||||||
|
|
||||||
|
# extract top-level response
|
||||||
|
response = parse_search_retrieve_response(data)
|
||||||
|
return response.records
|
||||||
|
|
||||||
|
def getBooks(self, query_args: Iterable[str]) -> List[BookData]:
|
||||||
|
records: List[Record] = self.get(query_args)
|
||||||
|
books: List[BookData] = []
|
||||||
|
title = query_args[1].split("=")[1]
|
||||||
|
# print(len(records), "records found")
|
||||||
|
for rec in records:
|
||||||
|
book = book_from_marc(rec.recordData)
|
||||||
|
books.append(book)
|
||||||
|
books = [
|
||||||
|
b for b in books if b.title and b.title.lower().startswith(title.lower())
|
||||||
|
]
|
||||||
|
return books
|
||||||
@@ -1,17 +1,18 @@
|
|||||||
|
import sys
|
||||||
|
from typing import Any, Optional, Union
|
||||||
|
|
||||||
|
import loguru
|
||||||
import requests
|
import requests
|
||||||
from bs4 import BeautifulSoup
|
from bs4 import BeautifulSoup
|
||||||
|
|
||||||
|
|
||||||
# import sleep_and_retry decorator to retry requests
|
# import sleep_and_retry decorator to retry requests
|
||||||
from ratelimit import limits, sleep_and_retry
|
from ratelimit import limits, sleep_and_retry
|
||||||
from typing import Union, Any, Optional
|
|
||||||
from src.logic.dataclass import BookData
|
|
||||||
|
|
||||||
|
from src import LOG_DIR
|
||||||
|
from src.logic.dataclass import BookData
|
||||||
from src.transformers import ARRAYData, BibTeXData, COinSData, RDSData, RISData
|
from src.transformers import ARRAYData, BibTeXData, COinSData, RDSData, RISData
|
||||||
from src.transformers.transformers import RDS_AVAIL_DATA, RDS_GENERIC_DATA
|
from src.transformers.transformers import RDS_AVAIL_DATA, RDS_GENERIC_DATA
|
||||||
import loguru
|
|
||||||
import sys
|
|
||||||
from src import LOG_DIR
|
|
||||||
log = loguru.logger
|
log = loguru.logger
|
||||||
log.remove()
|
log.remove()
|
||||||
log.add(sys.stdout, level="INFO")
|
log.add(sys.stdout, level="INFO")
|
||||||
@@ -20,7 +21,6 @@ log.add(f"{LOG_DIR}/application.log", rotation="1 MB", retention="10 days")
|
|||||||
# logger.add(sys.stderr, format="{time} {level} {message}", level="INFO")
|
# logger.add(sys.stderr, format="{time} {level} {message}", level="INFO")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
API_URL = "https://rds.ibs-bw.de/phfreiburg/opac/RDSIndexrecord/{}/"
|
API_URL = "https://rds.ibs-bw.de/phfreiburg/opac/RDSIndexrecord/{}/"
|
||||||
PPN_URL = "https://rds.ibs-bw.de/phfreiburg/opac/RDSIndex/Search?type0%5B%5D=allfields&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=au&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=ti&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=ct&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=isn&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=ta&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=co&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=py&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=pp&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=pu&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=si&lookfor0%5B%5D={}&join=AND&bool0%5B%5D=AND&type0%5B%5D=zr&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=cc&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND"
|
PPN_URL = "https://rds.ibs-bw.de/phfreiburg/opac/RDSIndex/Search?type0%5B%5D=allfields&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=au&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=ti&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=ct&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=isn&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=ta&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=co&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=py&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=pp&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=pu&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=si&lookfor0%5B%5D={}&join=AND&bool0%5B%5D=AND&type0%5B%5D=zr&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND&type0%5B%5D=cc&lookfor0%5B%5D=&join=AND&bool0%5B%5D=AND"
|
||||||
BASE = "https://rds.ibs-bw.de"
|
BASE = "https://rds.ibs-bw.de"
|
||||||
@@ -111,21 +111,8 @@ class WebRequest:
|
|||||||
locations = soup.find_all("div", class_="col-xs-12 rds-dl RDS_LOCATION")
|
locations = soup.find_all("div", class_="col-xs-12 rds-dl RDS_LOCATION")
|
||||||
if locations:
|
if locations:
|
||||||
for location in locations:
|
for location in locations:
|
||||||
item_location = location.find(
|
if "1. OG Semesterapparat" in location.text:
|
||||||
"div", class_="col-xs-12 col-md-7 col-lg-8 rds-dl-panel"
|
log.success("Found Semesterapparat, adding entry")
|
||||||
).text.strip()
|
|
||||||
log.debug(f"Item location: {item_location}")
|
|
||||||
if self.use_any:
|
|
||||||
pre_tag = soup.find_all("pre")
|
|
||||||
if pre_tag:
|
|
||||||
for tag in pre_tag:
|
|
||||||
data = tag.text.strip()
|
|
||||||
return_data.append(data)
|
|
||||||
return return_data
|
|
||||||
else:
|
|
||||||
log.error("No <pre> tag found")
|
|
||||||
raise ValueError("No <pre> tag found")
|
|
||||||
elif f"Semesterapparat-{self.apparat}" in item_location:
|
|
||||||
pre_tag = soup.find_all("pre")
|
pre_tag = soup.find_all("pre")
|
||||||
return_data = []
|
return_data = []
|
||||||
if pre_tag:
|
if pre_tag:
|
||||||
@@ -137,10 +124,36 @@ class WebRequest:
|
|||||||
log.error("No <pre> tag found")
|
log.error("No <pre> tag found")
|
||||||
return return_data
|
return return_data
|
||||||
else:
|
else:
|
||||||
log.error(
|
item_location = location.find(
|
||||||
f"Signature {self.signature} not found in {item_location}"
|
"div", class_="col-xs-12 col-md-7 col-lg-8 rds-dl-panel"
|
||||||
)
|
).text.strip()
|
||||||
# return_data = []
|
log.debug(f"Item location: {item_location}")
|
||||||
|
if self.use_any:
|
||||||
|
pre_tag = soup.find_all("pre")
|
||||||
|
if pre_tag:
|
||||||
|
for tag in pre_tag:
|
||||||
|
data = tag.text.strip()
|
||||||
|
return_data.append(data)
|
||||||
|
return return_data
|
||||||
|
else:
|
||||||
|
log.error("No <pre> tag found")
|
||||||
|
raise ValueError("No <pre> tag found")
|
||||||
|
elif f"Semesterapparat-{self.apparat}" in item_location:
|
||||||
|
pre_tag = soup.find_all("pre")
|
||||||
|
return_data = []
|
||||||
|
if pre_tag:
|
||||||
|
for tag in pre_tag:
|
||||||
|
data = tag.text.strip()
|
||||||
|
return_data.append(data)
|
||||||
|
return return_data
|
||||||
|
else:
|
||||||
|
log.error("No <pre> tag found")
|
||||||
|
return return_data
|
||||||
|
else:
|
||||||
|
log.error(
|
||||||
|
f"Signature {self.signature} not found in {item_location}"
|
||||||
|
)
|
||||||
|
# return_data = []
|
||||||
|
|
||||||
return return_data
|
return return_data
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ from bs4 import BeautifulSoup
|
|||||||
from docx import Document
|
from docx import Document
|
||||||
|
|
||||||
from src import LOG_DIR
|
from src import LOG_DIR
|
||||||
from src.backend import Semester
|
from src.backend.semester import Semester
|
||||||
from src.logic.openai import name_tester, run_shortener, semester_converter
|
from src.logic.openai import name_tester, run_shortener, semester_converter
|
||||||
|
|
||||||
log = loguru.logger
|
log = loguru.logger
|
||||||
@@ -18,7 +18,6 @@ log.add(sys.stdout, level="INFO")
|
|||||||
log.add(f"{LOG_DIR}/application.log", rotation="1 MB", retention="10 days")
|
log.add(f"{LOG_DIR}/application.log", rotation="1 MB", retention="10 days")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
|
letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
|
||||||
|
|
||||||
|
|
||||||
@@ -111,6 +110,7 @@ class SemapDocument:
|
|||||||
else:
|
else:
|
||||||
self.title_suggestions = []
|
self.title_suggestions = []
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def renameSemester(self) -> None:
|
def renameSemester(self) -> None:
|
||||||
if ", Dauer" in self.semester:
|
if ", Dauer" in self.semester:
|
||||||
@@ -141,8 +141,8 @@ def word_docx_to_csv(path: str) -> list[pd.DataFrame]:
|
|||||||
|
|
||||||
text = text.replace("\n", "")
|
text = text.replace("\n", "")
|
||||||
row_data.append(text)
|
row_data.append(text)
|
||||||
if text == "Ihr Fach:":
|
# if text == "Ihr Fach:":
|
||||||
row_data.append(get_fach(path))
|
# row_data.append(get_fach(path))
|
||||||
data.append(row_data)
|
data.append(row_data)
|
||||||
df = pd.DataFrame(data)
|
df = pd.DataFrame(data)
|
||||||
df.columns = df.iloc[0]
|
df.columns = df.iloc[0]
|
||||||
@@ -265,7 +265,7 @@ def elsa_word_to_csv(path: str):
|
|||||||
return tuple_to_dict(data, doctype), doctype
|
return tuple_to_dict(data, doctype), doctype
|
||||||
|
|
||||||
|
|
||||||
def word_to_semap(word_path: str) -> SemapDocument:
|
def word_to_semap(word_path: str, ai: bool = True) -> SemapDocument:
|
||||||
log.info("Parsing Word Document {}", word_path)
|
log.info("Parsing Word Document {}", word_path)
|
||||||
semap = SemapDocument()
|
semap = SemapDocument()
|
||||||
df = word_docx_to_csv(word_path)
|
df = word_docx_to_csv(word_path)
|
||||||
@@ -286,8 +286,9 @@ def word_to_semap(word_path: str) -> SemapDocument:
|
|||||||
appdata = {keys[i]: keys[i + 1] for i in range(0, len(keys), 2)}
|
appdata = {keys[i]: keys[i + 1] for i in range(0, len(keys), 2)}
|
||||||
semap.title = appdata["Veranstaltung:"]
|
semap.title = appdata["Veranstaltung:"]
|
||||||
semap.semester = appdata["Semester:"]
|
semap.semester = appdata["Semester:"]
|
||||||
semap.renameSemester
|
if ai:
|
||||||
semap.nameSetter
|
semap.renameSemester
|
||||||
|
semap.nameSetter
|
||||||
|
|
||||||
books = df[2]
|
books = df[2]
|
||||||
booklist = []
|
booklist = []
|
||||||
@@ -309,7 +310,5 @@ def word_to_semap(word_path: str) -> SemapDocument:
|
|||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
else_df = elsa_word_to_csv(
|
else_df = word_to_semap("C:/Users/aky547/Desktop/semap/db/temp/tmpzsz_hgdr.docx")
|
||||||
"C:/Users/aky547/Desktop/ELSA_Bestellung Scann Der Westen und der Rest.docx"
|
|
||||||
)
|
|
||||||
print(else_df)
|
print(else_df)
|
||||||
|
|||||||
BIN
src/sounds/ding.mp3
Normal file
BIN
src/sounds/ding.mp3
Normal file
Binary file not shown.
@@ -1,23 +1,21 @@
|
|||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
import loguru
|
||||||
from PySide6 import QtWidgets
|
from PySide6 import QtWidgets
|
||||||
|
|
||||||
from src import Icon, settings as config
|
from src import LOG_DIR, Icon
|
||||||
|
from src import settings as config
|
||||||
|
|
||||||
from .dialog_sources.Ui_mail_preview import Ui_eMailPreview as MailPreviewDialog
|
from .dialog_sources.Ui_mail_preview import Ui_eMailPreview as MailPreviewDialog
|
||||||
from .mailTemplate import MailTemplateDialog
|
from .mailTemplate import MailTemplateDialog
|
||||||
import loguru
|
|
||||||
import sys
|
|
||||||
from src import LOG_DIR
|
|
||||||
log = loguru.logger
|
log = loguru.logger
|
||||||
log.remove()
|
log.remove()
|
||||||
log.add(sys.stdout, level="INFO")
|
log.add(sys.stdout, level="INFO")
|
||||||
log.add(f"{LOG_DIR}/application.log", rotation="1 MB", retention="10 days")
|
log.add(f"{LOG_DIR}/application.log", rotation="1 MB", retention="10 days")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
empty_signature = """<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
|
empty_signature = """<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
|
||||||
|
|
||||||
<html><head><meta name="qrichtext" content="1" /><meta charset="utf-8" /><style
|
<html><head><meta name="qrichtext" content="1" /><meta charset="utf-8" /><style
|
||||||
@@ -47,6 +45,7 @@ class Mail_Dialog(QtWidgets.QDialog, MailPreviewDialog):
|
|||||||
app_subject,
|
app_subject,
|
||||||
prof_name,
|
prof_name,
|
||||||
prof_mail,
|
prof_mail,
|
||||||
|
accepted_books=None,
|
||||||
parent=None,
|
parent=None,
|
||||||
default_mail="Information zum Semesterapparat",
|
default_mail="Information zum Semesterapparat",
|
||||||
):
|
):
|
||||||
@@ -58,6 +57,7 @@ class Mail_Dialog(QtWidgets.QDialog, MailPreviewDialog):
|
|||||||
self.appname = app_name
|
self.appname = app_name
|
||||||
self.subject = app_subject
|
self.subject = app_subject
|
||||||
self.profname = prof_name
|
self.profname = prof_name
|
||||||
|
self.books = accepted_books if accepted_books is not None else []
|
||||||
self.mail_data = ""
|
self.mail_data = ""
|
||||||
self.signature = self.determine_signature()
|
self.signature = self.determine_signature()
|
||||||
self.prof_mail = prof_mail
|
self.prof_mail = prof_mail
|
||||||
@@ -148,6 +148,14 @@ Tel.: 0761/682-778 | 07617682-545"""
|
|||||||
AppSubject=self.subject,
|
AppSubject=self.subject,
|
||||||
greeting=self.get_greeting(),
|
greeting=self.get_greeting(),
|
||||||
signature=self.signature,
|
signature=self.signature,
|
||||||
|
newEditions="<br>".join(
|
||||||
|
[
|
||||||
|
f"{book.title} von {book.author} (ISBN: {book.isbn}, Auflage: {book.edition}, In Bibliothek: {'ja' if getattr(book, 'library_location', 1) == 1 else 'nein'})"
|
||||||
|
for book in self.books
|
||||||
|
]
|
||||||
|
)
|
||||||
|
if self.books
|
||||||
|
else "keine neuen Auflagen gefunden",
|
||||||
)
|
)
|
||||||
|
|
||||||
self.mail_body.setHtml(mail_html)
|
self.mail_body.setHtml(mail_html)
|
||||||
|
|||||||
91
src/ui/dialogs/progress.py
Normal file
91
src/ui/dialogs/progress.py
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
from typing import List, Optional, Set, Union
|
||||||
|
import re
|
||||||
|
|
||||||
|
from PySide6 import QtCore
|
||||||
|
from PySide6.QtWidgets import QDialog, QPushButton, QVBoxLayout
|
||||||
|
from qtqdm import Qtqdm, QtqdmProgressBar
|
||||||
|
|
||||||
|
from src.logic import BookData
|
||||||
|
from src.logic.lehmannsapi import LehmannsClient
|
||||||
|
from src.logic.swb import SWB
|
||||||
|
|
||||||
|
|
||||||
|
class CheckThread(QtCore.QThread):
|
||||||
|
updateSignal = QtCore.Signal()
|
||||||
|
total_entries_signal = QtCore.Signal(int)
|
||||||
|
resultSignal = QtCore.Signal(str)
|
||||||
|
etaSignal = QtCore.Signal(dict)
|
||||||
|
startSignal = QtCore.Signal()
|
||||||
|
progress = QtCore.Signal(dict)
|
||||||
|
|
||||||
|
def __init__(self, items: list[BookData]):
|
||||||
|
super().__init__()
|
||||||
|
self.items: List[BookData] = items
|
||||||
|
self.results: List[tuple[BookData, List[BookData]]] = []
|
||||||
|
self.total_entries_signal.emit(len(items))
|
||||||
|
|
||||||
|
def _update_callback(self, status):
|
||||||
|
self.progress.emit(status)
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
tqdm_object = Qtqdm(
|
||||||
|
range(len(self.items)),
|
||||||
|
unit_scale=True,
|
||||||
|
)
|
||||||
|
for i in tqdm_object:
|
||||||
|
book: BookData = self.items[i]
|
||||||
|
author = (
|
||||||
|
book.author.split(";")[0].replace(" ", "")
|
||||||
|
if ";" in book.author
|
||||||
|
else book.author.replace(" ", "")
|
||||||
|
)
|
||||||
|
# title = book.title.split(":")[0].strip()
|
||||||
|
# remove trailing punctuation from title
|
||||||
|
title = book.title.rstrip(" .:,;!?")
|
||||||
|
response: list[BookData] = []
|
||||||
|
response = SWB().getBooks(
|
||||||
|
[
|
||||||
|
"pica.bib=20735",
|
||||||
|
f"pica.tit={title.split(':')[0].strip()}",
|
||||||
|
# f"pica.per={author}",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
# in the response, remove the entry with the same ppn
|
||||||
|
response = [entry for entry in response if entry.ppn != book.ppn]
|
||||||
|
for respo in response:
|
||||||
|
respo.link = "SWB"
|
||||||
|
with LehmannsClient() as client:
|
||||||
|
results = client.search_by_title(title, strict=True)
|
||||||
|
client.enrich_pages(results)
|
||||||
|
if not results:
|
||||||
|
continue
|
||||||
|
for res in results:
|
||||||
|
response.append(BookData().from_LehmannsSearchResult(res))
|
||||||
|
if response == []:
|
||||||
|
continue
|
||||||
|
# check results if lehmanns has a result with the same isbn from the results of swb. if so, if we have a signature, remove, else keep
|
||||||
|
response = filter_prefer_swb(response)
|
||||||
|
|
||||||
|
result = (book, response)
|
||||||
|
|
||||||
|
self.results.append(result)
|
||||||
|
|
||||||
|
|
||||||
|
class ProgressDialog(QDialog):
|
||||||
|
def __init__(self, items: list):
|
||||||
|
super().__init__()
|
||||||
|
self.setWindowTitle("Progress")
|
||||||
|
self.setModal(True)
|
||||||
|
layout = QVBoxLayout(self)
|
||||||
|
self.progress_bar = QtqdmProgressBar(self)
|
||||||
|
self.items: List[BookData] = items
|
||||||
|
layout.addWidget(self.progress_bar)
|
||||||
|
self.results: List[tuple[BookData, List[BookData]]] = []
|
||||||
|
self.start_button = QPushButton("Start Progress", self)
|
||||||
|
self.start_button.hide()
|
||||||
|
self.start_button.clicked.connect(self.start)
|
||||||
|
layout.addWidget(self.start_button)
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
|
||||||
@@ -339,14 +339,22 @@
|
|||||||
</spacer>
|
</spacer>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<layout class="QHBoxLayout" name="avail_layout"/>
|
<layout class="QHBoxLayout" name="avail_layout">
|
||||||
</item>
|
<item>
|
||||||
<item>
|
<widget class="QLabel" name="label_20">
|
||||||
<widget class="QLabel" name="label_20">
|
<property name="text">
|
||||||
<property name="text">
|
<string>Medien werden geprüft</string>
|
||||||
<string>Medien werden geprüft</string>
|
</property>
|
||||||
</property>
|
</widget>
|
||||||
</widget>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QProgressBar" name="progressBar">
|
||||||
|
<property name="value">
|
||||||
|
<number>24</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="Line" name="line_3">
|
<widget class="Line" name="line_3">
|
||||||
@@ -1614,6 +1622,11 @@ Einige Angaben müssen ggf angepasst werden</string>
|
|||||||
<string>Lehrperson bearbeiten</string>
|
<string>Lehrperson bearbeiten</string>
|
||||||
</property>
|
</property>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Signaturen aktualisieren</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QGroupBox" name="admin_action">
|
<widget class="QGroupBox" name="admin_action">
|
||||||
<property name="geometry">
|
<property name="geometry">
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
################################################################################
|
################################################################################
|
||||||
## Form generated from reading UI file 'semesterapparat_ui.ui'
|
## Form generated from reading UI file 'semesterapparat_ui.ui'
|
||||||
##
|
##
|
||||||
## Created by: Qt User Interface Compiler version 6.9.1
|
## Created by: Qt User Interface Compiler version 6.9.2
|
||||||
##
|
##
|
||||||
## WARNING! All changes made in this file will be lost when recompiling UI file!
|
## WARNING! All changes made in this file will be lost when recompiling UI file!
|
||||||
################################################################################
|
################################################################################
|
||||||
@@ -20,9 +20,9 @@ from PySide6.QtWidgets import (QAbstractItemView, QAbstractScrollArea, QApplicat
|
|||||||
QComboBox, QFormLayout, QFrame, QGridLayout,
|
QComboBox, QFormLayout, QFrame, QGridLayout,
|
||||||
QGroupBox, QHBoxLayout, QHeaderView, QLabel,
|
QGroupBox, QHBoxLayout, QHeaderView, QLabel,
|
||||||
QLineEdit, QMainWindow, QMenu, QMenuBar,
|
QLineEdit, QMainWindow, QMenu, QMenuBar,
|
||||||
QPushButton, QSizePolicy, QSpacerItem, QStatusBar,
|
QProgressBar, QPushButton, QSizePolicy, QSpacerItem,
|
||||||
QTabWidget, QTableWidget, QTableWidgetItem, QToolButton,
|
QStatusBar, QTabWidget, QTableWidget, QTableWidgetItem,
|
||||||
QVBoxLayout, QWidget)
|
QToolButton, QVBoxLayout, QWidget)
|
||||||
|
|
||||||
class Ui_MainWindow(object):
|
class Ui_MainWindow(object):
|
||||||
def setupUi(self, MainWindow):
|
def setupUi(self, MainWindow):
|
||||||
@@ -207,13 +207,19 @@ class Ui_MainWindow(object):
|
|||||||
|
|
||||||
self.avail_layout = QHBoxLayout()
|
self.avail_layout = QHBoxLayout()
|
||||||
self.avail_layout.setObjectName(u"avail_layout")
|
self.avail_layout.setObjectName(u"avail_layout")
|
||||||
|
|
||||||
self.horizontalLayout_5.addLayout(self.avail_layout)
|
|
||||||
|
|
||||||
self.label_20 = QLabel(self.gridLayoutWidget_2)
|
self.label_20 = QLabel(self.gridLayoutWidget_2)
|
||||||
self.label_20.setObjectName(u"label_20")
|
self.label_20.setObjectName(u"label_20")
|
||||||
|
|
||||||
self.horizontalLayout_5.addWidget(self.label_20)
|
self.avail_layout.addWidget(self.label_20)
|
||||||
|
|
||||||
|
self.progressBar = QProgressBar(self.gridLayoutWidget_2)
|
||||||
|
self.progressBar.setObjectName(u"progressBar")
|
||||||
|
self.progressBar.setValue(24)
|
||||||
|
|
||||||
|
self.avail_layout.addWidget(self.progressBar)
|
||||||
|
|
||||||
|
|
||||||
|
self.horizontalLayout_5.addLayout(self.avail_layout)
|
||||||
|
|
||||||
self.line_3 = QFrame(self.gridLayoutWidget_2)
|
self.line_3 = QFrame(self.gridLayoutWidget_2)
|
||||||
self.line_3.setObjectName(u"line_3")
|
self.line_3.setObjectName(u"line_3")
|
||||||
@@ -618,6 +624,7 @@ class Ui_MainWindow(object):
|
|||||||
self.select_action_box.addItem("")
|
self.select_action_box.addItem("")
|
||||||
self.select_action_box.addItem("")
|
self.select_action_box.addItem("")
|
||||||
self.select_action_box.addItem("")
|
self.select_action_box.addItem("")
|
||||||
|
self.select_action_box.addItem("")
|
||||||
self.select_action_box.setObjectName(u"select_action_box")
|
self.select_action_box.setObjectName(u"select_action_box")
|
||||||
self.select_action_box.setGeometry(QRect(60, 30, 181, 22))
|
self.select_action_box.setGeometry(QRect(60, 30, 181, 22))
|
||||||
self.admin_action = QGroupBox(self.admin)
|
self.admin_action = QGroupBox(self.admin)
|
||||||
@@ -973,6 +980,7 @@ class Ui_MainWindow(object):
|
|||||||
self.select_action_box.setItemText(0, QCoreApplication.translate("MainWindow", u"Nutzer anlegen", None))
|
self.select_action_box.setItemText(0, QCoreApplication.translate("MainWindow", u"Nutzer anlegen", None))
|
||||||
self.select_action_box.setItemText(1, QCoreApplication.translate("MainWindow", u"Nutzer bearbeiten", None))
|
self.select_action_box.setItemText(1, QCoreApplication.translate("MainWindow", u"Nutzer bearbeiten", None))
|
||||||
self.select_action_box.setItemText(2, QCoreApplication.translate("MainWindow", u"Lehrperson bearbeiten", None))
|
self.select_action_box.setItemText(2, QCoreApplication.translate("MainWindow", u"Lehrperson bearbeiten", None))
|
||||||
|
self.select_action_box.setItemText(3, QCoreApplication.translate("MainWindow", u"Signaturen aktualisieren", None))
|
||||||
|
|
||||||
self.admin_action.setTitle(QCoreApplication.translate("MainWindow", u"GroupBox", None))
|
self.admin_action.setTitle(QCoreApplication.translate("MainWindow", u"GroupBox", None))
|
||||||
self.tabWidget.setTabText(self.tabWidget.indexOf(self.admin), QCoreApplication.translate("MainWindow", u"Admin", None))
|
self.tabWidget.setTabText(self.tabWidget.indexOf(self.admin), QCoreApplication.translate("MainWindow", u"Admin", None))
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -7,16 +7,23 @@ import time
|
|||||||
import webbrowser
|
import webbrowser
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Union
|
from typing import Any, List, Optional, Tuple, Union
|
||||||
|
|
||||||
import loguru
|
import loguru
|
||||||
from natsort import natsorted
|
from natsort import natsorted
|
||||||
from PySide6 import QtCore, QtGui, QtWidgets
|
from PySide6 import QtCore, QtGui, QtWidgets
|
||||||
from PySide6.QtCore import QThread, Qt
|
from PySide6.QtCore import QThread
|
||||||
from PySide6.QtGui import QRegularExpressionValidator
|
from PySide6.QtGui import QRegularExpressionValidator
|
||||||
|
from PySide6.QtMultimedia import QAudioOutput, QMediaPlayer
|
||||||
|
|
||||||
from src import LOG_DIR, Icon
|
from src import LOG_DIR, Icon
|
||||||
from src.backend import AvailChecker, BookGrabber, Database, DocumentationThread
|
from src.backend import (
|
||||||
|
AvailChecker,
|
||||||
|
BookGrabber,
|
||||||
|
Database,
|
||||||
|
DocumentationThread,
|
||||||
|
NewEditionCheckerThread,
|
||||||
|
)
|
||||||
from src.backend.create_file import recreateFile
|
from src.backend.create_file import recreateFile
|
||||||
from src.backend.delete_temp_contents import delete_temp_contents as tempdelete
|
from src.backend.delete_temp_contents import delete_temp_contents as tempdelete
|
||||||
from src.backend.semester import Semester
|
from src.backend.semester import Semester
|
||||||
@@ -51,7 +58,11 @@ from src.ui.widgets import (
|
|||||||
ElsaDialog,
|
ElsaDialog,
|
||||||
FilePicker,
|
FilePicker,
|
||||||
MessageCalendar,
|
MessageCalendar,
|
||||||
|
NewEditionChecker,
|
||||||
|
NewEditionCheckSelector,
|
||||||
SearchStatisticPage,
|
SearchStatisticPage,
|
||||||
|
UpdaterThread,
|
||||||
|
UpdateSignatures,
|
||||||
UserCreate,
|
UserCreate,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -65,7 +76,7 @@ log.add(
|
|||||||
rotation="1 day",
|
rotation="1 day",
|
||||||
retention="1 month",
|
retention="1 month",
|
||||||
)
|
)
|
||||||
log.critical("UI started")
|
log.success("UI started")
|
||||||
valid_input = (0, 0, 0, 0, 0, 0)
|
valid_input = (0, 0, 0, 0, 0, 0)
|
||||||
|
|
||||||
|
|
||||||
@@ -189,6 +200,9 @@ class Ui(Ui_Semesterapparat):
|
|||||||
self.btn_reserve.hide()
|
self.btn_reserve.hide()
|
||||||
self.label_20.hide()
|
self.label_20.hide()
|
||||||
self.line_3.hide()
|
self.line_3.hide()
|
||||||
|
self.progressBar.setValue(0)
|
||||||
|
self.progressBar.hide()
|
||||||
|
self.progressBar.setMinimum(0)
|
||||||
self.avail_status.hide()
|
self.avail_status.hide()
|
||||||
self.chkbx_show_del_media.hide()
|
self.chkbx_show_del_media.hide()
|
||||||
self.automation_add_selected_books.hide()
|
self.automation_add_selected_books.hide()
|
||||||
@@ -238,6 +252,7 @@ class Ui(Ui_Semesterapparat):
|
|||||||
self.availChecker = None
|
self.availChecker = None
|
||||||
self.mail_thread = None
|
self.mail_thread = None
|
||||||
self.autoGrabber = None
|
self.autoGrabber = None
|
||||||
|
self.newEditionChecker = NewEditionCheckerThread()
|
||||||
|
|
||||||
self.elsatab.setLayout(QtWidgets.QVBoxLayout())
|
self.elsatab.setLayout(QtWidgets.QVBoxLayout())
|
||||||
self.search_statistics.setLayout(QtWidgets.QVBoxLayout())
|
self.search_statistics.setLayout(QtWidgets.QVBoxLayout())
|
||||||
@@ -252,6 +267,9 @@ class Ui(Ui_Semesterapparat):
|
|||||||
|
|
||||||
self.steps.hide()
|
self.steps.hide()
|
||||||
|
|
||||||
|
self.player = QMediaPlayer()
|
||||||
|
self.audio_output = QAudioOutput()
|
||||||
|
|
||||||
self.valid_check_semester.clicked.connect(self.display_valid_semester) # type:ignore
|
self.valid_check_semester.clicked.connect(self.display_valid_semester) # type:ignore
|
||||||
|
|
||||||
def create_doc(self):
|
def create_doc(self):
|
||||||
@@ -295,6 +313,9 @@ class Ui(Ui_Semesterapparat):
|
|||||||
elif self.select_action_box.currentText() == "Lehrperson bearbeiten":
|
elif self.select_action_box.currentText() == "Lehrperson bearbeiten":
|
||||||
self.setWidget(EditProf())
|
self.setWidget(EditProf())
|
||||||
self.admin_action.setTitle("Lehrperson bearbeiten")
|
self.admin_action.setTitle("Lehrperson bearbeiten")
|
||||||
|
elif self.select_action_box.currentText() == "Signaturen aktualisieren":
|
||||||
|
self.setWidget(UpdateSignatures())
|
||||||
|
self.admin_action.setTitle("Signaturen aktualisieren")
|
||||||
else:
|
else:
|
||||||
self.hideWidget()
|
self.hideWidget()
|
||||||
self.admin_action.setTitle("")
|
self.admin_action.setTitle("")
|
||||||
@@ -312,6 +333,12 @@ class Ui(Ui_Semesterapparat):
|
|||||||
tempdelete()
|
tempdelete()
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
|
def play_sound(self, sound_file: str):
|
||||||
|
self.player.setAudioOutput(self.audio_output)
|
||||||
|
self.audio_output.setVolume(50)
|
||||||
|
self.player.setSource(QtCore.QUrl.fromLocalFile(f"src/sounds/{sound_file}"))
|
||||||
|
self.player.play()
|
||||||
|
|
||||||
def get_apparats(self):
|
def get_apparats(self):
|
||||||
alist = self.db.getAllAparats(deleted=0)
|
alist = self.db.getAllAparats(deleted=0)
|
||||||
alist = natsorted(alist, key=lambda x: x.appnr, reverse=True)
|
alist = natsorted(alist, key=lambda x: x.appnr, reverse=True)
|
||||||
@@ -399,7 +426,7 @@ class Ui(Ui_Semesterapparat):
|
|||||||
else:
|
else:
|
||||||
return f"WiSe {currentYear}/{currentYear + 1}"
|
return f"WiSe {currentYear}/{currentYear + 1}"
|
||||||
|
|
||||||
def open_apparat(self, apparat):
|
def open_apparat(self, apparat: Union[int, str]):
|
||||||
if self.load_app_data(apparat):
|
if self.load_app_data(apparat):
|
||||||
# change tab focus to tab 0
|
# change tab focus to tab 0
|
||||||
self.tabWidget.setCurrentIndex(0)
|
self.tabWidget.setCurrentIndex(0)
|
||||||
@@ -608,17 +635,17 @@ class Ui(Ui_Semesterapparat):
|
|||||||
self.prof_mail.setText(data.mail)
|
self.prof_mail.setText(data.mail)
|
||||||
self.app_name.setFocus()
|
self.app_name.setFocus()
|
||||||
|
|
||||||
def get_index_of_value(self, table_widget, value):
|
def get_index_of_value(self, table_widget: QtWidgets.QTableWidget, value: str):
|
||||||
for i in range(table_widget.rowCount()):
|
for i in range(table_widget.rowCount()):
|
||||||
for j in range(table_widget.columnCount()):
|
for j in range(table_widget.columnCount()):
|
||||||
if (
|
if (
|
||||||
table_widget.item(i, j) is not None
|
table_widget.item(i, j) is not None
|
||||||
and table_widget.item(i, j).text() == value
|
and table_widget.item(i, j).text() == value # type: ignore
|
||||||
):
|
):
|
||||||
return i, j
|
return i, j
|
||||||
return (None, None)
|
return (None, None)
|
||||||
|
|
||||||
def load_app_data(self, app_id=None):
|
def load_app_data(self, app_id: Optional[Union[int, str]] = None):
|
||||||
self.cancel_active_selection.setEnabled(True)
|
self.cancel_active_selection.setEnabled(True)
|
||||||
self.add_medium.setEnabled(True)
|
self.add_medium.setEnabled(True)
|
||||||
for child in self.app_group_box.findChildren(QtWidgets.QToolButton):
|
for child in self.app_group_box.findChildren(QtWidgets.QToolButton):
|
||||||
@@ -848,8 +875,9 @@ class Ui(Ui_Semesterapparat):
|
|||||||
|
|
||||||
def update_app_media_list(self):
|
def update_app_media_list(self):
|
||||||
deleted = 0 if not self.chkbx_show_del_media.isChecked() else 1
|
deleted = 0 if not self.chkbx_show_del_media.isChecked() else 1
|
||||||
app_id = self.active_apparat
|
app_id = self.db.getId(self.app_name.text())
|
||||||
prof_id = self.db.getProfId(self.profdata)
|
prof_id = self.db.getProfId(self.profdata)
|
||||||
|
print(app_id, prof_id)
|
||||||
books: list[dict[int, BookData, int]] = self.db.getBooks(
|
books: list[dict[int, BookData, int]] = self.db.getBooks(
|
||||||
app_id, prof_id, deleted
|
app_id, prof_id, deleted
|
||||||
)
|
)
|
||||||
@@ -889,10 +917,15 @@ class Ui(Ui_Semesterapparat):
|
|||||||
3,
|
3,
|
||||||
QtWidgets.QTableWidgetItem(book_data.author),
|
QtWidgets.QTableWidgetItem(book_data.author),
|
||||||
)
|
)
|
||||||
self.tableWidget_apparat_media.setItem(
|
label = QtWidgets.QLabel(f'<a href="{book_data.link}">Katalog</a>')
|
||||||
self.tableWidget_apparat_media.rowCount() - 1,
|
label.setOpenExternalLinks(True)
|
||||||
6,
|
label.setTextFormat(QtCore.Qt.TextFormat.RichText)
|
||||||
QtWidgets.QTableWidgetItem(book_data.link),
|
label.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
|
||||||
|
label.setTextInteractionFlags(
|
||||||
|
QtCore.Qt.TextInteractionFlag.TextBrowserInteraction
|
||||||
|
)
|
||||||
|
self.tableWidget_apparat_media.setCellWidget(
|
||||||
|
self.tableWidget_apparat_media.rowCount() - 1, 6, label
|
||||||
)
|
)
|
||||||
if availability == 1:
|
if availability == 1:
|
||||||
# display green checkmark at column 4 in the row
|
# display green checkmark at column 4 in the row
|
||||||
@@ -1099,7 +1132,9 @@ class Ui(Ui_Semesterapparat):
|
|||||||
log.info("File selected: {}, {}", file_name, file_location)
|
log.info("File selected: {}, {}", file_name, file_location)
|
||||||
if file_location == "Database":
|
if file_location == "Database":
|
||||||
# create warning, then return
|
# create warning, then return
|
||||||
self.db.recreateFile(file_name, self.active_apparat, filetype=file_type)
|
file = self.db.recreateFile(
|
||||||
|
file_name, self.active_apparat, filetype=file_type
|
||||||
|
)
|
||||||
if file_type == "pdf":
|
if file_type == "pdf":
|
||||||
# Todo: implement parser here
|
# Todo: implement parser here
|
||||||
self.confirm_popup("PDF Dateien werden nicht unterstützt!", title="Fehler")
|
self.confirm_popup("PDF Dateien werden nicht unterstützt!", title="Fehler")
|
||||||
@@ -1176,6 +1211,7 @@ class Ui(Ui_Semesterapparat):
|
|||||||
for runner in self.bookGrabber:
|
for runner in self.bookGrabber:
|
||||||
if not runner.isRunning():
|
if not runner.isRunning():
|
||||||
runner.deleteLater()
|
runner.deleteLater()
|
||||||
|
self.bookGrabber.remove(runner)
|
||||||
# #log.debug("Checking file")
|
# #log.debug("Checking file")
|
||||||
# get active app_id and prof_id
|
# get active app_id and prof_id
|
||||||
self.tableWidget_apparate.setEnabled(False)
|
self.tableWidget_apparate.setEnabled(False)
|
||||||
@@ -1434,10 +1470,14 @@ class Ui(Ui_Semesterapparat):
|
|||||||
contact_action = menu.addAction("Kontaktieren")
|
contact_action = menu.addAction("Kontaktieren")
|
||||||
delete_action = menu.addAction("Löschen")
|
delete_action = menu.addAction("Löschen")
|
||||||
remind_action = menu.addAction("Erinnerung")
|
remind_action = menu.addAction("Erinnerung")
|
||||||
|
new_edition_check = menu.addAction("Auf Neuauflagen prüfen")
|
||||||
menu.addAction(extend_action)
|
menu.addAction(extend_action)
|
||||||
menu.addActions([contact_action, delete_action, remind_action])
|
menu.addActions(
|
||||||
|
[contact_action, delete_action, remind_action, new_edition_check]
|
||||||
|
)
|
||||||
extend_action.triggered.connect(self.extend_apparat)
|
extend_action.triggered.connect(self.extend_apparat)
|
||||||
remind_action.triggered.connect(self.reminder)
|
remind_action.triggered.connect(self.reminder)
|
||||||
|
new_edition_check.triggered.connect(self.check_new_editions)
|
||||||
# convert point to row and column
|
# convert point to row and column
|
||||||
row = self.tableWidget_apparate.rowAt(position.y())
|
row = self.tableWidget_apparate.rowAt(position.y())
|
||||||
column = self.tableWidget_apparate.columnAt(position.x())
|
column = self.tableWidget_apparate.columnAt(position.x())
|
||||||
@@ -1454,6 +1494,79 @@ class Ui(Ui_Semesterapparat):
|
|||||||
)
|
)
|
||||||
menu.exec(self.tableWidget_apparate.mapToGlobal(position))
|
menu.exec(self.tableWidget_apparate.mapToGlobal(position))
|
||||||
|
|
||||||
|
def update_status(self, curr, total):
|
||||||
|
self.avail_status.show()
|
||||||
|
self.label_20.show()
|
||||||
|
self.progressBar.show()
|
||||||
|
self.avail_status.setText(f"{curr}/{total}")
|
||||||
|
self.progressBar.setValue(curr)
|
||||||
|
if curr == total:
|
||||||
|
self.avail_status.hide()
|
||||||
|
self.label_20.hide()
|
||||||
|
self.progressBar.hide()
|
||||||
|
self.progressBar.setValue(0)
|
||||||
|
self.avail_status.setText("0/0")
|
||||||
|
|
||||||
|
def check_new_editions(self):
|
||||||
|
# create a dialog that asks "Prof oder Apparat" with a button for each. based on that either search through the books of the apparat, or all books associated with the prof
|
||||||
|
selector = NewEditionCheckSelector()
|
||||||
|
selector.exec()
|
||||||
|
pick = selector.selection
|
||||||
|
|
||||||
|
app_id = self.tableWidget_apparate.item(
|
||||||
|
self.tableWidget_apparate.currentRow(), 0
|
||||||
|
).text()
|
||||||
|
prof_id: int = self.db.getProfIDByApparat(app_id)
|
||||||
|
app_name = self.tableWidget_apparate.item(
|
||||||
|
self.tableWidget_apparate.currentRow(), 1
|
||||||
|
).text()
|
||||||
|
subject = self.tableWidget_apparate.item(
|
||||||
|
self.tableWidget_apparate.currentRow(), 4
|
||||||
|
).text()
|
||||||
|
if pick == "professor":
|
||||||
|
books = self.db.getBooksByProfId(prof_id)
|
||||||
|
app_name = "Sammelmail"
|
||||||
|
app_id = ", ".join(
|
||||||
|
[str(app.appnr) for app in self.db.getApparatsByProf(prof_id)]
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
books = self.db.getBooks(app_id, prof_id, deleted=0)
|
||||||
|
books = [book["bookdata"] for book in books]
|
||||||
|
log.info(f"Checking {len(books)} for new editions")
|
||||||
|
|
||||||
|
self.newEditionChecker.entries = books
|
||||||
|
self.newEditionChecker.finished.connect(self.newEditionChecker.reset)
|
||||||
|
|
||||||
|
self.progressBar.setMaximum(len(books))
|
||||||
|
self.newEditionChecker.updateSignal.connect(self.update_status)
|
||||||
|
|
||||||
|
self.newEditionChecker.start()
|
||||||
|
while self.newEditionChecker.isRunning():
|
||||||
|
QtWidgets.QApplication.processEvents()
|
||||||
|
self.play_sound("ding.mp3")
|
||||||
|
results = self.newEditionChecker.results
|
||||||
|
if results == []:
|
||||||
|
return
|
||||||
|
log.info(f"Found {len(results)} possible new editions - opening dialog")
|
||||||
|
newEditionChecker = NewEditionChecker(results=results)
|
||||||
|
newEditionChecker.exec()
|
||||||
|
|
||||||
|
accepted_books = newEditionChecker.accepted_books
|
||||||
|
# print(accepted_books)
|
||||||
|
if accepted_books == []:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.mail_thread = Mail_Dialog(
|
||||||
|
prof_name=self.db.getSpecificProfData(prof_id, ["fullname"]),
|
||||||
|
prof_mail=self.db.getProfMailById(prof_id),
|
||||||
|
app_id=app_id,
|
||||||
|
app_name=app_name,
|
||||||
|
app_subject=subject,
|
||||||
|
accepted_books=accepted_books,
|
||||||
|
default_mail="Neuauflagen für Semesterapparat",
|
||||||
|
)
|
||||||
|
self.mail_thread.show()
|
||||||
|
|
||||||
def reminder(self):
|
def reminder(self):
|
||||||
log.info("Opening reminder dialog")
|
log.info("Opening reminder dialog")
|
||||||
reminder = ReminderDialog()
|
reminder = ReminderDialog()
|
||||||
@@ -1540,8 +1653,45 @@ class Ui(Ui_Semesterapparat):
|
|||||||
menu.exec(self.tableWidget_apparat_media.mapToGlobal(position)) # type: ignore
|
menu.exec(self.tableWidget_apparat_media.mapToGlobal(position)) # type: ignore
|
||||||
|
|
||||||
def update_data(self):
|
def update_data(self):
|
||||||
# TODO: use link in table, parse data and if needed, update location / signature
|
signatures = [
|
||||||
pass
|
self.tableWidget_apparat_media.item(row, 1).text()
|
||||||
|
for row in range(self.tableWidget_apparat_media.rowCount())
|
||||||
|
] # type: ignore
|
||||||
|
prof_id = self.db.getProfId(self.profdata) # type: ignore
|
||||||
|
app_id = self.active_apparat
|
||||||
|
books: List[Tuple[int, BookData]] = []
|
||||||
|
for signature in signatures:
|
||||||
|
book = self.db.getBookBasedOnSignature(
|
||||||
|
app_id=app_id,
|
||||||
|
signature=signature,
|
||||||
|
prof_id=prof_id,
|
||||||
|
)
|
||||||
|
book_id = self.db.getBookIdBasedOnSignature(
|
||||||
|
self.active_apparat,
|
||||||
|
prof_id,
|
||||||
|
signature,
|
||||||
|
)
|
||||||
|
books.append((book_id, book))
|
||||||
|
# self.autoUpdater.entries = books
|
||||||
|
# self.autoUpdater.finished.connect(self.autoUpdater.reset)
|
||||||
|
self.updater = UpdaterThread()
|
||||||
|
u_books = []
|
||||||
|
for book_id, book in books:
|
||||||
|
u_books.append({"id": book_id, "bookdata": book})
|
||||||
|
self.updater.books = u_books
|
||||||
|
self.progressBar.setMaximum(len(books))
|
||||||
|
self.updater.finished.connect(self.updater.deleteLater)
|
||||||
|
self.updater.finished.connect(self.update_app_media_list)
|
||||||
|
self.updater.currtot.connect(self.update_status)
|
||||||
|
self.updater.start()
|
||||||
|
# ppn = book.link.split("kid=")[-1]
|
||||||
|
# result = cat.get_book(ppn)
|
||||||
|
# if result:
|
||||||
|
# book.signature = result.signature
|
||||||
|
# book.in_apparat = True
|
||||||
|
# print(book)
|
||||||
|
# self.db.updateBookdata(book_id, book)
|
||||||
|
# self.update_app_media_list()
|
||||||
|
|
||||||
def copy_to_apparat(self):
|
def copy_to_apparat(self):
|
||||||
selected_rows = self.tableWidget_apparat_media.selectionModel().selectedRows() # type: ignore
|
selected_rows = self.tableWidget_apparat_media.selectionModel().selectedRows() # type: ignore
|
||||||
@@ -1639,12 +1789,12 @@ class Ui(Ui_Semesterapparat):
|
|||||||
).text()
|
).text()
|
||||||
prof_id = self.db.getProfId(self.profdata)
|
prof_id = self.db.getProfId(self.profdata)
|
||||||
data = self.db.getBookBasedOnSignature(
|
data = self.db.getBookBasedOnSignature(
|
||||||
app_id=self.active_apparat,
|
app_id=self.db.getId(self.app_name.text()),
|
||||||
signature=book,
|
signature=book,
|
||||||
prof_id=prof_id,
|
prof_id=prof_id,
|
||||||
)
|
)
|
||||||
book_id = self.db.getBookIdBasedOnSignature(
|
book_id = self.db.getBookIdBasedOnSignature(
|
||||||
self.active_apparat,
|
self.db.getId(self.app_name.text()),
|
||||||
prof_id,
|
prof_id,
|
||||||
book,
|
book,
|
||||||
)
|
)
|
||||||
@@ -1732,10 +1882,18 @@ class Ui(Ui_Semesterapparat):
|
|||||||
else:
|
else:
|
||||||
return
|
return
|
||||||
|
|
||||||
def __contact_dialog(self, apparat, location: tuple | str, mail=None, pid=""):
|
def __contact_dialog(
|
||||||
|
self,
|
||||||
|
apparat,
|
||||||
|
location: tuple | str,
|
||||||
|
mail=None,
|
||||||
|
pid="",
|
||||||
|
accepted_books=None,
|
||||||
|
app_id="",
|
||||||
|
):
|
||||||
log.debug(
|
log.debug(
|
||||||
"Got these values apparat: {}, location: {}, mail: {}, pid: {}".format(
|
"Got these values apparat: {}, location: {}, mail: {}, pid: {}, accepted_books: {}, app_id: {}".format(
|
||||||
apparat, location, mail, pid
|
apparat, location, mail, pid, accepted_books, app_id
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1798,10 +1956,16 @@ class Ui(Ui_Semesterapparat):
|
|||||||
if state == 1:
|
if state == 1:
|
||||||
log.debug("Deleting apparat {}", selected_apparat_id)
|
log.debug("Deleting apparat {}", selected_apparat_id)
|
||||||
pid = self.db.getProfIDByApparat(selected_apparat_id)
|
pid = self.db.getProfIDByApparat(selected_apparat_id)
|
||||||
self.db.deleteApparat(selected_apparat_id, Semester().value)
|
apparat = Apparat(
|
||||||
|
appnr=int(selected_apparat_id),
|
||||||
|
name=self.tableWidget_apparate.item(
|
||||||
|
self.tableWidget_apparate.currentRow(), 1
|
||||||
|
).text(),
|
||||||
|
)
|
||||||
|
self.db.deleteApparat(apparat=apparat, semester=Semester().value)
|
||||||
# delete the corresponding entry from self.apparats
|
# delete the corresponding entry from self.apparats
|
||||||
for apparat in self.apparats:
|
for apparat in self.apparats:
|
||||||
if apparat[4] == int(selected_apparat_id):
|
if apparat.appnr == int(selected_apparat_id):
|
||||||
self.apparats.remove(apparat)
|
self.apparats.remove(apparat)
|
||||||
break
|
break
|
||||||
self.old_apparats = self.apparats
|
self.old_apparats = self.apparats
|
||||||
@@ -1842,6 +2006,7 @@ def launch_gui():
|
|||||||
) # if that thread uses an event loop
|
) # if that thread uses an event loop
|
||||||
app.aboutToQuit.connect(aui.docu.terminate) # our new slot
|
app.aboutToQuit.connect(aui.docu.terminate) # our new slot
|
||||||
app.aboutToQuit.connect(aui.docu.wait)
|
app.aboutToQuit.connect(aui.docu.wait)
|
||||||
|
app.aboutToQuit.connect(aui.newEditionChecker.terminate)
|
||||||
atexit.register(tempdelete)
|
atexit.register(tempdelete)
|
||||||
# atexit.register(aui.validate_thread.quit)
|
# atexit.register(aui.validate_thread.quit)
|
||||||
# atexit.register(aui.docu.quit)
|
# atexit.register(aui.docu.quit)
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
__all__ = [
|
__all__ = [
|
||||||
"LoginWidget",
|
"DataQtGraph",
|
||||||
"RegisterWidget",
|
|
||||||
"StatusWidget",
|
"StatusWidget",
|
||||||
"FilePicker",
|
"FilePicker",
|
||||||
"DataGraph",
|
|
||||||
"CalendarEntry",
|
"CalendarEntry",
|
||||||
"MessageCalendar",
|
"MessageCalendar",
|
||||||
"SearchStatisticPage",
|
"SearchStatisticPage",
|
||||||
@@ -12,16 +10,23 @@ __all__ = [
|
|||||||
"EditUser",
|
"EditUser",
|
||||||
"EditProf",
|
"EditProf",
|
||||||
"IconWidget",
|
"IconWidget",
|
||||||
|
"NewEditionChecker",
|
||||||
|
"NewEditionCheckSelector",
|
||||||
|
"UpdateSignatures",
|
||||||
|
"UpdaterThread"
|
||||||
]
|
]
|
||||||
|
|
||||||
|
from .admin_create_user import UserCreate
|
||||||
|
from .admin_edit_prof import EditProf
|
||||||
|
from .admin_edit_user import EditUser
|
||||||
|
from .calendar_entry import CalendarEntry
|
||||||
from .collapse import StatusWidget
|
from .collapse import StatusWidget
|
||||||
|
from .elsa_main import ElsaDialog
|
||||||
from .filepicker import FilePicker
|
from .filepicker import FilePicker
|
||||||
from .graph import DataQtGraph
|
from .graph import DataQtGraph
|
||||||
from .calendar_entry import CalendarEntry
|
|
||||||
from .MessageCalendar import MessageCalendar
|
|
||||||
from .searchPage import SearchStatisticPage
|
|
||||||
from .elsa_main import ElsaDialog
|
|
||||||
from .admin_create_user import UserCreate
|
|
||||||
from .admin_edit_user import EditUser
|
|
||||||
from .admin_edit_prof import EditProf
|
|
||||||
from .iconLine import IconWidget
|
from .iconLine import IconWidget
|
||||||
|
from .MessageCalendar import MessageCalendar
|
||||||
|
from .new_edition_check import NewEditionChecker, NewEditionCheckSelector
|
||||||
|
from .searchPage import SearchStatisticPage
|
||||||
|
from .signature_update import UpdateSignatures, UpdaterThread
|
||||||
|
|
||||||
|
|||||||
@@ -1,24 +1,26 @@
|
|||||||
import os
|
import os
|
||||||
from .widget_sources.elsa_maindialog_ui import Ui_Dialog
|
|
||||||
from PySide6 import QtCore, QtWidgets, QtGui
|
|
||||||
from PySide6.QtGui import QRegularExpressionValidator
|
|
||||||
from PySide6.QtCore import QDate
|
|
||||||
from src import Icon
|
|
||||||
from src.backend import Semester, Database
|
|
||||||
from src.logic import elsa_word_to_csv, Prof
|
|
||||||
from src.ui.dialogs import ElsaAddEntry, popus_confirm
|
|
||||||
from src.ui.widgets import FilePicker, DataQtGraph
|
|
||||||
from src.backend import recreateElsaFile
|
|
||||||
import loguru
|
|
||||||
import sys
|
import sys
|
||||||
from src import LOG_DIR
|
|
||||||
|
import loguru
|
||||||
|
from PySide6 import QtCore, QtGui, QtWidgets
|
||||||
|
from PySide6.QtCore import QDate
|
||||||
|
from PySide6.QtGui import QRegularExpressionValidator
|
||||||
|
|
||||||
|
from src import LOG_DIR, Icon
|
||||||
|
from src.backend import Database, Semester, recreateElsaFile
|
||||||
|
from src.logic import Prof, elsa_word_to_csv
|
||||||
|
from src.ui.dialogs import ElsaAddEntry, popus_confirm
|
||||||
|
from src.ui.widgets.filepicker import FilePicker
|
||||||
|
from src.ui.widgets.graph import DataQtGraph
|
||||||
|
|
||||||
|
from .widget_sources.elsa_maindialog_ui import Ui_Dialog
|
||||||
|
|
||||||
log = loguru.logger
|
log = loguru.logger
|
||||||
log.remove()
|
log.remove()
|
||||||
log.add(sys.stdout, level="INFO")
|
log.add(sys.stdout, level="INFO")
|
||||||
log.add(f"{LOG_DIR}/application.log", rotation="1 MB", retention="10 days")
|
log.add(f"{LOG_DIR}/application.log", rotation="1 MB", retention="10 days")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class ElsaDialog(QtWidgets.QDialog, Ui_Dialog):
|
class ElsaDialog(QtWidgets.QDialog, Ui_Dialog):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
@@ -240,7 +242,7 @@ class ElsaDialog(QtWidgets.QDialog, Ui_Dialog):
|
|||||||
|
|
||||||
if prof_id is None:
|
if prof_id is None:
|
||||||
self.db.createProf(profdata)
|
self.db.createProf(profdata)
|
||||||
prof_id = self.db.getProfId(prof)
|
prof_id = self.db.getProfId(profdata)
|
||||||
self.profs.append(
|
self.profs.append(
|
||||||
"f{}, {}".format(profdata.lastname, profdata.firstname), prof_id
|
"f{}, {}".format(profdata.lastname, profdata.firstname), prof_id
|
||||||
)
|
)
|
||||||
@@ -413,6 +415,16 @@ class ElsaDialog(QtWidgets.QDialog, Ui_Dialog):
|
|||||||
log.debug(
|
log.debug(
|
||||||
f"elsa_id: {elsa_id}, prof: {self.elsa_prof.currentText()}, semester: {self.elsa_semester.text()}, date: {self.elsa_date.text()}"
|
f"elsa_id: {elsa_id}, prof: {self.elsa_prof.currentText()}, semester: {self.elsa_semester.text()}, date: {self.elsa_date.text()}"
|
||||||
)
|
)
|
||||||
|
self.db.insertElsaFile(
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"name": file.split("/")[-1],
|
||||||
|
"path": file,
|
||||||
|
"type": file.split(".")[-1],
|
||||||
|
}
|
||||||
|
],
|
||||||
|
elsa_id,
|
||||||
|
)
|
||||||
for row in data:
|
for row in data:
|
||||||
if self.seperateEntries.isChecked():
|
if self.seperateEntries.isChecked():
|
||||||
if ";" in row["pages"]:
|
if ";" in row["pages"]:
|
||||||
@@ -426,6 +438,7 @@ class ElsaDialog(QtWidgets.QDialog, Ui_Dialog):
|
|||||||
else:
|
else:
|
||||||
self.setElsaRow(row)
|
self.setElsaRow(row)
|
||||||
self.db.addElsaMedia(row, elsa_id)
|
self.db.addElsaMedia(row, elsa_id)
|
||||||
|
|
||||||
self.quote_entry.setEnabled(True)
|
self.quote_entry.setEnabled(True)
|
||||||
|
|
||||||
def openDocumentElsa(self):
|
def openDocumentElsa(self):
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ class FilePicker:
|
|||||||
|
|
||||||
files, _ = filepicker.getOpenFileNames(
|
files, _ = filepicker.getOpenFileNames(
|
||||||
caption="Open file",
|
caption="Open file",
|
||||||
directory=self.last_path,
|
dir=self.last_path,
|
||||||
filter="Unterstützte Dateien (*.docx *.csv *.eml);;Word (*.docx);;CSV Files (*.csv);;Mail (*.eml)",
|
filter="Unterstützte Dateien (*.docx *.csv *.eml);;Word (*.docx);;CSV Files (*.csv);;Mail (*.eml)",
|
||||||
)
|
)
|
||||||
if files:
|
if files:
|
||||||
|
|||||||
185
src/ui/widgets/new_edition_check.py
Normal file
185
src/ui/widgets/new_edition_check.py
Normal file
@@ -0,0 +1,185 @@
|
|||||||
|
from typing import List
|
||||||
|
|
||||||
|
from PySide6 import QtWidgets
|
||||||
|
from PySide6.QtCore import Qt
|
||||||
|
|
||||||
|
from src.logic import BookData
|
||||||
|
|
||||||
|
from .widget_sources.new_edition_check_book_ui import (
|
||||||
|
Ui_Dialog as Ui_NewEditionCheckBook,
|
||||||
|
)
|
||||||
|
from .widget_sources.new_edition_check_found_result_ui import (
|
||||||
|
Ui_Dialog as Ui_NewEditionCheckFoundResult,
|
||||||
|
)
|
||||||
|
from .widget_sources.new_edition_check_ui import Ui_Dialog as Ui_NewEditionCheck
|
||||||
|
from .widget_sources.new_edition_check_selector_ui import Ui_Dialog as Ui_NewEditionCheckSelector
|
||||||
|
|
||||||
|
class NewEditionCheckSelector(QtWidgets.QDialog, Ui_NewEditionCheckSelector):
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
self.setupUi(self)
|
||||||
|
self.setWindowTitle("Neuauflagen prüfen")
|
||||||
|
self.btn_apparat.clicked.connect(self.select_apparat)
|
||||||
|
self.btn_prof.clicked.connect(self.select_professor)
|
||||||
|
self.selection = None
|
||||||
|
|
||||||
|
def select_apparat(self):
|
||||||
|
self.selection = "apparat"
|
||||||
|
self.accept()
|
||||||
|
|
||||||
|
def select_professor(self):
|
||||||
|
self.selection = "professor"
|
||||||
|
self.accept()
|
||||||
|
|
||||||
|
class NewEditionCheckFoundResult(QtWidgets.QDialog, Ui_NewEditionCheckFoundResult):
|
||||||
|
def __init__(self, book: BookData, parent=None):
|
||||||
|
assert isinstance(book, BookData)
|
||||||
|
super().__init__(parent)
|
||||||
|
self.setupUi(self)
|
||||||
|
self.book = book
|
||||||
|
self.line_ppn.setText(self.book.ppn if self.book.ppn else "")
|
||||||
|
self.line_title.setText(self.book.title if self.book.title else "")
|
||||||
|
self.line_signature.setText(self.book.signature if self.book.signature else "")
|
||||||
|
self.line_edition.setText(self.book.edition if self.book.edition else "")
|
||||||
|
self.line_publisher.setText(self.book.publisher if self.book.publisher else "")
|
||||||
|
self.line_year.setText(self.book.year if self.book.year else "")
|
||||||
|
self.line_pages.setText(self.book.pages if self.book.pages else "")
|
||||||
|
link = self.book.link if self.book.link else ""
|
||||||
|
if self.book.link != "SWB":
|
||||||
|
link = f"<a href='{link}'>Lehmanns</a>"
|
||||||
|
self.line_source.setText(link)
|
||||||
|
self.line_source.setOpenExternalLinks(True)
|
||||||
|
self.line_source.setTextFormat(Qt.TextFormat.RichText)
|
||||||
|
self.line_source.setTextInteractionFlags(
|
||||||
|
Qt.TextInteractionFlag.TextBrowserInteraction
|
||||||
|
)
|
||||||
|
self.line_isbn.setText(
|
||||||
|
", ".join(self.book.isbn)
|
||||||
|
if isinstance(self.book.isbn, list)
|
||||||
|
else self.book.isbn
|
||||||
|
)
|
||||||
|
if (
|
||||||
|
self.book.link == "SWB"
|
||||||
|
and self.book.signature is not None
|
||||||
|
and self.book.signature != ""
|
||||||
|
):
|
||||||
|
self.in_library.setText(
|
||||||
|
"Diese Neuauflage ist bereits in der Bibliothek vorhanden."
|
||||||
|
)
|
||||||
|
self.book.library_location = 1
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class NewEditionCheckBook(QtWidgets.QDialog, Ui_NewEditionCheckBook):
|
||||||
|
def __init__(self, book: BookData, responses: List[BookData], parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
self.setupUi(self)
|
||||||
|
self.book = book
|
||||||
|
self.accepted_books = []
|
||||||
|
self.responses = responses
|
||||||
|
self.line_author.setText(self.book.author)
|
||||||
|
self.line_title.setText(self.book.title)
|
||||||
|
self.line_ppn.setText(self.book.ppn if self.book.ppn else "")
|
||||||
|
self.line_signature.setText(self.book.signature if self.book.signature else "")
|
||||||
|
self.line_edition.setText(self.book.edition if self.book.edition else "")
|
||||||
|
self.line_publisher.setText(self.book.publisher if self.book.publisher else "")
|
||||||
|
self.line_year.setText(self.book.year if self.book.year else "")
|
||||||
|
self.line_pages.setText(self.book.pages if self.book.pages else "")
|
||||||
|
self.line_isbn.setText(
|
||||||
|
", ".join(self.book.isbn)
|
||||||
|
if isinstance(self.book.isbn, list)
|
||||||
|
else self.book.isbn
|
||||||
|
)
|
||||||
|
|
||||||
|
for _ in range(self.stackedWidget.count()):
|
||||||
|
widget = self.stackedWidget.widget(0)
|
||||||
|
self.stackedWidget.removeWidget(widget)
|
||||||
|
widget.deleteLater()
|
||||||
|
for response in self.responses:
|
||||||
|
self.stackedWidget.addWidget(
|
||||||
|
NewEditionCheckFoundResult(parent=self, book=response)
|
||||||
|
)
|
||||||
|
self.label_book_index.setText(f"1 / {self.stackedWidget.count()}")
|
||||||
|
self.btn_next.clicked.connect(self.next)
|
||||||
|
self.btn_prev.clicked.connect(self.previous)
|
||||||
|
|
||||||
|
def next(self):
|
||||||
|
index = self.stackedWidget.currentIndex()
|
||||||
|
if index < self.stackedWidget.count() - 1:
|
||||||
|
index += 1
|
||||||
|
self.stackedWidget.setCurrentIndex(index)
|
||||||
|
self.label_book_index.setText(f"{index + 1} / {self.stackedWidget.count()}")
|
||||||
|
if index == self.stackedWidget.count() - 1:
|
||||||
|
self.btn_next.hide()
|
||||||
|
|
||||||
|
def previous(self):
|
||||||
|
index = self.stackedWidget.currentIndex()
|
||||||
|
if index > 0:
|
||||||
|
index -= 1
|
||||||
|
self.stackedWidget.setCurrentIndex(index)
|
||||||
|
self.label_book_index.setText(f"{index + 1} / {self.stackedWidget.count()}")
|
||||||
|
if index < self.stackedWidget.count() - 1:
|
||||||
|
self.btn_next.show()
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class NewEditionChecker(QtWidgets.QDialog, Ui_NewEditionCheck):
|
||||||
|
def __init__(self, results, parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
self.setupUi(self)
|
||||||
|
self.results = results
|
||||||
|
self.setWindowTitle("Prüfung auf Neuauflagen")
|
||||||
|
# remove pages from stacked widget
|
||||||
|
for _ in range(self.stackedWidget.count()):
|
||||||
|
widget = self.stackedWidget.widget(0)
|
||||||
|
self.stackedWidget.removeWidget(widget)
|
||||||
|
widget.deleteLater()
|
||||||
|
for resultset in self.results:
|
||||||
|
book, responses = resultset
|
||||||
|
self.stackedWidget.addWidget(
|
||||||
|
NewEditionCheckBook(parent=self, book=book, responses=responses)
|
||||||
|
)
|
||||||
|
self.accepted_books = []
|
||||||
|
self.stackedWidget.setCurrentIndex(0)
|
||||||
|
self.progressBar.setMaximum(len(self.results))
|
||||||
|
self.progressBar.setValue(1)
|
||||||
|
self.btn_next.clicked.connect(self.next)
|
||||||
|
self.btn_prev.clicked.connect(self.previous)
|
||||||
|
self.btn_finish.hide()
|
||||||
|
self.btn_finish.clicked.connect(self.accept)
|
||||||
|
self.btn_prev.hide()
|
||||||
|
|
||||||
|
def next(self):
|
||||||
|
index = self.stackedWidget.currentIndex()
|
||||||
|
if index < self.stackedWidget.count() - 1:
|
||||||
|
index += 1
|
||||||
|
self.stackedWidget.setCurrentIndex(index)
|
||||||
|
self.progressBar.setValue(index + 1)
|
||||||
|
self.btn_prev.show()
|
||||||
|
if index == self.stackedWidget.count() - 1:
|
||||||
|
self.btn_next.hide()
|
||||||
|
self.btn_finish.show()
|
||||||
|
|
||||||
|
def previous(self):
|
||||||
|
index = self.stackedWidget.currentIndex()
|
||||||
|
if index > 0:
|
||||||
|
index -= 1
|
||||||
|
self.stackedWidget.setCurrentIndex(index)
|
||||||
|
self.progressBar.setValue(index + 1)
|
||||||
|
|
||||||
|
def accept(self) -> None:
|
||||||
|
print("finished checking for new editions")
|
||||||
|
accepted_books = []
|
||||||
|
for i in range(self.stackedWidget.count()):
|
||||||
|
book_widget = self.stackedWidget.widget(i)
|
||||||
|
if isinstance(book_widget, NewEditionCheckBook):
|
||||||
|
for j in range(book_widget.stackedWidget.count()):
|
||||||
|
found_widget = book_widget.stackedWidget.widget(j)
|
||||||
|
if isinstance(found_widget, NewEditionCheckFoundResult):
|
||||||
|
if found_widget.checkBox.isChecked():
|
||||||
|
accepted_books.append(found_widget.book)
|
||||||
|
super().accept()
|
||||||
|
print("accepted", len(accepted_books), "new editions")
|
||||||
|
self.accepted_books = accepted_books
|
||||||
@@ -53,6 +53,8 @@ class SearchStatisticPage(QtWidgets.QDialog, Ui_Dialog):
|
|||||||
self.btn_notify_for_deletion.clicked.connect(self.notify_for_deletion)
|
self.btn_notify_for_deletion.clicked.connect(self.notify_for_deletion)
|
||||||
self.btn_notify_for_deletion.setEnabled(False)
|
self.btn_notify_for_deletion.setEnabled(False)
|
||||||
self.btn_del_select_apparats.setEnabled(False)
|
self.btn_del_select_apparats.setEnabled(False)
|
||||||
|
self.btn_extendSelection.clicked.connect(self.mass_extend_apparats)
|
||||||
|
self.btn_extendSelection.setEnabled(False)
|
||||||
self.tableWidget.resizeColumnsToContents()
|
self.tableWidget.resizeColumnsToContents()
|
||||||
self.tableWidget.resizeRowsToContents()
|
self.tableWidget.resizeRowsToContents()
|
||||||
self.db = Database()
|
self.db = Database()
|
||||||
@@ -72,6 +74,23 @@ class SearchStatisticPage(QtWidgets.QDialog, Ui_Dialog):
|
|||||||
self.search_by_title.returnPressed.connect(self.search_book)
|
self.search_by_title.returnPressed.connect(self.search_book)
|
||||||
self.populate_tab()
|
self.populate_tab()
|
||||||
|
|
||||||
|
def mass_extend_apparats(self):
|
||||||
|
extend = ApparatExtendDialog()
|
||||||
|
extend.exec()
|
||||||
|
if extend.result() == QtWidgets.QDialog.DialogCode.Accepted:
|
||||||
|
data = extend.get_data()
|
||||||
|
log.debug(data)
|
||||||
|
for i in range(self.tableWidget.rowCount()):
|
||||||
|
if self.tableWidget.cellWidget(i, 0).isChecked():
|
||||||
|
app_name = self.tableWidget.item(i, 1).text()
|
||||||
|
app_id = self.db.getApparatId(app_name)
|
||||||
|
self.db.setNewSemesterDate(app_id, data["semester"], data["dauerapp"])
|
||||||
|
# remove the row
|
||||||
|
self.tableWidget.removeRow(i)
|
||||||
|
self.refreshSignal.emit()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def restore_apparat(self):
|
def restore_apparat(self):
|
||||||
selected_rows = self.tableWidget.selectionModel().selectedRows()
|
selected_rows = self.tableWidget.selectionModel().selectedRows()
|
||||||
apparats = []
|
apparats = []
|
||||||
|
|||||||
59
src/ui/widgets/signature_update.py
Normal file
59
src/ui/widgets/signature_update.py
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
from PySide6 import QtCore, QtWidgets
|
||||||
|
|
||||||
|
from src.backend.catalogue import Catalogue
|
||||||
|
from src.backend.database import Database
|
||||||
|
|
||||||
|
from .widget_sources.admin_update_signatures_ui import Ui_Dialog
|
||||||
|
|
||||||
|
|
||||||
|
class UpdaterThread(QtCore.QThread):
|
||||||
|
progress = QtCore.Signal(int)
|
||||||
|
currtot = QtCore.Signal(int, int)
|
||||||
|
|
||||||
|
def __init__(self, books=None):
|
||||||
|
super().__init__()
|
||||||
|
self.books = books
|
||||||
|
self.db = Database()
|
||||||
|
self.catalogue = Catalogue()
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
total_books = len(self.books)
|
||||||
|
for index, book in enumerate(self.books):
|
||||||
|
id = book["id"]
|
||||||
|
bookdata = book["bookdata"]
|
||||||
|
ppn = bookdata.link.split("kid=")[-1]
|
||||||
|
result = self.catalogue.get_book(ppn)
|
||||||
|
if result:
|
||||||
|
bookdata.signature = result.signature
|
||||||
|
print(bookdata)
|
||||||
|
self.db.updateBookdata(id, bookdata)
|
||||||
|
else:
|
||||||
|
print(f"No result for {ppn}")
|
||||||
|
self.db.deleteBook(id)
|
||||||
|
self.progress.emit(index + 1)
|
||||||
|
self.currtot.emit(index + 1, total_books)
|
||||||
|
|
||||||
|
|
||||||
|
class UpdateSignatures(QtWidgets.QDialog, Ui_Dialog):
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super(UpdateSignatures, self).__init__(parent)
|
||||||
|
self.setupUi(self)
|
||||||
|
self.setWindowTitle("Updating signatures...")
|
||||||
|
self.progressBar.setValue(0)
|
||||||
|
self.pushButton.clicked.connect(self.start)
|
||||||
|
self.db = Database()
|
||||||
|
self.catalogue = Catalogue()
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
books = self.db.getAllBooks()
|
||||||
|
total_books = len(books)
|
||||||
|
self.progressBar.setMaximum(total_books)
|
||||||
|
self.updater = UpdaterThread(books)
|
||||||
|
self.updater.progress.connect(self.update_progress)
|
||||||
|
self.updater.start()
|
||||||
|
|
||||||
|
def update_progress(self, value):
|
||||||
|
self.progressBar.setValue(value)
|
||||||
|
if value >= self.progressBar.maximum():
|
||||||
|
self.pushButton.setText("Done")
|
||||||
|
self.pushButton.setEnabled(False)
|
||||||
35
src/ui/widgets/widget_sources/admin_update_signatures.ui
Normal file
35
src/ui/widgets/widget_sources/admin_update_signatures.ui
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>Dialog</class>
|
||||||
|
<widget class="QDialog" name="Dialog">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>400</width>
|
||||||
|
<height>300</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Dialog</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="pushButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>Signaturen aktualisieren</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QProgressBar" name="progressBar">
|
||||||
|
<property name="value">
|
||||||
|
<number>24</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
||||||
49
src/ui/widgets/widget_sources/admin_update_signatures_ui.py
Normal file
49
src/ui/widgets/widget_sources/admin_update_signatures_ui.py
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
## Form generated from reading UI file 'admin_update_signatures.ui'
|
||||||
|
##
|
||||||
|
## Created by: Qt User Interface Compiler version 6.9.2
|
||||||
|
##
|
||||||
|
## WARNING! All changes made in this file will be lost when recompiling UI file!
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
|
||||||
|
QMetaObject, QObject, QPoint, QRect,
|
||||||
|
QSize, QTime, QUrl, Qt)
|
||||||
|
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, QDialog, QProgressBar, QPushButton,
|
||||||
|
QSizePolicy, QVBoxLayout, QWidget)
|
||||||
|
|
||||||
|
class Ui_Dialog(object):
|
||||||
|
def setupUi(self, Dialog):
|
||||||
|
if not Dialog.objectName():
|
||||||
|
Dialog.setObjectName(u"Dialog")
|
||||||
|
Dialog.resize(400, 300)
|
||||||
|
self.verticalLayout = QVBoxLayout(Dialog)
|
||||||
|
self.verticalLayout.setObjectName(u"verticalLayout")
|
||||||
|
self.pushButton = QPushButton(Dialog)
|
||||||
|
self.pushButton.setObjectName(u"pushButton")
|
||||||
|
|
||||||
|
self.verticalLayout.addWidget(self.pushButton)
|
||||||
|
|
||||||
|
self.progressBar = QProgressBar(Dialog)
|
||||||
|
self.progressBar.setObjectName(u"progressBar")
|
||||||
|
self.progressBar.setValue(24)
|
||||||
|
|
||||||
|
self.verticalLayout.addWidget(self.progressBar)
|
||||||
|
|
||||||
|
|
||||||
|
self.retranslateUi(Dialog)
|
||||||
|
|
||||||
|
QMetaObject.connectSlotsByName(Dialog)
|
||||||
|
# setupUi
|
||||||
|
|
||||||
|
def retranslateUi(self, Dialog):
|
||||||
|
Dialog.setWindowTitle(QCoreApplication.translate("Dialog", u"Dialog", None))
|
||||||
|
self.pushButton.setText(QCoreApplication.translate("Dialog", u"Signaturen aktualisieren", None))
|
||||||
|
# retranslateUi
|
||||||
|
|
||||||
75
src/ui/widgets/widget_sources/new_edition_check.ui
Normal file
75
src/ui/widgets/widget_sources/new_edition_check.ui
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>Dialog</class>
|
||||||
|
<widget class="QDialog" name="Dialog">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>632</width>
|
||||||
|
<height>726</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Dialog</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||||
|
<item>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||||
|
<item>
|
||||||
|
<widget class="QFrame" name="frame">
|
||||||
|
<property name="frameShape">
|
||||||
|
<enum>QFrame::StyledPanel</enum>
|
||||||
|
</property>
|
||||||
|
<property name="frameShadow">
|
||||||
|
<enum>QFrame::Raised</enum>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||||
|
<item>
|
||||||
|
<widget class="QStackedWidget" name="stackedWidget">
|
||||||
|
<widget class="QWidget" name="page"/>
|
||||||
|
<widget class="QWidget" name="page_2"/>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QProgressBar" name="progressBar">
|
||||||
|
<property name="value">
|
||||||
|
<number>24</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="btn_prev">
|
||||||
|
<property name="text">
|
||||||
|
<string>Previous</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="btn_next">
|
||||||
|
<property name="text">
|
||||||
|
<string>Next</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="btn_finish">
|
||||||
|
<property name="text">
|
||||||
|
<string>Finish</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
||||||
182
src/ui/widgets/widget_sources/new_edition_check_book.ui
Normal file
182
src/ui/widgets/widget_sources/new_edition_check_book.ui
Normal file
@@ -0,0 +1,182 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>Dialog</class>
|
||||||
|
<widget class="QDialog" name="Dialog">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>618</width>
|
||||||
|
<height>637</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Dialog</string>
|
||||||
|
</property>
|
||||||
|
<property name="sizeGripEnabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="modal">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="label_10">
|
||||||
|
<property name="text">
|
||||||
|
<string>Ausgewähltes Buch</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_9">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Gefundenes Buch</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_book_index">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>TextLabel</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<layout class="QFormLayout" name="formLayout_2">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="text">
|
||||||
|
<string>PPN</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QLineEdit" name="line_ppn"/>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="label_2">
|
||||||
|
<property name="text">
|
||||||
|
<string>Titel</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QLineEdit" name="line_title"/>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QLabel" name="label_3">
|
||||||
|
<property name="text">
|
||||||
|
<string>Signatur</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QLineEdit" name="line_signature"/>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<widget class="QLabel" name="label_4">
|
||||||
|
<property name="text">
|
||||||
|
<string>Auflage</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="1">
|
||||||
|
<widget class="QLineEdit" name="line_edition"/>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="0">
|
||||||
|
<widget class="QLabel" name="label_5">
|
||||||
|
<property name="text">
|
||||||
|
<string>Verlag</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="1">
|
||||||
|
<widget class="QLineEdit" name="line_publisher"/>
|
||||||
|
</item>
|
||||||
|
<item row="5" column="0">
|
||||||
|
<widget class="QLabel" name="label_6">
|
||||||
|
<property name="text">
|
||||||
|
<string>Jahr</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="5" column="1">
|
||||||
|
<widget class="QLineEdit" name="line_year"/>
|
||||||
|
</item>
|
||||||
|
<item row="6" column="0">
|
||||||
|
<widget class="QLabel" name="label_7">
|
||||||
|
<property name="text">
|
||||||
|
<string>Seiten</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="6" column="1">
|
||||||
|
<widget class="QLineEdit" name="line_pages"/>
|
||||||
|
</item>
|
||||||
|
<item row="7" column="0">
|
||||||
|
<widget class="QLabel" name="label_8">
|
||||||
|
<property name="text">
|
||||||
|
<string>Autor</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="7" column="1">
|
||||||
|
<widget class="QLineEdit" name="line_author"/>
|
||||||
|
</item>
|
||||||
|
<item row="8" column="0">
|
||||||
|
<widget class="QLabel" name="label_11">
|
||||||
|
<property name="text">
|
||||||
|
<string>ISBN</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="8" column="1">
|
||||||
|
<widget class="QLineEdit" name="line_isbn"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QStackedWidget" name="stackedWidget">
|
||||||
|
<widget class="QWidget" name="page"/>
|
||||||
|
<widget class="QWidget" name="page_2"/>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="btn_prev">
|
||||||
|
<property name="text">
|
||||||
|
<string>Previous</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="btn_next">
|
||||||
|
<property name="text">
|
||||||
|
<string>Next</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
||||||
202
src/ui/widgets/widget_sources/new_edition_check_book_ui.py
Normal file
202
src/ui/widgets/widget_sources/new_edition_check_book_ui.py
Normal file
@@ -0,0 +1,202 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
## Form generated from reading UI file 'new_edition_check_book.ui'
|
||||||
|
##
|
||||||
|
## Created by: Qt User Interface Compiler version 6.9.2
|
||||||
|
##
|
||||||
|
## WARNING! All changes made in this file will be lost when recompiling UI file!
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
|
||||||
|
QMetaObject, QObject, QPoint, QRect,
|
||||||
|
QSize, QTime, QUrl, Qt)
|
||||||
|
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, QDialog, QFormLayout, QGridLayout,
|
||||||
|
QHBoxLayout, QLabel, QLineEdit, QPushButton,
|
||||||
|
QSizePolicy, QStackedWidget, QWidget)
|
||||||
|
|
||||||
|
class Ui_Dialog(object):
|
||||||
|
def setupUi(self, Dialog):
|
||||||
|
if not Dialog.objectName():
|
||||||
|
Dialog.setObjectName(u"Dialog")
|
||||||
|
Dialog.resize(618, 637)
|
||||||
|
Dialog.setSizeGripEnabled(False)
|
||||||
|
Dialog.setModal(False)
|
||||||
|
self.gridLayout = QGridLayout(Dialog)
|
||||||
|
self.gridLayout.setObjectName(u"gridLayout")
|
||||||
|
self.label_10 = QLabel(Dialog)
|
||||||
|
self.label_10.setObjectName(u"label_10")
|
||||||
|
|
||||||
|
self.gridLayout.addWidget(self.label_10, 0, 0, 1, 1)
|
||||||
|
|
||||||
|
self.horizontalLayout_2 = QHBoxLayout()
|
||||||
|
self.horizontalLayout_2.setObjectName(u"horizontalLayout_2")
|
||||||
|
self.label_9 = QLabel(Dialog)
|
||||||
|
self.label_9.setObjectName(u"label_9")
|
||||||
|
sizePolicy = QSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Preferred)
|
||||||
|
sizePolicy.setHorizontalStretch(0)
|
||||||
|
sizePolicy.setVerticalStretch(0)
|
||||||
|
sizePolicy.setHeightForWidth(self.label_9.sizePolicy().hasHeightForWidth())
|
||||||
|
self.label_9.setSizePolicy(sizePolicy)
|
||||||
|
|
||||||
|
self.horizontalLayout_2.addWidget(self.label_9)
|
||||||
|
|
||||||
|
self.label_book_index = QLabel(Dialog)
|
||||||
|
self.label_book_index.setObjectName(u"label_book_index")
|
||||||
|
sizePolicy.setHeightForWidth(self.label_book_index.sizePolicy().hasHeightForWidth())
|
||||||
|
self.label_book_index.setSizePolicy(sizePolicy)
|
||||||
|
|
||||||
|
self.horizontalLayout_2.addWidget(self.label_book_index)
|
||||||
|
|
||||||
|
|
||||||
|
self.gridLayout.addLayout(self.horizontalLayout_2, 0, 1, 1, 1)
|
||||||
|
|
||||||
|
self.formLayout_2 = QFormLayout()
|
||||||
|
self.formLayout_2.setObjectName(u"formLayout_2")
|
||||||
|
self.label = QLabel(Dialog)
|
||||||
|
self.label.setObjectName(u"label")
|
||||||
|
|
||||||
|
self.formLayout_2.setWidget(0, QFormLayout.ItemRole.LabelRole, self.label)
|
||||||
|
|
||||||
|
self.line_ppn = QLineEdit(Dialog)
|
||||||
|
self.line_ppn.setObjectName(u"line_ppn")
|
||||||
|
|
||||||
|
self.formLayout_2.setWidget(0, QFormLayout.ItemRole.FieldRole, self.line_ppn)
|
||||||
|
|
||||||
|
self.label_2 = QLabel(Dialog)
|
||||||
|
self.label_2.setObjectName(u"label_2")
|
||||||
|
|
||||||
|
self.formLayout_2.setWidget(1, QFormLayout.ItemRole.LabelRole, self.label_2)
|
||||||
|
|
||||||
|
self.line_title = QLineEdit(Dialog)
|
||||||
|
self.line_title.setObjectName(u"line_title")
|
||||||
|
|
||||||
|
self.formLayout_2.setWidget(1, QFormLayout.ItemRole.FieldRole, self.line_title)
|
||||||
|
|
||||||
|
self.label_3 = QLabel(Dialog)
|
||||||
|
self.label_3.setObjectName(u"label_3")
|
||||||
|
|
||||||
|
self.formLayout_2.setWidget(2, QFormLayout.ItemRole.LabelRole, self.label_3)
|
||||||
|
|
||||||
|
self.line_signature = QLineEdit(Dialog)
|
||||||
|
self.line_signature.setObjectName(u"line_signature")
|
||||||
|
|
||||||
|
self.formLayout_2.setWidget(2, QFormLayout.ItemRole.FieldRole, self.line_signature)
|
||||||
|
|
||||||
|
self.label_4 = QLabel(Dialog)
|
||||||
|
self.label_4.setObjectName(u"label_4")
|
||||||
|
|
||||||
|
self.formLayout_2.setWidget(3, QFormLayout.ItemRole.LabelRole, self.label_4)
|
||||||
|
|
||||||
|
self.line_edition = QLineEdit(Dialog)
|
||||||
|
self.line_edition.setObjectName(u"line_edition")
|
||||||
|
|
||||||
|
self.formLayout_2.setWidget(3, QFormLayout.ItemRole.FieldRole, self.line_edition)
|
||||||
|
|
||||||
|
self.label_5 = QLabel(Dialog)
|
||||||
|
self.label_5.setObjectName(u"label_5")
|
||||||
|
|
||||||
|
self.formLayout_2.setWidget(4, QFormLayout.ItemRole.LabelRole, self.label_5)
|
||||||
|
|
||||||
|
self.line_publisher = QLineEdit(Dialog)
|
||||||
|
self.line_publisher.setObjectName(u"line_publisher")
|
||||||
|
|
||||||
|
self.formLayout_2.setWidget(4, QFormLayout.ItemRole.FieldRole, self.line_publisher)
|
||||||
|
|
||||||
|
self.label_6 = QLabel(Dialog)
|
||||||
|
self.label_6.setObjectName(u"label_6")
|
||||||
|
|
||||||
|
self.formLayout_2.setWidget(5, QFormLayout.ItemRole.LabelRole, self.label_6)
|
||||||
|
|
||||||
|
self.line_year = QLineEdit(Dialog)
|
||||||
|
self.line_year.setObjectName(u"line_year")
|
||||||
|
|
||||||
|
self.formLayout_2.setWidget(5, QFormLayout.ItemRole.FieldRole, self.line_year)
|
||||||
|
|
||||||
|
self.label_7 = QLabel(Dialog)
|
||||||
|
self.label_7.setObjectName(u"label_7")
|
||||||
|
|
||||||
|
self.formLayout_2.setWidget(6, QFormLayout.ItemRole.LabelRole, self.label_7)
|
||||||
|
|
||||||
|
self.line_pages = QLineEdit(Dialog)
|
||||||
|
self.line_pages.setObjectName(u"line_pages")
|
||||||
|
|
||||||
|
self.formLayout_2.setWidget(6, QFormLayout.ItemRole.FieldRole, self.line_pages)
|
||||||
|
|
||||||
|
self.label_8 = QLabel(Dialog)
|
||||||
|
self.label_8.setObjectName(u"label_8")
|
||||||
|
|
||||||
|
self.formLayout_2.setWidget(7, QFormLayout.ItemRole.LabelRole, self.label_8)
|
||||||
|
|
||||||
|
self.line_author = QLineEdit(Dialog)
|
||||||
|
self.line_author.setObjectName(u"line_author")
|
||||||
|
|
||||||
|
self.formLayout_2.setWidget(7, QFormLayout.ItemRole.FieldRole, self.line_author)
|
||||||
|
|
||||||
|
self.label_11 = QLabel(Dialog)
|
||||||
|
self.label_11.setObjectName(u"label_11")
|
||||||
|
|
||||||
|
self.formLayout_2.setWidget(8, QFormLayout.ItemRole.LabelRole, self.label_11)
|
||||||
|
|
||||||
|
self.line_isbn = QLineEdit(Dialog)
|
||||||
|
self.line_isbn.setObjectName(u"line_isbn")
|
||||||
|
|
||||||
|
self.formLayout_2.setWidget(8, QFormLayout.ItemRole.FieldRole, self.line_isbn)
|
||||||
|
|
||||||
|
|
||||||
|
self.gridLayout.addLayout(self.formLayout_2, 1, 0, 1, 1)
|
||||||
|
|
||||||
|
self.stackedWidget = QStackedWidget(Dialog)
|
||||||
|
self.stackedWidget.setObjectName(u"stackedWidget")
|
||||||
|
self.page = QWidget()
|
||||||
|
self.page.setObjectName(u"page")
|
||||||
|
self.stackedWidget.addWidget(self.page)
|
||||||
|
self.page_2 = QWidget()
|
||||||
|
self.page_2.setObjectName(u"page_2")
|
||||||
|
self.stackedWidget.addWidget(self.page_2)
|
||||||
|
|
||||||
|
self.gridLayout.addWidget(self.stackedWidget, 1, 1, 1, 1)
|
||||||
|
|
||||||
|
self.horizontalLayout = QHBoxLayout()
|
||||||
|
self.horizontalLayout.setObjectName(u"horizontalLayout")
|
||||||
|
self.btn_prev = QPushButton(Dialog)
|
||||||
|
self.btn_prev.setObjectName(u"btn_prev")
|
||||||
|
|
||||||
|
self.horizontalLayout.addWidget(self.btn_prev)
|
||||||
|
|
||||||
|
self.btn_next = QPushButton(Dialog)
|
||||||
|
self.btn_next.setObjectName(u"btn_next")
|
||||||
|
|
||||||
|
self.horizontalLayout.addWidget(self.btn_next)
|
||||||
|
|
||||||
|
|
||||||
|
self.gridLayout.addLayout(self.horizontalLayout, 2, 1, 1, 1)
|
||||||
|
|
||||||
|
|
||||||
|
self.retranslateUi(Dialog)
|
||||||
|
|
||||||
|
QMetaObject.connectSlotsByName(Dialog)
|
||||||
|
# setupUi
|
||||||
|
|
||||||
|
def retranslateUi(self, Dialog):
|
||||||
|
Dialog.setWindowTitle(QCoreApplication.translate("Dialog", u"Dialog", None))
|
||||||
|
self.label_10.setText(QCoreApplication.translate("Dialog", u"Ausgew\u00e4hltes Buch", None))
|
||||||
|
self.label_9.setText(QCoreApplication.translate("Dialog", u"Gefundenes Buch", None))
|
||||||
|
self.label_book_index.setText(QCoreApplication.translate("Dialog", u"TextLabel", None))
|
||||||
|
self.label.setText(QCoreApplication.translate("Dialog", u"PPN", None))
|
||||||
|
self.label_2.setText(QCoreApplication.translate("Dialog", u"Titel", None))
|
||||||
|
self.label_3.setText(QCoreApplication.translate("Dialog", u"Signatur", None))
|
||||||
|
self.label_4.setText(QCoreApplication.translate("Dialog", u"Auflage", None))
|
||||||
|
self.label_5.setText(QCoreApplication.translate("Dialog", u"Verlag", None))
|
||||||
|
self.label_6.setText(QCoreApplication.translate("Dialog", u"Jahr", None))
|
||||||
|
self.label_7.setText(QCoreApplication.translate("Dialog", u"Seiten", None))
|
||||||
|
self.label_8.setText(QCoreApplication.translate("Dialog", u"Autor", None))
|
||||||
|
self.label_11.setText(QCoreApplication.translate("Dialog", u"ISBN", None))
|
||||||
|
self.btn_prev.setText(QCoreApplication.translate("Dialog", u"Previous", None))
|
||||||
|
self.btn_next.setText(QCoreApplication.translate("Dialog", u"Next", None))
|
||||||
|
# retranslateUi
|
||||||
|
|
||||||
145
src/ui/widgets/widget_sources/new_edition_check_found_result.ui
Normal file
145
src/ui/widgets/widget_sources/new_edition_check_found_result.ui
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>Dialog</class>
|
||||||
|
<widget class="QDialog" name="Dialog">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>400</width>
|
||||||
|
<height>300</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Dialog</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QFormLayout" name="formLayout">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="text">
|
||||||
|
<string>PPN</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QLineEdit" name="line_ppn"/>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="label_2">
|
||||||
|
<property name="text">
|
||||||
|
<string>Titel</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QLineEdit" name="line_title"/>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QLabel" name="label_3">
|
||||||
|
<property name="text">
|
||||||
|
<string>Signatur</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QLineEdit" name="line_signature"/>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<widget class="QLabel" name="label_4">
|
||||||
|
<property name="text">
|
||||||
|
<string>Auflage</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="1">
|
||||||
|
<widget class="QLineEdit" name="line_edition"/>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="0">
|
||||||
|
<widget class="QLabel" name="label_5">
|
||||||
|
<property name="text">
|
||||||
|
<string>Verlag</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="1">
|
||||||
|
<widget class="QLineEdit" name="line_publisher"/>
|
||||||
|
</item>
|
||||||
|
<item row="5" column="0">
|
||||||
|
<widget class="QLabel" name="label_6">
|
||||||
|
<property name="text">
|
||||||
|
<string>Jahr</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="5" column="1">
|
||||||
|
<widget class="QLineEdit" name="line_year"/>
|
||||||
|
</item>
|
||||||
|
<item row="6" column="0">
|
||||||
|
<widget class="QLabel" name="label_7">
|
||||||
|
<property name="text">
|
||||||
|
<string>Seiten</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="6" column="1">
|
||||||
|
<widget class="QLineEdit" name="line_pages"/>
|
||||||
|
</item>
|
||||||
|
<item row="11" column="1">
|
||||||
|
<spacer name="verticalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item row="9" column="1">
|
||||||
|
<widget class="QCheckBox" name="checkBox">
|
||||||
|
<property name="text">
|
||||||
|
<string>Bestellen</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="8" column="0">
|
||||||
|
<widget class="QLabel" name="label_8">
|
||||||
|
<property name="text">
|
||||||
|
<string>Quelle</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="8" column="1">
|
||||||
|
<widget class="QLabel" name="line_source">
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
<property name="textFormat">
|
||||||
|
<enum>Qt::PlainText</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="7" column="0">
|
||||||
|
<widget class="QLabel" name="label_9">
|
||||||
|
<property name="text">
|
||||||
|
<string>ISBN</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="7" column="1">
|
||||||
|
<widget class="QLineEdit" name="line_isbn"/>
|
||||||
|
</item>
|
||||||
|
<item row="10" column="1">
|
||||||
|
<widget class="QLabel" name="in_library">
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
||||||
@@ -0,0 +1,155 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
## Form generated from reading UI file 'new_edition_check_found_result.ui'
|
||||||
|
##
|
||||||
|
## Created by: Qt User Interface Compiler version 6.9.2
|
||||||
|
##
|
||||||
|
## WARNING! All changes made in this file will be lost when recompiling UI file!
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
|
||||||
|
QMetaObject, QObject, QPoint, QRect,
|
||||||
|
QSize, QTime, QUrl, Qt)
|
||||||
|
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, QCheckBox, QDialog, QFormLayout,
|
||||||
|
QLabel, QLineEdit, QSizePolicy, QSpacerItem,
|
||||||
|
QWidget)
|
||||||
|
|
||||||
|
class Ui_Dialog(object):
|
||||||
|
def setupUi(self, Dialog):
|
||||||
|
if not Dialog.objectName():
|
||||||
|
Dialog.setObjectName(u"Dialog")
|
||||||
|
Dialog.resize(400, 300)
|
||||||
|
self.formLayout = QFormLayout(Dialog)
|
||||||
|
self.formLayout.setObjectName(u"formLayout")
|
||||||
|
self.label = QLabel(Dialog)
|
||||||
|
self.label.setObjectName(u"label")
|
||||||
|
|
||||||
|
self.formLayout.setWidget(0, QFormLayout.ItemRole.LabelRole, self.label)
|
||||||
|
|
||||||
|
self.line_ppn = QLineEdit(Dialog)
|
||||||
|
self.line_ppn.setObjectName(u"line_ppn")
|
||||||
|
|
||||||
|
self.formLayout.setWidget(0, QFormLayout.ItemRole.FieldRole, self.line_ppn)
|
||||||
|
|
||||||
|
self.label_2 = QLabel(Dialog)
|
||||||
|
self.label_2.setObjectName(u"label_2")
|
||||||
|
|
||||||
|
self.formLayout.setWidget(1, QFormLayout.ItemRole.LabelRole, self.label_2)
|
||||||
|
|
||||||
|
self.line_title = QLineEdit(Dialog)
|
||||||
|
self.line_title.setObjectName(u"line_title")
|
||||||
|
|
||||||
|
self.formLayout.setWidget(1, QFormLayout.ItemRole.FieldRole, self.line_title)
|
||||||
|
|
||||||
|
self.label_3 = QLabel(Dialog)
|
||||||
|
self.label_3.setObjectName(u"label_3")
|
||||||
|
|
||||||
|
self.formLayout.setWidget(2, QFormLayout.ItemRole.LabelRole, self.label_3)
|
||||||
|
|
||||||
|
self.line_signature = QLineEdit(Dialog)
|
||||||
|
self.line_signature.setObjectName(u"line_signature")
|
||||||
|
|
||||||
|
self.formLayout.setWidget(2, QFormLayout.ItemRole.FieldRole, self.line_signature)
|
||||||
|
|
||||||
|
self.label_4 = QLabel(Dialog)
|
||||||
|
self.label_4.setObjectName(u"label_4")
|
||||||
|
|
||||||
|
self.formLayout.setWidget(3, QFormLayout.ItemRole.LabelRole, self.label_4)
|
||||||
|
|
||||||
|
self.line_edition = QLineEdit(Dialog)
|
||||||
|
self.line_edition.setObjectName(u"line_edition")
|
||||||
|
|
||||||
|
self.formLayout.setWidget(3, QFormLayout.ItemRole.FieldRole, self.line_edition)
|
||||||
|
|
||||||
|
self.label_5 = QLabel(Dialog)
|
||||||
|
self.label_5.setObjectName(u"label_5")
|
||||||
|
|
||||||
|
self.formLayout.setWidget(4, QFormLayout.ItemRole.LabelRole, self.label_5)
|
||||||
|
|
||||||
|
self.line_publisher = QLineEdit(Dialog)
|
||||||
|
self.line_publisher.setObjectName(u"line_publisher")
|
||||||
|
|
||||||
|
self.formLayout.setWidget(4, QFormLayout.ItemRole.FieldRole, self.line_publisher)
|
||||||
|
|
||||||
|
self.label_6 = QLabel(Dialog)
|
||||||
|
self.label_6.setObjectName(u"label_6")
|
||||||
|
|
||||||
|
self.formLayout.setWidget(5, QFormLayout.ItemRole.LabelRole, self.label_6)
|
||||||
|
|
||||||
|
self.line_year = QLineEdit(Dialog)
|
||||||
|
self.line_year.setObjectName(u"line_year")
|
||||||
|
|
||||||
|
self.formLayout.setWidget(5, QFormLayout.ItemRole.FieldRole, self.line_year)
|
||||||
|
|
||||||
|
self.label_7 = QLabel(Dialog)
|
||||||
|
self.label_7.setObjectName(u"label_7")
|
||||||
|
|
||||||
|
self.formLayout.setWidget(6, QFormLayout.ItemRole.LabelRole, self.label_7)
|
||||||
|
|
||||||
|
self.line_pages = QLineEdit(Dialog)
|
||||||
|
self.line_pages.setObjectName(u"line_pages")
|
||||||
|
|
||||||
|
self.formLayout.setWidget(6, QFormLayout.ItemRole.FieldRole, self.line_pages)
|
||||||
|
|
||||||
|
self.verticalSpacer = QSpacerItem(20, 40, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding)
|
||||||
|
|
||||||
|
self.formLayout.setItem(11, QFormLayout.ItemRole.FieldRole, self.verticalSpacer)
|
||||||
|
|
||||||
|
self.checkBox = QCheckBox(Dialog)
|
||||||
|
self.checkBox.setObjectName(u"checkBox")
|
||||||
|
|
||||||
|
self.formLayout.setWidget(9, QFormLayout.ItemRole.FieldRole, self.checkBox)
|
||||||
|
|
||||||
|
self.label_8 = QLabel(Dialog)
|
||||||
|
self.label_8.setObjectName(u"label_8")
|
||||||
|
|
||||||
|
self.formLayout.setWidget(8, QFormLayout.ItemRole.LabelRole, self.label_8)
|
||||||
|
|
||||||
|
self.line_source = QLabel(Dialog)
|
||||||
|
self.line_source.setObjectName(u"line_source")
|
||||||
|
self.line_source.setTextFormat(Qt.PlainText)
|
||||||
|
|
||||||
|
self.formLayout.setWidget(8, QFormLayout.ItemRole.FieldRole, self.line_source)
|
||||||
|
|
||||||
|
self.label_9 = QLabel(Dialog)
|
||||||
|
self.label_9.setObjectName(u"label_9")
|
||||||
|
|
||||||
|
self.formLayout.setWidget(7, QFormLayout.ItemRole.LabelRole, self.label_9)
|
||||||
|
|
||||||
|
self.line_isbn = QLineEdit(Dialog)
|
||||||
|
self.line_isbn.setObjectName(u"line_isbn")
|
||||||
|
|
||||||
|
self.formLayout.setWidget(7, QFormLayout.ItemRole.FieldRole, self.line_isbn)
|
||||||
|
|
||||||
|
self.in_library = QLabel(Dialog)
|
||||||
|
self.in_library.setObjectName(u"in_library")
|
||||||
|
|
||||||
|
self.formLayout.setWidget(10, QFormLayout.ItemRole.FieldRole, self.in_library)
|
||||||
|
|
||||||
|
|
||||||
|
self.retranslateUi(Dialog)
|
||||||
|
|
||||||
|
QMetaObject.connectSlotsByName(Dialog)
|
||||||
|
# setupUi
|
||||||
|
|
||||||
|
def retranslateUi(self, Dialog):
|
||||||
|
Dialog.setWindowTitle(QCoreApplication.translate("Dialog", u"Dialog", None))
|
||||||
|
self.label.setText(QCoreApplication.translate("Dialog", u"PPN", None))
|
||||||
|
self.label_2.setText(QCoreApplication.translate("Dialog", u"Titel", None))
|
||||||
|
self.label_3.setText(QCoreApplication.translate("Dialog", u"Signatur", None))
|
||||||
|
self.label_4.setText(QCoreApplication.translate("Dialog", u"Auflage", None))
|
||||||
|
self.label_5.setText(QCoreApplication.translate("Dialog", u"Verlag", None))
|
||||||
|
self.label_6.setText(QCoreApplication.translate("Dialog", u"Jahr", None))
|
||||||
|
self.label_7.setText(QCoreApplication.translate("Dialog", u"Seiten", None))
|
||||||
|
self.checkBox.setText(QCoreApplication.translate("Dialog", u"Bestellen", None))
|
||||||
|
self.label_8.setText(QCoreApplication.translate("Dialog", u"Quelle", None))
|
||||||
|
self.line_source.setText("")
|
||||||
|
self.label_9.setText(QCoreApplication.translate("Dialog", u"ISBN", None))
|
||||||
|
self.in_library.setText("")
|
||||||
|
# retranslateUi
|
||||||
|
|
||||||
46
src/ui/widgets/widget_sources/new_edition_check_selector.ui
Normal file
46
src/ui/widgets/widget_sources/new_edition_check_selector.ui
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>Dialog</class>
|
||||||
|
<widget class="QDialog" name="Dialog">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>475</width>
|
||||||
|
<height>66</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Dialog</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="text">
|
||||||
|
<string>Soll nur der Apparat geprüft werden, oder sollen alle Medien des Profs geprüft werden?</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="btn_apparat">
|
||||||
|
<property name="text">
|
||||||
|
<string>Apparat</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="btn_prof">
|
||||||
|
<property name="text">
|
||||||
|
<string>Prof</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
## Form generated from reading UI file 'new_edition_check_selector.ui'
|
||||||
|
##
|
||||||
|
## Created by: Qt User Interface Compiler version 6.9.2
|
||||||
|
##
|
||||||
|
## WARNING! All changes made in this file will be lost when recompiling UI file!
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
|
||||||
|
QMetaObject, QObject, QPoint, QRect,
|
||||||
|
QSize, QTime, QUrl, Qt)
|
||||||
|
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, QDialog, QHBoxLayout, QLabel,
|
||||||
|
QPushButton, QSizePolicy, QVBoxLayout, QWidget)
|
||||||
|
|
||||||
|
class Ui_Dialog(object):
|
||||||
|
def setupUi(self, Dialog):
|
||||||
|
if not Dialog.objectName():
|
||||||
|
Dialog.setObjectName(u"Dialog")
|
||||||
|
Dialog.resize(475, 66)
|
||||||
|
self.verticalLayout = QVBoxLayout(Dialog)
|
||||||
|
self.verticalLayout.setObjectName(u"verticalLayout")
|
||||||
|
self.label = QLabel(Dialog)
|
||||||
|
self.label.setObjectName(u"label")
|
||||||
|
|
||||||
|
self.verticalLayout.addWidget(self.label)
|
||||||
|
|
||||||
|
self.horizontalLayout = QHBoxLayout()
|
||||||
|
self.horizontalLayout.setObjectName(u"horizontalLayout")
|
||||||
|
self.btn_apparat = QPushButton(Dialog)
|
||||||
|
self.btn_apparat.setObjectName(u"btn_apparat")
|
||||||
|
|
||||||
|
self.horizontalLayout.addWidget(self.btn_apparat)
|
||||||
|
|
||||||
|
self.btn_prof = QPushButton(Dialog)
|
||||||
|
self.btn_prof.setObjectName(u"btn_prof")
|
||||||
|
|
||||||
|
self.horizontalLayout.addWidget(self.btn_prof)
|
||||||
|
|
||||||
|
|
||||||
|
self.verticalLayout.addLayout(self.horizontalLayout)
|
||||||
|
|
||||||
|
|
||||||
|
self.retranslateUi(Dialog)
|
||||||
|
|
||||||
|
QMetaObject.connectSlotsByName(Dialog)
|
||||||
|
# setupUi
|
||||||
|
|
||||||
|
def retranslateUi(self, Dialog):
|
||||||
|
Dialog.setWindowTitle(QCoreApplication.translate("Dialog", u"Dialog", None))
|
||||||
|
self.label.setText(QCoreApplication.translate("Dialog", u"Soll nur der Apparat gepr\u00fcft werden, oder sollen alle Medien des Profs gepr\u00fcft werden?", None))
|
||||||
|
self.btn_apparat.setText(QCoreApplication.translate("Dialog", u"Apparat", None))
|
||||||
|
self.btn_prof.setText(QCoreApplication.translate("Dialog", u"Prof", None))
|
||||||
|
# retranslateUi
|
||||||
|
|
||||||
92
src/ui/widgets/widget_sources/new_edition_check_ui.py
Normal file
92
src/ui/widgets/widget_sources/new_edition_check_ui.py
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
## Form generated from reading UI file 'new_edition_check.ui'
|
||||||
|
##
|
||||||
|
## Created by: Qt User Interface Compiler version 6.9.2
|
||||||
|
##
|
||||||
|
## WARNING! All changes made in this file will be lost when recompiling UI file!
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
|
||||||
|
QMetaObject, QObject, QPoint, QRect,
|
||||||
|
QSize, QTime, QUrl, Qt)
|
||||||
|
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, QDialog, QFrame, QHBoxLayout,
|
||||||
|
QProgressBar, QPushButton, QSizePolicy, QStackedWidget,
|
||||||
|
QVBoxLayout, QWidget)
|
||||||
|
|
||||||
|
class Ui_Dialog(object):
|
||||||
|
def setupUi(self, Dialog):
|
||||||
|
if not Dialog.objectName():
|
||||||
|
Dialog.setObjectName(u"Dialog")
|
||||||
|
Dialog.resize(632, 726)
|
||||||
|
self.verticalLayout_4 = QVBoxLayout(Dialog)
|
||||||
|
self.verticalLayout_4.setObjectName(u"verticalLayout_4")
|
||||||
|
self.verticalLayout_3 = QVBoxLayout()
|
||||||
|
self.verticalLayout_3.setObjectName(u"verticalLayout_3")
|
||||||
|
self.frame = QFrame(Dialog)
|
||||||
|
self.frame.setObjectName(u"frame")
|
||||||
|
self.frame.setFrameShape(QFrame.StyledPanel)
|
||||||
|
self.frame.setFrameShadow(QFrame.Raised)
|
||||||
|
self.verticalLayout_5 = QVBoxLayout(self.frame)
|
||||||
|
self.verticalLayout_5.setObjectName(u"verticalLayout_5")
|
||||||
|
self.stackedWidget = QStackedWidget(self.frame)
|
||||||
|
self.stackedWidget.setObjectName(u"stackedWidget")
|
||||||
|
self.page = QWidget()
|
||||||
|
self.page.setObjectName(u"page")
|
||||||
|
self.stackedWidget.addWidget(self.page)
|
||||||
|
self.page_2 = QWidget()
|
||||||
|
self.page_2.setObjectName(u"page_2")
|
||||||
|
self.stackedWidget.addWidget(self.page_2)
|
||||||
|
|
||||||
|
self.verticalLayout_5.addWidget(self.stackedWidget)
|
||||||
|
|
||||||
|
|
||||||
|
self.verticalLayout_3.addWidget(self.frame)
|
||||||
|
|
||||||
|
self.progressBar = QProgressBar(Dialog)
|
||||||
|
self.progressBar.setObjectName(u"progressBar")
|
||||||
|
self.progressBar.setValue(24)
|
||||||
|
|
||||||
|
self.verticalLayout_3.addWidget(self.progressBar)
|
||||||
|
|
||||||
|
self.horizontalLayout = QHBoxLayout()
|
||||||
|
self.horizontalLayout.setObjectName(u"horizontalLayout")
|
||||||
|
self.btn_prev = QPushButton(Dialog)
|
||||||
|
self.btn_prev.setObjectName(u"btn_prev")
|
||||||
|
|
||||||
|
self.horizontalLayout.addWidget(self.btn_prev)
|
||||||
|
|
||||||
|
self.btn_next = QPushButton(Dialog)
|
||||||
|
self.btn_next.setObjectName(u"btn_next")
|
||||||
|
|
||||||
|
self.horizontalLayout.addWidget(self.btn_next)
|
||||||
|
|
||||||
|
self.btn_finish = QPushButton(Dialog)
|
||||||
|
self.btn_finish.setObjectName(u"btn_finish")
|
||||||
|
|
||||||
|
self.horizontalLayout.addWidget(self.btn_finish)
|
||||||
|
|
||||||
|
|
||||||
|
self.verticalLayout_3.addLayout(self.horizontalLayout)
|
||||||
|
|
||||||
|
|
||||||
|
self.verticalLayout_4.addLayout(self.verticalLayout_3)
|
||||||
|
|
||||||
|
|
||||||
|
self.retranslateUi(Dialog)
|
||||||
|
|
||||||
|
QMetaObject.connectSlotsByName(Dialog)
|
||||||
|
# setupUi
|
||||||
|
|
||||||
|
def retranslateUi(self, Dialog):
|
||||||
|
Dialog.setWindowTitle(QCoreApplication.translate("Dialog", u"Dialog", None))
|
||||||
|
self.btn_prev.setText(QCoreApplication.translate("Dialog", u"Previous", None))
|
||||||
|
self.btn_next.setText(QCoreApplication.translate("Dialog", u"Next", None))
|
||||||
|
self.btn_finish.setText(QCoreApplication.translate("Dialog", u"Finish", None))
|
||||||
|
# retranslateUi
|
||||||
|
|
||||||
@@ -357,6 +357,13 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="btn_extendSelection">
|
||||||
|
<property name="text">
|
||||||
|
<string>Ausgewählte Verlängern</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<spacer name="horizontalSpacer_5">
|
<spacer name="horizontalSpacer_5">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
|
|||||||
@@ -3,131 +3,142 @@
|
|||||||
################################################################################
|
################################################################################
|
||||||
## Form generated from reading UI file 'search_statistic_page.ui'
|
## Form generated from reading UI file 'search_statistic_page.ui'
|
||||||
##
|
##
|
||||||
## Created by: Qt User Interface Compiler version 6.9.1
|
## Created by: Qt User Interface Compiler version 6.9.2
|
||||||
##
|
##
|
||||||
## WARNING! All changes made in this file will be lost when recompiling UI file!
|
## WARNING! All changes made in this file will be lost when recompiling UI file!
|
||||||
################################################################################
|
################################################################################
|
||||||
|
|
||||||
from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
|
from PySide6.QtCore import QCoreApplication, Qt
|
||||||
QMetaObject, QObject, QPoint, QRect,
|
from PySide6.QtWidgets import (
|
||||||
QSize, QTime, QUrl, Qt)
|
QAbstractItemView,
|
||||||
from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
|
QCheckBox,
|
||||||
QFont, QFontDatabase, QGradient, QIcon,
|
QComboBox,
|
||||||
QImage, QKeySequence, QLinearGradient, QPainter,
|
QFrame,
|
||||||
QPalette, QPixmap, QRadialGradient, QTransform)
|
QGridLayout,
|
||||||
from PySide6.QtWidgets import (QAbstractItemView, QApplication, QCheckBox, QComboBox,
|
QHBoxLayout,
|
||||||
QDialog, QFrame, QGridLayout, QHBoxLayout,
|
QLabel,
|
||||||
QHeaderView, QLabel, QLayout, QLineEdit,
|
QLayout,
|
||||||
QPushButton, QSizePolicy, QSpacerItem, QStackedWidget,
|
QLineEdit,
|
||||||
QTabWidget, QTableWidget, QTableWidgetItem, QVBoxLayout,
|
QPushButton,
|
||||||
QWidget)
|
QSizePolicy,
|
||||||
|
QSpacerItem,
|
||||||
|
QStackedWidget,
|
||||||
|
QTableWidget,
|
||||||
|
QTableWidgetItem,
|
||||||
|
QTabWidget,
|
||||||
|
QVBoxLayout,
|
||||||
|
QWidget,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class Ui_Dialog(object):
|
class Ui_Dialog(object):
|
||||||
def setupUi(self, Dialog):
|
def setupUi(self, Dialog):
|
||||||
if not Dialog.objectName():
|
if not Dialog.objectName():
|
||||||
Dialog.setObjectName(u"Dialog")
|
Dialog.setObjectName("Dialog")
|
||||||
Dialog.resize(1244, 767)
|
Dialog.resize(1244, 767)
|
||||||
self.verticalLayout = QVBoxLayout(Dialog)
|
self.verticalLayout = QVBoxLayout(Dialog)
|
||||||
self.verticalLayout.setObjectName(u"verticalLayout")
|
self.verticalLayout.setObjectName("verticalLayout")
|
||||||
self.tabWidget_2 = QTabWidget(Dialog)
|
self.tabWidget_2 = QTabWidget(Dialog)
|
||||||
self.tabWidget_2.setObjectName(u"tabWidget_2")
|
self.tabWidget_2.setObjectName("tabWidget_2")
|
||||||
self.tabWidget_2.setMaximumSize(QSize(16777215, 250))
|
self.tabWidget_2.setMaximumSize(QSize(16777215, 250))
|
||||||
self.tabWidget_2.setFocusPolicy(Qt.ClickFocus)
|
self.tabWidget_2.setFocusPolicy(Qt.ClickFocus)
|
||||||
self.tabWidget_2.setTabPosition(QTabWidget.North)
|
self.tabWidget_2.setTabPosition(QTabWidget.North)
|
||||||
self.tabWidget_2.setTabShape(QTabWidget.Rounded)
|
self.tabWidget_2.setTabShape(QTabWidget.Rounded)
|
||||||
self.tab_3 = QWidget()
|
self.tab_3 = QWidget()
|
||||||
self.tab_3.setObjectName(u"tab_3")
|
self.tab_3.setObjectName("tab_3")
|
||||||
self.horizontalLayout_2 = QHBoxLayout(self.tab_3)
|
self.horizontalLayout_2 = QHBoxLayout(self.tab_3)
|
||||||
self.horizontalLayout_2.setObjectName(u"horizontalLayout_2")
|
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
|
||||||
self.gridLayout_3 = QGridLayout()
|
self.gridLayout_3 = QGridLayout()
|
||||||
self.gridLayout_3.setObjectName(u"gridLayout_3")
|
self.gridLayout_3.setObjectName("gridLayout_3")
|
||||||
self.box_semester = QComboBox(self.tab_3)
|
self.box_semester = QComboBox(self.tab_3)
|
||||||
self.box_semester.setObjectName(u"box_semester")
|
self.box_semester.setObjectName("box_semester")
|
||||||
self.box_semester.setEditable(True)
|
self.box_semester.setEditable(True)
|
||||||
|
|
||||||
self.gridLayout_3.addWidget(self.box_semester, 0, 3, 1, 1)
|
self.gridLayout_3.addWidget(self.box_semester, 0, 3, 1, 1)
|
||||||
|
|
||||||
self.label_18 = QLabel(self.tab_3)
|
self.label_18 = QLabel(self.tab_3)
|
||||||
self.label_18.setObjectName(u"label_18")
|
self.label_18.setObjectName("label_18")
|
||||||
|
|
||||||
self.gridLayout_3.addWidget(self.label_18, 2, 2, 1, 1)
|
self.gridLayout_3.addWidget(self.label_18, 2, 2, 1, 1)
|
||||||
|
|
||||||
self.box_fach = QComboBox(self.tab_3)
|
self.box_fach = QComboBox(self.tab_3)
|
||||||
self.box_fach.setObjectName(u"box_fach")
|
self.box_fach.setObjectName("box_fach")
|
||||||
self.box_fach.setEditable(True)
|
self.box_fach.setEditable(True)
|
||||||
|
|
||||||
self.gridLayout_3.addWidget(self.box_fach, 2, 1, 1, 1)
|
self.gridLayout_3.addWidget(self.box_fach, 2, 1, 1, 1)
|
||||||
|
|
||||||
self.label_15 = QLabel(self.tab_3)
|
self.label_15 = QLabel(self.tab_3)
|
||||||
self.label_15.setObjectName(u"label_15")
|
self.label_15.setObjectName("label_15")
|
||||||
|
|
||||||
self.gridLayout_3.addWidget(self.label_15, 3, 0, 1, 1)
|
self.gridLayout_3.addWidget(self.label_15, 3, 0, 1, 1)
|
||||||
|
|
||||||
self.label_11 = QLabel(self.tab_3)
|
self.label_11 = QLabel(self.tab_3)
|
||||||
self.label_11.setObjectName(u"label_11")
|
self.label_11.setObjectName("label_11")
|
||||||
|
|
||||||
self.gridLayout_3.addWidget(self.label_11, 1, 0, 1, 1)
|
self.gridLayout_3.addWidget(self.label_11, 1, 0, 1, 1)
|
||||||
|
|
||||||
self.verticalSpacer_3 = QSpacerItem(20, 40, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding)
|
self.verticalSpacer_3 = QSpacerItem(
|
||||||
|
20, 40, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding
|
||||||
|
)
|
||||||
|
|
||||||
self.gridLayout_3.addItem(self.verticalSpacer_3, 4, 0, 1, 1)
|
self.gridLayout_3.addItem(self.verticalSpacer_3, 4, 0, 1, 1)
|
||||||
|
|
||||||
self.label_7 = QLabel(self.tab_3)
|
self.label_7 = QLabel(self.tab_3)
|
||||||
self.label_7.setObjectName(u"label_7")
|
self.label_7.setObjectName("label_7")
|
||||||
|
|
||||||
self.gridLayout_3.addWidget(self.label_7, 0, 0, 1, 1)
|
self.gridLayout_3.addWidget(self.label_7, 0, 0, 1, 1)
|
||||||
|
|
||||||
self.label_17 = QLabel(self.tab_3)
|
self.label_17 = QLabel(self.tab_3)
|
||||||
self.label_17.setObjectName(u"label_17")
|
self.label_17.setObjectName("label_17")
|
||||||
|
|
||||||
self.gridLayout_3.addWidget(self.label_17, 0, 2, 1, 1)
|
self.gridLayout_3.addWidget(self.label_17, 0, 2, 1, 1)
|
||||||
|
|
||||||
self.box_appnrs = QComboBox(self.tab_3)
|
self.box_appnrs = QComboBox(self.tab_3)
|
||||||
self.box_appnrs.setObjectName(u"box_appnrs")
|
self.box_appnrs.setObjectName("box_appnrs")
|
||||||
self.box_appnrs.setEditable(True)
|
self.box_appnrs.setEditable(True)
|
||||||
|
|
||||||
self.gridLayout_3.addWidget(self.box_appnrs, 0, 1, 1, 1)
|
self.gridLayout_3.addWidget(self.box_appnrs, 0, 1, 1, 1)
|
||||||
|
|
||||||
self.box_dauerapp = QComboBox(self.tab_3)
|
self.box_dauerapp = QComboBox(self.tab_3)
|
||||||
self.box_dauerapp.setObjectName(u"box_dauerapp")
|
self.box_dauerapp.setObjectName("box_dauerapp")
|
||||||
|
|
||||||
self.gridLayout_3.addWidget(self.box_dauerapp, 2, 3, 1, 1)
|
self.gridLayout_3.addWidget(self.box_dauerapp, 2, 3, 1, 1)
|
||||||
|
|
||||||
self.box_person = QComboBox(self.tab_3)
|
self.box_person = QComboBox(self.tab_3)
|
||||||
self.box_person.setObjectName(u"box_person")
|
self.box_person.setObjectName("box_person")
|
||||||
self.box_person.setEditable(True)
|
self.box_person.setEditable(True)
|
||||||
|
|
||||||
self.gridLayout_3.addWidget(self.box_person, 1, 1, 1, 1)
|
self.gridLayout_3.addWidget(self.box_person, 1, 1, 1, 1)
|
||||||
|
|
||||||
self.box_erstellsemester = QComboBox(self.tab_3)
|
self.box_erstellsemester = QComboBox(self.tab_3)
|
||||||
self.box_erstellsemester.setObjectName(u"box_erstellsemester")
|
self.box_erstellsemester.setObjectName("box_erstellsemester")
|
||||||
self.box_erstellsemester.setEditable(True)
|
self.box_erstellsemester.setEditable(True)
|
||||||
|
|
||||||
self.gridLayout_3.addWidget(self.box_erstellsemester, 1, 3, 1, 1)
|
self.gridLayout_3.addWidget(self.box_erstellsemester, 1, 3, 1, 1)
|
||||||
|
|
||||||
self.label_19 = QLabel(self.tab_3)
|
self.label_19 = QLabel(self.tab_3)
|
||||||
self.label_19.setObjectName(u"label_19")
|
self.label_19.setObjectName("label_19")
|
||||||
|
|
||||||
self.gridLayout_3.addWidget(self.label_19, 1, 2, 1, 1)
|
self.gridLayout_3.addWidget(self.label_19, 1, 2, 1, 1)
|
||||||
|
|
||||||
self.label_16 = QLabel(self.tab_3)
|
self.label_16 = QLabel(self.tab_3)
|
||||||
self.label_16.setObjectName(u"label_16")
|
self.label_16.setObjectName("label_16")
|
||||||
|
|
||||||
self.gridLayout_3.addWidget(self.label_16, 2, 0, 1, 1)
|
self.gridLayout_3.addWidget(self.label_16, 2, 0, 1, 1)
|
||||||
|
|
||||||
self.check_deletable = QCheckBox(self.tab_3)
|
self.check_deletable = QCheckBox(self.tab_3)
|
||||||
self.check_deletable.setObjectName(u"check_deletable")
|
self.check_deletable.setObjectName("check_deletable")
|
||||||
self.check_deletable.setFocusPolicy(Qt.StrongFocus)
|
self.check_deletable.setFocusPolicy(Qt.StrongFocus)
|
||||||
|
|
||||||
self.gridLayout_3.addWidget(self.check_deletable, 3, 1, 1, 1)
|
self.gridLayout_3.addWidget(self.check_deletable, 3, 1, 1, 1)
|
||||||
|
|
||||||
self.btn_search = QPushButton(self.tab_3)
|
self.btn_search = QPushButton(self.tab_3)
|
||||||
self.btn_search.setObjectName(u"btn_search")
|
self.btn_search.setObjectName("btn_search")
|
||||||
|
|
||||||
self.gridLayout_3.addWidget(self.btn_search, 5, 0, 1, 1)
|
self.gridLayout_3.addWidget(self.btn_search, 5, 0, 1, 1)
|
||||||
|
|
||||||
self.db_err_message = QLabel(self.tab_3)
|
self.db_err_message = QLabel(self.tab_3)
|
||||||
self.db_err_message.setObjectName(u"db_err_message")
|
self.db_err_message.setObjectName("db_err_message")
|
||||||
|
|
||||||
self.gridLayout_3.addWidget(self.db_err_message, 5, 1, 1, 1)
|
self.gridLayout_3.addWidget(self.db_err_message, 5, 1, 1, 1)
|
||||||
|
|
||||||
@@ -139,7 +150,9 @@ class Ui_Dialog(object):
|
|||||||
|
|
||||||
self.horizontalLayout_2.addLayout(self.gridLayout_3)
|
self.horizontalLayout_2.addLayout(self.gridLayout_3)
|
||||||
|
|
||||||
self.horizontalSpacer = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum)
|
self.horizontalSpacer = QSpacerItem(
|
||||||
|
40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum
|
||||||
|
)
|
||||||
|
|
||||||
self.horizontalLayout_2.addItem(self.horizontalSpacer)
|
self.horizontalLayout_2.addItem(self.horizontalSpacer)
|
||||||
|
|
||||||
@@ -147,58 +160,60 @@ class Ui_Dialog(object):
|
|||||||
self.horizontalLayout_2.setStretch(1, 1)
|
self.horizontalLayout_2.setStretch(1, 1)
|
||||||
self.tabWidget_2.addTab(self.tab_3, "")
|
self.tabWidget_2.addTab(self.tab_3, "")
|
||||||
self.tab_4 = QWidget()
|
self.tab_4 = QWidget()
|
||||||
self.tab_4.setObjectName(u"tab_4")
|
self.tab_4.setObjectName("tab_4")
|
||||||
self.horizontalLayout_3 = QHBoxLayout(self.tab_4)
|
self.horizontalLayout_3 = QHBoxLayout(self.tab_4)
|
||||||
self.horizontalLayout_3.setObjectName(u"horizontalLayout_3")
|
self.horizontalLayout_3.setObjectName("horizontalLayout_3")
|
||||||
self.gridLayout = QGridLayout()
|
self.gridLayout = QGridLayout()
|
||||||
self.gridLayout.setObjectName(u"gridLayout")
|
self.gridLayout.setObjectName("gridLayout")
|
||||||
self.search_by_signature = QLineEdit(self.tab_4)
|
self.search_by_signature = QLineEdit(self.tab_4)
|
||||||
self.search_by_signature.setObjectName(u"search_by_signature")
|
self.search_by_signature.setObjectName("search_by_signature")
|
||||||
self.search_by_signature.setFocusPolicy(Qt.ClickFocus)
|
self.search_by_signature.setFocusPolicy(Qt.ClickFocus)
|
||||||
self.search_by_signature.setClearButtonEnabled(True)
|
self.search_by_signature.setClearButtonEnabled(True)
|
||||||
|
|
||||||
self.gridLayout.addWidget(self.search_by_signature, 0, 1, 1, 1)
|
self.gridLayout.addWidget(self.search_by_signature, 0, 1, 1, 1)
|
||||||
|
|
||||||
self.label_25 = QLabel(self.tab_4)
|
self.label_25 = QLabel(self.tab_4)
|
||||||
self.label_25.setObjectName(u"label_25")
|
self.label_25.setObjectName("label_25")
|
||||||
|
|
||||||
self.gridLayout.addWidget(self.label_25, 0, 0, 1, 1)
|
self.gridLayout.addWidget(self.label_25, 0, 0, 1, 1)
|
||||||
|
|
||||||
self.verticalSpacer = QSpacerItem(20, 40, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding)
|
self.verticalSpacer = QSpacerItem(
|
||||||
|
20, 40, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding
|
||||||
|
)
|
||||||
|
|
||||||
self.gridLayout.addItem(self.verticalSpacer, 5, 0, 1, 1)
|
self.gridLayout.addItem(self.verticalSpacer, 5, 0, 1, 1)
|
||||||
|
|
||||||
self.search_by_title = QLineEdit(self.tab_4)
|
self.search_by_title = QLineEdit(self.tab_4)
|
||||||
self.search_by_title.setObjectName(u"search_by_title")
|
self.search_by_title.setObjectName("search_by_title")
|
||||||
self.search_by_title.setFocusPolicy(Qt.ClickFocus)
|
self.search_by_title.setFocusPolicy(Qt.ClickFocus)
|
||||||
self.search_by_title.setClearButtonEnabled(True)
|
self.search_by_title.setClearButtonEnabled(True)
|
||||||
|
|
||||||
self.gridLayout.addWidget(self.search_by_title, 1, 1, 1, 1)
|
self.gridLayout.addWidget(self.search_by_title, 1, 1, 1, 1)
|
||||||
|
|
||||||
self.label_26 = QLabel(self.tab_4)
|
self.label_26 = QLabel(self.tab_4)
|
||||||
self.label_26.setObjectName(u"label_26")
|
self.label_26.setObjectName("label_26")
|
||||||
|
|
||||||
self.gridLayout.addWidget(self.label_26, 1, 0, 1, 1)
|
self.gridLayout.addWidget(self.label_26, 1, 0, 1, 1)
|
||||||
|
|
||||||
self.horizontalLayout_4 = QHBoxLayout()
|
self.horizontalLayout_4 = QHBoxLayout()
|
||||||
self.horizontalLayout_4.setObjectName(u"horizontalLayout_4")
|
self.horizontalLayout_4.setObjectName("horizontalLayout_4")
|
||||||
self.label = QLabel(self.tab_4)
|
self.label = QLabel(self.tab_4)
|
||||||
self.label.setObjectName(u"label")
|
self.label.setObjectName("label")
|
||||||
|
|
||||||
self.horizontalLayout_4.addWidget(self.label)
|
self.horizontalLayout_4.addWidget(self.label)
|
||||||
|
|
||||||
self.no_result = QLabel(self.tab_4)
|
self.no_result = QLabel(self.tab_4)
|
||||||
self.no_result.setObjectName(u"no_result")
|
self.no_result.setObjectName("no_result")
|
||||||
|
|
||||||
self.horizontalLayout_4.addWidget(self.no_result)
|
self.horizontalLayout_4.addWidget(self.no_result)
|
||||||
|
|
||||||
|
|
||||||
self.gridLayout.addLayout(self.horizontalLayout_4, 3, 1, 1, 1)
|
self.gridLayout.addLayout(self.horizontalLayout_4, 3, 1, 1, 1)
|
||||||
|
|
||||||
|
|
||||||
self.horizontalLayout_3.addLayout(self.gridLayout)
|
self.horizontalLayout_3.addLayout(self.gridLayout)
|
||||||
|
|
||||||
self.horizontalSpacer_2 = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum)
|
self.horizontalSpacer_2 = QSpacerItem(
|
||||||
|
40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum
|
||||||
|
)
|
||||||
|
|
||||||
self.horizontalLayout_3.addItem(self.horizontalSpacer_2)
|
self.horizontalLayout_3.addItem(self.horizontalSpacer_2)
|
||||||
|
|
||||||
@@ -209,54 +224,64 @@ class Ui_Dialog(object):
|
|||||||
self.verticalLayout.addWidget(self.tabWidget_2)
|
self.verticalLayout.addWidget(self.tabWidget_2)
|
||||||
|
|
||||||
self.verticalLayout_3 = QVBoxLayout()
|
self.verticalLayout_3 = QVBoxLayout()
|
||||||
self.verticalLayout_3.setObjectName(u"verticalLayout_3")
|
self.verticalLayout_3.setObjectName("verticalLayout_3")
|
||||||
self.verticalLayout_3.setSizeConstraint(QLayout.SetDefaultConstraint)
|
self.verticalLayout_3.setSizeConstraint(QLayout.SetDefaultConstraint)
|
||||||
self.stackedWidget_4 = QStackedWidget(Dialog)
|
self.stackedWidget_4 = QStackedWidget(Dialog)
|
||||||
self.stackedWidget_4.setObjectName(u"stackedWidget_4")
|
self.stackedWidget_4.setObjectName("stackedWidget_4")
|
||||||
sizePolicy = QSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
|
sizePolicy = QSizePolicy(
|
||||||
|
QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding
|
||||||
|
)
|
||||||
sizePolicy.setHorizontalStretch(0)
|
sizePolicy.setHorizontalStretch(0)
|
||||||
sizePolicy.setVerticalStretch(0)
|
sizePolicy.setVerticalStretch(0)
|
||||||
sizePolicy.setHeightForWidth(self.stackedWidget_4.sizePolicy().hasHeightForWidth())
|
sizePolicy.setHeightForWidth(
|
||||||
|
self.stackedWidget_4.sizePolicy().hasHeightForWidth()
|
||||||
|
)
|
||||||
self.stackedWidget_4.setSizePolicy(sizePolicy)
|
self.stackedWidget_4.setSizePolicy(sizePolicy)
|
||||||
self.stackedWidget_4.setFrameShape(QFrame.StyledPanel)
|
self.stackedWidget_4.setFrameShape(QFrame.StyledPanel)
|
||||||
self.stackedWidget_4.setFrameShadow(QFrame.Raised)
|
self.stackedWidget_4.setFrameShadow(QFrame.Raised)
|
||||||
self.apparatResult = QWidget()
|
self.apparatResult = QWidget()
|
||||||
self.apparatResult.setObjectName(u"apparatResult")
|
self.apparatResult.setObjectName("apparatResult")
|
||||||
self.horizontalLayout = QHBoxLayout(self.apparatResult)
|
self.horizontalLayout = QHBoxLayout(self.apparatResult)
|
||||||
self.horizontalLayout.setObjectName(u"horizontalLayout")
|
self.horizontalLayout.setObjectName("horizontalLayout")
|
||||||
self.app_results = QWidget(self.apparatResult)
|
self.app_results = QWidget(self.apparatResult)
|
||||||
self.app_results.setObjectName(u"app_results")
|
self.app_results.setObjectName("app_results")
|
||||||
self.verticalLayout_6 = QVBoxLayout(self.app_results)
|
self.verticalLayout_6 = QVBoxLayout(self.app_results)
|
||||||
self.verticalLayout_6.setObjectName(u"verticalLayout_6")
|
self.verticalLayout_6.setObjectName("verticalLayout_6")
|
||||||
self.verticalLayout_4 = QVBoxLayout()
|
self.verticalLayout_4 = QVBoxLayout()
|
||||||
self.verticalLayout_4.setObjectName(u"verticalLayout_4")
|
self.verticalLayout_4.setObjectName("verticalLayout_4")
|
||||||
self.horizontalLayout_7 = QHBoxLayout()
|
self.horizontalLayout_7 = QHBoxLayout()
|
||||||
self.horizontalLayout_7.setObjectName(u"horizontalLayout_7")
|
self.horizontalLayout_7.setObjectName("horizontalLayout_7")
|
||||||
self.verticalLayout_5 = QVBoxLayout()
|
self.verticalLayout_5 = QVBoxLayout()
|
||||||
self.verticalLayout_5.setObjectName(u"verticalLayout_5")
|
self.verticalLayout_5.setObjectName("verticalLayout_5")
|
||||||
|
|
||||||
self.horizontalLayout_7.addLayout(self.verticalLayout_5)
|
self.horizontalLayout_7.addLayout(self.verticalLayout_5)
|
||||||
|
|
||||||
self.btn_del_select_apparats = QPushButton(self.app_results)
|
self.btn_del_select_apparats = QPushButton(self.app_results)
|
||||||
self.btn_del_select_apparats.setObjectName(u"btn_del_select_apparats")
|
self.btn_del_select_apparats.setObjectName("btn_del_select_apparats")
|
||||||
self.btn_del_select_apparats.setFocusPolicy(Qt.StrongFocus)
|
self.btn_del_select_apparats.setFocusPolicy(Qt.StrongFocus)
|
||||||
|
|
||||||
self.horizontalLayout_7.addWidget(self.btn_del_select_apparats)
|
self.horizontalLayout_7.addWidget(self.btn_del_select_apparats)
|
||||||
|
|
||||||
self.btn_notify_for_deletion = QPushButton(self.app_results)
|
self.btn_notify_for_deletion = QPushButton(self.app_results)
|
||||||
self.btn_notify_for_deletion.setObjectName(u"btn_notify_for_deletion")
|
self.btn_notify_for_deletion.setObjectName("btn_notify_for_deletion")
|
||||||
|
|
||||||
self.horizontalLayout_7.addWidget(self.btn_notify_for_deletion)
|
self.horizontalLayout_7.addWidget(self.btn_notify_for_deletion)
|
||||||
|
|
||||||
self.horizontalSpacer_5 = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum)
|
self.btn_extendSelection = QPushButton(self.app_results)
|
||||||
|
self.btn_extendSelection.setObjectName("btn_extendSelection")
|
||||||
|
|
||||||
|
self.horizontalLayout_7.addWidget(self.btn_extendSelection)
|
||||||
|
|
||||||
|
self.horizontalSpacer_5 = QSpacerItem(
|
||||||
|
40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum
|
||||||
|
)
|
||||||
|
|
||||||
self.horizontalLayout_7.addItem(self.horizontalSpacer_5)
|
self.horizontalLayout_7.addItem(self.horizontalSpacer_5)
|
||||||
|
|
||||||
|
|
||||||
self.verticalLayout_4.addLayout(self.horizontalLayout_7)
|
self.verticalLayout_4.addLayout(self.horizontalLayout_7)
|
||||||
|
|
||||||
self.tableWidget = QTableWidget(self.app_results)
|
self.tableWidget = QTableWidget(self.app_results)
|
||||||
if (self.tableWidget.columnCount() < 5):
|
if self.tableWidget.columnCount() < 5:
|
||||||
self.tableWidget.setColumnCount(5)
|
self.tableWidget.setColumnCount(5)
|
||||||
__qtablewidgetitem = QTableWidgetItem()
|
__qtablewidgetitem = QTableWidgetItem()
|
||||||
self.tableWidget.setHorizontalHeaderItem(0, __qtablewidgetitem)
|
self.tableWidget.setHorizontalHeaderItem(0, __qtablewidgetitem)
|
||||||
@@ -268,35 +293,33 @@ class Ui_Dialog(object):
|
|||||||
self.tableWidget.setHorizontalHeaderItem(3, __qtablewidgetitem3)
|
self.tableWidget.setHorizontalHeaderItem(3, __qtablewidgetitem3)
|
||||||
__qtablewidgetitem4 = QTableWidgetItem()
|
__qtablewidgetitem4 = QTableWidgetItem()
|
||||||
self.tableWidget.setHorizontalHeaderItem(4, __qtablewidgetitem4)
|
self.tableWidget.setHorizontalHeaderItem(4, __qtablewidgetitem4)
|
||||||
self.tableWidget.setObjectName(u"tableWidget")
|
self.tableWidget.setObjectName("tableWidget")
|
||||||
self.tableWidget.setFocusPolicy(Qt.NoFocus)
|
self.tableWidget.setFocusPolicy(Qt.NoFocus)
|
||||||
self.tableWidget.setContextMenuPolicy(Qt.CustomContextMenu)
|
self.tableWidget.setContextMenuPolicy(Qt.CustomContextMenu)
|
||||||
self.tableWidget.setEditTriggers(QAbstractItemView.NoEditTriggers)
|
self.tableWidget.setEditTriggers(QAbstractItemView.NoEditTriggers)
|
||||||
self.tableWidget.setGridStyle(Qt.NoPen)
|
self.tableWidget.setGridStyle(Qt.NoPen)
|
||||||
self.tableWidget.setSortingEnabled(True)
|
self.tableWidget.setSortingEnabled(True)
|
||||||
self.tableWidget.horizontalHeader().setStretchLastSection(True)
|
self.tableWidget.horizontalHeader().setStretchLastSection(True)
|
||||||
self.tableWidget.verticalHeader().setProperty(u"showSortIndicator", True)
|
self.tableWidget.verticalHeader().setProperty("showSortIndicator", True)
|
||||||
|
|
||||||
self.verticalLayout_4.addWidget(self.tableWidget)
|
self.verticalLayout_4.addWidget(self.tableWidget)
|
||||||
|
|
||||||
|
|
||||||
self.verticalLayout_6.addLayout(self.verticalLayout_4)
|
self.verticalLayout_6.addLayout(self.verticalLayout_4)
|
||||||
|
|
||||||
|
|
||||||
self.horizontalLayout.addWidget(self.app_results)
|
self.horizontalLayout.addWidget(self.app_results)
|
||||||
|
|
||||||
self.stats = QFrame(self.apparatResult)
|
self.stats = QFrame(self.apparatResult)
|
||||||
self.stats.setObjectName(u"stats")
|
self.stats.setObjectName("stats")
|
||||||
self.verticalLayout_8 = QVBoxLayout(self.stats)
|
self.verticalLayout_8 = QVBoxLayout(self.stats)
|
||||||
self.verticalLayout_8.setObjectName(u"verticalLayout_8")
|
self.verticalLayout_8.setObjectName("verticalLayout_8")
|
||||||
self.tabWidget_3 = QTabWidget(self.stats)
|
self.tabWidget_3 = QTabWidget(self.stats)
|
||||||
self.tabWidget_3.setObjectName(u"tabWidget_3")
|
self.tabWidget_3.setObjectName("tabWidget_3")
|
||||||
self.statistic_table = QWidget()
|
self.statistic_table = QWidget()
|
||||||
self.statistic_table.setObjectName(u"statistic_table")
|
self.statistic_table.setObjectName("statistic_table")
|
||||||
self.verticalLayout_7 = QVBoxLayout(self.statistic_table)
|
self.verticalLayout_7 = QVBoxLayout(self.statistic_table)
|
||||||
self.verticalLayout_7.setObjectName(u"verticalLayout_7")
|
self.verticalLayout_7.setObjectName("verticalLayout_7")
|
||||||
self.statistics_table = QTableWidget(self.statistic_table)
|
self.statistics_table = QTableWidget(self.statistic_table)
|
||||||
if (self.statistics_table.columnCount() < 3):
|
if self.statistics_table.columnCount() < 3:
|
||||||
self.statistics_table.setColumnCount(3)
|
self.statistics_table.setColumnCount(3)
|
||||||
__qtablewidgetitem5 = QTableWidgetItem()
|
__qtablewidgetitem5 = QTableWidgetItem()
|
||||||
self.statistics_table.setHorizontalHeaderItem(0, __qtablewidgetitem5)
|
self.statistics_table.setHorizontalHeaderItem(0, __qtablewidgetitem5)
|
||||||
@@ -304,8 +327,10 @@ class Ui_Dialog(object):
|
|||||||
self.statistics_table.setHorizontalHeaderItem(1, __qtablewidgetitem6)
|
self.statistics_table.setHorizontalHeaderItem(1, __qtablewidgetitem6)
|
||||||
__qtablewidgetitem7 = QTableWidgetItem()
|
__qtablewidgetitem7 = QTableWidgetItem()
|
||||||
self.statistics_table.setHorizontalHeaderItem(2, __qtablewidgetitem7)
|
self.statistics_table.setHorizontalHeaderItem(2, __qtablewidgetitem7)
|
||||||
self.statistics_table.setObjectName(u"statistics_table")
|
self.statistics_table.setObjectName("statistics_table")
|
||||||
sizePolicy.setHeightForWidth(self.statistics_table.sizePolicy().hasHeightForWidth())
|
sizePolicy.setHeightForWidth(
|
||||||
|
self.statistics_table.sizePolicy().hasHeightForWidth()
|
||||||
|
)
|
||||||
self.statistics_table.setSizePolicy(sizePolicy)
|
self.statistics_table.setSizePolicy(sizePolicy)
|
||||||
self.statistics_table.setMaximumSize(QSize(16777215, 16777215))
|
self.statistics_table.setMaximumSize(QSize(16777215, 16777215))
|
||||||
self.statistics_table.setFocusPolicy(Qt.NoFocus)
|
self.statistics_table.setFocusPolicy(Qt.NoFocus)
|
||||||
@@ -315,36 +340,35 @@ class Ui_Dialog(object):
|
|||||||
self.statistics_table.horizontalHeader().setCascadingSectionResizes(True)
|
self.statistics_table.horizontalHeader().setCascadingSectionResizes(True)
|
||||||
self.statistics_table.horizontalHeader().setMinimumSectionSize(40)
|
self.statistics_table.horizontalHeader().setMinimumSectionSize(40)
|
||||||
self.statistics_table.horizontalHeader().setDefaultSectionSize(80)
|
self.statistics_table.horizontalHeader().setDefaultSectionSize(80)
|
||||||
self.statistics_table.horizontalHeader().setProperty(u"showSortIndicator", True)
|
self.statistics_table.horizontalHeader().setProperty("showSortIndicator", True)
|
||||||
self.statistics_table.horizontalHeader().setStretchLastSection(False)
|
self.statistics_table.horizontalHeader().setStretchLastSection(False)
|
||||||
self.statistics_table.verticalHeader().setStretchLastSection(True)
|
self.statistics_table.verticalHeader().setStretchLastSection(True)
|
||||||
|
|
||||||
self.verticalLayout_7.addWidget(self.statistics_table)
|
self.verticalLayout_7.addWidget(self.statistics_table)
|
||||||
|
|
||||||
self.dataLayout = QHBoxLayout()
|
self.dataLayout = QHBoxLayout()
|
||||||
self.dataLayout.setObjectName(u"dataLayout")
|
self.dataLayout.setObjectName("dataLayout")
|
||||||
|
|
||||||
self.verticalLayout_7.addLayout(self.dataLayout)
|
self.verticalLayout_7.addLayout(self.dataLayout)
|
||||||
|
|
||||||
self.tabWidget_3.addTab(self.statistic_table, "")
|
self.tabWidget_3.addTab(self.statistic_table, "")
|
||||||
self.graph_table = QWidget()
|
self.graph_table = QWidget()
|
||||||
self.graph_table.setObjectName(u"graph_table")
|
self.graph_table.setObjectName("graph_table")
|
||||||
self.tabWidget_3.addTab(self.graph_table, "")
|
self.tabWidget_3.addTab(self.graph_table, "")
|
||||||
|
|
||||||
self.verticalLayout_8.addWidget(self.tabWidget_3)
|
self.verticalLayout_8.addWidget(self.tabWidget_3)
|
||||||
|
|
||||||
|
|
||||||
self.horizontalLayout.addWidget(self.stats)
|
self.horizontalLayout.addWidget(self.stats)
|
||||||
|
|
||||||
self.stackedWidget_4.addWidget(self.apparatResult)
|
self.stackedWidget_4.addWidget(self.apparatResult)
|
||||||
self.bookresult = QWidget()
|
self.bookresult = QWidget()
|
||||||
self.bookresult.setObjectName(u"bookresult")
|
self.bookresult.setObjectName("bookresult")
|
||||||
sizePolicy.setHeightForWidth(self.bookresult.sizePolicy().hasHeightForWidth())
|
sizePolicy.setHeightForWidth(self.bookresult.sizePolicy().hasHeightForWidth())
|
||||||
self.bookresult.setSizePolicy(sizePolicy)
|
self.bookresult.setSizePolicy(sizePolicy)
|
||||||
self.verticalLayout_2 = QVBoxLayout(self.bookresult)
|
self.verticalLayout_2 = QVBoxLayout(self.bookresult)
|
||||||
self.verticalLayout_2.setObjectName(u"verticalLayout_2")
|
self.verticalLayout_2.setObjectName("verticalLayout_2")
|
||||||
self.book_search_result = QTableWidget(self.bookresult)
|
self.book_search_result = QTableWidget(self.bookresult)
|
||||||
if (self.book_search_result.columnCount() < 3):
|
if self.book_search_result.columnCount() < 3:
|
||||||
self.book_search_result.setColumnCount(3)
|
self.book_search_result.setColumnCount(3)
|
||||||
__qtablewidgetitem8 = QTableWidgetItem()
|
__qtablewidgetitem8 = QTableWidgetItem()
|
||||||
self.book_search_result.setHorizontalHeaderItem(0, __qtablewidgetitem8)
|
self.book_search_result.setHorizontalHeaderItem(0, __qtablewidgetitem8)
|
||||||
@@ -352,7 +376,7 @@ class Ui_Dialog(object):
|
|||||||
self.book_search_result.setHorizontalHeaderItem(1, __qtablewidgetitem9)
|
self.book_search_result.setHorizontalHeaderItem(1, __qtablewidgetitem9)
|
||||||
__qtablewidgetitem10 = QTableWidgetItem()
|
__qtablewidgetitem10 = QTableWidgetItem()
|
||||||
self.book_search_result.setHorizontalHeaderItem(2, __qtablewidgetitem10)
|
self.book_search_result.setHorizontalHeaderItem(2, __qtablewidgetitem10)
|
||||||
self.book_search_result.setObjectName(u"book_search_result")
|
self.book_search_result.setObjectName("book_search_result")
|
||||||
self.book_search_result.setFrameShadow(QFrame.Plain)
|
self.book_search_result.setFrameShadow(QFrame.Plain)
|
||||||
self.book_search_result.setEditTriggers(QAbstractItemView.NoEditTriggers)
|
self.book_search_result.setEditTriggers(QAbstractItemView.NoEditTriggers)
|
||||||
self.book_search_result.setAlternatingRowColors(True)
|
self.book_search_result.setAlternatingRowColors(True)
|
||||||
@@ -360,9 +384,11 @@ class Ui_Dialog(object):
|
|||||||
self.book_search_result.horizontalHeader().setCascadingSectionResizes(True)
|
self.book_search_result.horizontalHeader().setCascadingSectionResizes(True)
|
||||||
self.book_search_result.horizontalHeader().setMinimumSectionSize(100)
|
self.book_search_result.horizontalHeader().setMinimumSectionSize(100)
|
||||||
self.book_search_result.horizontalHeader().setDefaultSectionSize(200)
|
self.book_search_result.horizontalHeader().setDefaultSectionSize(200)
|
||||||
self.book_search_result.horizontalHeader().setProperty(u"showSortIndicator", True)
|
self.book_search_result.horizontalHeader().setProperty(
|
||||||
|
"showSortIndicator", True
|
||||||
|
)
|
||||||
self.book_search_result.horizontalHeader().setStretchLastSection(True)
|
self.book_search_result.horizontalHeader().setStretchLastSection(True)
|
||||||
self.book_search_result.verticalHeader().setProperty(u"showSortIndicator", False)
|
self.book_search_result.verticalHeader().setProperty("showSortIndicator", False)
|
||||||
|
|
||||||
self.verticalLayout_2.addWidget(self.book_search_result)
|
self.verticalLayout_2.addWidget(self.book_search_result)
|
||||||
|
|
||||||
@@ -370,10 +396,9 @@ class Ui_Dialog(object):
|
|||||||
|
|
||||||
self.verticalLayout_3.addWidget(self.stackedWidget_4)
|
self.verticalLayout_3.addWidget(self.stackedWidget_4)
|
||||||
|
|
||||||
|
|
||||||
self.verticalLayout.addLayout(self.verticalLayout_3)
|
self.verticalLayout.addLayout(self.verticalLayout_3)
|
||||||
|
|
||||||
#if QT_CONFIG(shortcut)
|
# if QT_CONFIG(shortcut)
|
||||||
self.label_18.setBuddy(self.box_dauerapp)
|
self.label_18.setBuddy(self.box_dauerapp)
|
||||||
self.label_15.setBuddy(self.check_deletable)
|
self.label_15.setBuddy(self.check_deletable)
|
||||||
self.label_11.setBuddy(self.box_person)
|
self.label_11.setBuddy(self.box_person)
|
||||||
@@ -383,7 +408,7 @@ class Ui_Dialog(object):
|
|||||||
self.label_16.setBuddy(self.box_fach)
|
self.label_16.setBuddy(self.box_fach)
|
||||||
self.label_25.setBuddy(self.search_by_signature)
|
self.label_25.setBuddy(self.search_by_signature)
|
||||||
self.label_26.setBuddy(self.search_by_title)
|
self.label_26.setBuddy(self.search_by_title)
|
||||||
#endif // QT_CONFIG(shortcut)
|
# endif // QT_CONFIG(shortcut)
|
||||||
QWidget.setTabOrder(self.box_appnrs, self.box_person)
|
QWidget.setTabOrder(self.box_appnrs, self.box_person)
|
||||||
QWidget.setTabOrder(self.box_person, self.box_fach)
|
QWidget.setTabOrder(self.box_person, self.box_fach)
|
||||||
QWidget.setTabOrder(self.box_fach, self.check_deletable)
|
QWidget.setTabOrder(self.box_fach, self.check_deletable)
|
||||||
@@ -401,57 +426,118 @@ class Ui_Dialog(object):
|
|||||||
self.stackedWidget_4.setCurrentIndex(0)
|
self.stackedWidget_4.setCurrentIndex(0)
|
||||||
self.tabWidget_3.setCurrentIndex(0)
|
self.tabWidget_3.setCurrentIndex(0)
|
||||||
|
|
||||||
|
|
||||||
QMetaObject.connectSlotsByName(Dialog)
|
QMetaObject.connectSlotsByName(Dialog)
|
||||||
|
|
||||||
# setupUi
|
# setupUi
|
||||||
|
|
||||||
def retranslateUi(self, Dialog):
|
def retranslateUi(self, Dialog):
|
||||||
Dialog.setWindowTitle(QCoreApplication.translate("Dialog", u"Dialog", None))
|
Dialog.setWindowTitle(QCoreApplication.translate("Dialog", "Dialog", None))
|
||||||
self.label_18.setText(QCoreApplication.translate("Dialog", u"Dauerapp:", None))
|
self.label_18.setText(QCoreApplication.translate("Dialog", "Dauerapp:", None))
|
||||||
self.label_15.setText(QCoreApplication.translate("Dialog", u"L\u00f6schbar", None))
|
self.label_15.setText(
|
||||||
self.label_11.setText(QCoreApplication.translate("Dialog", u"Person:", None))
|
QCoreApplication.translate("Dialog", "L\u00f6schbar", None)
|
||||||
self.label_7.setText(QCoreApplication.translate("Dialog", u"Appnr.:", None))
|
)
|
||||||
self.label_17.setText(QCoreApplication.translate("Dialog", u"Endsemester:", None))
|
self.label_11.setText(QCoreApplication.translate("Dialog", "Person:", None))
|
||||||
self.label_19.setText(QCoreApplication.translate("Dialog", u"Erstellsemester:", None))
|
self.label_7.setText(QCoreApplication.translate("Dialog", "Appnr.:", None))
|
||||||
self.label_16.setText(QCoreApplication.translate("Dialog", u"Fach:", None))
|
self.label_17.setText(
|
||||||
|
QCoreApplication.translate("Dialog", "Endsemester:", None)
|
||||||
|
)
|
||||||
|
self.label_19.setText(
|
||||||
|
QCoreApplication.translate("Dialog", "Erstellsemester:", None)
|
||||||
|
)
|
||||||
|
self.label_16.setText(QCoreApplication.translate("Dialog", "Fach:", None))
|
||||||
self.check_deletable.setText("")
|
self.check_deletable.setText("")
|
||||||
self.btn_search.setText(QCoreApplication.translate("Dialog", u"Suchen", None))
|
self.btn_search.setText(QCoreApplication.translate("Dialog", "Suchen", None))
|
||||||
self.db_err_message.setText("")
|
self.db_err_message.setText("")
|
||||||
self.tabWidget_2.setTabText(self.tabWidget_2.indexOf(self.tab_3), QCoreApplication.translate("Dialog", u"Statistik", None))
|
self.tabWidget_2.setTabText(
|
||||||
#if QT_CONFIG(statustip)
|
self.tabWidget_2.indexOf(self.tab_3),
|
||||||
self.search_by_signature.setStatusTip(QCoreApplication.translate("Dialog", u"Trunkierung mit * am Ende unterst\u00fctzt", None))
|
QCoreApplication.translate("Dialog", "Statistik", None),
|
||||||
#endif // QT_CONFIG(statustip)
|
)
|
||||||
self.label_25.setText(QCoreApplication.translate("Dialog", u"Signatur", None))
|
# if QT_CONFIG(statustip)
|
||||||
self.label_26.setText(QCoreApplication.translate("Dialog", u"Titel", None))
|
self.search_by_signature.setStatusTip(
|
||||||
self.label.setText(QCoreApplication.translate("Dialog", u"Suche mit Enter starten", None))
|
QCoreApplication.translate(
|
||||||
|
"Dialog", "Trunkierung mit * am Ende unterst\u00fctzt", None
|
||||||
|
)
|
||||||
|
)
|
||||||
|
# endif // QT_CONFIG(statustip)
|
||||||
|
self.label_25.setText(QCoreApplication.translate("Dialog", "Signatur", None))
|
||||||
|
self.label_26.setText(QCoreApplication.translate("Dialog", "Titel", None))
|
||||||
|
self.label.setText(
|
||||||
|
QCoreApplication.translate("Dialog", "Suche mit Enter starten", None)
|
||||||
|
)
|
||||||
self.no_result.setText("")
|
self.no_result.setText("")
|
||||||
self.tabWidget_2.setTabText(self.tabWidget_2.indexOf(self.tab_4), QCoreApplication.translate("Dialog", u"Suchen", None))
|
self.tabWidget_2.setTabText(
|
||||||
self.btn_del_select_apparats.setText(QCoreApplication.translate("Dialog", u"Ausgew\u00e4hlte L\u00f6schen", None))
|
self.tabWidget_2.indexOf(self.tab_4),
|
||||||
#if QT_CONFIG(statustip)
|
QCoreApplication.translate("Dialog", "Suchen", None),
|
||||||
self.btn_notify_for_deletion.setStatusTip(QCoreApplication.translate("Dialog", u"Zeigt f\u00fcr jeden ausgew\u00e4hlten Apparat eine eMail-Vorlage an", None))
|
)
|
||||||
#endif // QT_CONFIG(statustip)
|
self.btn_del_select_apparats.setText(
|
||||||
self.btn_notify_for_deletion.setText(QCoreApplication.translate("Dialog", u"Ausgew\u00e4hlte Benachrichtigen", None))
|
QCoreApplication.translate("Dialog", "Ausgew\u00e4hlte L\u00f6schen", None)
|
||||||
|
)
|
||||||
|
# if QT_CONFIG(statustip)
|
||||||
|
self.btn_notify_for_deletion.setStatusTip(
|
||||||
|
QCoreApplication.translate(
|
||||||
|
"Dialog",
|
||||||
|
"Zeigt f\u00fcr jeden ausgew\u00e4hlten Apparat eine eMail-Vorlage an",
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
# endif // QT_CONFIG(statustip)
|
||||||
|
self.btn_notify_for_deletion.setText(
|
||||||
|
QCoreApplication.translate(
|
||||||
|
"Dialog", "Ausgew\u00e4hlte Benachrichtigen", None
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.btn_extendSelection.setText(
|
||||||
|
QCoreApplication.translate(
|
||||||
|
"Dialog", "Ausgew\u00e4hlte Verl\u00e4ngern", None
|
||||||
|
)
|
||||||
|
)
|
||||||
___qtablewidgetitem = self.tableWidget.horizontalHeaderItem(1)
|
___qtablewidgetitem = self.tableWidget.horizontalHeaderItem(1)
|
||||||
___qtablewidgetitem.setText(QCoreApplication.translate("Dialog", u"Apparatsname", None));
|
___qtablewidgetitem.setText(
|
||||||
|
QCoreApplication.translate("Dialog", "Apparatsname", None)
|
||||||
|
)
|
||||||
___qtablewidgetitem1 = self.tableWidget.horizontalHeaderItem(2)
|
___qtablewidgetitem1 = self.tableWidget.horizontalHeaderItem(2)
|
||||||
___qtablewidgetitem1.setText(QCoreApplication.translate("Dialog", u"Apparatsnummer", None));
|
___qtablewidgetitem1.setText(
|
||||||
|
QCoreApplication.translate("Dialog", "Apparatsnummer", None)
|
||||||
|
)
|
||||||
___qtablewidgetitem2 = self.tableWidget.horizontalHeaderItem(3)
|
___qtablewidgetitem2 = self.tableWidget.horizontalHeaderItem(3)
|
||||||
___qtablewidgetitem2.setText(QCoreApplication.translate("Dialog", u"Person", None));
|
___qtablewidgetitem2.setText(
|
||||||
|
QCoreApplication.translate("Dialog", "Person", None)
|
||||||
|
)
|
||||||
___qtablewidgetitem3 = self.tableWidget.horizontalHeaderItem(4)
|
___qtablewidgetitem3 = self.tableWidget.horizontalHeaderItem(4)
|
||||||
___qtablewidgetitem3.setText(QCoreApplication.translate("Dialog", u"Fach", None));
|
___qtablewidgetitem3.setText(QCoreApplication.translate("Dialog", "Fach", None))
|
||||||
___qtablewidgetitem4 = self.statistics_table.horizontalHeaderItem(0)
|
___qtablewidgetitem4 = self.statistics_table.horizontalHeaderItem(0)
|
||||||
___qtablewidgetitem4.setText(QCoreApplication.translate("Dialog", u"Semester", None));
|
___qtablewidgetitem4.setText(
|
||||||
|
QCoreApplication.translate("Dialog", "Semester", None)
|
||||||
|
)
|
||||||
___qtablewidgetitem5 = self.statistics_table.horizontalHeaderItem(1)
|
___qtablewidgetitem5 = self.statistics_table.horizontalHeaderItem(1)
|
||||||
___qtablewidgetitem5.setText(QCoreApplication.translate("Dialog", u"Zugang", None));
|
___qtablewidgetitem5.setText(
|
||||||
|
QCoreApplication.translate("Dialog", "Zugang", None)
|
||||||
|
)
|
||||||
___qtablewidgetitem6 = self.statistics_table.horizontalHeaderItem(2)
|
___qtablewidgetitem6 = self.statistics_table.horizontalHeaderItem(2)
|
||||||
___qtablewidgetitem6.setText(QCoreApplication.translate("Dialog", u"Abgang", None));
|
___qtablewidgetitem6.setText(
|
||||||
self.tabWidget_3.setTabText(self.tabWidget_3.indexOf(self.statistic_table), QCoreApplication.translate("Dialog", u"Tabelle", None))
|
QCoreApplication.translate("Dialog", "Abgang", None)
|
||||||
self.tabWidget_3.setTabText(self.tabWidget_3.indexOf(self.graph_table), QCoreApplication.translate("Dialog", u"Erstellte und gel\u00f6schte Semesterapparate", None))
|
)
|
||||||
|
self.tabWidget_3.setTabText(
|
||||||
|
self.tabWidget_3.indexOf(self.statistic_table),
|
||||||
|
QCoreApplication.translate("Dialog", "Tabelle", None),
|
||||||
|
)
|
||||||
|
self.tabWidget_3.setTabText(
|
||||||
|
self.tabWidget_3.indexOf(self.graph_table),
|
||||||
|
QCoreApplication.translate(
|
||||||
|
"Dialog", "Erstellte und gel\u00f6schte Semesterapparate", None
|
||||||
|
),
|
||||||
|
)
|
||||||
___qtablewidgetitem7 = self.book_search_result.horizontalHeaderItem(0)
|
___qtablewidgetitem7 = self.book_search_result.horizontalHeaderItem(0)
|
||||||
___qtablewidgetitem7.setText(QCoreApplication.translate("Dialog", u"Titel", None));
|
___qtablewidgetitem7.setText(
|
||||||
|
QCoreApplication.translate("Dialog", "Titel", None)
|
||||||
|
)
|
||||||
___qtablewidgetitem8 = self.book_search_result.horizontalHeaderItem(1)
|
___qtablewidgetitem8 = self.book_search_result.horizontalHeaderItem(1)
|
||||||
___qtablewidgetitem8.setText(QCoreApplication.translate("Dialog", u"Signatur", None));
|
___qtablewidgetitem8.setText(
|
||||||
|
QCoreApplication.translate("Dialog", "Signatur", None)
|
||||||
|
)
|
||||||
___qtablewidgetitem9 = self.book_search_result.horizontalHeaderItem(2)
|
___qtablewidgetitem9 = self.book_search_result.horizontalHeaderItem(2)
|
||||||
___qtablewidgetitem9.setText(QCoreApplication.translate("Dialog", u"Apparat", None));
|
___qtablewidgetitem9.setText(
|
||||||
# retranslateUi
|
QCoreApplication.translate("Dialog", "Apparat", None)
|
||||||
|
)
|
||||||
|
|
||||||
|
# retranslateUi
|
||||||
|
|||||||
Reference in New Issue
Block a user