diff --git a/bookops_worldcat/metadata_api.py b/bookops_worldcat/metadata_api.py index 9dc677d..031d7a4 100644 --- a/bookops_worldcat/metadata_api.py +++ b/bookops_worldcat/metadata_api.py @@ -52,21 +52,6 @@ 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_manage_bibs(self, oclcNumber: str) -> str: - return f"{self.BASE_URL}/manage/bibs/{oclcNumber}" - - def _url_manage_bibs_current_oclc_number(self) -> str: - return f"{self.BASE_URL}/manage/bibs/current" - - def _url_manage_ih_current(self) -> str: - return f"{self.BASE_URL}/manage/institution/holdings/current" - - def _url_manage_ih_set(self, oclcNumber: str) -> str: - return f"{self.BASE_URL}/manage/institution/holdings/{oclcNumber}/set" - - def _url_manage_ih_unset(self, oclcNumber: str) -> str: - return f"{self.BASE_URL}/manage/institution/holdings/{oclcNumber}/unset" - def _url_search_shared_print_holdings(self) -> str: return f"{self.BASE_URL}/search/bibs-retained-holdings" @@ -82,14 +67,20 @@ def _url_search_brief_bibs_oclc_number(self, oclcNumber: str) -> str: def _url_search_brief_bibs_other_editions(self, oclcNumber: str) -> str: return f"{self.BASE_URL}/search/brief-bibs/{oclcNumber}/other-editions" - def _url_lhr_shared_print(self) -> str: - return f"{self.BASE_URL}/retained-holdings" + def _url_manage_bibs(self, oclcNumber: str) -> str: + return f"{self.BASE_URL}/manage/bibs/{oclcNumber}" + + 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: + return f"{self.BASE_URL}/manage/institution/holdings/{oclcNumber}/set" - def _url_lhr_control_number(self, controlNumber: str) -> str: - return f"{self.BASE_URL}/my-holdings/{controlNumber}" + def _url_manage_ih_unset(self, oclcNumber: str) -> str: + return f"{self.BASE_URL}/manage/institution/holdings/{oclcNumber}/unset" - def _url_lhr_search(self) -> str: - return f"{self.BASE_URL}/my-holdings" + def _url_manage_ih_current(self) -> str: + return f"{self.BASE_URL}/manage/institution/holdings/current" def get_brief_bib( self, oclcNumber: Union[int, str], hooks: Optional[Dict[str, Callable]] = None @@ -122,43 +113,6 @@ def get_brief_bib( return query.response - def get_current_oclc_number( - self, - oclcNumbers: Union[str, List[Union[str, int]]], - hooks: Optional[Dict[str, Callable]] = None, - ) -> Optional[Response]: - """ - Retrieve current OCLC control numbers - Uses /manage/bibs/current endpoint. - - Args: - oclcNumbers: string or list containing one or more OCLC numbers - to be checked; numbers can be integers or strings - with or without OCLC # prefix; - if str, the numbers must be separated by a comma - 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 - """ - - vetted_numbers = verify_oclc_numbers(oclcNumbers) - - header = {"Accept": "application/json"} - url = self._url_manage_bibs_current_oclc_number() - payload = {"oclcNumbers": ",".join(vetted_numbers)} - - # prep request - req = Request("GET", 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 get_full_bib( self, oclcNumber: Union[int, str], @@ -304,14 +258,14 @@ def holding_unset( return query.response - def search_brief_bibs( + def search_brief_bibs_other_editions( self, - q: str, + oclcNumber: Union[int, str], deweyNumber: Optional[Union[str, List[str]]] = None, datePublished: Optional[Union[str, List[str]]] = None, heldByGroup: Optional[str] = None, heldBySymbol: Optional[Union[str, List[str]]] = None, - heldByInstitutionID: Optional[Union[str, int, List[str], List[int]]] = None, + heldByInstitutionID: Optional[Union[str, int, List[Union[str, int]]]] = None, inLanguage: Optional[Union[str, List[str]]] = None, inCatalogLanguage: Optional[str] = None, materialType: Optional[str] = None, @@ -328,36 +282,24 @@ def search_brief_bibs( openAccess: Optional[bool] = None, peerReviewed: Optional[bool] = None, facets: Optional[Union[str, List[str]]] = None, - groupRelatedEditions: Optional[bool] = None, groupVariantRecords: Optional[bool] = None, preferredLanguage: Optional[str] = None, showHoldingsIndicators: Optional[bool] = None, - lat: Optional[float] = None, - lon: Optional[float] = None, - distance: Optional[int] = None, - unit: Optional[str] = None, - orderBy: Optional[str] = "mostWidelyHeld", offset: Optional[int] = None, limit: Optional[int] = None, + orderBy: Optional[str] = None, hooks: Optional[Dict[str, Callable]] = None, ) -> Optional[Response]: """ - Send a GET request for brief bibliographic resources. - Uses /search/brief-bibs endpoint. + Retrieve other editions related to bibliographic resource with provided + OCLC #. + Uses /brief-bibs/{oclcNumber}/other-editions endpoint. Args: - q: query in the form of a keyword search or - fielded search; - examples: - ti:Zendegi - ti:"Czarne oceany" - bn:9781680502404 - kw:python databases - ti:Zendegi AND au:greg egan - (au:Okken OR au:Myers) AND su:python + oclcNumber: OCLC bibliographic record number; can be an + integer, or string with or without OCLC # prefix deweyNumber: limits the response to the - specified dewey classification number(s); - for multiple values repeat the parameter, + specified dewey classification number(s) example: '794,180' datePublished: restricts the response to one or @@ -367,10 +309,9 @@ def search_brief_bibs( '2000-2005' '2000,2005' heldByGroup: restricts to holdings held by group symbol - heldBySymbol: restricts response to holdings held by specified - institution symbol - heldByInstitutionID: restricts response to holdings held by specified - institution registryId + heldBySymbol: restricts to holdings with specified intitution + symbol + heldByInstitutionID: restrict to specified institution regisgtryId inLanguage: restrics the response to the single specified language, example: 'fre' inCataloglanguage: restrics the response to specified @@ -385,60 +326,48 @@ def search_brief_bibs( itemSubType: restricts responses to single specified OCLC sub facet type, example: 'digital' retentionCommitments: restricts responses to bibliographic records - with retention commitment; True or False + with retention commitment; True or False, + default False spProgram: restricts responses to bibliographic records associated with particular shared print program - genre: genre to limit results to (ge index) - topic: topic to limit results to (s0 index) - subtopic: subtopic to limit results to (s1 index) + genre: genre to limit results to + topic: topic to limit results to + subtopic: subtopic to limit results to audience: audience to limit results to, - available values: 'juv', 'nonJuv' - content: content to limit results to - available values: 'fic', 'nonFic', 'bio' - openAccess: restricts response to just open access content - peerReviewed: restricts response to just peer reviewed content + example: + juv, + nonJuv + content: content to limit resutls to, + example: + fic, + nonFic, + fic,bio + openAccess: filter to only open access content, False or True + peerReviewed: filter to only peer reviewed content, False or True facets: list of facets to restrict responses - groupRelatedEditions: whether or not use FRBR grouping, - options: False, True (default is False) groupVariantRecords: whether or not to group variant records. options: False, True (default False) preferredLanguage: language of metadata description, - default value "en" (English) - showHoldingsIndicators: whether or not to show holdings indicators in - response. options: True, False, default is False - lat: limit to latitude, example: 37.502508 - lon: limit to longitute, example: -122.22702 - distance: distance from latitude and longitude - unit: unit of distance param; options: - 'M' (miles) or 'K' (kilometers) - orderBy: results sort key; - options: - 'recency' - 'bestMatch' - 'creator' - 'library' - 'publicationDateAsc' - 'publicationDateDesc' - 'mostWidelyHeld' - 'title' offset: start position of bibliographic records to return; default 1 limit: maximum nuber of records to return; maximum 50, default 10 + orderBy: sort of restuls; + available values: + +date, -date, +language, -language; + default value: -date 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 """ - if not q: - raise TypeError("Argument 'q' is requried to construct query.") + oclcNumber = verify_oclc_number(oclcNumber) - url = self._url_search_brief_bibs() + url = self._url_search_brief_bibs_other_editions(oclcNumber) header = {"Accept": "application/json"} payload = { - "q": q, "deweyNumber": deweyNumber, "datePublished": datePublished, "heldByGroup": heldByGroup, @@ -460,17 +389,12 @@ def search_brief_bibs( "openAccess": openAccess, "peerReviewed": peerReviewed, "facets": facets, - "groupRelatedEditions": groupRelatedEditions, "groupVariantRecords": groupVariantRecords, "preferredLanguage": preferredLanguage, "showHoldingsIndicators": showHoldingsIndicators, - "lat": lat, - "lon": lon, - "distance": distance, - "unit": unit, - "orderBy": orderBy, "offset": offset, "limit": limit, + "orderBy": orderBy, } # prep request @@ -482,14 +406,14 @@ def search_brief_bibs( return query.response - def search_brief_bibs_other_editions( + def search_brief_bibs( self, - oclcNumber: Union[int, str], + q: str, deweyNumber: Optional[Union[str, List[str]]] = None, datePublished: Optional[Union[str, List[str]]] = None, heldByGroup: Optional[str] = None, heldBySymbol: Optional[Union[str, List[str]]] = None, - heldByInstitutionID: Optional[Union[str, int, List[Union[str, int]]]] = None, + heldByInstitutionID: Optional[Union[str, int, List[str], List[int]]] = None, inLanguage: Optional[Union[str, List[str]]] = None, inCatalogLanguage: Optional[str] = None, materialType: Optional[str] = None, @@ -506,24 +430,36 @@ def search_brief_bibs_other_editions( openAccess: Optional[bool] = None, peerReviewed: Optional[bool] = None, facets: Optional[Union[str, List[str]]] = None, + groupRelatedEditions: Optional[bool] = None, groupVariantRecords: Optional[bool] = None, preferredLanguage: Optional[str] = None, showHoldingsIndicators: Optional[bool] = None, + lat: Optional[float] = None, + lon: Optional[float] = None, + distance: Optional[int] = None, + unit: Optional[str] = None, + orderBy: Optional[str] = "mostWidelyHeld", offset: Optional[int] = None, limit: Optional[int] = None, - orderBy: Optional[str] = None, hooks: Optional[Dict[str, Callable]] = None, ) -> Optional[Response]: """ - Retrieve other editions related to bibliographic resource with provided - OCLC #. - Uses /brief-bibs/{oclcNumber}/other-editions endpoint. + Send a GET request for brief bibliographic resources. + Uses /search/brief-bibs endpoint. Args: - oclcNumber: OCLC bibliographic record number; can be an - integer, or string with or without OCLC # prefix + q: query in the form of a keyword search or + fielded search; + examples: + ti:Zendegi + ti:"Czarne oceany" + bn:9781680502404 + kw:python databases + ti:Zendegi AND au:greg egan + (au:Okken OR au:Myers) AND su:python deweyNumber: limits the response to the - specified dewey classification number(s) + specified dewey classification number(s); + for multiple values repeat the parameter, example: '794,180' datePublished: restricts the response to one or @@ -533,9 +469,10 @@ def search_brief_bibs_other_editions( '2000-2005' '2000,2005' heldByGroup: restricts to holdings held by group symbol - heldBySymbol: restricts to holdings with specified intitution - symbol - heldByInstitutionID: restrict to specified institution regisgtryId + heldBySymbol: restricts response to holdings held by specified + institution symbol + heldByInstitutionID: restricts response to holdings held by specified + institution registryId inLanguage: restrics the response to the single specified language, example: 'fre' inCataloglanguage: restrics the response to specified @@ -550,48 +487,60 @@ def search_brief_bibs_other_editions( itemSubType: restricts responses to single specified OCLC sub facet type, example: 'digital' retentionCommitments: restricts responses to bibliographic records - with retention commitment; True or False, - default False + with retention commitment; True or False spProgram: restricts responses to bibliographic records associated with particular shared print program - genre: genre to limit results to - topic: topic to limit results to - subtopic: subtopic to limit results to + genre: genre to limit results to (ge index) + topic: topic to limit results to (s0 index) + subtopic: subtopic to limit results to (s1 index) audience: audience to limit results to, - example: - juv, - nonJuv - content: content to limit resutls to, - example: - fic, - nonFic, - fic,bio - openAccess: filter to only open access content, False or True - peerReviewed: filter to only peer reviewed content, False or True + available values: 'juv', 'nonJuv' + content: content to limit results to + available values: 'fic', 'nonFic', 'bio' + openAccess: restricts response to just open access content + peerReviewed: restricts response to just peer reviewed content facets: list of facets to restrict responses + groupRelatedEditions: whether or not use FRBR grouping, + options: False, True (default is False) groupVariantRecords: whether or not to group variant records. options: False, True (default False) preferredLanguage: language of metadata description, + default value "en" (English) + showHoldingsIndicators: whether or not to show holdings indicators in + response. options: True, False, default is False + lat: limit to latitude, example: 37.502508 + lon: limit to longitute, example: -122.22702 + distance: distance from latitude and longitude + unit: unit of distance param; options: + 'M' (miles) or 'K' (kilometers) + orderBy: results sort key; + options: + 'recency' + 'bestMatch' + 'creator' + 'library' + 'publicationDateAsc' + 'publicationDateDesc' + 'mostWidelyHeld' + 'title' offset: start position of bibliographic records to return; default 1 limit: maximum nuber of records to return; maximum 50, default 10 - orderBy: sort of restuls; - available values: - +date, -date, +language, -language; - default value: -date 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) + if not q: + raise TypeError("Argument 'q' is requried to construct query.") - url = self._url_search_brief_bibs_other_editions(oclcNumber) + url = self._url_search_brief_bibs() header = {"Accept": "application/json"} payload = { + "q": q, "deweyNumber": deweyNumber, "datePublished": datePublished, "heldByGroup": heldByGroup, @@ -613,12 +562,17 @@ def search_brief_bibs_other_editions( "openAccess": openAccess, "peerReviewed": peerReviewed, "facets": facets, + "groupRelatedEditions": groupRelatedEditions, "groupVariantRecords": groupVariantRecords, "preferredLanguage": preferredLanguage, "showHoldingsIndicators": showHoldingsIndicators, + "lat": lat, + "lon": lon, + "distance": distance, + "unit": unit, + "orderBy": orderBy, "offset": offset, "limit": limit, - "orderBy": orderBy, } # prep request @@ -630,6 +584,43 @@ def search_brief_bibs_other_editions( return query.response + def get_current_oclc_number( + self, + oclcNumbers: Union[str, List[Union[str, int]]], + hooks: Optional[Dict[str, Callable]] = None, + ) -> Optional[Response]: + """ + Retrieve current OCLC control numbers + Uses /manage/bibs/current endpoint. + + Args: + oclcNumbers: string or list containing one or more OCLC numbers + to be checked; numbers can be integers or strings + with or without OCLC # prefix; + if str, the numbers must be separated by a comma + 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 + """ + + vetted_numbers = verify_oclc_numbers(oclcNumbers) + + header = {"Accept": "application/json"} + url = self._url_manage_bibs_current_oclc_number() + payload = {"oclcNumbers": ",".join(vetted_numbers)} + + # prep request + req = Request("GET", 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_bibs_holdings( self, oclcNumber: Optional[Union[int, str]] = None, diff --git a/tests/test_metadata_api.py b/tests/test_metadata_api.py index d5963a7..a3d2236 100644 --- a/tests/test_metadata_api.py +++ b/tests/test_metadata_api.py @@ -82,35 +82,8 @@ def test_split_into_legal_volume(self, stub_session, oclcNumbers, expectation): all_batches = [b for b in batches] assert all_batches == expectation - def test_url_manage_bibs(self, stub_session): - assert ( - stub_session._url_manage_bibs(oclcNumber="12345") - == "https://metadata.api.oclc.org/worldcat/manage/bibs/12345" - ) - - def test_url_manage_bibs_current_oclc_number(self, stub_session): - assert ( - stub_session._url_manage_bibs_current_oclc_number() - == "https://metadata.api.oclc.org/worldcat/manage/bibs/current" - ) - - def test_url_manage_ih_current(self, stub_session): - assert ( - stub_session._url_manage_ih_current() - == "https://metadata.api.oclc.org/worldcat/manage/institution/holdings/current" - ) - - def test_url_manage_ih_set(self, stub_session): - assert ( - stub_session._url_manage_ih_set(oclcNumber="12345") - == "https://metadata.api.oclc.org/worldcat/manage/institution/holdings/12345/set" - ) - - def test_url_manage_ih_unset(self, stub_session): - assert ( - stub_session._url_manage_ih_unset(oclcNumber="12345") - == "https://metadata.api.oclc.org/worldcat/manage/institution/holdings/12345/unset" - ) + def test_url_base(self, stub_session): + assert stub_session.BASE_URL == "https://metadata.api.oclc.org/worldcat" def test_url_search_shared_print_holdings(self, stub_session): assert ( @@ -146,22 +119,34 @@ def test_url_search_brief_bibs_other_editions(self, stub_session): == "https://metadata.api.oclc.org/worldcat/search/brief-bibs/12345/other-editions" ) - def test_url_lhr_shared_print(self, stub_session): + def test_url_manage_bib(self, stub_session): assert ( - stub_session._url_lhr_shared_print() - == "https://metadata.api.oclc.org/worldcat/retained-holdings" + stub_session._url_manage_bibs(oclcNumber="12345") + == "https://metadata.api.oclc.org/worldcat/manage/bibs/12345" ) - def test_url_lhr_control_number(self, stub_session): + def test_url_manage_bib_current_oclc_number(self, stub_session): assert ( - stub_session._url_lhr_control_number(controlNumber="12345") - == "https://metadata.api.oclc.org/worldcat/my-holdings/12345" + stub_session._url_manage_bibs_current_oclc_number() + == "https://metadata.api.oclc.org/worldcat/manage/bibs/current" ) - def test_url_lhr_search(self, stub_session): + def test_url_manage_ih_set(self, stub_session): assert ( - stub_session._url_lhr_search() - == "https://metadata.api.oclc.org/worldcat/my-holdings" + stub_session._url_manage_ih_set(oclcNumber="12345") + == "https://metadata.api.oclc.org/worldcat/manage/institution/holdings/12345/set" + ) + + def test_url_manage_ih_unset(self, stub_session): + assert ( + stub_session._url_manage_ih_unset(oclcNumber="12345") + == "https://metadata.api.oclc.org/worldcat/manage/institution/holdings/12345/unset" + ) + + def test_url_manage_ih_current(self, stub_session): + assert ( + stub_session._url_manage_ih_current() + == "https://metadata.api.oclc.org/worldcat/manage/institution/holdings/current" ) @pytest.mark.http_code(200)