Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MetadataSession cleanup #80

Merged
merged 3 commits into from
Feb 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
303 changes: 28 additions & 275 deletions bookops_worldcat/metadata_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
class MetadataSession(WorldcatSession):
"""OCLC Metadata API wrapper session. Inherits `requests.Session` methods"""

BASE_URL = "https://metadata.api.oclc.org/worldcat"

def __init__(
self,
authorization: WorldcatAccessToken,
Expand Down Expand Up @@ -50,78 +52,35 @@ def _split_into_legal_volume(
for i in range(0, len(oclc_numbers), n):
yield ",".join(oclc_numbers[i : i + n]) # noqa: E203

def URL_BASE(self) -> str:
return "https://metadata.api.oclc.org/worldcat"

def _url_base(self) -> str:
return "https://worldcat.org"

def _url_search_base(self) -> str:
return "https://americas.metadata.api.oclc.org/worldcat/search/v1"

def _url_search_shared_print_holdings(self) -> str:
base_url = self.URL_BASE()
return f"{base_url}/search/bibs-retained-holdings"
return f"{self.BASE_URL}/search/bibs-retained-holdings"

def _url_search_general_holdings(self) -> str:
base_url = self.URL_BASE()
return f"{base_url}/search/bibs-summary-holdings"
return f"{self.BASE_URL}/search/bibs-summary-holdings"

def _url_search_brief_bibs(self) -> str:
base_url = self.URL_BASE()
return f"{base_url}/search/brief-bibs"
return f"{self.BASE_URL}/search/brief-bibs"

def _url_search_brief_bibs_oclc_number(self, oclcNumber: str) -> str:
base_url = self.URL_BASE()
return f"{base_url}/search/brief-bibs/{oclcNumber}"
return f"{self.BASE_URL}/search/brief-bibs/{oclcNumber}"

def _url_search_brief_bibs_other_editions(self, oclcNumber: str) -> str:
base_url = self.URL_BASE()
return f"{base_url}/search/brief-bibs/{oclcNumber}/other-editions"

def _url_lhr_control_number(self, controlNumber: str) -> str:
base_url = self._url_search_base()
return f"{base_url}/my-holdings/{controlNumber}"
return f"{self.BASE_URL}/search/brief-bibs/{oclcNumber}/other-editions"

def _url_lhr_search(self) -> str:
base_url = self._url_search_base()
return f"{base_url}/my-holdings"
def _url_manage_bibs(self, oclcNumber: str) -> str:
return f"{self.BASE_URL}/manage/bibs/{oclcNumber}"

def _url_lhr_shared_print(self) -> str:
base_url = self._url_search_base()
return f"{base_url}/retained-holdings"

def _url_manage_bib(self, oclcNumber: str) -> str:
base_url = self.URL_BASE()
return f"{base_url}/manage/bibs/{oclcNumber}"

def _url_manage_bib_current_oclc_number(self) -> str:
base_url = self.URL_BASE()
return f"{base_url}/manage/bibs/current"

def _url_bib_holding_libraries(self) -> str:
base_url = self._url_base()
return f"{base_url}/bib/holdinglibraries"
def _url_manage_bibs_current_oclc_number(self) -> str:
return f"{self.BASE_URL}/manage/bibs/current"

def _url_manage_ih_set(self, oclcNumber: str) -> str:
base_url = self.URL_BASE()
return f"{base_url}/manage/institution/holdings/{oclcNumber}/set"
return f"{self.BASE_URL}/manage/institution/holdings/{oclcNumber}/set"

def _url_manage_ih_unset(self, oclcNumber: str) -> str:
base_url = self.URL_BASE()
return f"{base_url}/manage/institution/holdings/{oclcNumber}/unset"
return f"{self.BASE_URL}/manage/institution/holdings/{oclcNumber}/unset"

def _url_manage_ih_current(self) -> str:
base_url = self.URL_BASE()
return f"{base_url}/manage/institution/holdings/current"

def _url_bib_holdings_batch_action(self) -> str:
base_url = self._url_base()
return f"{base_url}/ih/datalist"

def _url_bib_holdings_multi_institution_batch_action(self) -> str:
base_url = self._url_base()
return f"{base_url}/ih/institutionlist"
return f"{self.BASE_URL}/manage/institution/holdings/current"

def get_brief_bib(
self, oclcNumber: Union[int, str], hooks: Optional[Dict[str, Callable]] = None
Expand Down Expand Up @@ -178,7 +137,7 @@ def get_full_bib(
"""
oclcNumber = verify_oclc_number(oclcNumber)

url = self._url_manage_bib(oclcNumber)
url = self._url_manage_bibs(oclcNumber)
header = {"Accept": response_format}

# prep request
Expand All @@ -194,7 +153,7 @@ def get_institution_holdings(
self,
oclcNumbers: Union[str, List[Union[str, int]]],
hooks: Optional[Dict[str, Callable]] = None,
) -> Optional[Response]:
) -> List[Optional[Response]]:
"""
Retrieves Worlcat holdings status of a record with provided OCLC number.
The service automatically recognizes institution based on the issued access
Expand All @@ -213,20 +172,24 @@ def get_institution_holdings(
Returns:
`requests.Response` object
"""
responses = []
vetted_numbers = verify_oclc_numbers(oclcNumbers)

url = self._url_manage_ih_current()
header = {"Accept": "application/json"}
payload = {"oclcNumbers": ",".join(vetted_numbers)}

# prep request
req = Request("GET", url, params=payload, headers=header, hooks=hooks)
prepared_request = self.prepare_request(req)
for batch in self._split_into_legal_volume(oclc_numbers=vetted_numbers, n=10):
payload = {"oclcNumbers": batch}

# send request
query = Query(self, prepared_request, timeout=self.timeout)
# prep request
req = Request("GET", url, params=payload, headers=header, hooks=hooks)
prepared_request = self.prepare_request(req)

return query.response
# send request
query = Query(self, prepared_request, timeout=self.timeout)
responses.append(query.response)

return responses

def holding_set(
self,
Expand Down Expand Up @@ -295,216 +258,6 @@ def holding_unset(

return query.response

def holdings_set(
self,
oclcNumbers: Union[str, List],
inst: Optional[str] = None,
instSymbol: Optional[str] = None,
response_format: str = "application/atom+json",
hooks: Optional[Dict[str, Callable]] = None,
) -> List[Optional[Response]]:
"""
Set institution holdings for multiple OCLC numbers
Uses /ih/datalist endpoint.

Args:
oclcNumbers: list of OCLC control numbers for which holdings
should be set;
they can be integers or strings with or
without OCLC # prefix;
if str the numbers must be separated by comma
inst: registry ID of the institution whose holdings
are being checked
instSymbol: optional; OCLC symbol of the institution whose
holdings are being checked
response_format: 'application/atom+json' (default) or
'application/atom+xml'
hooks: Requests library hook system that can be
used for signal event handling, see more at:
https://requests.readthedocs.io/en/master/user/advanced/#event-hooks
Returns:
list of `requests.Response` objects
"""
responses = []
vetted_numbers = verify_oclc_numbers(oclcNumbers)

url = self._url_bib_holdings_batch_action()
header = {"Accept": response_format}

# split into batches of 50 and issue request for each batch
for batch in self._split_into_legal_volume(vetted_numbers):
payload = {
"oclcNumbers": batch,
"inst": inst,
"instSymbol": instSymbol,
}

# prep request
req = Request("POST", url, params=payload, headers=header, hooks=hooks)
prepared_request = self.prepare_request(req)

# send request
query = Query(self, prepared_request, timeout=self.timeout)

responses.append(query.response)

return responses

def holdings_unset(
self,
oclcNumbers: Union[str, List],
cascade: str = "0",
inst: Optional[str] = None,
instSymbol: Optional[str] = None,
response_format: str = "application/atom+json",
hooks: Optional[Dict[str, Callable]] = None,
) -> List[Optional[Response]]:
"""
Set institution holdings for multiple OCLC numbers
Uses /ih/datalist endpoint.

Args:
oclcNumbers: list of OCLC control numbers for which holdings
should be set;
they can be integers or strings with or
without OCLC # prefix;
if str the numbers must be separated by comma
cascade: 0 or 1, default 0;
0 - don't remove holdings if local holding
record or local bibliographic records exists;
1 - remove holding and delete local holdings
record and local bibliographic record
inst: registry ID of the institution whose holdings
are being checked
instSymbol: optional; OCLC symbol of the institution whose
holdings are being checked
response_format: 'application/atom+json' (default) or
'application/atom+xml'
hooks: Requests library hook system that can be
used for signal event handling, see more at:
https://requests.readthedocs.io/en/master/user/advanced/#event-hooks
Returns:
list of `requests.Response` objects
"""
responses = []
vetted_numbers = verify_oclc_numbers(oclcNumbers)

url = self._url_bib_holdings_batch_action()
header = {"Accept": response_format}

# split into batches of 50 and issue request for each batch
for batch in self._split_into_legal_volume(vetted_numbers):
payload = {
"oclcNumbers": batch,
"cascade": cascade,
"inst": inst,
"instSymbol": instSymbol,
}

# prep request
req = Request("DELETE", url, params=payload, headers=header, hooks=hooks)
prepared_request = self.prepare_request(req)

# send request
query = Query(self, prepared_request, timeout=self.timeout)

responses.append(query.response)

return responses

def holdings_set_multi_institutions(
self,
oclcNumber: Union[int, str],
instSymbols: str,
response_format: str = "application/atom+json",
hooks: Optional[Dict[str, Callable]] = None,
) -> Optional[Response]:
"""
Batch sets intitution holdings for multiple intitutions

Uses /ih/institutionlist endpoint

Args:
oclcNumber: OCLC bibliographic record number; can be an
integer, or string with or without OCLC # prefix
instSymbols: a comma-separated list of OCLC symbols of the
institution whose holdings are being set
response_format: 'application/atom+json' (default) or
'application/atom+xml'
hooks: Requests library hook system that can be
used for signal event handling, see more at:
https://requests.readthedocs.io/en/master/user/advanced/#event-hooks
Returns:
`requests.Response` object
"""
oclcNumber = verify_oclc_number(oclcNumber)

url = self._url_bib_holdings_multi_institution_batch_action()
header = {"Accept": response_format}
payload = {
"oclcNumber": oclcNumber,
"instSymbols": instSymbols,
}

# prep request
req = Request("POST", url, params=payload, headers=header, hooks=hooks)
prepared_request = self.prepare_request(req)

# send request
query = Query(self, prepared_request, timeout=self.timeout)

return query.response

def holdings_unset_multi_institutions(
self,
oclcNumber: Union[int, str],
instSymbols: str,
cascade: str = "0",
response_format: str = "application/atom+json",
hooks: Optional[Dict[str, Callable]] = None,
) -> Optional[Response]:
"""
Batch unsets intitution holdings for multiple intitutions

Uses /ih/institutionlist endpoint

Args:
oclcNumber: OCLC bibliographic record number; can be an
integer, or string with or without OCLC # prefix
instSymbols: a comma-separated list of OCLC symbols of the
institution whose holdings are being set
cascade: 0 or 1, default 0;
0 - don't remove holdings if local holding
record or local bibliographic records exists;
1 - remove holding and delete local holdings
record and local bibliographic record
response_format: 'application/atom+json' (default) or
'application/atom+xml'
hooks: Requests library hook system that can be
used for signal event handling, see more at:
https://requests.readthedocs.io/en/master/user/advanced/#event-hooks
Returns:
`requests.Response` object
"""
oclcNumber = verify_oclc_number(oclcNumber)

url = self._url_bib_holdings_multi_institution_batch_action()
header = {"Accept": response_format}
payload = {
"oclcNumber": oclcNumber,
"instSymbols": instSymbols,
"cascade": cascade,
}

# prep request
req = Request("DELETE", url, params=payload, headers=header, hooks=hooks)
prepared_request = self.prepare_request(req)

# send request
query = Query(self, prepared_request, timeout=self.timeout)

return query.response

def search_brief_bibs_other_editions(
self,
oclcNumber: Union[int, str],
Expand Down Expand Up @@ -856,7 +609,7 @@ def get_current_oclc_number(
vetted_numbers = verify_oclc_numbers(oclcNumbers)

header = {"Accept": "application/json"}
url = self._url_manage_bib_current_oclc_number()
url = self._url_manage_bibs_current_oclc_number()
payload = {"oclcNumbers": ",".join(vetted_numbers)}

# prep request
Expand Down
13 changes: 1 addition & 12 deletions bookops_worldcat/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,18 +57,7 @@ def __init__(

try:
self.response = session.send(prepared_request, timeout=timeout)

if "/ih/data" in prepared_request.url: # type: ignore
if self.response.status_code == 409:
# HTTP 409 code returns when trying to set/unset
# holdings on already set/unset record
# It is reasonable not to raise any exceptions
# in this case
pass # pragma: no cover
else:
self.response.raise_for_status()
else:
self.response.raise_for_status()
self.response.raise_for_status()

except HTTPError as exc:
raise WorldcatRequestError(
Expand Down
Loading