pub-analyzer 0.5.6__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pub_analyzer/__init__.py +1 -0
- pub_analyzer/__main__.py +7 -0
- pub_analyzer/css/body.tcss +87 -0
- pub_analyzer/css/buttons.tcss +24 -0
- pub_analyzer/css/checkbox.tcss +29 -0
- pub_analyzer/css/collapsible.tcss +31 -0
- pub_analyzer/css/datatable.tcss +50 -0
- pub_analyzer/css/editor.tcss +60 -0
- pub_analyzer/css/main.tcss +50 -0
- pub_analyzer/css/report.tcss +131 -0
- pub_analyzer/css/search.tcss +81 -0
- pub_analyzer/css/summary.tcss +75 -0
- pub_analyzer/css/tabs.tcss +18 -0
- pub_analyzer/css/tree.tcss +44 -0
- pub_analyzer/internal/__init__.py +1 -0
- pub_analyzer/internal/identifier.py +106 -0
- pub_analyzer/internal/limiter.py +34 -0
- pub_analyzer/internal/render.py +41 -0
- pub_analyzer/internal/report.py +497 -0
- pub_analyzer/internal/templates/author_report.typ +591 -0
- pub_analyzer/main.py +81 -0
- pub_analyzer/models/__init__.py +1 -0
- pub_analyzer/models/author.py +87 -0
- pub_analyzer/models/concept.py +19 -0
- pub_analyzer/models/institution.py +138 -0
- pub_analyzer/models/report.py +111 -0
- pub_analyzer/models/source.py +77 -0
- pub_analyzer/models/topic.py +59 -0
- pub_analyzer/models/work.py +158 -0
- pub_analyzer/widgets/__init__.py +1 -0
- pub_analyzer/widgets/author/__init__.py +1 -0
- pub_analyzer/widgets/author/cards.py +65 -0
- pub_analyzer/widgets/author/core.py +122 -0
- pub_analyzer/widgets/author/tables.py +50 -0
- pub_analyzer/widgets/body.py +55 -0
- pub_analyzer/widgets/common/__init__.py +18 -0
- pub_analyzer/widgets/common/card.py +29 -0
- pub_analyzer/widgets/common/filesystem.py +203 -0
- pub_analyzer/widgets/common/filters.py +111 -0
- pub_analyzer/widgets/common/input.py +97 -0
- pub_analyzer/widgets/common/label.py +36 -0
- pub_analyzer/widgets/common/modal.py +43 -0
- pub_analyzer/widgets/common/selector.py +66 -0
- pub_analyzer/widgets/common/summary.py +7 -0
- pub_analyzer/widgets/institution/__init__.py +1 -0
- pub_analyzer/widgets/institution/cards.py +78 -0
- pub_analyzer/widgets/institution/core.py +122 -0
- pub_analyzer/widgets/institution/tables.py +24 -0
- pub_analyzer/widgets/report/__init__.py +1 -0
- pub_analyzer/widgets/report/author.py +43 -0
- pub_analyzer/widgets/report/cards.py +130 -0
- pub_analyzer/widgets/report/concept.py +47 -0
- pub_analyzer/widgets/report/core.py +308 -0
- pub_analyzer/widgets/report/editor.py +80 -0
- pub_analyzer/widgets/report/export.py +112 -0
- pub_analyzer/widgets/report/grants.py +85 -0
- pub_analyzer/widgets/report/institution.py +39 -0
- pub_analyzer/widgets/report/locations.py +75 -0
- pub_analyzer/widgets/report/source.py +90 -0
- pub_analyzer/widgets/report/topic.py +55 -0
- pub_analyzer/widgets/report/work.py +391 -0
- pub_analyzer/widgets/search/__init__.py +11 -0
- pub_analyzer/widgets/search/core.py +96 -0
- pub_analyzer/widgets/search/results.py +82 -0
- pub_analyzer/widgets/sidebar.py +70 -0
- pub_analyzer-0.5.6.dist-info/METADATA +102 -0
- pub_analyzer-0.5.6.dist-info/RECORD +70 -0
- pub_analyzer-0.5.6.dist-info/WHEEL +4 -0
- pub_analyzer-0.5.6.dist-info/entry_points.txt +3 -0
- pub_analyzer-0.5.6.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"""Authors models from OpenAlex API Schema definition."""
|
|
2
|
+
|
|
3
|
+
from typing import TypeAlias
|
|
4
|
+
|
|
5
|
+
from pydantic import BaseModel, Field, HttpUrl
|
|
6
|
+
|
|
7
|
+
from pub_analyzer.models.institution import DehydratedInstitution
|
|
8
|
+
|
|
9
|
+
AuthorOpenAlexID: TypeAlias = HttpUrl
|
|
10
|
+
"""OpenAlex ID for Author Objects with the format `https://openalex.org/A000000000`"""
|
|
11
|
+
|
|
12
|
+
AuthorOpenAlexKey: TypeAlias = str
|
|
13
|
+
"""OpenAlex author entity Key with the format `A000000000`"""
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class AuthorIDs(BaseModel):
|
|
17
|
+
"""IDs from an Author."""
|
|
18
|
+
|
|
19
|
+
openalex: AuthorOpenAlexID
|
|
20
|
+
orcid: HttpUrl | None = None
|
|
21
|
+
scopus: HttpUrl | None = None
|
|
22
|
+
twitter: HttpUrl | None = None
|
|
23
|
+
wikipedia: HttpUrl | None = None
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class AuthorYearCount(BaseModel):
|
|
27
|
+
"""Summary of published papers and number of citations in a year."""
|
|
28
|
+
|
|
29
|
+
year: int
|
|
30
|
+
works_count: int
|
|
31
|
+
cited_by_count: int
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class AuthorSummaryStats(BaseModel):
|
|
35
|
+
"""Citation metrics for this author."""
|
|
36
|
+
|
|
37
|
+
two_yr_mean_citedness: float = Field(..., alias="2yr_mean_citedness")
|
|
38
|
+
h_index: int
|
|
39
|
+
i10_index: int
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class AuthorAffiliation(BaseModel):
|
|
43
|
+
"""List of affiliations this author has claimed in their publications."""
|
|
44
|
+
|
|
45
|
+
institution: DehydratedInstitution
|
|
46
|
+
years: list[int]
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class Author(BaseModel):
|
|
50
|
+
"""Author Model Object from OpenAlex API definition."""
|
|
51
|
+
|
|
52
|
+
id: AuthorOpenAlexID
|
|
53
|
+
ids: AuthorIDs
|
|
54
|
+
orcid: str | None = ""
|
|
55
|
+
|
|
56
|
+
display_name: str
|
|
57
|
+
display_name_alternatives: list[str]
|
|
58
|
+
last_known_institutions: list[DehydratedInstitution] | None = Field(default_factory=list)
|
|
59
|
+
affiliations: list[AuthorAffiliation]
|
|
60
|
+
|
|
61
|
+
works_count: int
|
|
62
|
+
cited_by_count: int
|
|
63
|
+
|
|
64
|
+
counts_by_year: list[AuthorYearCount]
|
|
65
|
+
summary_stats: AuthorSummaryStats
|
|
66
|
+
|
|
67
|
+
works_api_url: str
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class DehydratedAuthor(BaseModel):
|
|
71
|
+
"""Stripped-down Author Model."""
|
|
72
|
+
|
|
73
|
+
id: AuthorOpenAlexID
|
|
74
|
+
display_name: str | None = None
|
|
75
|
+
orcid: HttpUrl | None = None
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class AuthorResult(BaseModel):
|
|
79
|
+
"""Author result Model resulting from a search in OpenAlex."""
|
|
80
|
+
|
|
81
|
+
id: AuthorOpenAlexID
|
|
82
|
+
display_name: str
|
|
83
|
+
hint: str | None = None
|
|
84
|
+
cited_by_count: int
|
|
85
|
+
works_count: int
|
|
86
|
+
entity_type: str
|
|
87
|
+
external_id: str | None = None
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""Concept model from OpenAlex API Schema definition."""
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel, HttpUrl
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class DehydratedConcept(BaseModel):
|
|
7
|
+
"""Stripped-down Concept Model."""
|
|
8
|
+
|
|
9
|
+
id: HttpUrl
|
|
10
|
+
"""The OpenAlex ID for this concept."""
|
|
11
|
+
display_name: str
|
|
12
|
+
"""The English-language label of the concept."""
|
|
13
|
+
|
|
14
|
+
wikidata: HttpUrl
|
|
15
|
+
"""The Wikidata ID for this concept. All OpenAlex concepts are also Wikidata concepts."""
|
|
16
|
+
level: int
|
|
17
|
+
"""The level in the concept. Lower-level concepts are more general, and higher-level concepts are more specific."""
|
|
18
|
+
score: float
|
|
19
|
+
"""The strength of the connection between the work and this concept (higher is stronger)."""
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"""Institutions models from OpenAlex API Schema definition."""
|
|
2
|
+
|
|
3
|
+
from enum import Enum
|
|
4
|
+
from typing import TypeAlias
|
|
5
|
+
|
|
6
|
+
from pydantic import BaseModel, Field, HttpUrl
|
|
7
|
+
|
|
8
|
+
InstitutionOpenAlexID: TypeAlias = HttpUrl
|
|
9
|
+
"""OpenAlex ID for Institution Objects with the format `https://openalex.org/I000000000`"""
|
|
10
|
+
|
|
11
|
+
InstitutionOpenAlexKey: TypeAlias = str
|
|
12
|
+
"""OpenAlex Institution entity Key with the format `I000000000`"""
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class InstitutionIDs(BaseModel):
|
|
16
|
+
"""IDs from an Institution."""
|
|
17
|
+
|
|
18
|
+
openalex: InstitutionOpenAlexID
|
|
19
|
+
grid: str | None = None
|
|
20
|
+
ror: HttpUrl | None = None
|
|
21
|
+
wikipedia: HttpUrl | None = None
|
|
22
|
+
wikidata: HttpUrl | None = None
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class InstitutionType(str, Enum):
|
|
26
|
+
"""The institution's primary type, using the ROR "type" controlled vocabulary."""
|
|
27
|
+
|
|
28
|
+
Education = "education"
|
|
29
|
+
Healthcare = "healthcare"
|
|
30
|
+
Company = "company"
|
|
31
|
+
Archive = "archive"
|
|
32
|
+
Nonprofit = "nonprofit"
|
|
33
|
+
Government = "government"
|
|
34
|
+
Facility = "facility"
|
|
35
|
+
Funder = "funder"
|
|
36
|
+
Other = "other"
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class InstitutionSummaryStats(BaseModel):
|
|
40
|
+
"""Citation metrics for this Institution."""
|
|
41
|
+
|
|
42
|
+
two_yr_mean_citedness: float = Field(..., alias="2yr_mean_citedness")
|
|
43
|
+
h_index: int
|
|
44
|
+
i10_index: int
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class InstitutionYearCount(BaseModel):
|
|
48
|
+
"""Summary of published papers and number of citations in a year."""
|
|
49
|
+
|
|
50
|
+
year: int
|
|
51
|
+
works_count: int
|
|
52
|
+
cited_by_count: int
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class InstitutionGeo(BaseModel):
|
|
56
|
+
"""Location of the institution."""
|
|
57
|
+
|
|
58
|
+
city: str
|
|
59
|
+
geonames_city_id: str
|
|
60
|
+
|
|
61
|
+
region: str | None = None
|
|
62
|
+
country_code: str | None = None
|
|
63
|
+
country: str
|
|
64
|
+
|
|
65
|
+
latitude: float
|
|
66
|
+
longitude: float
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class InstitutionRoleType(str, Enum):
|
|
70
|
+
"""Possible institution roles."""
|
|
71
|
+
|
|
72
|
+
funder = "funder"
|
|
73
|
+
publisher = "publisher"
|
|
74
|
+
institution = "institution"
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class InstitutionRole(BaseModel):
|
|
78
|
+
"""Institution role."""
|
|
79
|
+
|
|
80
|
+
role: InstitutionRoleType
|
|
81
|
+
id: HttpUrl
|
|
82
|
+
works_count: int
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class International(BaseModel):
|
|
86
|
+
"""The institution's display name in different languages."""
|
|
87
|
+
|
|
88
|
+
display_name: dict[str, str] | None = None
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class Institution(BaseModel):
|
|
92
|
+
"""Universities and other organizations to which authors claim affiliations."""
|
|
93
|
+
|
|
94
|
+
id: InstitutionOpenAlexID
|
|
95
|
+
ids: InstitutionIDs
|
|
96
|
+
|
|
97
|
+
display_name: str
|
|
98
|
+
country_code: str | None = None
|
|
99
|
+
type: InstitutionType
|
|
100
|
+
homepage_url: HttpUrl | None = None
|
|
101
|
+
image_url: HttpUrl | None = None
|
|
102
|
+
|
|
103
|
+
display_name_acronyms: list[str]
|
|
104
|
+
international: International
|
|
105
|
+
|
|
106
|
+
works_count: int
|
|
107
|
+
cited_by_count: int
|
|
108
|
+
summary_stats: InstitutionSummaryStats
|
|
109
|
+
counts_by_year: list[InstitutionYearCount]
|
|
110
|
+
|
|
111
|
+
geo: InstitutionGeo
|
|
112
|
+
roles: list[InstitutionRole]
|
|
113
|
+
|
|
114
|
+
works_api_url: str
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
class DehydratedInstitution(BaseModel):
|
|
118
|
+
"""Stripped-down Institution Model."""
|
|
119
|
+
|
|
120
|
+
id: InstitutionOpenAlexID
|
|
121
|
+
ror: str
|
|
122
|
+
display_name: str
|
|
123
|
+
country_code: str | None = None
|
|
124
|
+
type: InstitutionType
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
class InstitutionResult(BaseModel):
|
|
128
|
+
"""Institution result Model resulting from a search in OpenAlex."""
|
|
129
|
+
|
|
130
|
+
id: InstitutionOpenAlexID
|
|
131
|
+
display_name: str
|
|
132
|
+
hint: str | None = None
|
|
133
|
+
|
|
134
|
+
cited_by_count: int
|
|
135
|
+
works_count: int
|
|
136
|
+
|
|
137
|
+
entity_type: str
|
|
138
|
+
external_id: str | None = None
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"""Reports Structure Objects."""
|
|
2
|
+
|
|
3
|
+
from enum import Enum
|
|
4
|
+
|
|
5
|
+
from pydantic import BaseModel
|
|
6
|
+
|
|
7
|
+
from .author import Author
|
|
8
|
+
from .institution import Institution
|
|
9
|
+
from .source import Source
|
|
10
|
+
from .work import OpenAccessStatus, Work
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class CitationType(Enum):
|
|
14
|
+
"""Citation type Work."""
|
|
15
|
+
|
|
16
|
+
TypeA = 0
|
|
17
|
+
TypeB = 1
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class CitationReport(BaseModel):
|
|
21
|
+
"""Cited by Works with stats."""
|
|
22
|
+
|
|
23
|
+
work: Work
|
|
24
|
+
citation_type: CitationType
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class CitationSummary(BaseModel):
|
|
28
|
+
"""Summary of citation information in all works."""
|
|
29
|
+
|
|
30
|
+
type_a_count: int = 0
|
|
31
|
+
type_b_count: int = 0
|
|
32
|
+
|
|
33
|
+
def add_cite_type(self, cite_type: CitationType) -> None:
|
|
34
|
+
"""Add the type of cite in the corresponding counter."""
|
|
35
|
+
if cite_type.value == CitationType.TypeA.value:
|
|
36
|
+
self.type_a_count += 1
|
|
37
|
+
elif cite_type.value == CitationType.TypeB.value:
|
|
38
|
+
self.type_b_count += 1
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class OpenAccessSummary(BaseModel):
|
|
42
|
+
"""Open Access Type counter."""
|
|
43
|
+
|
|
44
|
+
diamond: int = 0
|
|
45
|
+
gold: int = 0
|
|
46
|
+
green: int = 0
|
|
47
|
+
hybrid: int = 0
|
|
48
|
+
bronze: int = 0
|
|
49
|
+
closed: int = 0
|
|
50
|
+
|
|
51
|
+
def add_oa_type(self, open_access_type: OpenAccessStatus) -> None:
|
|
52
|
+
"""Add the type of Open Access in the corresponding counter."""
|
|
53
|
+
match open_access_type:
|
|
54
|
+
case OpenAccessStatus.diamond:
|
|
55
|
+
self.diamond += 1
|
|
56
|
+
case OpenAccessStatus.gold:
|
|
57
|
+
self.gold += 1
|
|
58
|
+
case OpenAccessStatus.green:
|
|
59
|
+
self.green += 1
|
|
60
|
+
case OpenAccessStatus.hybrid:
|
|
61
|
+
self.hybrid += 1
|
|
62
|
+
case OpenAccessStatus.bronze:
|
|
63
|
+
self.bronze += 1
|
|
64
|
+
case OpenAccessStatus.closed:
|
|
65
|
+
self.closed += 1
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class WorkTypeCounter(BaseModel):
|
|
69
|
+
"""Work Type Counter."""
|
|
70
|
+
|
|
71
|
+
type_name: str
|
|
72
|
+
count: int
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class WorkReport(BaseModel):
|
|
76
|
+
"""Work model with stats."""
|
|
77
|
+
|
|
78
|
+
work: Work
|
|
79
|
+
cited_by: list[CitationReport]
|
|
80
|
+
|
|
81
|
+
citation_summary: CitationSummary
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class SourcesSummary(BaseModel):
|
|
85
|
+
"""Sources model with stats."""
|
|
86
|
+
|
|
87
|
+
sources: list[Source]
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class AuthorReport(BaseModel):
|
|
91
|
+
"""Report of scientific production of an author."""
|
|
92
|
+
|
|
93
|
+
author: Author
|
|
94
|
+
works: list[WorkReport]
|
|
95
|
+
|
|
96
|
+
citation_summary: CitationSummary
|
|
97
|
+
open_access_summary: OpenAccessSummary
|
|
98
|
+
works_type_summary: list[WorkTypeCounter]
|
|
99
|
+
sources_summary: SourcesSummary
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class InstitutionReport(BaseModel):
|
|
103
|
+
"""Scientific production report of the Institution."""
|
|
104
|
+
|
|
105
|
+
institution: Institution
|
|
106
|
+
works: list[WorkReport]
|
|
107
|
+
|
|
108
|
+
citation_summary: CitationSummary
|
|
109
|
+
open_access_summary: OpenAccessSummary
|
|
110
|
+
works_type_summary: list[WorkTypeCounter]
|
|
111
|
+
sources_summary: SourcesSummary
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"""Sources models from OpenAlex API Schema definition."""
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel, Field, HttpUrl
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class SourceSummaryStats(BaseModel):
|
|
7
|
+
"""Citation metrics for this Source."""
|
|
8
|
+
|
|
9
|
+
two_yr_mean_citedness: float = Field(..., alias="2yr_mean_citedness")
|
|
10
|
+
"""The 2-year mean citedness for this source. Also known as impact factor."""
|
|
11
|
+
h_index: int
|
|
12
|
+
"""The h-index for this source."""
|
|
13
|
+
i10_index: int
|
|
14
|
+
"""The i-10 index for this source."""
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class SourceYearCount(BaseModel):
|
|
18
|
+
"""Summary of published papers and number of citations in a year."""
|
|
19
|
+
|
|
20
|
+
year: int
|
|
21
|
+
"""Year."""
|
|
22
|
+
works_count: int
|
|
23
|
+
"""The number of Works this source hosts in this year."""
|
|
24
|
+
cited_by_count: int
|
|
25
|
+
"""The total number of Works that cite a Work hosted in this source in this year."""
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class DehydratedSource(BaseModel):
|
|
29
|
+
"""Stripped-down Source Model."""
|
|
30
|
+
|
|
31
|
+
id: HttpUrl
|
|
32
|
+
"""The OpenAlex ID for this source."""
|
|
33
|
+
display_name: str
|
|
34
|
+
"""The name of the source."""
|
|
35
|
+
|
|
36
|
+
issn_l: str | None = None
|
|
37
|
+
"""The ISSN-L identifying this source. The ISSN-L designating a single canonical ISSN
|
|
38
|
+
for all media versions of the title. It's usually the same as the print ISSN.
|
|
39
|
+
"""
|
|
40
|
+
issn: list[str] | None = None
|
|
41
|
+
"""The ISSNs used by this source. An ISSN identifies all continuing resources, irrespective
|
|
42
|
+
of their medium (print or electronic). [More info](https://www.issn.org/){target=_blank}.
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
is_oa: bool
|
|
46
|
+
"""Whether this is currently fully-open-access source."""
|
|
47
|
+
is_in_doaj: bool
|
|
48
|
+
"""Whether this is a journal listed in the [Directory of Open Access Journals](https://doaj.org){target=_blank} (DOAJ)."""
|
|
49
|
+
|
|
50
|
+
host_organization: HttpUrl | None = None
|
|
51
|
+
"""The host organization for this source as an OpenAlex ID. This will be an
|
|
52
|
+
[Institution.id][pub_analyzer.models.institution.Institution.id] if the source is a repository,
|
|
53
|
+
and a Publisher.id if the source is a journal, conference, or eBook platform
|
|
54
|
+
"""
|
|
55
|
+
host_organization_name: str | None = None
|
|
56
|
+
"""The display_name from the host_organization."""
|
|
57
|
+
|
|
58
|
+
type: str | None = None
|
|
59
|
+
"""The type of source, which will be one of: `journal`, `repository`, `conference`,
|
|
60
|
+
`ebook platform`, or `book series`.
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class Source(DehydratedSource):
|
|
65
|
+
"""Where works are hosted."""
|
|
66
|
+
|
|
67
|
+
homepage_url: HttpUrl | None = None
|
|
68
|
+
"""The homepage for this source's website."""
|
|
69
|
+
|
|
70
|
+
is_in_doaj: bool
|
|
71
|
+
"""Whether this is a journal listed in the Directory of Open Access Journals (DOAJ)."""
|
|
72
|
+
|
|
73
|
+
summary_stats: SourceSummaryStats
|
|
74
|
+
"""Citation metrics for this source."""
|
|
75
|
+
|
|
76
|
+
counts_by_year: list[SourceYearCount]
|
|
77
|
+
"""works_count and cited_by_count for each of the last ten years, binned by year."""
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"""Topics models from OpenAlex API Schema definition."""
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel, HttpUrl
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class TopicIDs(BaseModel):
|
|
7
|
+
"""External identifiers for a Topic."""
|
|
8
|
+
|
|
9
|
+
openalex: HttpUrl
|
|
10
|
+
"""The OpenAlex ID for this Topic."""
|
|
11
|
+
wikipedia: HttpUrl | None = None
|
|
12
|
+
"""This topic's Wikipedia page URL."""
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class TopicLevel(BaseModel):
|
|
16
|
+
"""Topic level information."""
|
|
17
|
+
|
|
18
|
+
id: HttpUrl
|
|
19
|
+
"""ID for the topic level. For more info, consult the
|
|
20
|
+
[OpenAlex topic mapping table](https://docs.google.com/spreadsheets/d/1v-MAq64x4YjhO7RWcB-yrKV5D_2vOOsxl4u6GBKEXY8/){target=_blank}.
|
|
21
|
+
"""
|
|
22
|
+
display_name: str
|
|
23
|
+
"""The English-language label of the level."""
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class DehydratedTopic(BaseModel):
|
|
27
|
+
"""Stripped-down Topic Model."""
|
|
28
|
+
|
|
29
|
+
id: HttpUrl
|
|
30
|
+
"""The OpenAlex ID for this Topic."""
|
|
31
|
+
display_name: str
|
|
32
|
+
"""The English-language label of the topic."""
|
|
33
|
+
score: float
|
|
34
|
+
"""The strength of the connection between the work and this topic (higher is stronger)."""
|
|
35
|
+
|
|
36
|
+
domain: TopicLevel
|
|
37
|
+
"""The highest level in the Topics structure."""
|
|
38
|
+
field: TopicLevel
|
|
39
|
+
"""The second-highest level in the Topics structure."""
|
|
40
|
+
subfield: TopicLevel
|
|
41
|
+
"""The third-highest level in the Topics structure."""
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class Topic(DehydratedTopic):
|
|
45
|
+
"""Labels which can be used to describe what a paper is about."""
|
|
46
|
+
|
|
47
|
+
ids: TopicIDs
|
|
48
|
+
"""All the external identifiers for a Topic."""
|
|
49
|
+
description: str
|
|
50
|
+
"""A description of this topic, generated by AI."""
|
|
51
|
+
keywords: list[str]
|
|
52
|
+
"""Keywords consisting of one or several words each, meant to represent
|
|
53
|
+
the content of the papers in the topic.
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
works_count: int
|
|
57
|
+
"""The number of works tagged with this topic."""
|
|
58
|
+
cited_by_count: int
|
|
59
|
+
"""The number of citations to works that have been tagged with this topic."""
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
"""Works models from OpenAlex API Schema definition."""
|
|
2
|
+
|
|
3
|
+
from enum import Enum
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from pydantic import BaseModel, HttpUrl, field_validator
|
|
7
|
+
|
|
8
|
+
from .author import DehydratedAuthor
|
|
9
|
+
from .concept import DehydratedConcept
|
|
10
|
+
from .source import DehydratedSource
|
|
11
|
+
from .topic import DehydratedTopic
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class WorkIDs(BaseModel):
|
|
15
|
+
"""IDs from a Work."""
|
|
16
|
+
|
|
17
|
+
openalex: HttpUrl
|
|
18
|
+
doi: HttpUrl | None = None
|
|
19
|
+
pmid: str | None = None
|
|
20
|
+
pmcid: str | None = None
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class WorkDrivenVersion(str, Enum):
|
|
24
|
+
"""The version of the work, based on the Driver Guidelines versioning scheme."""
|
|
25
|
+
|
|
26
|
+
submitted = "submittedVersion"
|
|
27
|
+
accepted = "acceptedVersion"
|
|
28
|
+
published = "publishedVersion"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class Location(BaseModel):
|
|
32
|
+
"""Describes the location of a given work."""
|
|
33
|
+
|
|
34
|
+
is_oa: bool
|
|
35
|
+
landing_page_url: str
|
|
36
|
+
license: str | None
|
|
37
|
+
pdf_url: str | None
|
|
38
|
+
version: WorkDrivenVersion | None
|
|
39
|
+
source: DehydratedSource | None = None
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class OpenAccessStatus(str, Enum):
|
|
43
|
+
"""The Open Access (OA) status of this work."""
|
|
44
|
+
|
|
45
|
+
diamond = "diamond"
|
|
46
|
+
gold = "gold"
|
|
47
|
+
green = "green"
|
|
48
|
+
hybrid = "hybrid"
|
|
49
|
+
bronze = "bronze"
|
|
50
|
+
closed = "closed"
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class WorkAccessInfo(BaseModel):
|
|
54
|
+
"""Information about the access status of this work."""
|
|
55
|
+
|
|
56
|
+
is_oa: bool
|
|
57
|
+
oa_status: OpenAccessStatus
|
|
58
|
+
oa_url: str | None = None
|
|
59
|
+
any_repository_has_fulltext: bool
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class Authorship(BaseModel):
|
|
63
|
+
"""Information of author and her institutional affiliations in the context of work."""
|
|
64
|
+
|
|
65
|
+
author_position: str
|
|
66
|
+
author: DehydratedAuthor
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class ArticleProcessingCharge(BaseModel):
|
|
70
|
+
"""Information about the paid APC for this work."""
|
|
71
|
+
|
|
72
|
+
value: int
|
|
73
|
+
currency: str
|
|
74
|
+
provenance: str | None = None
|
|
75
|
+
value_usd: int | None
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class Grant(BaseModel):
|
|
79
|
+
"""Grant Model Object from OpenAlex API definition."""
|
|
80
|
+
|
|
81
|
+
funder: HttpUrl
|
|
82
|
+
funder_display_name: str
|
|
83
|
+
award_id: str | None = None
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class Award(BaseModel):
|
|
87
|
+
"""Award work details."""
|
|
88
|
+
|
|
89
|
+
id: HttpUrl
|
|
90
|
+
display_name: str | None = None
|
|
91
|
+
funder_award_id: str | None = None
|
|
92
|
+
funder_id: HttpUrl
|
|
93
|
+
funder_display_name: str
|
|
94
|
+
doi: str | None = None
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
class Keyword(BaseModel):
|
|
98
|
+
"""Keyword extracted from the work's title and confidence score."""
|
|
99
|
+
|
|
100
|
+
id: HttpUrl
|
|
101
|
+
display_name: str
|
|
102
|
+
score: float
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
class Work(BaseModel):
|
|
106
|
+
"""Work Model Object from OpenAlex API definition."""
|
|
107
|
+
|
|
108
|
+
id: HttpUrl
|
|
109
|
+
ids: WorkIDs
|
|
110
|
+
|
|
111
|
+
title: str
|
|
112
|
+
abstract: str | None = None
|
|
113
|
+
publication_year: int | None = None
|
|
114
|
+
publication_date: str | None = None
|
|
115
|
+
language: str | None = None
|
|
116
|
+
type: str
|
|
117
|
+
|
|
118
|
+
primary_location: Location | None = None
|
|
119
|
+
best_oa_location: Location | None = None
|
|
120
|
+
locations: list[Location]
|
|
121
|
+
|
|
122
|
+
open_access: WorkAccessInfo
|
|
123
|
+
authorships: list[Authorship]
|
|
124
|
+
|
|
125
|
+
cited_by_count: int
|
|
126
|
+
"""This number comes from the OpenAlex API, represents ALL citations to this work, and may not always be correct.
|
|
127
|
+
To use a verified number that respects the applied filters use [WorkReport][pub_analyzer.models.report.WorkReport].
|
|
128
|
+
"""
|
|
129
|
+
|
|
130
|
+
awards: list[Award]
|
|
131
|
+
keywords: list[Keyword]
|
|
132
|
+
concepts: list[DehydratedConcept]
|
|
133
|
+
topics: list[DehydratedTopic]
|
|
134
|
+
|
|
135
|
+
referenced_works: list[HttpUrl]
|
|
136
|
+
|
|
137
|
+
apc_list: ArticleProcessingCharge | None = None
|
|
138
|
+
"""The price as listed by the journal's publisher."""
|
|
139
|
+
apc_paid: ArticleProcessingCharge | None = None
|
|
140
|
+
"""APC actually paid by authors."""
|
|
141
|
+
|
|
142
|
+
@field_validator("locations", mode="before")
|
|
143
|
+
def valid_locations(cls, locations: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
|
144
|
+
"""Skip locations that do not contain enough data."""
|
|
145
|
+
return [location for location in locations if location["landing_page_url"] is not None]
|
|
146
|
+
|
|
147
|
+
@field_validator("primary_location", "best_oa_location", mode="before")
|
|
148
|
+
def valid_location(cls, location: dict[str, Any]) -> dict[str, Any] | None:
|
|
149
|
+
"""Skip location that do not contain enough data."""
|
|
150
|
+
if location and location["landing_page_url"] is None:
|
|
151
|
+
return None
|
|
152
|
+
else:
|
|
153
|
+
return location
|
|
154
|
+
|
|
155
|
+
@field_validator("authorships", mode="before")
|
|
156
|
+
def valid_authorships(cls, authorships: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
|
157
|
+
"""Skip authorship's that do not contain enough data."""
|
|
158
|
+
return [authorship for authorship in authorships if authorship["author"].get("id") is not None]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""TUI module."""
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Author Widgets."""
|