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.
Files changed (70) hide show
  1. pub_analyzer/__init__.py +1 -0
  2. pub_analyzer/__main__.py +7 -0
  3. pub_analyzer/css/body.tcss +87 -0
  4. pub_analyzer/css/buttons.tcss +24 -0
  5. pub_analyzer/css/checkbox.tcss +29 -0
  6. pub_analyzer/css/collapsible.tcss +31 -0
  7. pub_analyzer/css/datatable.tcss +50 -0
  8. pub_analyzer/css/editor.tcss +60 -0
  9. pub_analyzer/css/main.tcss +50 -0
  10. pub_analyzer/css/report.tcss +131 -0
  11. pub_analyzer/css/search.tcss +81 -0
  12. pub_analyzer/css/summary.tcss +75 -0
  13. pub_analyzer/css/tabs.tcss +18 -0
  14. pub_analyzer/css/tree.tcss +44 -0
  15. pub_analyzer/internal/__init__.py +1 -0
  16. pub_analyzer/internal/identifier.py +106 -0
  17. pub_analyzer/internal/limiter.py +34 -0
  18. pub_analyzer/internal/render.py +41 -0
  19. pub_analyzer/internal/report.py +497 -0
  20. pub_analyzer/internal/templates/author_report.typ +591 -0
  21. pub_analyzer/main.py +81 -0
  22. pub_analyzer/models/__init__.py +1 -0
  23. pub_analyzer/models/author.py +87 -0
  24. pub_analyzer/models/concept.py +19 -0
  25. pub_analyzer/models/institution.py +138 -0
  26. pub_analyzer/models/report.py +111 -0
  27. pub_analyzer/models/source.py +77 -0
  28. pub_analyzer/models/topic.py +59 -0
  29. pub_analyzer/models/work.py +158 -0
  30. pub_analyzer/widgets/__init__.py +1 -0
  31. pub_analyzer/widgets/author/__init__.py +1 -0
  32. pub_analyzer/widgets/author/cards.py +65 -0
  33. pub_analyzer/widgets/author/core.py +122 -0
  34. pub_analyzer/widgets/author/tables.py +50 -0
  35. pub_analyzer/widgets/body.py +55 -0
  36. pub_analyzer/widgets/common/__init__.py +18 -0
  37. pub_analyzer/widgets/common/card.py +29 -0
  38. pub_analyzer/widgets/common/filesystem.py +203 -0
  39. pub_analyzer/widgets/common/filters.py +111 -0
  40. pub_analyzer/widgets/common/input.py +97 -0
  41. pub_analyzer/widgets/common/label.py +36 -0
  42. pub_analyzer/widgets/common/modal.py +43 -0
  43. pub_analyzer/widgets/common/selector.py +66 -0
  44. pub_analyzer/widgets/common/summary.py +7 -0
  45. pub_analyzer/widgets/institution/__init__.py +1 -0
  46. pub_analyzer/widgets/institution/cards.py +78 -0
  47. pub_analyzer/widgets/institution/core.py +122 -0
  48. pub_analyzer/widgets/institution/tables.py +24 -0
  49. pub_analyzer/widgets/report/__init__.py +1 -0
  50. pub_analyzer/widgets/report/author.py +43 -0
  51. pub_analyzer/widgets/report/cards.py +130 -0
  52. pub_analyzer/widgets/report/concept.py +47 -0
  53. pub_analyzer/widgets/report/core.py +308 -0
  54. pub_analyzer/widgets/report/editor.py +80 -0
  55. pub_analyzer/widgets/report/export.py +112 -0
  56. pub_analyzer/widgets/report/grants.py +85 -0
  57. pub_analyzer/widgets/report/institution.py +39 -0
  58. pub_analyzer/widgets/report/locations.py +75 -0
  59. pub_analyzer/widgets/report/source.py +90 -0
  60. pub_analyzer/widgets/report/topic.py +55 -0
  61. pub_analyzer/widgets/report/work.py +391 -0
  62. pub_analyzer/widgets/search/__init__.py +11 -0
  63. pub_analyzer/widgets/search/core.py +96 -0
  64. pub_analyzer/widgets/search/results.py +82 -0
  65. pub_analyzer/widgets/sidebar.py +70 -0
  66. pub_analyzer-0.5.6.dist-info/METADATA +102 -0
  67. pub_analyzer-0.5.6.dist-info/RECORD +70 -0
  68. pub_analyzer-0.5.6.dist-info/WHEEL +4 -0
  69. pub_analyzer-0.5.6.dist-info/entry_points.txt +3 -0
  70. pub_analyzer-0.5.6.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,96 @@
1
+ """Searchbar widget."""
2
+
3
+ from enum import Enum
4
+
5
+ import httpx
6
+ from pydantic import TypeAdapter
7
+ from textual import on
8
+ from textual.app import ComposeResult
9
+ from textual.containers import Horizontal, VerticalScroll
10
+ from textual.widgets import Static
11
+
12
+ from pub_analyzer.models.author import AuthorResult
13
+ from pub_analyzer.models.institution import InstitutionResult
14
+ from pub_analyzer.widgets.common import Input, Select
15
+
16
+ from .results import AuthorResultWidget, InstitutionResultWidget
17
+
18
+
19
+ class SearchBar(Input):
20
+ """SearchBar."""
21
+
22
+
23
+ class FinderWidget(Static):
24
+ """Search in Open Alex API as-you-type Widget."""
25
+
26
+ class OpenAlexEndPoint(Enum):
27
+ """OpenAlex Endpoints."""
28
+
29
+ AUTHOR = "https://api.openalex.org/autocomplete/authors?author_hint=institution"
30
+ INSTITUTION = "https://api.openalex.org/autocomplete/institutions?"
31
+
32
+ class EntityTypeSelector(Select[OpenAlexEndPoint]):
33
+ """Entity_type Selector."""
34
+
35
+ def __init__(self, url: OpenAlexEndPoint = OpenAlexEndPoint.AUTHOR) -> None:
36
+ self.url = url
37
+ super().__init__()
38
+
39
+ def compose(self) -> ComposeResult:
40
+ """Generate an input field and displays the results."""
41
+ entity_options = [(name.title(), endpoint) for name, endpoint in self.OpenAlexEndPoint.__members__.items()]
42
+
43
+ with Horizontal(classes="searchbar-container"):
44
+ yield SearchBar(placeholder="Search author")
45
+ yield self.EntityTypeSelector(options=entity_options, value=self.OpenAlexEndPoint.AUTHOR, allow_blank=False)
46
+ yield VerticalScroll(id="results-container")
47
+
48
+ async def lookup(self, input: str) -> None:
49
+ """Search in OpenAlex API."""
50
+ async with httpx.AsyncClient() as client:
51
+ url = self.url.value + f"&q={input}"
52
+ response = (await client.get(url)).json().get("results")
53
+
54
+ if input == self.query_one(Input).value:
55
+ # Clear the results
56
+ await self.query("#results-container > *").remove()
57
+
58
+ match self.url:
59
+ case self.OpenAlexEndPoint.AUTHOR:
60
+ author_results: list[AuthorResult] = TypeAdapter(list[AuthorResult]).validate_python(response)
61
+ for author_result in author_results:
62
+ await self.query_one("#results-container").mount(AuthorResultWidget(author_result))
63
+ return
64
+
65
+ case self.OpenAlexEndPoint.INSTITUTION:
66
+ institution_results: list[InstitutionResult] = TypeAdapter(list[InstitutionResult]).validate_python(response)
67
+ for institution_result in institution_results:
68
+ await self.query_one("#results-container").mount(InstitutionResultWidget(institution_result))
69
+ return
70
+
71
+ @on(Input.Changed)
72
+ async def on_type(self, event: Input.Changed) -> None:
73
+ """Coroutine to handle search input."""
74
+ if event.value:
75
+ # Look up the author in the background
76
+ self.run_worker(self.lookup(event.value), exclusive=True)
77
+ else:
78
+ # Clear the results
79
+ await self.query("#results-container > *").remove()
80
+
81
+ @on(Select.Changed)
82
+ async def on_select_entity(self, event: Select.Changed) -> None:
83
+ """Change entity endpoint."""
84
+ search_bar = self.query_one(SearchBar)
85
+ match event.value:
86
+ case self.OpenAlexEndPoint.AUTHOR:
87
+ self.url = self.OpenAlexEndPoint.AUTHOR
88
+ search_bar.placeholder = "Search author"
89
+ case self.OpenAlexEndPoint.INSTITUTION:
90
+ self.url = self.OpenAlexEndPoint.INSTITUTION
91
+ search_bar.placeholder = "Search institution"
92
+ case _:
93
+ self.url = self.OpenAlexEndPoint.AUTHOR
94
+ search_bar.placeholder = "Search author"
95
+
96
+ search_bar.value = ""
@@ -0,0 +1,82 @@
1
+ """Module that allows searching for authors using OpenAlex."""
2
+
3
+ from urllib.parse import quote
4
+
5
+ from textual.app import ComposeResult
6
+ from textual.containers import Horizontal, Vertical
7
+ from textual.widgets import Button, Label, Static
8
+
9
+ from pub_analyzer.models.author import AuthorResult
10
+ from pub_analyzer.models.institution import InstitutionResult
11
+ from pub_analyzer.widgets.author.core import AuthorSummaryWidget
12
+ from pub_analyzer.widgets.institution.core import InstitutionSummaryWidget
13
+
14
+
15
+ class ResultWidget(Static):
16
+ """Result Widget."""
17
+
18
+ DEFAULT_CLASSES = "result-widget"
19
+
20
+
21
+ class AuthorResultWidget(ResultWidget):
22
+ """Author result widget."""
23
+
24
+ def __init__(self, author_result: AuthorResult) -> None:
25
+ self.author_result = author_result
26
+ super().__init__()
27
+
28
+ def compose(self) -> ComposeResult:
29
+ """Compose Author result widget."""
30
+ orcid_link = self.author_result.external_id
31
+
32
+ yield Button(label=self.author_result.display_name)
33
+ with Vertical(classes="vertical-content"):
34
+ # Main info
35
+ with Horizontal(classes="main-info-container"):
36
+ yield Label(f"[bold]Cited by count:[/bold] {self.author_result.cited_by_count}", classes="cited-by-count")
37
+ yield Label(f"[bold]Works count:[/bold] {self.author_result.works_count}", classes="works-count")
38
+
39
+ if orcid_link:
40
+ yield Label(f"""[@click=app.open_link('{quote(str(orcid_link))}')]ORCID[/]""", classes="external-id")
41
+ else:
42
+ yield Label(f"""[@click=app.open_link('{quote(str(self.author_result.id))}')]OpenAlexID[/]""", classes="external-id")
43
+
44
+ # Author hint
45
+ yield Label(self.author_result.hint or "", classes="text-hint")
46
+
47
+ async def on_button_pressed(self, event: Button.Pressed) -> None:
48
+ """Go to the Author summary page."""
49
+ from pub_analyzer.widgets.body import MainContent
50
+
51
+ author_summary_widget = AuthorSummaryWidget(author_result=self.author_result)
52
+ self.post_message(MainContent.UpdateMainContent(new_widget=author_summary_widget, title=self.author_result.display_name))
53
+
54
+
55
+ class InstitutionResultWidget(ResultWidget):
56
+ """Institution result widget."""
57
+
58
+ def __init__(self, institution_result: InstitutionResult) -> None:
59
+ self.institution_result = institution_result
60
+ super().__init__()
61
+
62
+ def compose(self) -> ComposeResult:
63
+ """Compose Institution result widget."""
64
+ yield Button(label=self.institution_result.display_name)
65
+ with Vertical(classes="vertical-content"):
66
+ # Main info
67
+ with Horizontal(classes="main-info-container"):
68
+ external_id = self.institution_result.external_id or self.institution_result.id
69
+
70
+ yield Label(f"[bold]Cited by count:[/bold] {self.institution_result.cited_by_count}", classes="cited-by-count")
71
+ yield Label(f"[bold]Works count:[/bold] {self.institution_result.works_count}", classes="works-count")
72
+ yield Label(f"""[@click=app.open_link('{quote(str(external_id))}')]External ID[/]""", classes="external-id")
73
+
74
+ # Institution hint
75
+ yield Label(self.institution_result.hint or "", classes="text-hint")
76
+
77
+ async def on_button_pressed(self, event: Button.Pressed) -> None:
78
+ """Go to the Institution summary page."""
79
+ from pub_analyzer.widgets.body import MainContent
80
+
81
+ institution_summary_widget = InstitutionSummaryWidget(institution_result=self.institution_result)
82
+ self.post_message(MainContent.UpdateMainContent(new_widget=institution_summary_widget, title=self.institution_result.display_name))
@@ -0,0 +1,70 @@
1
+ """Sidebar components and options."""
2
+
3
+ from enum import Enum
4
+ from importlib.metadata import version
5
+
6
+ from textual import on
7
+ from textual.app import ComposeResult
8
+ from textual.containers import Vertical
9
+ from textual.widget import Widget
10
+ from textual.widgets import Button, Label, Static
11
+
12
+ from pub_analyzer.widgets.report.core import LoadReportWidget
13
+ from pub_analyzer.widgets.search import FinderWidget
14
+
15
+
16
+ class SideBarOptionsName(Enum):
17
+ """List of existing Tabs titles."""
18
+
19
+ SEARCH = "Search"
20
+ LOAD_REPORT = "Load report"
21
+
22
+
23
+ class SideBar(Static):
24
+ """SideBar Widget."""
25
+
26
+ DEFAULT_CLASSES = "sidebar"
27
+
28
+ def compose(self) -> ComposeResult:
29
+ """Compose dynamically the sidebar options."""
30
+ pub_analyzer_version = version("pub-analyzer")
31
+
32
+ with Vertical(classes="sidebar-options-column"):
33
+ yield Label("Menu", id="sidebar-title")
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")
38
+
39
+ yield Label(f"v{pub_analyzer_version}", id="module-version-label")
40
+
41
+ def toggle(self) -> None:
42
+ """Show/Hide Sidebar."""
43
+ if self.has_class("-hidden"):
44
+ self.remove_class("-hidden")
45
+ self.styles.animate("width", value=20, duration=0.5)
46
+ else:
47
+ if self.query("*:focus"):
48
+ self.screen.set_focus(None)
49
+ self.styles.animate("width", value=0, duration=0.5)
50
+ self.add_class("-hidden")
51
+
52
+ async def _replace_main_content(self, new_title: str, new_widget: Widget) -> None:
53
+ """Delete the old widgets in the main section, update the main title and replace it with the given Widget."""
54
+ from pub_analyzer.widgets.body import MainContent
55
+
56
+ main_content = self.app.query_one(MainContent)
57
+ await main_content.query("*").exclude("#page-title").remove()
58
+ await main_content.mount(new_widget)
59
+
60
+ main_content.update_title(title=new_title)
61
+
62
+ @on(Button.Pressed, "#search-sidebar-button")
63
+ async def search(self) -> None:
64
+ """Load the FinderWidget in the main view."""
65
+ await self._replace_main_content(new_title=SideBarOptionsName.SEARCH.value, new_widget=FinderWidget())
66
+
67
+ @on(Button.Pressed, "#load-sidebar-button")
68
+ async def load_report(self) -> None:
69
+ """Load the LoadReportWidget in the main view."""
70
+ await self._replace_main_content(new_title=SideBarOptionsName.LOAD_REPORT.value, new_widget=LoadReportWidget())
@@ -0,0 +1,102 @@
1
+ Metadata-Version: 2.4
2
+ Name: pub-analyzer
3
+ Version: 0.5.6
4
+ Summary: A text user interface, written in python, which automates the generation of scientific production reports using OpenAlex
5
+ License: MIT
6
+ License-File: LICENSE
7
+ Author: Alejandro Gaspar
8
+ Author-email: alejandro@gaspar.land
9
+ Maintainer: Alejandro Gaspar
10
+ Maintainer-email: alejandro@gaspar.land
11
+ Requires-Python: >=3.10,<4.0
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Environment :: Console
14
+ Classifier: Intended Audience :: Science/Research
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Operating System :: MacOS
17
+ Classifier: Operating System :: Microsoft :: Windows :: Windows 10
18
+ Classifier: Operating System :: Microsoft :: Windows :: Windows 11
19
+ Classifier: Operating System :: POSIX :: Linux
20
+ Classifier: Programming Language :: Python :: 3
21
+ Classifier: Programming Language :: Python :: 3.10
22
+ Classifier: Programming Language :: Python :: 3.11
23
+ Classifier: Programming Language :: Python :: 3.12
24
+ Classifier: Programming Language :: Python :: 3.13
25
+ Classifier: Programming Language :: Python :: 3.14
26
+ Classifier: Typing :: Typed
27
+ Requires-Dist: httpx[http2] (==0.28.1)
28
+ Requires-Dist: pydantic (==2.12.5)
29
+ Requires-Dist: textual (==0.85.2)
30
+ Requires-Dist: typst (==0.13.2)
31
+ Project-URL: Documentation, https://pub-analyzer.com/
32
+ Project-URL: Homepage, https://github.com/alejandrgaspar/pub-analyzer
33
+ Project-URL: Repository, https://github.com/alejandrgaspar/pub-analyzer
34
+ Description-Content-Type: text/markdown
35
+
36
+ # Pub Analyzer
37
+
38
+ <p align="center">
39
+ <img src="https://raw.githubusercontent.com/alejandrgaspar/pub-analyzer/main/docs/assets/img/logo.png" alt="PubAnalyzer splash image" width="275">
40
+ </p>
41
+
42
+ <p align="center">
43
+ <a href="https://github.com/alejandrgaspar/pub-analyzer/actions/workflows/python-test.yml" target="_blank">
44
+ <img src="https://github.com/alejandrgaspar/pub-analyzer/actions/workflows/python-test.yml/badge.svg?branch=main" alt="Test status">
45
+ </a>
46
+ <a href="https://pypi.org/project/pub-analyzer/" target="_blank">
47
+ <img src="https://img.shields.io/pypi/v/pub-analyzer?color=%230f80c1" alt="PyPI - Version">
48
+ </a>
49
+ <a href="https://pypi.org/project/pub-analyzer/" target="_blank">
50
+ <img src="https://img.shields.io/pypi/pyversions/pub-analyzer?color=%230f80c1" alt="PyPI - Python Version">
51
+ </a>
52
+ <a href="https://github.com/alejandrgaspar/pub-analyzer/blob/main/LICENSE" target="_blank">
53
+ <img src="https://img.shields.io/github/license/alejandrgaspar/pub-analyzer?color=%2331c553" alt="License MIT">
54
+ </a>
55
+ </p>
56
+
57
+ <p align="center">
58
+ Pub-Analyzer &mdash;<a href="https://github.com/alejandrgaspar/pub-analyzer" target="_blank"><em>Publication Analyzer</em></a>&mdash; is a Text User Interface &mdash;<em>TUI</em>&mdash; written in Python, which automates the generation of scientific production reports using <a href="https://openalex.org/" target="_blank"><em>OpenAlex</em></a>.
59
+ </p>
60
+
61
+ ---
62
+
63
+ ## What is Pub Analyzer for?
64
+
65
+ Pub Analyzer is a tool designed to retrieve, process and present in a concise and understandable way the scientific production of a researcher, including detailed information about their articles, citations, collaborations and other relevant metrics. The tool automates the collection and processing of data, providing users with a comprehensive view of the impact of their research and contributions in academia.
66
+
67
+ All our information comes from OpenAlex, an open and complete catalog of the world's scholarly papers, researchers, journals, and institutions &mdash; along with all the ways they're connected to one another. This is the key piece to **make all this possible**.
68
+
69
+ Pub Analyzer generates reports that you can view directly within the app, **export as PDF** files to submit as evidence, or **export as JSON** for analysis with the tools you use every day.
70
+
71
+ ## Why use Pub Analyzer?
72
+
73
+ Researchers are generally required to submit scientific production reports when seeking promotions or funding to support their ongoing research. Instead of laborious manual tracking of their publications and citations, researchers now have the opportunity to perform all of these tasks automatically **in a matter of minutes**.
74
+
75
+ ## Requirements
76
+
77
+ Pub Analyzer requires **Python 3.10 or later** and can run on Linux, macOS, and Windows.
78
+
79
+ ## Installation
80
+
81
+ Install Pub Analyzer via PyPI, with the following command:
82
+
83
+ ```
84
+ pip install pub-analyzer
85
+ ```
86
+
87
+ ## Usage
88
+
89
+ Open the app with the following command:
90
+
91
+ ```
92
+ pub-analyzer
93
+ ```
94
+
95
+ ## Documentation
96
+
97
+ See [documentation](https://pub-analyzer.com/) for more details.
98
+
99
+ ## Contributing
100
+
101
+ Pull requests are welcome!
102
+
@@ -0,0 +1,70 @@
1
+ pub_analyzer/__init__.py,sha256=8xDULqkSyOBAa5bvzcnteo5nW1sqXB6pR9fSc9DpXFo,40
2
+ pub_analyzer/__main__.py,sha256=iCZ8He6JBJ4I7reLgIMf3ISggRiFg2Zuf8fgiTh6dXg,139
3
+ pub_analyzer/css/body.tcss,sha256=Yw9bx5eWABVQPKWDAfWk8AdSXnBAg396JB9Lm4LXD-o,1682
4
+ pub_analyzer/css/buttons.tcss,sha256=FruJ39dXmKnZm3_y0CxAJByKHrbmt6RQky0T0uM906g,546
5
+ pub_analyzer/css/checkbox.tcss,sha256=FblyIHns-r1K0ikOnSJtoTMz57C6iDEcscdFAsJ7s48,506
6
+ pub_analyzer/css/collapsible.tcss,sha256=Rh-L5PcIMhnZ7RhY1udd_BcYC1mfCMew2m6o6ty3juE,605
7
+ pub_analyzer/css/datatable.tcss,sha256=JgdMUPc4fYmZlXi_FxbuD88pegK6Pi4FgDHIfA_TKxo,994
8
+ pub_analyzer/css/editor.tcss,sha256=1IzL92r7sJlnzIJbrTuEGyB16jFCCcAZ9eDDECCZGLc,1198
9
+ pub_analyzer/css/main.tcss,sha256=rtHLKCVZaBuJ83dAg840cetzVtEy8Rjbw1UKxgafeE8,1027
10
+ pub_analyzer/css/report.tcss,sha256=y3-N0mgfyGp4X02LRwFwhxMXMuY7oDsoA9tLDEaUNm8,2047
11
+ pub_analyzer/css/search.tcss,sha256=rovbWjp4pYfCF_OyAC_QrV_0WdMUlsYoQ3vbs9pGw7g,1326
12
+ pub_analyzer/css/summary.tcss,sha256=i4ixICwoQFj2BToW9NVmJGUIYk5upbukbTCnDgT40ds,1350
13
+ pub_analyzer/css/tabs.tcss,sha256=dS7y6ZZmo1Vw7Wqpx66-O-oE7zeqPE9reWqIhQ1KcZs,311
14
+ pub_analyzer/css/tree.tcss,sha256=5BSabX9ZmRL3VTz0Gya2RRJnWrwdIF9cTf6dXj2R4kE,818
15
+ pub_analyzer/internal/__init__.py,sha256=9aqrBJDedUiBO5kEO81kSAuPbOSFoaDZZK8w5NydPhs,22
16
+ pub_analyzer/internal/identifier.py,sha256=LDYew25TLuwqJHmLg9iRNTURWynN27ZbTxTVGbuOUD0,2939
17
+ pub_analyzer/internal/limiter.py,sha256=1YaVBSSG7IfFg0nhD_up21NNL_H2Q4qaIQTvZS674Vo,1002
18
+ pub_analyzer/internal/render.py,sha256=uF1LsY39UkTpkTJgU4hyYnVv6b1MCQayubrPwrGW2DI,1271
19
+ pub_analyzer/internal/report.py,sha256=7geNhs1JRSi2-cqQgQFzMpO0C8lRKa0Ld8oPdFrkYPM,20798
20
+ pub_analyzer/internal/templates/author_report.typ,sha256=URYvit6bsp_8XmdY3Q43vdaiCSMfzrOgE621rFRJfCM,17861
21
+ pub_analyzer/main.py,sha256=0iNj4cggG-HJ8FMODwZ67Yp3-GaFPw-gUEcSCCzwMcc,2332
22
+ pub_analyzer/models/__init__.py,sha256=hvR6m379slQw7gSwnl_OFY21Ytv90mmmOe7bp8vZYkk,59
23
+ pub_analyzer/models/author.py,sha256=7N7DFStmyJAiceNAH-0v8rro8eE9GC9ZXa0v01IX9Hs,2155
24
+ pub_analyzer/models/concept.py,sha256=yNvajKWTn6uBalNoJmlobitvbFBOjF80jlZnjKjwDRw,677
25
+ pub_analyzer/models/institution.py,sha256=1WEfwx-XLeMo-4B5lECNKUFmWbywCvn5QyUfRtDCbDo,3189
26
+ pub_analyzer/models/report.py,sha256=yma70xpD6I8hv_kk5ylMhylLu60eOpQwNEJa-Dd-XQI,2672
27
+ pub_analyzer/models/source.py,sha256=sZkWcf7clYmIBVwgVFKzJcHLAGQRdgkfPL36zGL2zt4,2744
28
+ pub_analyzer/models/topic.py,sha256=3MBQV-njnjfmOVvgmFZxy8fFU7sMj5yxUW8EHFAjlD4,1825
29
+ pub_analyzer/models/work.py,sha256=xFG54n64XhiDUxFpOGYJ9eObqYBNqtDUbYd2a7Ezge4,4375
30
+ pub_analyzer/widgets/__init__.py,sha256=JALs1yGE06XYwjoY_0AG-Wt_pMknI1WEWNYK3atQaEA,18
31
+ pub_analyzer/widgets/author/__init__.py,sha256=oiJibt7YiuGpovOnFIAlC9YwLO-0LN3SDgPWFL-LVPQ,22
32
+ pub_analyzer/widgets/author/cards.py,sha256=JWZxYy4Oen5fToiSBgvfEgmBJlrIVXCWpT-XjkLbxY4,2445
33
+ pub_analyzer/widgets/author/core.py,sha256=9i3U7jSeyQdAdWAslQUMd0_juw3yy9s4mEgl-nJ1LVg,5068
34
+ pub_analyzer/widgets/author/tables.py,sha256=szuRctMtfsivw3OCwJuja4k45A2h636AlOxZHJm0aUA,1893
35
+ pub_analyzer/widgets/body.py,sha256=BvNDe63FE0-KLl89SlhDXtALlLZX4cRqedEG55urTrM,1656
36
+ pub_analyzer/widgets/common/__init__.py,sha256=a65SpyN6RPKA8caSEyDm8mIzW_r4e0LdQGjJaLk07v8,341
37
+ pub_analyzer/widgets/common/card.py,sha256=GGSaeuZt6AqY7kAvcVnWNMrhNPzr7do66YRQOYNSYvU,595
38
+ pub_analyzer/widgets/common/filesystem.py,sha256=i0S3D6JJzPkF1Sqm83SSQlmYFKRf82SnoFgKVE6BdYI,6460
39
+ pub_analyzer/widgets/common/filters.py,sha256=bdtWaxahbFksaZZf6l0Yhgi9opH_RygFHXQV4_CYYj0,3372
40
+ pub_analyzer/widgets/common/input.py,sha256=tK_UCtLDGHlI_NKpKjGkVu4gWiwMAIHixT9Im--Un4c,2649
41
+ pub_analyzer/widgets/common/label.py,sha256=03tl0RdayTeSixdRu4Fyt-jU1cDrI99iQww0hEXahCA,1091
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=6Tb1xQFJERswE1ra2FQFeZcQrb8zSsg-UHfPjWYUOWY,5331
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=IEfRDfsA8jcmFwQQk1O-iuh8MKr4DbzBPpjoE8xECZA,1459
51
+ pub_analyzer/widgets/report/cards.py,sha256=2jf9cpfzVFZO0I9b29bkNaVhENMnfL26etEpUG-NMk0,4854
52
+ pub_analyzer/widgets/report/concept.py,sha256=xiGXy_RXO_XmdqnlePkOozYPmQrsDdqKPMRXHsZbDP0,1485
53
+ pub_analyzer/widgets/report/core.py,sha256=Bgy_fK-IwGjoIidcr687xXsHzN3LEml-A3ykyXNeVW8,11704
54
+ pub_analyzer/widgets/report/editor.py,sha256=WlhjNQCrqeot2rvV1266Vr8yDYJQLL1lJ1XY040UoJI,2768
55
+ pub_analyzer/widgets/report/export.py,sha256=as2yM2FXsqgvMnF4KVWVuxboULXqJ62v7wzMYek23s4,4633
56
+ pub_analyzer/widgets/report/grants.py,sha256=-RP8LXiXdp0YMaTjrSPcMzlpXYYzRNYK_aStwrLQMj8,2725
57
+ pub_analyzer/widgets/report/institution.py,sha256=PDPE9fK18l9kKKch5sJrbnHHDss0kJ6bgVhM4hTyrAo,1297
58
+ pub_analyzer/widgets/report/locations.py,sha256=0K0p6EkkxRHB6AdPbFJy3k_xH6D4UK9vTH39qTE8Y2g,2899
59
+ pub_analyzer/widgets/report/source.py,sha256=_09lA0bfXAyrm88bdqnQZgRDtXKsuWLCqSegXGpkP8k,3052
60
+ pub_analyzer/widgets/report/topic.py,sha256=SI3STTBFlpR-VJcsNhJyu6vc9uyytU_ASKuWXb-qr60,1969
61
+ pub_analyzer/widgets/report/work.py,sha256=kRIWTokXTvycl1dlLFHNBo3y0s2Ltq7966XTAnbQacc,16112
62
+ pub_analyzer/widgets/search/__init__.py,sha256=90L9IghqXD2jAWBKWK6-UeHLSVlci7D3_OGjFSSRgEs,239
63
+ pub_analyzer/widgets/search/core.py,sha256=4NvowtBcrH1fmob9kuF7v9Tq3Nd99jzB2S7xaD8OYeI,3861
64
+ pub_analyzer/widgets/search/results.py,sha256=3ko7zcToGp9MV-mzz_9uTJxSec7IozlIWDZe7QeRmj0,3703
65
+ pub_analyzer/widgets/sidebar.py,sha256=XlIshlCVW5Bb3MXFPnU9is0qQrUrGdT6xlkKiYNEcAM,2704
66
+ pub_analyzer-0.5.6.dist-info/METADATA,sha256=DNdotrEjI_pVTM897gt3dadF8vdjMJQdoe2ZL1L8bg0,4620
67
+ pub_analyzer-0.5.6.dist-info/WHEEL,sha256=3ny-bZhpXrU6vSQ1UPG34FoxZBp3lVcvK0LkgUz6VLk,88
68
+ pub_analyzer-0.5.6.dist-info/entry_points.txt,sha256=mVb_gUNX_-aVWHlNKLjcMAS8YLgNnSq9JLRXVJGIF2c,54
69
+ pub_analyzer-0.5.6.dist-info/licenses/LICENSE,sha256=OPopoEowTMKqIea8Kbxk3TKdCQ97YkLvIknjTHE5oCI,1080
70
+ pub_analyzer-0.5.6.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: poetry-core 2.3.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,3 @@
1
+ [console_scripts]
2
+ pub-analyzer=pub_analyzer.main:run
3
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2022 Alejandro Gaspar Flores
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.