Skip to content

Commit a593f4f

Browse files
authored
Merge pull request #6 from DavidFichtmueller/main
Added support for ORCID search
2 parents 8404eb2 + 61852aa commit a593f4f

File tree

5 files changed

+173
-3
lines changed

5 files changed

+173
-3
lines changed

README.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,13 @@ for key, value in works_data.items():
126126
# Generate a markdown file with the summary of various section's data
127127
orcid.generate_markdown_file(output_file = "md_generator_example.md")
128128
```
129-
129+
#### Searching ORCID records
130+
```python
131+
from pyorcid import OrcidSearch
132+
#search through ORCID records, for details on the query format see https://info.orcid.org/documentation/api-tutorials/api-tutorial-searching-the-orcid-registry/
133+
orcidSearch = OrcidSearch(orcid_access_token=access_token)
134+
orcidSearch.search("John Smith")
135+
```
130136
## Access through OrcidScrapper feature of PyOrcid
131137
This is an alternative to Orcid API. You can only read the orcid profiles on public database. All you need is the Orcid ID of the researchers you wish to retrieve.
132138
OrcidScrapper can access all methods of Orcid class as it is inherited from it.

src/pyorcid/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
from .orcid import Orcid
22
from .orcid_authentication import OrcidAuthentication
3-
from .orcid_scrapper import OrcidScrapper
3+
from .orcid_scrapper import OrcidScrapper
4+
from .orcid_search import OrcidSearch

src/pyorcid/orcid.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ def __init__(self,orcid_id, orcid_access_token = " ", state="public", sandbox=Fa
1313
orcid_id : Orcid ID of the user
1414
orcid_access_token : Orcid access token obtained from the user with this orcid_id
1515
state : Whether to use public or member API of ORCID
16+
sandbox : a boolean value to show if the ORCID sandbox API should be used (default: False)
1617
'''
1718
self._orcid_id = orcid_id
1819
self._orcid_access_token = orcid_access_token

src/pyorcid/orcid_authentication.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ def __init__(self, client_id, client_secret, redirect_uri="", sandbox=False):
1515
client_id : str : client id obtained from the registered application
1616
client_secret : str : client secret obtained from the registered application
1717
redirect_uri : str : redirect uri obtained from the registered application
18-
sandbox : str : a boolean value to show if the ORCID sandbox API should be used (default: False)
18+
sandbox : bool : a boolean value to show if the ORCID sandbox API should be used (default: False)
1919
2020
'''
2121
self.__client_id = client_id

src/pyorcid/orcid_search.py

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
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

Comments
 (0)