from typing import List, Literal, Tuple, Union
import asyncio
import nest_asyncio
from semanticscholar.PaginatedResults import PaginatedResults
from semanticscholar.AsyncSemanticScholar import AsyncSemanticScholar
from semanticscholar.Author import Author
from semanticscholar.Dataset import Dataset
from semanticscholar.DatasetDiff import DatasetDiff
from semanticscholar.Paper import Paper
from semanticscholar.Release import Release
from semanticscholar.Autocomplete import Autocomplete
class SemanticScholar():
'''
Main class to retrieve data from Semantic Scholar Graph API synchronously.
'''
def __init__(
self,
timeout: int = 30,
api_key: str = None,
api_url: str = None,
debug: bool = False,
retry: bool = True,
) -> None:
'''
:param float timeout: (optional) an exception is raised
if the server has not issued a response for timeout seconds.
:param str api_key: (optional) private API key.
:param str api_url: (optional) custom API url.
:param bool debug: (optional) enable debug mode.
:param bool retry: enable retry mode.
'''
nest_asyncio.apply()
self._timeout = timeout
self._retry = retry
self._AsyncSemanticScholar = AsyncSemanticScholar(
timeout=timeout,
api_key=api_key,
api_url=api_url,
debug=debug,
retry=retry
)
self.debug = debug
@property
def timeout(self) -> int:
'''
Timeout for server response in seconds.
:type: :class:`int`
'''
return self._timeout
@timeout.setter
def timeout(self, timeout: int) -> None:
'''
:param int timeout:i
'''
self._timeout = timeout
self._AsyncSemanticScholar.timeout = timeout
@property
def debug(self) -> bool:
'''
Enable/disable debug mode.
:type: :class:`bool`
.. deprecated:: 0.8.4
Use Python\'s standard logging in DEBUG level instead.
'''
return self._debug
@debug.setter
def debug(self, debug: bool) -> None:
'''
:param bool debug:
'''
self._debug = debug
self._AsyncSemanticScholar.debug = debug
@property
def retry(self) -> bool:
'''
Enable/disable retry mode.
:type: :class:`bool`
'''
return self._retry
@retry.setter
def retry(self, retry: bool) -> None:
'''
:param bool retry:
'''
self._retry = retry
self._AsyncSemanticScholar.retry = retry
def get_paper(
self,
paper_id: str,
fields: list = None
) -> Paper:
'''
Paper lookup
:calls: `GET /graph/v1/paper/{paper_id} \
`_
:param str paper_id: S2PaperId, CorpusId, DOI, ArXivId, MAG, ACL,
PMID, PMCID, or URL from:
- semanticscholar.org
- arxiv.org
- aclweb.org
- acm.org
- biorxiv.org
:param list fields: (optional) list of the fields to be returned.
:returns: paper data
:rtype: :class:`semanticscholar.Paper.Paper`
:raises: ObjectNotFoundException: if Paper ID not found.
'''
loop = asyncio.get_event_loop()
paper = loop.run_until_complete(
self._AsyncSemanticScholar.get_paper(
paper_id=paper_id,
fields=fields
)
)
return paper
def get_papers(
self,
paper_ids: List[str],
fields: list = None,
return_not_found: bool = False
) -> Union[List[Paper], Tuple[List[Paper], List[str]]]:
'''
Get details for multiple papers at once
:calls: `POST /graph/v1/paper/batch \
`_
:param str paper_ids: list of IDs (must be <= 500) - S2PaperId,
CorpusId, DOI, ArXivId, MAG, ACL, PMID, PMCID, or URL from:
- semanticscholar.org
- arxiv.org
- aclweb.org
- acm.org
- biorxiv.org
:param list fields: (optional) list of the fields to be returned.
:param bool return_not_found: (optional) flag to include not found IDs
in the return, except for IDs in URL: format.
:returns: papers data, and optionally list of IDs not found.
:rtype: :class:`List` of :class:`semanticscholar.Paper.Paper`
or :class:`Tuple` [:class:`List` of
:class:`semanticscholar.Paper.Paper`,
:class:`List` of :class:`str`]
:raises: BadQueryParametersException: if no paper was found.
'''
loop = asyncio.get_event_loop()
papers = loop.run_until_complete(
self._AsyncSemanticScholar.get_papers(
paper_ids=paper_ids,
fields=fields,
return_not_found=return_not_found
)
)
return papers
def get_paper_authors(
self,
paper_id: str,
fields: list = None,
limit: int = 100
) -> PaginatedResults:
'''
Get details about a paper's authors
:calls: `POST /graph/v1/paper/{paper_id}/authors \
`_
:param str paper_id: S2PaperId, CorpusId, DOI, ArXivId, MAG, ACL,
PMID, PMCID, or URL from:
- semanticscholar.org
- arxiv.org
- aclweb.org
- acm.org
- biorxiv.org
:param list fields: (optional) list of the fields to be returned.
:param int limit: (optional) maximum number of results to return
(must be <= 1000).
'''
loop = asyncio.get_event_loop()
results = loop.run_until_complete(
self._AsyncSemanticScholar.get_paper_authors(
paper_id=paper_id,
fields=fields,
limit=limit
)
)
return results
def get_paper_citations(
self,
paper_id: str,
fields: list = None,
limit: int = 100
) -> PaginatedResults:
'''
Get details about a paper's citations
:calls: `POST /graph/v1/paper/{paper_id}/citations \
`_
:param str paper_id: S2PaperId, CorpusId, DOI, ArXivId, MAG, ACL,
PMID, PMCID, or URL from:
- semanticscholar.org
- arxiv.org
- aclweb.org
- acm.org
- biorxiv.org
:param list fields: (optional) list of the fields to be returned.
:param int limit: (optional) maximum number of results to return
(must be <= 1000).
'''
loop = asyncio.get_event_loop()
results = loop.run_until_complete(
self._AsyncSemanticScholar.get_paper_citations(
paper_id=paper_id,
fields=fields,
limit=limit
)
)
return results
def get_paper_references(
self,
paper_id: str,
fields: list = None,
limit: int = 100
) -> PaginatedResults:
'''
Get details about a paper's references
:calls: `POST /graph/v1/paper/{paper_id}/references \
`_
:param str paper_id: S2PaperId, CorpusId, DOI, ArXivId, MAG, ACL,
PMID, PMCID, or URL from:
- semanticscholar.org
- arxiv.org
- aclweb.org
- acm.org
- biorxiv.org
:param list fields: (optional) list of the fields to be returned.
:param int limit: (optional) maximum number of results to return
(must be <= 1000).
'''
loop = asyncio.get_event_loop()
results = loop.run_until_complete(
self._AsyncSemanticScholar.get_paper_references(
paper_id=paper_id,
fields=fields,
limit=limit
)
)
return results
def search_paper(
self,
query: str,
year: str = None,
publication_types: list = None,
open_access_pdf: bool = None,
venue: list = None,
fields_of_study: list = None,
fields: list = None,
publication_date_or_year: str = None,
min_citation_count: int = None,
limit: int = 100,
bulk: bool = False,
sort: str = None,
match_title: bool = False
) -> Union[PaginatedResults, Paper]:
'''
Search for papers by keyword. Performs a search query based on the
S2 search relevance algorithm, or a bulk retrieval of basic paper
data without search relevance (if bulk=True). Paper relevance
search is the default behavior and returns up to 1,000 results.
Bulk retrieval instead returns up to 10,000,000 results (1,000
in each page).
:calls: `GET /graph/v1/paper/search \
`_
:calls: `GET /graph/v1/paper/search/bulk \
`_
:param str query: plain-text search query string.
:param str year: (optional) restrict results to the given range of
publication year.
:param list publication_type: (optional) restrict results to the given
publication type list.
:param bool open_access_pdf: (optional) restrict results to papers
with public PDFs.
:param list venue: (optional) restrict results to the given venue list.
:param list fields_of_study: (optional) restrict results to given
field-of-study list, using the s2FieldsOfStudy paper field.
:param list fields: (optional) list of the fields to be returned.
:param str publication_date_or_year: (optional) restrict results to
the given range of publication date in the format
:, where dates are in the format
YYYY-MM-DD, YYYY-MM, or YYYY.
:param int min_citation_count: (optional) restrict results to papers
with at least the given number of citations.
:param int limit: (optional) maximum number of results to return
(must be <= 100).
:param bool bulk: (optional) bulk retrieval of basic paper data
without search relevance (ignores the limit parameter if True
and returns up to 1,000 results in each page).
:param str sort: (optional) sorts results (only if bulk=True) using
: format, where "field" is either paperId,
publicationDate, or citationCount, and "order" is asc
(ascending) or desc (descending).
:param bool match_title: (optional) retrieve a single paper whose
title best matches the given query.
:returns: query results.
:rtype: :class:`semanticscholar.PaginatedResults.PaginatedResults` or
:class:`semanticscholar.Paper.Paper`
'''
loop = asyncio.get_event_loop()
results = loop.run_until_complete(
self._AsyncSemanticScholar.search_paper(
query=query,
year=year,
publication_types=publication_types,
open_access_pdf=open_access_pdf,
venue=venue,
fields_of_study=fields_of_study,
fields=fields,
publication_date_or_year=publication_date_or_year,
min_citation_count=min_citation_count,
limit=limit,
bulk=bulk,
sort=sort,
match_title=match_title
)
)
return results
def get_author(
self,
author_id: str,
fields: list = None
) -> Author:
'''
Author lookup
:calls: `GET /graph/v1/author/{author_id} \
`_
:param str author_id: S2AuthorId.
:returns: author data
:rtype: :class:`semanticscholar.Author.Author`
:raises: ObjectNotFoundException: if Author ID not found.
'''
loop = asyncio.get_event_loop()
author = loop.run_until_complete(
self._AsyncSemanticScholar.get_author(
author_id=author_id,
fields=fields
)
)
return author
def get_authors(
self,
author_ids: List[str],
fields: list = None,
return_not_found: bool = False
) -> Union[List[Author], Tuple[List[Author], List[str]]]:
'''
Get details for multiple authors at once
:calls: `POST /graph/v1/author/batch \
`_
:param str author_ids: list of S2AuthorId (must be <= 1000).
:returns: author data, and optionally list of IDs not found.
:rtype: :class:`List` of :class:`semanticscholar.Author.Author`
or :class:`Tuple` [:class:`List` of
:class:`semanticscholar.Author.Author`,
:class:`List` of :class:`str`]
:raises: BadQueryParametersException: if no author was found.
'''
loop = asyncio.get_event_loop()
authors = loop.run_until_complete(
self._AsyncSemanticScholar.get_authors(
author_ids=author_ids,
fields=fields,
return_not_found=return_not_found
)
)
return authors
def get_author_papers(
self,
author_id: str,
fields: list = None,
limit: int = 100
) -> PaginatedResults:
'''
Get details about a author's papers
:calls: `POST /graph/v1/paper/{author_id}/papers \
`_
:param str paper_id: S2PaperId, CorpusId, DOI, ArXivId, MAG, ACL,
PMID, PMCID, or URL from:
- semanticscholar.org
- arxiv.org
- aclweb.org
- acm.org
- biorxiv.org
:param list fields: (optional) list of the fields to be returned.
:param int limit: (optional) maximum number of results to return
(must be <= 1000).
'''
loop = asyncio.get_event_loop()
results = loop.run_until_complete(
self._AsyncSemanticScholar.get_author_papers(
author_id=author_id,
fields=fields,
limit=limit
)
)
return results
def search_author(
self,
query: str,
fields: list = None,
limit: int = 100
) -> PaginatedResults:
'''
Search for authors by name
:calls: `GET /graph/v1/author/search \
`_
:param str query: plain-text search query string.
:param list fields: (optional) list of the fields to be returned.
:param int limit: (optional) maximum number of results to return
(must be <= 1000).
:returns: query results.
:rtype: :class:`semanticscholar.PaginatedResults.PaginatedResults`
'''
loop = asyncio.get_event_loop()
results = loop.run_until_complete(
self._AsyncSemanticScholar.search_author(
query=query,
fields=fields,
limit=limit
)
)
return results
def get_recommended_papers(
self,
paper_id: str,
fields: list = None,
limit: int = 100,
pool_from: Literal["recent", "all-cs"] = "recent"
) -> List[Paper]:
'''
Get recommended papers for a single positive example.
:calls: `GET /recommendations/v1/papers/forpaper/{paper_id} \
`_
:param str paper_id: S2PaperId, CorpusId, DOI, ArXivId, MAG, ACL,
PMID, PMCID, or URL from:
- semanticscholar.org
- arxiv.org
- aclweb.org
- acm.org
- biorxiv.org
:param list fields: (optional) list of the fields to be returned.
:param int limit: (optional) maximum number of recommendations to
return (must be <= 500).
:param str pool_from: (optional) which pool of papers to recommend
from. Must be either "recent" or "all-cs".
:returns: list of recommendations.
:rtype: :class:`List` of :class:`semanticscholar.Paper.Paper`
'''
loop = asyncio.get_event_loop()
papers = loop.run_until_complete(
self._AsyncSemanticScholar.get_recommended_papers(
paper_id=paper_id,
fields=fields,
limit=limit,
pool_from=pool_from
)
)
return papers
def get_recommended_papers_from_lists(
self,
positive_paper_ids: List[str],
negative_paper_ids: List[str] = None,
fields: list = None,
limit: int = 100
) -> List[Paper]:
'''
Get recommended papers for lists of positive and negative examples.
:calls: `POST /recommendations/v1/papers/ \
`_
:param list positive_paper_ids: list of paper IDs
that the returned papers should be related to.
:param list negative_paper_ids: (optional) list of paper IDs
that the returned papers should not be related to.
:param list fields: (optional) list of the fields to be returned.
:param int limit: (optional) maximum number of recommendations to
return (must be <= 500).
:returns: list of recommendations.
:rtype: :class:`List` of :class:`semanticscholar.Paper.Paper`
'''
loop = asyncio.get_event_loop()
papers = loop.run_until_complete(
self._AsyncSemanticScholar.get_recommended_papers_from_lists(
positive_paper_ids=positive_paper_ids,
negative_paper_ids=negative_paper_ids,
fields=fields,
limit=limit
)
)
return papers
def get_autocomplete(self, query: str) -> List[Autocomplete]:
"""
Get autocomplete suggestions for a paper query.
:calls: `GET /graph/v1/paper/autocomplete?query={query} \
`_
:param str query: query to get autocomplete suggestions for.
:returns: list of autocomplete suggestions.
:rtype: :class:`List` of
:class:`semanticscholar.Autocomplete.Autocomplete`
"""
loop = asyncio.get_event_loop()
results = loop.run_until_complete(
self._AsyncSemanticScholar.get_autocomplete(query=query)
)
return results
def get_available_releases(self) -> List[str]:
"""
Gets all available dataset releases.
:calls: `GET /datasets/v1/release/ \
`_
:returns: list of available release ids.
:rtype: :class:`List` of :class:`str`
"""
loop = asyncio.get_event_loop()
releases = loop.run_until_complete(
self._AsyncSemanticScholar.get_available_releases()
)
return releases
def get_release(self, release_id: str) -> Release:
"""
Get a specific release.
:calls: `GET /datasets/v1/release/{release_id} \
`_
:param str release_id: Release identifier (e.g., '2023-12-01').
:returns: release information including datasets.
:rtype: :class:`semanticscholar.Release.Release`
"""
loop = asyncio.get_event_loop()
release = loop.run_until_complete(
self._AsyncSemanticScholar.get_release(release_id=release_id)
)
return release
def get_dataset_download_links(
self,
release_id: str,
dataset_name: str
) -> Dataset:
"""
Get download links for a specific dataset in a release.
:calls: `GET /datasets/v1/release/{release_id}/dataset/{dataset_name} \
`_
:param str release_id: Release identifier (e.g., '2023-12-01').
:param str dataset_name: Name of the dataset.
:returns: dataset information including download links.
:rtype: :class:`semanticscholar.Dataset.Dataset`
"""
loop = asyncio.get_event_loop()
dataset = loop.run_until_complete(
self._AsyncSemanticScholar.get_dataset_download_links(
release_id=release_id,
dataset_name=dataset_name
)
)
return dataset
def get_dataset_diffs(
self,
dataset_name: str,
start_release_id: str,
end_release_id: str
) -> DatasetDiff:
"""
Get incremental diffs for a dataset between two releases.
:calls: `GET /datasets/v1/diffs/{start_release_id}/to/\
{end_release_id}/{dataset_name} \
`_
:param str dataset_name: Name of the dataset.
:param str start_release_id: ID of the release currently held by the
client.
:param str end_release_id: ID of the release the client wishes to
update to, or 'latest' for the most recent release.
:returns: information containing dataset, start_release, end_release,
and list of diffs.
:rtype: :class:`semanticscholar.DatasetDiff.DatasetDiff`
"""
loop = asyncio.get_event_loop()
result = loop.run_until_complete(
self._AsyncSemanticScholar.get_dataset_diffs(
dataset_name=dataset_name,
start_release_id=start_release_id,
end_release_id=end_release_id
)
)
return result