1+ import requests
2+ import os
3+ from urllib import parse
4+
5+ class OrcidSearch ():
6+ '''
7+ This is a wrapper class for ORCID Search API
8+ '''
9+ def __init__ (self , orcid_access_token = " " , state = "public" , sandbox = False ) -> None :
10+ '''
11+ Initialize orcid search instance
12+ state : Whether to use public or member API of ORCID
13+ orcid_access_token : Orcid access token obtained from the user with this orcid_id (default: "public")
14+ sandbox : bool : a boolean value to show if the ORCID sandbox API should be used (default: False)
15+ '''
16+ self ._orcid_access_token = orcid_access_token
17+ self ._state = state
18+ self ._sandbox = sandbox
19+ # For testing purposes (pytesting on github workflow)
20+ if orcid_access_token != " " :
21+ try :
22+ self .__is_access_token_valid ()
23+ except :
24+ if not self .__test_is_access_token_valid ():
25+ raise ValueError (
26+ f"Invalid access token! Please make sure the provided credentials are correct." )
27+
28+ return
29+
30+ def search (self , query , start = 0 , rows = 1000 , search_mode = "expanded-search" , columns = "orcid,given-names,family-name,current-institution-affiliation-name" ):
31+ '''
32+ Search orcid records
33+ for details on the query format see https://info.orcid.org/documentation/api-tutorials/api-tutorial-searching-the-orcid-registry/
34+
35+ query : the search query
36+ start : the offset for the paginated search, default = 0
37+ rows : the number of rows to be returned, default = 1000
38+ search_mode : the search mode, either "expanded-search" (default), "search", or "csv-search"
39+ columns : for the csv-search, default: "orcid,given-names,family-name,current-institution-affiliation-name"
40+ return : a dictionary of search results
41+ '''
42+
43+ access_token = self ._orcid_access_token
44+
45+
46+ _search_mode = "expanded-search"
47+ if search_mode == "search" or search_mode == "csv-search" :
48+ _search_mode = search_mode
49+ _columns = columns
50+ query_encoded = parse .quote_plus (query )
51+
52+ api_url = ""
53+
54+ if self ._state == "public" :
55+ # Specify the ORCID record endpoint for the desired ORCID iD
56+ api_url = f'https://pub.orcid.org/'
57+ if (self ._sandbox ):
58+ api_url = f'https://pub.sandbox.orcid.org/' # for testing
59+
60+ elif self ._state == "member" :
61+ api_url = f'https://api.orcid.org/'
62+ if (self ._sandbox ):
63+ api_url = f'https://api.sandbox.orcid.org/' # for testing
64+
65+ api_url = api_url + f'v3.0/{ _search_mode } /?q={ query_encoded } &start={ start } &rows={ rows } '
66+
67+ content_type = 'application/json'
68+ if search_mode == "csv-search" :
69+ api_url = api_url + f'&fl={ columns } '
70+ content_type = "text/csv"
71+
72+ # Set the headers with the access token for authentication
73+ headers = {
74+ 'Authorization' : f'Bearer { access_token } ' ,
75+ 'Content-Type' : f'{ content_type } '
76+ }
77+
78+ #print(api_url)
79+
80+ # Make a GET request to retrieve the ORCID record
81+ response = requests .get (api_url , headers = headers )
82+
83+ # The request was successful
84+ data = response .json ()
85+ # Check the response status code
86+ if response .status_code == 200 or data is not None :
87+ return data
88+ else :
89+ # Handle the case where the request failed
90+ print ("Failed to retrieve ORCID search results. Status code:" , response .status_code )
91+ return None
92+ def __is_access_token_valid (self ):
93+ '''
94+ Checks if the current access token is valid
95+ '''
96+ access_token = self ._orcid_access_token
97+
98+ if access_token == "" :
99+ raise ValueError (
100+ "Empty value for access token! Please make sure you are authenticated by ORCID as developer." )
101+ # Make a test request to the API using the token
102+ headers = {
103+ 'Authorization' : f'Bearer { access_token } ' ,
104+ 'Content-Type' : 'application/json'
105+ }
106+
107+ api_url = ""
108+
109+ if self ._state == "public" :
110+ # Specify the ORCID record endpoint for the desired ORCID iD
111+ api_url = f'https://pub.orcid.org/v3.0/search'
112+ if (self ._sandbox ):
113+ api_url = f'https://pub.sandbox.orcid.org/v3.0/search' # for testing
114+
115+ elif self ._state == "member" :
116+ api_url = f'https://api.orcid.org/v3.0/search'
117+ if (self ._sandbox ):
118+ api_url = f'https://api.sandbox.orcid.org/v3.0/search' # for testing
119+
120+ response = requests .get (api_url , headers = headers )
121+
122+ if response .status_code == 404 :
123+ # The request was successful, and the token is likely valid
124+ return False
125+ else :
126+ # The request failed, indicating that the token may have expired or is invalid
127+ return True
128+
129+ ## THESE FUNCTIONS ARE FOR TESTING PURPOSES ##
130+
131+ def __test_is_access_token_valid (self ):
132+ '''
133+ FOR TESTING PURPOSES ONLY
134+ Checks if the current access token is valid
135+ '''
136+ # Access the environment variable from github secrets
137+ access_token = os .environ ["ORCID_ACCESS_TOKEN" ]
138+ if access_token == "" :
139+ raise ValueError (
140+ "Empty value for access token! Please make sure you are authenticated by ORCID as developer." )
141+ # Make a test request to the API using the token
142+ headers = {
143+ 'Authorization' : f'Bearer { access_token } ' ,
144+ 'Content-Type' : 'application/json'
145+ }
146+
147+ api_url = ""
148+
149+ if self ._state == "public" :
150+ # Specify the ORCID record endpoint for the desired ORCID iD
151+ api_url = f'https://pub.sandbox.orcid.org/v3.0/search'
152+
153+ elif self ._state == "member" :
154+ api_url = f'https://api.sandbox.orcid.org/v3.0/search'
155+
156+ response = requests .get (api_url , headers = headers )
157+ if response .status_code == 404 :
158+ # The request was successful, and the token is likely valid
159+ return False
160+ else :
161+ # The request failed, indicating that the token may have expired or is invalid
162+ return True
0 commit comments