pub-analyzer 0.1.2__py3-none-any.whl → 0.3.0__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.
Potentially problematic release.
This version of pub-analyzer might be problematic. Click here for more details.
- pub_analyzer/css/body.tcss +48 -35
- pub_analyzer/css/buttons.tcss +0 -1
- pub_analyzer/css/collapsible.tcss +31 -0
- pub_analyzer/css/main.tcss +4 -0
- pub_analyzer/css/summary.tcss +75 -0
- pub_analyzer/internal/identifier.py +36 -10
- pub_analyzer/internal/render.py +1 -1
- pub_analyzer/internal/report.py +177 -53
- pub_analyzer/internal/templates/author/{author_resume.typ → author_summary.typ} +4 -3
- pub_analyzer/internal/templates/author/report.typ +4 -3
- pub_analyzer/internal/templates/author/sources.typ +7 -5
- pub_analyzer/internal/templates/author/works.typ +12 -12
- pub_analyzer/internal/templates/author/works_extended.typ +4 -4
- pub_analyzer/main.py +6 -7
- pub_analyzer/models/author.py +20 -28
- pub_analyzer/models/concept.py +19 -0
- pub_analyzer/models/institution.py +22 -5
- pub_analyzer/models/report.py +14 -14
- pub_analyzer/models/source.py +59 -3
- pub_analyzer/models/topic.py +59 -0
- pub_analyzer/models/work.py +30 -7
- pub_analyzer/widgets/author/cards.py +15 -14
- pub_analyzer/widgets/author/core.py +80 -115
- pub_analyzer/widgets/author/tables.py +1 -1
- pub_analyzer/widgets/common/__init__.py +6 -6
- pub_analyzer/widgets/common/filesystem.py +16 -13
- pub_analyzer/widgets/common/filters.py +111 -0
- pub_analyzer/widgets/common/input.py +14 -5
- pub_analyzer/widgets/common/selector.py +1 -1
- pub_analyzer/widgets/common/summary.py +7 -0
- pub_analyzer/widgets/institution/cards.py +13 -15
- pub_analyzer/widgets/institution/core.py +81 -115
- pub_analyzer/widgets/institution/tables.py +1 -1
- pub_analyzer/widgets/report/cards.py +33 -31
- pub_analyzer/widgets/report/concept.py +47 -0
- pub_analyzer/widgets/report/core.py +90 -20
- pub_analyzer/widgets/report/export.py +2 -2
- pub_analyzer/widgets/report/grants.py +46 -0
- pub_analyzer/widgets/report/locations.py +14 -12
- pub_analyzer/widgets/report/source.py +22 -14
- pub_analyzer/widgets/report/topic.py +55 -0
- pub_analyzer/widgets/report/work.py +70 -34
- pub_analyzer/widgets/search/__init__.py +4 -4
- pub_analyzer/widgets/search/results.py +15 -16
- pub_analyzer/widgets/sidebar.py +11 -9
- {pub_analyzer-0.1.2.dist-info → pub_analyzer-0.3.0.dist-info}/METADATA +31 -7
- pub_analyzer-0.3.0.dist-info/RECORD +69 -0
- {pub_analyzer-0.1.2.dist-info → pub_analyzer-0.3.0.dist-info}/WHEEL +1 -1
- pub_analyzer/css/author.tcss +0 -78
- pub_analyzer/css/institution.tcss +0 -78
- pub_analyzer-0.1.2.dist-info/RECORD +0 -62
- {pub_analyzer-0.1.2.dist-info → pub_analyzer-0.3.0.dist-info}/LICENSE +0 -0
- {pub_analyzer-0.1.2.dist-info → pub_analyzer-0.3.0.dist-info}/entry_points.txt +0 -0
|
@@ -17,12 +17,15 @@ from pub_analyzer.widgets.report.cards import (
|
|
|
17
17
|
AuthorshipCard,
|
|
18
18
|
CitationMetricsCard,
|
|
19
19
|
OpenAccessCard,
|
|
20
|
-
|
|
20
|
+
OpenAccessSummaryCard,
|
|
21
21
|
ReportCitationMetricsCard,
|
|
22
|
-
|
|
22
|
+
WorksTypeSummaryCard,
|
|
23
23
|
)
|
|
24
24
|
|
|
25
|
+
from .concept import ConceptsTable
|
|
26
|
+
from .grants import GrantsTable
|
|
25
27
|
from .locations import LocationsTable
|
|
28
|
+
from .topic import TopicsTable
|
|
26
29
|
|
|
27
30
|
|
|
28
31
|
class CitedByTable(Static):
|
|
@@ -41,16 +44,16 @@ class CitedByTable(Static):
|
|
|
41
44
|
|
|
42
45
|
def compose(self) -> ComposeResult:
|
|
43
46
|
"""Compose Table."""
|
|
44
|
-
citations_table = Table(title=
|
|
47
|
+
citations_table = Table(title="Cited By", expand=True, show_lines=True)
|
|
45
48
|
|
|
46
49
|
# Define Columns
|
|
47
|
-
citations_table.add_column(
|
|
48
|
-
citations_table.add_column(
|
|
49
|
-
citations_table.add_column(
|
|
50
|
-
citations_table.add_column(
|
|
51
|
-
citations_table.add_column(
|
|
52
|
-
citations_table.add_column(
|
|
53
|
-
citations_table.add_column(
|
|
50
|
+
citations_table.add_column("", justify="center", vertical="middle")
|
|
51
|
+
citations_table.add_column("Title", ratio=3)
|
|
52
|
+
citations_table.add_column("Type", ratio=2)
|
|
53
|
+
citations_table.add_column("DOI")
|
|
54
|
+
citations_table.add_column("Cite Type", justify="center")
|
|
55
|
+
citations_table.add_column("Publication Date")
|
|
56
|
+
citations_table.add_column("Cited by count")
|
|
54
57
|
|
|
55
58
|
# Yield Rows
|
|
56
59
|
for idx, cited_by_work in enumerate(self.citations_list):
|
|
@@ -66,15 +69,15 @@ class CitedByTable(Static):
|
|
|
66
69
|
|
|
67
70
|
citations_table.add_row(
|
|
68
71
|
str(idx),
|
|
69
|
-
Text.from_markup(title, overflow=
|
|
72
|
+
Text.from_markup(title, overflow="ellipsis"),
|
|
70
73
|
work.type,
|
|
71
|
-
Text.from_markup(doi_url, overflow=
|
|
74
|
+
Text.from_markup(doi_url, overflow="ellipsis"),
|
|
72
75
|
citation_type,
|
|
73
76
|
work.publication_date,
|
|
74
|
-
str(work.cited_by_count)
|
|
77
|
+
str(work.cited_by_count),
|
|
75
78
|
)
|
|
76
79
|
|
|
77
|
-
yield Static(citations_table, classes=
|
|
80
|
+
yield Static(citations_table, classes="citations-table")
|
|
78
81
|
|
|
79
82
|
|
|
80
83
|
class WorkModal(Modal[None]):
|
|
@@ -88,16 +91,16 @@ class WorkModal(Modal[None]):
|
|
|
88
91
|
@on(events.Key)
|
|
89
92
|
def exit_modal(self, message: events.Key) -> None:
|
|
90
93
|
"""Exit from the modal with esc KEY."""
|
|
91
|
-
if message.key ==
|
|
94
|
+
if message.key == "escape":
|
|
92
95
|
self.app.pop_screen()
|
|
93
96
|
|
|
94
97
|
def compose(self) -> ComposeResult:
|
|
95
98
|
"""Compose metrics and Cited by Table."""
|
|
96
|
-
with VerticalScroll(id=
|
|
97
|
-
yield Label(self.work_report.work.title, classes=
|
|
99
|
+
with VerticalScroll(id="dialog"):
|
|
100
|
+
yield Label(self.work_report.work.title, classes="dialog-title")
|
|
98
101
|
|
|
99
102
|
# Cards
|
|
100
|
-
with Horizontal(classes=
|
|
103
|
+
with Horizontal(classes="cards-container"):
|
|
101
104
|
# Authorships
|
|
102
105
|
yield AuthorshipCard(work=self.work_report.work, author=self.author)
|
|
103
106
|
|
|
@@ -108,22 +111,40 @@ class WorkModal(Modal[None]):
|
|
|
108
111
|
yield CitationMetricsCard(work_report=self.work_report)
|
|
109
112
|
|
|
110
113
|
with TabbedContent(id="tables-container"):
|
|
114
|
+
# Abtract if exists
|
|
115
|
+
if self.work_report.work.abstract:
|
|
116
|
+
with TabPane("Abstract"):
|
|
117
|
+
yield Label(self.work_report.work.abstract, classes="abstract")
|
|
111
118
|
# Citations Table
|
|
112
119
|
with TabPane("Cited By Works"):
|
|
113
120
|
if len(self.work_report.cited_by):
|
|
114
121
|
yield CitedByTable(citations_list=self.work_report.cited_by)
|
|
115
122
|
else:
|
|
116
123
|
yield Label("No works found.")
|
|
124
|
+
# Concepts Table
|
|
125
|
+
with TabPane("Concepts"):
|
|
126
|
+
if len(self.work_report.work.concepts):
|
|
127
|
+
yield ConceptsTable(self.work_report.work.concepts)
|
|
128
|
+
else:
|
|
129
|
+
yield Label("No Concepts found.")
|
|
130
|
+
# Grants Table
|
|
131
|
+
with TabPane("Grants"):
|
|
132
|
+
if len(self.work_report.work.grants):
|
|
133
|
+
yield GrantsTable(self.work_report.work.grants)
|
|
134
|
+
else:
|
|
135
|
+
yield Label("No Grants found.")
|
|
117
136
|
# Locations Table
|
|
118
137
|
with TabPane("Locations"):
|
|
119
138
|
if len(self.work_report.work.locations):
|
|
120
139
|
yield LocationsTable(self.work_report.work.locations)
|
|
121
140
|
else:
|
|
122
141
|
yield Label("No sources found.")
|
|
123
|
-
#
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
yield
|
|
142
|
+
# Topics Table
|
|
143
|
+
with TabPane("Topics"):
|
|
144
|
+
if len(self.work_report.work.topics):
|
|
145
|
+
yield TopicsTable(self.work_report.work.topics)
|
|
146
|
+
else:
|
|
147
|
+
yield Label("No Topics found.")
|
|
127
148
|
|
|
128
149
|
|
|
129
150
|
class WorksTable(Static):
|
|
@@ -136,8 +157,9 @@ class WorksTable(Static):
|
|
|
136
157
|
}
|
|
137
158
|
"""
|
|
138
159
|
|
|
139
|
-
def __init__(self, report: AuthorReport | InstitutionReport) -> None:
|
|
160
|
+
def __init__(self, report: AuthorReport | InstitutionReport, show_empty_works: bool = True) -> None:
|
|
140
161
|
self.report = report
|
|
162
|
+
self.show_empty_works = show_empty_works
|
|
141
163
|
super().__init__()
|
|
142
164
|
|
|
143
165
|
class _WorksTableRenderer(Static):
|
|
@@ -165,25 +187,28 @@ class WorksTable(Static):
|
|
|
165
187
|
title = "Works"
|
|
166
188
|
|
|
167
189
|
work_table = Table(title=title, expand=True, show_lines=True)
|
|
168
|
-
work_table.add_column(
|
|
169
|
-
work_table.add_column(
|
|
170
|
-
work_table.add_column(
|
|
171
|
-
work_table.add_column(
|
|
172
|
-
work_table.add_column(
|
|
173
|
-
work_table.add_column(
|
|
190
|
+
work_table.add_column("", justify="center", vertical="middle")
|
|
191
|
+
work_table.add_column("Title", ratio=3)
|
|
192
|
+
work_table.add_column("Type", ratio=2)
|
|
193
|
+
work_table.add_column("DOI")
|
|
194
|
+
work_table.add_column("Publication Date")
|
|
195
|
+
work_table.add_column("Cited by count")
|
|
174
196
|
|
|
175
197
|
for idx, work_report in enumerate(self.report.works):
|
|
176
198
|
work = work_report.work
|
|
199
|
+
if not self.show_empty_works and len(work_report.cited_by) < 1:
|
|
200
|
+
continue
|
|
201
|
+
|
|
177
202
|
doi = work.ids.doi
|
|
178
203
|
doi_url = f"""[@click=app.open_link("{quote(str(doi))}")]DOI[/]""" if doi else "-"
|
|
179
204
|
|
|
180
205
|
work_table.add_row(
|
|
181
206
|
str(f"""[@click=open_work_details({idx})]{idx}[/]"""),
|
|
182
|
-
Text(work.title, overflow=
|
|
207
|
+
Text(work.title, overflow="ellipsis"),
|
|
183
208
|
Text(work.type),
|
|
184
|
-
Text.from_markup(doi_url, overflow=
|
|
209
|
+
Text.from_markup(doi_url, overflow="ellipsis"),
|
|
185
210
|
Text(work.publication_date),
|
|
186
|
-
str(
|
|
211
|
+
str(len(work_report.cited_by)),
|
|
187
212
|
)
|
|
188
213
|
|
|
189
214
|
yield self._WorksTableRenderer(work_table, report=self.report)
|
|
@@ -204,12 +229,23 @@ class WorkReportPane(VerticalScroll):
|
|
|
204
229
|
self.report = report
|
|
205
230
|
super().__init__()
|
|
206
231
|
|
|
232
|
+
async def toggle_empty_works(self) -> None:
|
|
233
|
+
"""Hide/show works if cites are cero."""
|
|
234
|
+
report_works_status: bool = self.app.query_one("ReportWidget").show_empty_works # type: ignore
|
|
235
|
+
table_works_status = self.query_one(WorksTable).show_empty_works
|
|
236
|
+
|
|
237
|
+
if self.report.works and (report_works_status != table_works_status):
|
|
238
|
+
self.loading = True
|
|
239
|
+
await self.query_one(WorksTable).remove()
|
|
240
|
+
await self.mount(WorksTable(report=self.report, show_empty_works=report_works_status))
|
|
241
|
+
self.loading = False
|
|
242
|
+
|
|
207
243
|
def compose(self) -> ComposeResult:
|
|
208
244
|
"""Compose content pane."""
|
|
209
245
|
with Horizontal(classes="cards-container"):
|
|
210
246
|
yield ReportCitationMetricsCard(report=self.report)
|
|
211
|
-
yield
|
|
212
|
-
yield
|
|
247
|
+
yield WorksTypeSummaryCard(report=self.report)
|
|
248
|
+
yield OpenAccessSummaryCard(report=self.report)
|
|
213
249
|
|
|
214
250
|
if self.report.works:
|
|
215
251
|
yield WorksTable(report=self.report)
|
|
@@ -4,8 +4,8 @@ from .core import FinderWidget, SearchBar
|
|
|
4
4
|
from .results import AuthorResultWidget, InstitutionResultWidget
|
|
5
5
|
|
|
6
6
|
__all__ = [
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
"FinderWidget",
|
|
8
|
+
"SearchBar",
|
|
9
|
+
"AuthorResultWidget",
|
|
10
|
+
"InstitutionResultWidget",
|
|
11
11
|
]
|
|
@@ -8,8 +8,8 @@ from textual.widgets import Button, Label, Static
|
|
|
8
8
|
|
|
9
9
|
from pub_analyzer.models.author import AuthorResult
|
|
10
10
|
from pub_analyzer.models.institution import InstitutionResult
|
|
11
|
-
from pub_analyzer.widgets.author.core import
|
|
12
|
-
from pub_analyzer.widgets.institution.core import
|
|
11
|
+
from pub_analyzer.widgets.author.core import AuthorSummaryWidget
|
|
12
|
+
from pub_analyzer.widgets.institution.core import InstitutionSummaryWidget
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
class ResultWidget(Static):
|
|
@@ -31,21 +31,22 @@ class AuthorResultWidget(ResultWidget):
|
|
|
31
31
|
with Vertical(classes="vertical-content"):
|
|
32
32
|
# Main info
|
|
33
33
|
with Horizontal(classes="main-info-container"):
|
|
34
|
-
yield Label(f
|
|
35
|
-
yield Label(f
|
|
34
|
+
yield Label(f"[bold]Cited by count:[/bold] {self.author_result.cited_by_count}", classes="cited-by-count")
|
|
35
|
+
yield Label(f"[bold]Works count:[/bold] {self.author_result.works_count}", classes="works-count")
|
|
36
36
|
yield Label(f"""[@click=app.open_link('{quote(str(self.author_result.external_id))}')]ORCID[/]""", classes="external-id")
|
|
37
37
|
|
|
38
38
|
# Author hint
|
|
39
39
|
yield Label(self.author_result.hint or "", classes="text-hint")
|
|
40
40
|
|
|
41
41
|
async def on_button_pressed(self, event: Button.Pressed) -> None:
|
|
42
|
-
"""Go to the Author
|
|
42
|
+
"""Go to the Author summary page."""
|
|
43
43
|
from pub_analyzer.widgets.body import MainContent
|
|
44
|
-
|
|
44
|
+
|
|
45
|
+
author_summary_widget = AuthorSummaryWidget(author_result=self.author_result)
|
|
45
46
|
|
|
46
47
|
main_content = self.app.query_one(MainContent)
|
|
47
48
|
main_content.update_title(title=self.author_result.display_name)
|
|
48
|
-
await main_content.mount(
|
|
49
|
+
await main_content.mount(author_summary_widget)
|
|
49
50
|
|
|
50
51
|
await self.app.query_one("FinderWidget").remove()
|
|
51
52
|
|
|
@@ -65,23 +66,21 @@ class InstitutionResultWidget(ResultWidget):
|
|
|
65
66
|
with Horizontal(classes="main-info-container"):
|
|
66
67
|
external_id = self.institution_result.external_id or self.institution_result.id
|
|
67
68
|
|
|
68
|
-
yield Label(f
|
|
69
|
-
yield Label(f
|
|
70
|
-
yield Label(
|
|
71
|
-
f"""[@click=app.open_link('{quote(str(external_id))}')]External ID[/]""",
|
|
72
|
-
classes="external-id"
|
|
73
|
-
)
|
|
69
|
+
yield Label(f"[bold]Cited by count:[/bold] {self.institution_result.cited_by_count}", classes="cited-by-count")
|
|
70
|
+
yield Label(f"[bold]Works count:[/bold] {self.institution_result.works_count}", classes="works-count")
|
|
71
|
+
yield Label(f"""[@click=app.open_link('{quote(str(external_id))}')]External ID[/]""", classes="external-id")
|
|
74
72
|
|
|
75
73
|
# Institution hint
|
|
76
74
|
yield Label(self.institution_result.hint or "", classes="text-hint")
|
|
77
75
|
|
|
78
76
|
async def on_button_pressed(self, event: Button.Pressed) -> None:
|
|
79
|
-
"""Go to the Institution
|
|
77
|
+
"""Go to the Institution summary page."""
|
|
80
78
|
from pub_analyzer.widgets.body import MainContent
|
|
81
|
-
|
|
79
|
+
|
|
80
|
+
institution_summary_widget = InstitutionSummaryWidget(institution_result=self.institution_result)
|
|
82
81
|
|
|
83
82
|
main_content = self.app.query_one(MainContent)
|
|
84
83
|
main_content.update_title(title=self.institution_result.display_name)
|
|
85
|
-
await main_content.mount(
|
|
84
|
+
await main_content.mount(institution_summary_widget)
|
|
86
85
|
|
|
87
86
|
await self.app.query_one("FinderWidget").remove()
|
pub_analyzer/widgets/sidebar.py
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"""Sidebar components and options."""
|
|
2
|
+
|
|
2
3
|
from enum import Enum
|
|
4
|
+
from importlib.metadata import version
|
|
3
5
|
|
|
4
6
|
from textual import on
|
|
5
7
|
from textual.app import ComposeResult
|
|
@@ -25,26 +27,26 @@ class SideBar(Static):
|
|
|
25
27
|
|
|
26
28
|
def compose(self) -> ComposeResult:
|
|
27
29
|
"""Compose dynamically the sidebar options."""
|
|
30
|
+
pub_analyzer_version = version("pub-analyzer")
|
|
31
|
+
|
|
28
32
|
with Vertical(classes="sidebar-options-column"):
|
|
29
|
-
yield Label("Menu", id=
|
|
33
|
+
yield Label("Menu", id="sidebar-title")
|
|
30
34
|
|
|
31
|
-
|
|
32
|
-
SideBarOptionsName.SEARCH.value,
|
|
33
|
-
variant="primary", id="
|
|
34
|
-
)
|
|
35
|
+
with Vertical(classes="sidebar-buttons-column"):
|
|
36
|
+
yield Button(SideBarOptionsName.SEARCH.value, variant="primary", id="search-sidebar-button", classes="sidebar-option")
|
|
37
|
+
yield Button(SideBarOptionsName.LOAD_REPORT.value, variant="primary", id="load-sidebar-button", classes="sidebar-option")
|
|
35
38
|
|
|
36
|
-
yield
|
|
37
|
-
SideBarOptionsName.LOAD_REPORT.value,
|
|
38
|
-
variant="primary", id="load-sidebar-button", classes="sidebar-option"
|
|
39
|
-
)
|
|
39
|
+
yield Label(f"v{pub_analyzer_version}", id="module-version-label")
|
|
40
40
|
|
|
41
41
|
def toggle(self) -> None:
|
|
42
42
|
"""Show/Hide Sidebar."""
|
|
43
43
|
if self.has_class("-hidden"):
|
|
44
44
|
self.remove_class("-hidden")
|
|
45
|
+
self.styles.animate("width", value=20, duration=0.5)
|
|
45
46
|
else:
|
|
46
47
|
if self.query("*:focus"):
|
|
47
48
|
self.screen.set_focus(None)
|
|
49
|
+
self.styles.animate("width", value=0, duration=0.5)
|
|
48
50
|
self.add_class("-hidden")
|
|
49
51
|
|
|
50
52
|
async def _replace_main_content(self, new_title: str, new_widget: Widget) -> None:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: pub-analyzer
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: A text user interface, written in python, which automates the generation of scientific production reports using OpenAlex
|
|
5
5
|
Home-page: https://github.com/alejandrgaspar/pub-analyzer
|
|
6
6
|
License: MIT
|
|
@@ -20,19 +20,43 @@ Classifier: Operating System :: POSIX :: Linux
|
|
|
20
20
|
Classifier: Programming Language :: Python :: 3
|
|
21
21
|
Classifier: Programming Language :: Python :: 3.10
|
|
22
22
|
Classifier: Programming Language :: Python :: 3.11
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
23
24
|
Classifier: Typing :: Typed
|
|
24
|
-
Requires-Dist: httpx (==0.
|
|
25
|
-
Requires-Dist: jinja2 (==3.1.
|
|
26
|
-
Requires-Dist: pydantic (==2.
|
|
27
|
-
Requires-Dist: textual (==0.
|
|
28
|
-
Requires-Dist: typst (==0.
|
|
25
|
+
Requires-Dist: httpx (==0.27.0)
|
|
26
|
+
Requires-Dist: jinja2 (==3.1.3)
|
|
27
|
+
Requires-Dist: pydantic (==2.7.1)
|
|
28
|
+
Requires-Dist: textual (==0.58.0)
|
|
29
|
+
Requires-Dist: typst (==0.11.0)
|
|
29
30
|
Project-URL: Documentation, https://pub-analyzer.com/
|
|
30
31
|
Project-URL: Repository, https://github.com/alejandrgaspar/pub-analyzer
|
|
31
32
|
Description-Content-Type: text/markdown
|
|
32
33
|
|
|
33
34
|
# Pub Analyzer
|
|
34
35
|
|
|
35
|
-
|
|
36
|
+
<p align="center">
|
|
37
|
+
<img src="https://raw.githubusercontent.com/alejandrgaspar/pub-analyzer/main/docs/assets/img/logo.png" alt="PubAnalyzer splash image" width="275">
|
|
38
|
+
</p>
|
|
39
|
+
|
|
40
|
+
<p align="center">
|
|
41
|
+
<a href="https://github.com/alejandrgaspar/pub-analyzer/actions/workflows/python-test.yml" target="_blank">
|
|
42
|
+
<img src="https://github.com/alejandrgaspar/pub-analyzer/actions/workflows/python-test.yml/badge.svg?branch=main" alt="Test status">
|
|
43
|
+
</a>
|
|
44
|
+
<a href="https://pypi.org/project/pub-analyzer/" target="_blank">
|
|
45
|
+
<img src="https://img.shields.io/pypi/v/pub-analyzer?color=%230f80c1" alt="PyPI - Version">
|
|
46
|
+
</a>
|
|
47
|
+
<a href="https://pypi.org/project/pub-analyzer/" target="_blank">
|
|
48
|
+
<img src="https://img.shields.io/pypi/pyversions/pub-analyzer?color=%230f80c1" alt="PyPI - Python Version">
|
|
49
|
+
</a>
|
|
50
|
+
<a href="https://github.com/alejandrgaspar/pub-analyzer/blob/main/LICENSE" target="_blank">
|
|
51
|
+
<img src="https://img.shields.io/github/license/alejandrgaspar/pub-analyzer?color=%2331c553" alt="License MIT">
|
|
52
|
+
</a>
|
|
53
|
+
</p>
|
|
54
|
+
|
|
55
|
+
<p align="center">
|
|
56
|
+
Pub-Analyzer —<a href="https://github.com/alejandrgaspar/pub-analyzer" target="_blank"><em>Publication Analyzer</em></a>— is a Text User Interface —<em>TUI</em>— written in Python, which automates the generation of scientific production reports using <a href="https://openalex.org/" target="_blank"><em>OpenAlex</em></a>.
|
|
57
|
+
</p>
|
|
58
|
+
|
|
59
|
+
---
|
|
36
60
|
|
|
37
61
|
## What is Pub Analyzer for?
|
|
38
62
|
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
pub_analyzer/__init__.py,sha256=8xDULqkSyOBAa5bvzcnteo5nW1sqXB6pR9fSc9DpXFo,40
|
|
2
|
+
pub_analyzer/css/body.tcss,sha256=Yw9bx5eWABVQPKWDAfWk8AdSXnBAg396JB9Lm4LXD-o,1682
|
|
3
|
+
pub_analyzer/css/buttons.tcss,sha256=FruJ39dXmKnZm3_y0CxAJByKHrbmt6RQky0T0uM906g,546
|
|
4
|
+
pub_analyzer/css/checkbox.tcss,sha256=FblyIHns-r1K0ikOnSJtoTMz57C6iDEcscdFAsJ7s48,506
|
|
5
|
+
pub_analyzer/css/collapsible.tcss,sha256=Rh-L5PcIMhnZ7RhY1udd_BcYC1mfCMew2m6o6ty3juE,605
|
|
6
|
+
pub_analyzer/css/datatable.tcss,sha256=JgdMUPc4fYmZlXi_FxbuD88pegK6Pi4FgDHIfA_TKxo,994
|
|
7
|
+
pub_analyzer/css/main.tcss,sha256=wL8KD2gwRDrE_u0EbZrvu5s3eTFwWLtbP8RMLgCv2Sk,834
|
|
8
|
+
pub_analyzer/css/report.tcss,sha256=5v-h4Y5gUdonLQTVkVm-HYrYT1xDS-4NYHVZDYghVEs,2066
|
|
9
|
+
pub_analyzer/css/search.tcss,sha256=rovbWjp4pYfCF_OyAC_QrV_0WdMUlsYoQ3vbs9pGw7g,1326
|
|
10
|
+
pub_analyzer/css/summary.tcss,sha256=i4ixICwoQFj2BToW9NVmJGUIYk5upbukbTCnDgT40ds,1350
|
|
11
|
+
pub_analyzer/css/tabs.tcss,sha256=dS7y6ZZmo1Vw7Wqpx66-O-oE7zeqPE9reWqIhQ1KcZs,311
|
|
12
|
+
pub_analyzer/css/tree.tcss,sha256=5BSabX9ZmRL3VTz0Gya2RRJnWrwdIF9cTf6dXj2R4kE,818
|
|
13
|
+
pub_analyzer/internal/__init__.py,sha256=9aqrBJDedUiBO5kEO81kSAuPbOSFoaDZZK8w5NydPhs,22
|
|
14
|
+
pub_analyzer/internal/identifier.py,sha256=LDYew25TLuwqJHmLg9iRNTURWynN27ZbTxTVGbuOUD0,2939
|
|
15
|
+
pub_analyzer/internal/render.py,sha256=gq5dScWs507tne3glJbTQ-PekmALvtcZPB1p_mVAkE0,2144
|
|
16
|
+
pub_analyzer/internal/report.py,sha256=zeDn4mR8G7-NA0SxelIhanZ2e4mLecbiuVqlv2_hMb8,16320
|
|
17
|
+
pub_analyzer/internal/templates/author/author_summary.typ,sha256=0qyS2C075wpj3mOxY4Ka5-aNjQ5RAjoJskkIb8tnLFE,2478
|
|
18
|
+
pub_analyzer/internal/templates/author/report.typ,sha256=izdq5Z-504hkLwI_C47RsqDDu_K8N74h_3KCetzSies,1444
|
|
19
|
+
pub_analyzer/internal/templates/author/sources.typ,sha256=_yFNDuYPu1casCy73JVL7l9qZ_YYRqTGLLgr5tnQW5A,814
|
|
20
|
+
pub_analyzer/internal/templates/author/works.typ,sha256=wKgg4EoPYLzY2q3UBsBKZADOmTy4-1r8CD1pt8A3VYo,1980
|
|
21
|
+
pub_analyzer/internal/templates/author/works_extended.typ,sha256=B38IHOwlBuPzUA6SewoHEp5eU-9kEH3yH5KvK7Nm7a0,3527
|
|
22
|
+
pub_analyzer/main.py,sha256=-yv34eVkLT8cyJ-wCl3vuoprMmqpsMhc3arGAnytCuY,2201
|
|
23
|
+
pub_analyzer/models/__init__.py,sha256=hvR6m379slQw7gSwnl_OFY21Ytv90mmmOe7bp8vZYkk,59
|
|
24
|
+
pub_analyzer/models/author.py,sha256=9DzBQY4JoDv5ilBNOLyquOqnlwbXCTpmZq25cQLCgYU,1900
|
|
25
|
+
pub_analyzer/models/concept.py,sha256=yNvajKWTn6uBalNoJmlobitvbFBOjF80jlZnjKjwDRw,677
|
|
26
|
+
pub_analyzer/models/institution.py,sha256=kjS3U0w454SBCZNsZ03-XZqciZR0ubwsCRo2ix6RojM,3067
|
|
27
|
+
pub_analyzer/models/report.py,sha256=zrAkUYgGCGnDzvQYbidyf1cmi7iXbXxsskfqZaeurbw,2574
|
|
28
|
+
pub_analyzer/models/source.py,sha256=o3ich4iDYB_PH_cVbrZtVRFVLQlPS3W5ajgBQQGzYqM,2730
|
|
29
|
+
pub_analyzer/models/topic.py,sha256=3MBQV-njnjfmOVvgmFZxy8fFU7sMj5yxUW8EHFAjlD4,1825
|
|
30
|
+
pub_analyzer/models/work.py,sha256=vN2mSB6oiPbkhaGy0pjWGc2meWjkkHAz5Eubed3BO6w,4115
|
|
31
|
+
pub_analyzer/widgets/__init__.py,sha256=JALs1yGE06XYwjoY_0AG-Wt_pMknI1WEWNYK3atQaEA,18
|
|
32
|
+
pub_analyzer/widgets/author/__init__.py,sha256=oiJibt7YiuGpovOnFIAlC9YwLO-0LN3SDgPWFL-LVPQ,22
|
|
33
|
+
pub_analyzer/widgets/author/cards.py,sha256=JWZxYy4Oen5fToiSBgvfEgmBJlrIVXCWpT-XjkLbxY4,2445
|
|
34
|
+
pub_analyzer/widgets/author/core.py,sha256=XaqjOajU0zz8rtraF5Y9vjjiLyMKJmke8GblFaa6UwU,5069
|
|
35
|
+
pub_analyzer/widgets/author/tables.py,sha256=kowgw5_NIRKdLvL9b97q9PSugHQ__eMcLWfahKETZdA,793
|
|
36
|
+
pub_analyzer/widgets/body.py,sha256=wN9cMcm1MaRTjuHYt8RWrG8D_ngg5cn-hVllvmzPX_o,972
|
|
37
|
+
pub_analyzer/widgets/common/__init__.py,sha256=Fx5Gl17Rd_wueZjNElBtI1kCn-4DMSbC3lEA8u7PSto,287
|
|
38
|
+
pub_analyzer/widgets/common/card.py,sha256=GGSaeuZt6AqY7kAvcVnWNMrhNPzr7do66YRQOYNSYvU,595
|
|
39
|
+
pub_analyzer/widgets/common/filesystem.py,sha256=i0S3D6JJzPkF1Sqm83SSQlmYFKRf82SnoFgKVE6BdYI,6460
|
|
40
|
+
pub_analyzer/widgets/common/filters.py,sha256=7KNRcSAED0EGxIZp6o1zxmGpzbmEM2yozDeYw-5ysSM,3373
|
|
41
|
+
pub_analyzer/widgets/common/input.py,sha256=tK_UCtLDGHlI_NKpKjGkVu4gWiwMAIHixT9Im--Un4c,2649
|
|
42
|
+
pub_analyzer/widgets/common/modal.py,sha256=otLQZotdTRTlSeTBknIxqRyduVY6lRZ5yW5u20SLcwI,882
|
|
43
|
+
pub_analyzer/widgets/common/selector.py,sha256=Jh5bsn-zYmHGfEE3eO9XL6BsgKpLMGfg8FJur4gQmH0,1493
|
|
44
|
+
pub_analyzer/widgets/common/summary.py,sha256=Qj-FRfAVgJmCaVUJI-jQrHX2sGKHTP2b75KukuJWlog,165
|
|
45
|
+
pub_analyzer/widgets/institution/__init__.py,sha256=T_WDTDistaaq2obl1Cy_wZI5nTBiJNUnB-_OwBOLFTE,27
|
|
46
|
+
pub_analyzer/widgets/institution/cards.py,sha256=OgLWP8M0xENa5NnY9NtmwjdqOwZJUN77fXSHFNT3BYU,2862
|
|
47
|
+
pub_analyzer/widgets/institution/core.py,sha256=q21GXawR9g-pN68YgdiHc3r_ZxiKq5JYbakeLvdu4dk,5332
|
|
48
|
+
pub_analyzer/widgets/institution/tables.py,sha256=tXjrop9HGSkZGjYIOGQEOKVoyoeIGPd-8oSh08iuTRw,838
|
|
49
|
+
pub_analyzer/widgets/report/__init__.py,sha256=oolRVss3JKaQHaQVDncjtxbLINRJ5Rd1ulW1uk7MLhc,54
|
|
50
|
+
pub_analyzer/widgets/report/author.py,sha256=orlq-YSHeRcEyCXrQHiRpp2tdPC9SO1MjQ9uhNpU0-k,1227
|
|
51
|
+
pub_analyzer/widgets/report/cards.py,sha256=NtsGCdlAqsuocun_QwE7dJeZetfAqKAjduHYpVtq-ic,4826
|
|
52
|
+
pub_analyzer/widgets/report/concept.py,sha256=xiGXy_RXO_XmdqnlePkOozYPmQrsDdqKPMRXHsZbDP0,1485
|
|
53
|
+
pub_analyzer/widgets/report/core.py,sha256=V4fwBHnSqkUzC1da8sGYqzsPKQRofA7GqpEhbEXsk4s,11436
|
|
54
|
+
pub_analyzer/widgets/report/export.py,sha256=EQzF5fMZgHtLv3f5hITDgf9WW2XytRX_foeLWwcIHkM,4762
|
|
55
|
+
pub_analyzer/widgets/report/grants.py,sha256=m183W6djVhucAuYs-EhjkHuA9heqpGwsW_iRouVQsns,1347
|
|
56
|
+
pub_analyzer/widgets/report/institution.py,sha256=PDPE9fK18l9kKKch5sJrbnHHDss0kJ6bgVhM4hTyrAo,1297
|
|
57
|
+
pub_analyzer/widgets/report/locations.py,sha256=s6O5v_jX_oPsKOf2fEujtDxLHQRVsqrIcgN4rZkRKkg,2892
|
|
58
|
+
pub_analyzer/widgets/report/source.py,sha256=WJhJc0_sZOcAtkmh9-VjbgugoArZgxKoXlITqVaBYK0,3045
|
|
59
|
+
pub_analyzer/widgets/report/topic.py,sha256=SI3STTBFlpR-VJcsNhJyu6vc9uyytU_ASKuWXb-qr60,1969
|
|
60
|
+
pub_analyzer/widgets/report/work.py,sha256=8PwuEkTN-cvteMv9xULLfaDrTZpogX6z0DBiOjN0JKA,9590
|
|
61
|
+
pub_analyzer/widgets/search/__init__.py,sha256=8C3IQtFkiIL8hlQbhJ_fAHM59-TAoe29wBAM2ptozhw,239
|
|
62
|
+
pub_analyzer/widgets/search/core.py,sha256=4NvowtBcrH1fmob9kuF7v9Tq3Nd99jzB2S7xaD8OYeI,3861
|
|
63
|
+
pub_analyzer/widgets/search/results.py,sha256=6Sl-shkGCf-jcMmalXpk1n8oBHk1aZNzFPJfHSRP1gA,3702
|
|
64
|
+
pub_analyzer/widgets/sidebar.py,sha256=XlIshlCVW5Bb3MXFPnU9is0qQrUrGdT6xlkKiYNEcAM,2704
|
|
65
|
+
pub_analyzer-0.3.0.dist-info/LICENSE,sha256=OPopoEowTMKqIea8Kbxk3TKdCQ97YkLvIknjTHE5oCI,1080
|
|
66
|
+
pub_analyzer-0.3.0.dist-info/METADATA,sha256=sMpAloa5JJUR1k3N7lgpLATfrn4Q8lZqERK5_NBYPaE,4508
|
|
67
|
+
pub_analyzer-0.3.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
68
|
+
pub_analyzer-0.3.0.dist-info/entry_points.txt,sha256=mVb_gUNX_-aVWHlNKLjcMAS8YLgNnSq9JLRXVJGIF2c,54
|
|
69
|
+
pub_analyzer-0.3.0.dist-info/RECORD,,
|
pub_analyzer/css/author.tcss
DELETED
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
/* COLORS */
|
|
2
|
-
$bg-main-color: white;
|
|
3
|
-
$bg-secondary-color: #e5e7eb;
|
|
4
|
-
$bg-secondary-color-accent: #d1d5db;
|
|
5
|
-
$text-primary-color: black;
|
|
6
|
-
|
|
7
|
-
$bg-main-color-darken: #1e293b;
|
|
8
|
-
$bg-secondary-color-darken: #0f172a;
|
|
9
|
-
$text-primary-color-darken: black;
|
|
10
|
-
|
|
11
|
-
AuthorResumeWidget {
|
|
12
|
-
height: 1fr;
|
|
13
|
-
margin: 1 2;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
.-dark-mode AuthorResumeWidget {
|
|
17
|
-
color: $text-primary-color-darken;
|
|
18
|
-
background: $bg-secondary-color;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/* Main Container */
|
|
22
|
-
AuthorResumeWidget #main-container{
|
|
23
|
-
height: 1fr;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/* Block Container */
|
|
27
|
-
AuthorResumeWidget .block-container {
|
|
28
|
-
padding: 1;
|
|
29
|
-
height: auto;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
AuthorResumeWidget .block-title {
|
|
33
|
-
text-align: center;
|
|
34
|
-
width: 100%;
|
|
35
|
-
border-bottom: solid $text-primary-color;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/* Info Container */
|
|
39
|
-
AuthorResumeWidget .info-container {
|
|
40
|
-
height: auto;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
AuthorResumeWidget .info-container Label {
|
|
44
|
-
text-align: center;
|
|
45
|
-
width: 1fr;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/* Filter Container */
|
|
49
|
-
AuthorResumeWidget .filter-container Input {
|
|
50
|
-
width: 3fr;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
AuthorResumeWidget .filter-container Checkbox {
|
|
54
|
-
width: 1fr;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/* Cards */
|
|
58
|
-
AuthorResumeWidget .cards-container {
|
|
59
|
-
height: auto;
|
|
60
|
-
margin: 1 0 0 0 ;
|
|
61
|
-
|
|
62
|
-
layout: grid;
|
|
63
|
-
grid-size: 3 1;
|
|
64
|
-
grid-rows: 11;
|
|
65
|
-
grid-columns: 1fr;
|
|
66
|
-
grid-gutter: 1 2;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/* Table */
|
|
70
|
-
AuthorResumeWidget .table-container {
|
|
71
|
-
height: auto;
|
|
72
|
-
margin: 1 0 0 0;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/* Buttons */
|
|
76
|
-
AuthorResumeWidget .button-container {
|
|
77
|
-
align: center middle;
|
|
78
|
-
}
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
/* COLORS */
|
|
2
|
-
$bg-main-color: white;
|
|
3
|
-
$bg-secondary-color: #e5e7eb;
|
|
4
|
-
$bg-secondary-color-accent: #d1d5db;
|
|
5
|
-
$text-primary-color: black;
|
|
6
|
-
|
|
7
|
-
$bg-main-color-darken: #1e293b;
|
|
8
|
-
$bg-secondary-color-darken: #0f172a;
|
|
9
|
-
$text-primary-color-darken: black;
|
|
10
|
-
|
|
11
|
-
InstitutionResumeWidget {
|
|
12
|
-
height: 1fr;
|
|
13
|
-
margin: 1 2;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
.-dark-mode InstitutionResumeWidget {
|
|
17
|
-
color: $text-primary-color-darken;
|
|
18
|
-
background: $bg-secondary-color;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/* Main Container */
|
|
22
|
-
InstitutionResumeWidget #main-container{
|
|
23
|
-
height: 1fr;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/* Block Container */
|
|
27
|
-
InstitutionResumeWidget .block-container {
|
|
28
|
-
padding: 1;
|
|
29
|
-
height: auto;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
InstitutionResumeWidget .block-title {
|
|
33
|
-
text-align: center;
|
|
34
|
-
width: 100%;
|
|
35
|
-
border-bottom: solid $text-primary-color;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/* Info Container */
|
|
39
|
-
InstitutionResumeWidget .info-container {
|
|
40
|
-
height: auto;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
InstitutionResumeWidget .info-container Label {
|
|
44
|
-
text-align: center;
|
|
45
|
-
width: 1fr;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/* Filter Container */
|
|
49
|
-
InstitutionResumeWidget .filter-container Input {
|
|
50
|
-
width: 3fr;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
InstitutionResumeWidget .filter-container Checkbox {
|
|
54
|
-
width: 1fr;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/* Cards */
|
|
58
|
-
InstitutionResumeWidget .cards-container {
|
|
59
|
-
height: auto;
|
|
60
|
-
margin: 1 0 0 0 ;
|
|
61
|
-
|
|
62
|
-
layout: grid;
|
|
63
|
-
grid-size: 3 1;
|
|
64
|
-
grid-rows: 14;
|
|
65
|
-
grid-columns: 1fr;
|
|
66
|
-
grid-gutter: 1 2;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/* Table */
|
|
70
|
-
InstitutionResumeWidget .table-container {
|
|
71
|
-
height: auto;
|
|
72
|
-
margin: 1 0 0 0 ;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/* Buttons */
|
|
76
|
-
InstitutionResumeWidget .button-container {
|
|
77
|
-
align: center middle;
|
|
78
|
-
}
|