feat: implement wrappers for more endpoints

This commit is contained in:
2025-10-29 14:28:46 +01:00
parent 49b347deec
commit dab9d08297
3 changed files with 144 additions and 31 deletions

View File

@@ -82,10 +82,12 @@ class BookData:
setattr(self, key, value) setattr(self, key, value)
def get_book_type(self) -> str: def get_book_type(self) -> str:
if "Online" in self.pages: if isinstance(self.media_type, str):
return "eBook" if "Online" in self.pages:
else: return "eBook"
return "Druckausgabe" else:
return "Druckausgabe"
return None
def from_string(self, data: str) -> "BookData": def from_string(self, data: str) -> "BookData":
ndata = json.loads(data) ndata = json.loads(data)

View File

@@ -54,3 +54,15 @@ class SearchRetrieveResponse:
numberOfRecords: int numberOfRecords: int
records: List[Record] = field(default_factory=list) records: List[Record] = field(default_factory=list)
echoedSearchRetrieveRequest: Optional[EchoedSearchRequest] = None echoedSearchRetrieveRequest: Optional[EchoedSearchRequest] = None
@dataclass
class FormattedResponse:
title: str
edition: Optional[str] = None
publisher: Optional[str] = None
year: Optional[str] = None
authors: List[str] = field(default_factory=list)
isbn: List[str] = field(default_factory=list)
ppn: Optional[str] = None
libraries: List[str] = field(default_factory=list)

View File

@@ -18,15 +18,6 @@ from .schemas.marcxml import (
SubField, SubField,
) )
# -----------------------
# Dataclasses
# -----------------------
# -----------------------
# Parser
# -----------------------
ZS = "http://www.loc.gov/zing/srw/" ZS = "http://www.loc.gov/zing/srw/"
MARC = "http://www.loc.gov/MARC21/slim" MARC = "http://www.loc.gov/MARC21/slim"
NS = {"zs": ZS, "marc": MARC} NS = {"zs": ZS, "marc": MARC}
@@ -375,18 +366,44 @@ def book_from_marc(rec: MarcRecord) -> BookData:
) )
class SWBData(Enum): class SWBSchema(Enum):
URL = "https://sru.k10plus.de/opac-de-627!rec=1?version=1.1&operation=searchRetrieve&query={}&maximumRecords=100&recordSchema=marcxml" URL = "https://sru.k10plus.de/opac-de-627!rec=1?version=1.1&operation=searchRetrieve&query={}&maximumRecords=100&recordSchema=marcxml"
ARGSCHEMA = "pica." ARGSCHEMA = "pica."
NAME = "SWB" NAME = "SWB"
class DNBData(Enum): class DNBSchema(Enum):
URL = "https://services.dnb.de/sru/dnb?version=1.1&operation=searchRetrieve&query={}&maximumRecords=100&recordSchema=MARC21-xml" URL = "https://services.dnb.de/sru/dnb?version=1.1&operation=searchRetrieve&query={}&maximumRecords=100&recordSchema=MARC21-xml"
ARGSCHEMA = "" ARGSCHEMA = ""
NAME = "DNB" NAME = "DNB"
class KOBVSchema(Enum):
URL = "https://sru.kobv.de/k2?version=1.1&operation=searchRetrieve&query={}&startRecord=1&maximumRecords=100&recordSchema=marcxml"
ARGSCHEMA = "dc."
NAME = "KOBV"
class HebisSchema(Enum):
URL = "http://sru.hebis.de/sru/DB=2.1?query={}&version=1.1&operation=searchRetrieve&stylesheet=http%3A%2F%2Fsru.hebis.de%2Fsru%2F%3Fxsl%3DsearchRetrieveResponse&recordSchema=marc21&maximumRecords=100&startRecord=1&recordPacking=xml&sortKeys=LST_Y%2Cpica%2C0%2C%2C"
ARGSCHEMA = "pica."
NAME = "HEBIS"
REPLACE = {" ": "+", "&": "%26", "=": "+%3D+"}
class OEVKSchema(Enum):
URL = "https://sru.k10plus.de/opac-de-627-2?version=1.1&operation=searchRetrieve&query={}&maximumRecords=100&recordSchema=marcxml"
ARGSCHEMA = "pica."
NAME = "OEVK"
class HBZSchema(Enum):
URL = "https://eu04.alma.exlibrisgroup.com/view/sru/49HBZ_NETWORK?version=1.2&operation=searchRetrieve&recordSchema=marcxml&query={}&maximumRecords=100&recordSchema=marcxml"
ARGSCHEMA = "alma."
NAME = "HBZ"
RVK_ALLOWED = r"[A-Z0-9.\-\/]" # conservative char set typically seen in RVK notations RVK_ALLOWED = r"[A-Z0-9.\-\/]" # conservative char set typically seen in RVK notations
@@ -489,11 +506,14 @@ def find_newer_edition(
return [best] if best else None return [best] if best else None
class Api: class _Api:
def __init__(self, site: str, url: str, prefix: str): def __init__(
self, site: str, url: str, prefix: str, replace: Optional[Dict[str, str]] = None
):
self.site = site self.site = site
self.url = url self.url = url
self.prefix = prefix self.prefix = prefix
self.replace = replace or {}
# Reuse TCP connections across requests for better performance # Reuse TCP connections across requests for better performance
self._session = requests.Session() self._session = requests.Session()
# Slightly larger connection pool for concurrent calls # Slightly larger connection pool for concurrent calls
@@ -522,13 +542,11 @@ class Api:
query_args = args query_args = args
# query_args = [f"{self.prefix}{arg}" for arg in query_args] # query_args = [f"{self.prefix}{arg}" for arg in query_args]
query = "+and+".join(query_args) query = "+and+".join(query_args)
query = query.replace(" ", "%20").replace("&", "%26") for old, new in self.replace.items():
# query_args = [arg for arg in query_args if not arg.endswith("=")] query = query.replace(old, new)
# query = "+and+".join(query_args)
# query = query.replace(" ", "%20").replace("&", "%26")
# insert the query into the url url is
url = self.url.format(query)
url = self.url.format(query)
print(url)
headers = { headers = {
"User-Agent": f"{self.site} SRU Client, <alexander.kirchner@ph-freiburg.de>", "User-Agent": f"{self.site} SRU Client, <alexander.kirchner@ph-freiburg.de>",
"Accept": "application/xml", "Accept": "application/xml",
@@ -568,17 +586,98 @@ class Api:
return "" return ""
class SWB(Api): class SWB(_Api):
def __init__(self): def __init__(self):
self.site = SWBData.NAME.value self.site = SWBSchema.NAME.value
self.url = SWBData.URL.value self.url = SWBSchema.URL.value
self.prefix = SWBData.ARGSCHEMA.value self.prefix = SWBSchema.ARGSCHEMA.value
super().__init__(self.site, self.url, self.prefix) super().__init__(self.site, self.url, self.prefix)
class DNB(Api): class DNB(_Api):
def __init__(self): def __init__(self):
self.site = DNBData.NAME.value self.site = DNBSchema.NAME.value
self.url = DNBData.URL.value self.url = DNBSchema.URL.value
self.prefix = DNBData.ARGSCHEMA.value self.prefix = DNBSchema.ARGSCHEMA.value
super().__init__(self.site, self.url, self.prefix) super().__init__(self.site, self.url, self.prefix)
class KOBV(_Api):
def __init__(self):
self.site = KOBVSchema.NAME.value
self.url = KOBVSchema.URL.value
self.prefix = KOBVSchema.ARGSCHEMA.value
super().__init__(self.site, self.url, self.prefix)
class HEBIS(_Api):
def __init__(self):
self.site = HebisSchema.NAME.value
self.url = HebisSchema.URL.value
self.prefix = HebisSchema.ARGSCHEMA.value
self.replace = HebisSchema.REPLACE.value
super().__init__(self.site, self.url, self.prefix, self.replace)
class OEVK(_Api):
def __init__(self):
self.site = OEVKSchema.NAME.value
self.url = OEVKSchema.URL.value
self.prefix = OEVKSchema.ARGSCHEMA.value
super().__init__(self.site, self.url, self.prefix)
class HBZ(_Api):
"""
Small wrapper of the SRU API used to retrieve data from the HBZ libraries
All fields are available [here](https://eu04.alma.exlibrisgroup.com/view/sru/49HBZ_NETWORK?version=1.2)
Schema
------
HBZSchema: <HBZSchema>
query prefix: alma.
"""
def __init__(self):
self.site = HBZSchema.NAME.value
self.url = HBZSchema.URL.value
self.prefix = HBZSchema.ARGSCHEMA.value
super().__init__(self.site, self.url, self.prefix)
def search(self, query_args: Union[Iterable[str], str]):
arguments =
# async KVK class:
class KVK:
def __init__(self):
self.k10plus = SWB()
self.dnb = DNB()
self.hebis = HEBIS()
self.oevk = OEVK()
self.hbz = HBZ()
self.kobv = KOBV()
def close(self):
self.k10plus.close()
self.dnb.close()
self.hebis.close()
self.oevk.close()
self.hbz.close()
self.kobv.close()
def __del__(self):
self.close()
# async def get_all(self, query_args: Union[Iterable[str], str]) -> Dict[str, List[BookData]]:
async def get_all(
self, query_args: Union[Iterable[str], str]
) -> Dict[str, List[BookData]]:
results = {}
results["K10Plus"] = self.k10plus.getBooks(query_args)
results["DNB"] = self.dnb.getBooks(query_args)
results["HEBIS"] = self.hebis.getBooks(query_args)
results["OEVK"] = self.oevk.getBooks(query_args)
results["HBZ"] = self.hbz.getBooks(query_args)
results["KOBV"] = self.kobv.getBooks(query_args)
return results