Skip to content

Commit df67920

Browse files
publication lookup edits post feedback startup refactor and constants sorted
1 parent 28b1f8c commit df67920

File tree

5 files changed

+106
-73
lines changed

5 files changed

+106
-73
lines changed

pybliometrics/scival/publication_lookup.py

Lines changed: 35 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
"""Module with the PublicationLookup class."""
12
from collections import namedtuple
23
from typing import Union, Optional
34

@@ -14,51 +15,52 @@ def id(self) -> Optional[int]:
1415

1516
@property
1617
def title(self) -> Optional[str]:
17-
"""Publication title"""
18+
"""Publication title."""
1819
return chained_get(self._json, ['publication', 'title'])
1920

2021
@property
2122
def doi(self) -> Optional[str]:
22-
"""Digital Object Identifier (DOI)"""
23+
"""Digital Object Identifier (DOI)."""
2324
return chained_get(self._json, ['publication', 'doi'])
2425

2526
@property
2627
def type(self) -> Optional[str]:
27-
"""Type of publication"""
28+
"""Type of publication."""
2829
return chained_get(self._json, ['publication', 'type'])
2930

3031
@property
3132
def publication_year(self) -> Optional[int]:
32-
"""Year of publication"""
33+
"""Year of publication."""
3334
return make_int_if_possible(chained_get(self._json, ['publication', 'publicationYear']))
3435

3536
@property
3637
def citation_count(self) -> Optional[int]:
37-
"""Count of citations"""
38+
"""Count of citations."""
3839
return make_int_if_possible(chained_get(self._json, ['publication', 'citationCount']))
3940

4041
@property
4142
def source_title(self) -> Optional[str]:
42-
"""Title of source"""
43+
"""Title of source."""
4344
return chained_get(self._json, ['publication', 'sourceTitle'])
4445

4546
@property
4647
def topic_id(self) -> Optional[int]:
47-
"""Topic id"""
48+
"""Topic id."""
4849
return make_int_if_possible(chained_get(self._json, ['publication', 'topicId']))
4950

5051
@property
5152
def topic_cluster_id(self) -> Optional[int]:
52-
"""Topic cluster id"""
53+
"""Topic cluster id."""
5354
return make_int_if_possible(chained_get(self._json, ['publication', 'topicClusterId']))
5455

5556
@property
5657
def link(self) -> Optional[str]:
57-
"""URL link"""
58+
"""URL link."""
5859
return chained_get(self._json, ['link', '@href'])
5960

6061
@property
6162
def authors(self) -> Optional[list[namedtuple]]:
63+
"""Publication authors."""
6264
out = []
6365
fields = 'id name link'
6466
auth = namedtuple('Author', fields)
@@ -70,50 +72,49 @@ def authors(self) -> Optional[list[namedtuple]]:
7072

7173
@property
7274
def institutions(self) -> Optional[list[namedtuple]]:
75+
"""Institutions linked to publication authors."""
7376
out = []
7477
fields = 'id name country country_code link'
7578
auth = namedtuple('Institution', fields)
7679
for item in chained_get(self._json, ['publication', 'institutions'], []):
77-
new = auth(id=make_int_if_possible(item['id']), name=item.get('name'),
78-
country=item.get('country'), country_code=item.get('countryCode'),
79-
link=chained_get(item, ['link', '@href']))
80+
new = auth(id=make_int_if_possible(item['id']), name=item.get('name'), country=item.get('country'),
81+
country_code=item.get('countryCode'), link=chained_get(item, ['link', '@href']))
8082
out.append(new)
8183
return out or None
8284

8385
@property
8486
def sdgs(self) -> Optional[list[str]]:
85-
"""Sustainable Development Goals."""
87+
"""List of Sustainable Development Goals (SDG)."""
8688
return chained_get(self._json, ['publication', 'sdg'])
8789

8890
def __str__(self):
8991
"""Print a summary string."""
9092
authors = ', '.join(a.name for a in self.authors) if self.authors else "N/A"
9193
institutions = ', '.join(i.name for i in self.institutions) if self.institutions else "N/A"
9294
sdgs = ', '.join(self.sdgs) if self.sdgs else "N/A"
93-
s = (
94-
f"Publication Summary:\n"
95-
f"- ID: {self.id or 'N/A'}\n"
96-
f"- Title: {self.title or 'N/A'}\n"
97-
f"- DOI: {self.doi or 'N/A'}\n"
98-
f"- Type: {self.type or 'N/A'}\n"
99-
f"- Year: {self.publication_year or 'N/A'}\n"
100-
f"- Citation Count: {self.citation_count or 'N/A'}\n"
101-
f"- Source Title: {self.source_title or 'N/A'}\n"
102-
f"- Topic ID: {self.topic_id or 'N/A'}\n"
103-
f"- Topic Cluster ID: {self.topic_cluster_id or 'N/A'}\n"
104-
f"- Link: {self.link or 'N/A'}\n"
105-
f"- Authors: {authors}\n"
106-
f"- Institutions: {institutions}\n"
107-
f"- SDGs: {sdgs}\n"
108-
)
95+
s = (f"Publication Summary:\n"
96+
f"- ID: {self.id or 'N/A'}\n"
97+
f"- Title: {self.title or 'N/A'}\n"
98+
f"- DOI: {self.doi or 'N/A'}\n"
99+
f"- Type: {self.type or 'N/A'}\n"
100+
f"- Year: {self.publication_year or 'N/A'}\n"
101+
f"- Citation Count: {self.citation_count or 'N/A'}\n"
102+
f"- Source Title: {self.source_title or 'N/A'}\n"
103+
f"- Topic ID: {self.topic_id or 'N/A'}\n"
104+
f"- Topic Cluster ID: {self.topic_cluster_id or 'N/A'}\n"
105+
f"- Link: {self.link or 'N/A'}\n"
106+
f"- Authors: {authors}\n"
107+
f"- Institutions: {institutions}\n"
108+
f"- SDGs: {sdgs}\n")
109109
return s
110110

111-
def __init__(self,
112-
identifier: int = None,
113-
refresh: Union[bool, int] = False,
114-
**kwds: str
115-
) -> None:
111+
def __init__(self, identifier: int = None, refresh: Union[bool, int] = False, **kwds: str) -> None:
116112
"""Interaction with the Publication Lookup API.
113+
:param identifier: The Scopus ID of the object.
114+
:param refresh: Whether to refresh the cached file if it exists. Default: `False`.
115+
:param kwds: Keywords passed on to requests header. Must contain
116+
fields and values specified in the respective
117+
API specification.
117118
"""
118119
self._view = ''
119120
self._refresh = refresh
Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
"""Tests for the PublicationLookup() class."""
2+
13
from pybliometrics.scival import PublicationLookup
24
from pybliometrics.utils import init
35

@@ -7,20 +9,50 @@
79
pub1 = PublicationLookup(85036568406)
810

911

10-
def test_publication():
12+
def test_publication_id():
1113
assert pub1.id == 85036568406
14+
15+
16+
def test_publication_doi():
1217
assert pub1.doi == "10.1002/anie.201709271"
18+
19+
20+
def test_publication_type():
1321
assert pub1.type == "Article"
22+
23+
24+
def test_publication_year():
1425
assert pub1.publication_year == 2017
26+
27+
28+
def test_publication_source_title():
1529
assert pub1.source_title == 'Angewandte Chemie - International Edition'
30+
31+
32+
def test_publication_citation_count():
1633
assert pub1.citation_count > 0
34+
35+
36+
def test_publication_authors_count():
1737
assert len(pub1.authors) >= 7
38+
39+
40+
def test_publication_first_author():
1841
assert pub1.authors[0].id == 7404861905
1942
assert pub1.authors[0].name == "Lin, T.-E."
43+
44+
45+
def test_publication_institutions_count():
2046
assert len(pub1.institutions) >= 3
47+
48+
49+
def test_publication_first_institution():
2150
assert pub1.institutions[0].id == 217002
2251
assert pub1.institutions[0].name == "Chang Gung University"
2352
assert pub1.institutions[0].country == "Taiwan"
2453
assert pub1.institutions[0].country_code == "TWN"
54+
55+
56+
def test_publication_sdgs():
2557
assert len(pub1.sdgs) >= 1
26-
assert pub1.sdgs[0] == 'SDG 3: Good Health and Well-being'
58+
assert pub1.sdgs[0] == 'SDG 3: Good Health and Well-being'

pybliometrics/superclasses/retrieval.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,5 +47,7 @@ def __init__(self,
4747
self._cache_file_path = parent/self._view/stem
4848

4949
# Parse file contents
50-
params = {'view': self._view, **kwds}
50+
params = {**kwds}
51+
if self._view:
52+
params['view'] = self._view
5153
Base.__init__(self, params=params, url=url)

pybliometrics/utils/constants.py

Lines changed: 27 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -26,21 +26,20 @@
2626
'AbstractRetrieval': BASE_PATH_SCOPUS / 'abstract_retrieval',
2727
'AffiliationRetrieval': BASE_PATH_SCOPUS / 'affiliation_retrieval',
2828
'AffiliationSearch': BASE_PATH_SCOPUS / 'affiliation_search',
29+
'ArticleEntitlement': BASE_PATH_SCIENCEDIRECT / 'article_entitlement',
30+
'ArticleMetadata': BASE_PATH_SCIENCEDIRECT / 'article_metadata / ',
31+
'ArticleRetrieval': BASE_PATH_SCIENCEDIRECT / 'article_retrieval',
2932
'AuthorRetrieval': BASE_PATH_SCOPUS / 'author_retrieval',
3033
'AuthorSearch': BASE_PATH_SCOPUS / 'author_search',
3134
'CitationOverview': BASE_PATH_SCOPUS / 'citation_overview',
35+
'PlumXMetrics': BASE_PATH_SCOPUS / 'plumx',
36+
'PublicationLookup': BASE_PATH_SCIVAL / 'publication_lookup',
37+
'ScDirSubjectClassifications': BASE_PATH_SCIENCEDIRECT / 'subject_classification',
38+
'ScienceDirectSearch': BASE_PATH_SCIENCEDIRECT / 'science_direct_search',
3239
'ScopusSearch': BASE_PATH_SCOPUS / 'scopus_search',
3340
'SerialSearch': BASE_PATH_SCOPUS / 'serial_search',
3441
'SerialTitle': BASE_PATH_SCOPUS / 'serial_title',
35-
'PlumXMetrics': BASE_PATH_SCOPUS / 'plumx',
3642
'SubjectClassifications': BASE_PATH_SCOPUS / 'subject_classification',
37-
'ArticleEntitlement': BASE_PATH_SCIENCEDIRECT / 'article_entitlement',
38-
'ArticleMetadata': BASE_PATH_SCIENCEDIRECT / 'article_metadata / ',
39-
'ArticleRetrieval': BASE_PATH_SCIENCEDIRECT / 'article_retrieval',
40-
'ScienceDirectSearch': BASE_PATH_SCIENCEDIRECT / 'science_direct_search',
41-
'ScDirSubjectClassifications': BASE_PATH_SCIENCEDIRECT / 'subject_classification',
42-
43-
'PublicationLookup': BASE_PATH_SCIVAL / 'publication_lookup',
4443
}
4544

4645
# URLs for all classes
@@ -53,66 +52,61 @@
5352
'AbstractRetrieval': RETRIEVAL_BASE + 'abstract/',
5453
'AffiliationRetrieval': RETRIEVAL_BASE + 'affiliation/affiliation_id/',
5554
'AffiliationSearch': SEARCH_BASE + 'affiliation',
55+
'ArticleEntitlement': RETRIEVAL_BASE + 'article/entitlement/',
56+
'ArticleMetadata': RETRIEVAL_BASE + 'metadata/article/',
57+
'ArticleRetrieval': RETRIEVAL_BASE + 'article/',
5658
'AuthorRetrieval': RETRIEVAL_BASE + 'author/author_id/',
5759
'AuthorSearch': SEARCH_BASE + 'author',
5860
'CitationOverview': RETRIEVAL_BASE + 'abstract/citations/',
61+
'PlumXMetrics': 'https://api.elsevier.com/analytics/plumx/',
62+
'PublicationLookup': SCIVAL_BASE + 'publication/',
5963
'ScopusSearch': SEARCH_BASE + 'scopus',
64+
'ScienceDirectSearch': SEARCH_BASE + 'sciencedirect/',
65+
'ScDirSubjectClassifications': RETRIEVAL_BASE + 'subject/scidir/',
6066
'SerialSearch': RETRIEVAL_BASE + 'serial/title',
6167
'SerialTitle': RETRIEVAL_BASE + 'serial/title/issn/',
6268
'SubjectClassifications': RETRIEVAL_BASE + 'subject/scopus',
63-
'PlumXMetrics': 'https://api.elsevier.com/analytics/plumx/',
64-
'ArticleEntitlement': RETRIEVAL_BASE + 'article/entitlement/',
65-
'ArticleMetadata': RETRIEVAL_BASE + 'metadata/article/',
66-
'ArticleRetrieval': RETRIEVAL_BASE + 'article/',
67-
'ScienceDirectSearch': SEARCH_BASE + 'sciencedirect/',
68-
'ScDirSubjectClassifications': RETRIEVAL_BASE + 'subject/scidir/',
69-
70-
'PublicationLookup': SCIVAL_BASE + 'publication/',
71-
'PublicationMetrics': SCIVAL_BASE + 'publication/metrics/'
7269
}
7370

7471
# Valid views for all classes
7572
VIEWS = {
76-
"CitationOverview": ["STANDARD"],
7773
"AbstractRetrieval": ["META", "META_ABS", "FULL", "REF", "ENTITLED"],
7874
"AffiliationRetrieval": ["LIGHT", "STANDARD", "ENTITLED"],
7975
"AffiliationSearch": ["STANDARD"],
76+
"ArticleEntitlement": ["FULL"],
77+
"ArticleMetadata": ["STANDARD", "COMPLETE"],
78+
"ArticleRetrieval": ["META", "META_ABS", "META_ABS_REF", "FULL", "ENTITLED"],
8079
"AuthorRetrieval": ["LIGHT", "STANDARD", "ENHANCED", "METRICS", "ENTITLED"],
8180
"AuthorSearch": ["STANDARD"],
81+
"CitationOverview": ["STANDARD"],
8282
"PlumXMetrics": ["ENHANCED"],
83+
"ScDirSubjectClassifications": [''],
84+
"ScienceDirectSearch": ["STANDARD"],
8385
"ScopusSearch": ["STANDARD", "COMPLETE"],
8486
"SerialSearch": ["STANDARD", "ENHANCED", "CITESCORE"],
8587
"SerialTitle": ["STANDARD", "ENHANCED", "CITESCORE"],
8688
"SubjectClassifications": [''],
87-
"ArticleEntitlement": ["FULL"],
88-
"ArticleRetrieval": ["META", "META_ABS", "META_ABS_REF", "FULL", "ENTITLED"],
89-
"ArticleMetadata": ["STANDARD", "COMPLETE"],
90-
"ScienceDirectSearch": ["STANDARD"],
91-
"ScDirSubjectClassifications": [''],
92-
93-
"PublicationLookup": ['']
9489
}
9590

9691
# Throttling limits (in queries per second) // 0 = no limit
9792
RATELIMITS = {
9893
'AbstractRetrieval': 9,
9994
'AffiliationRetrieval': 9,
10095
'AffiliationSearch': 6,
96+
'ArticleEntitlement': 0,
97+
'ArticleMetadata': 6,
98+
'ArticleRetrieval': 10,
10199
'AuthorRetrieval': 3,
102100
'AuthorSearch': 2,
103101
'CitationOverview': 4,
102+
'PlumXMetrics': 6,
103+
'PublicationLookup': 6,
104+
'ScDirSubjectClassifications': 0,
105+
'ScienceDirectSearch': 2,
104106
'ScopusSearch': 9,
105107
'SerialSearch': 6,
106108
'SerialTitle': 6,
107-
'PlumXMetrics': 6,
108109
'SubjectClassifications': 0,
109-
'ArticleEntitlement': 0,
110-
'ArticleMetadata': 6,
111-
'ArticleRetrieval': 10,
112-
'ScienceDirectSearch': 2,
113-
'ScDirSubjectClassifications': 0,
114-
115-
'PublicationLookup': 6
116110
}
117111

118112
# Other API restrictions

pybliometrics/utils/startup.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,9 +114,13 @@ def check_keys_tokens() -> None:
114114
def create_cache_folders(config: Type[ConfigParser]) -> None:
115115
"""Auxiliary function to create cache folders."""
116116
for api, path in config.items('Directories'):
117-
for view in VIEWS[api]:
118-
view_path = Path(path, view)
119-
view_path.mkdir(parents=True, exist_ok=True)
117+
base_dir = Path(path)
118+
views = VIEWS.get(api, [])
119+
if views:
120+
for view in views:
121+
(base_dir / view).mkdir(parents=True, exist_ok=True)
122+
else:
123+
base_dir.mkdir(parents=True, exist_ok=True)
120124

121125

122126
def get_config() -> Type[ConfigParser]:

0 commit comments

Comments
 (0)