pbn-client 0.1.0__tar.gz

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 (36) hide show
  1. pbn_client-0.1.0/.github/workflows/ci.yml +31 -0
  2. pbn_client-0.1.0/.gitignore +27 -0
  3. pbn_client-0.1.0/.pre-commit-config.yaml +18 -0
  4. pbn_client-0.1.0/LICENSE +21 -0
  5. pbn_client-0.1.0/PKG-INFO +108 -0
  6. pbn_client-0.1.0/README.md +81 -0
  7. pbn_client-0.1.0/pyproject.toml +59 -0
  8. pbn_client-0.1.0/src/pbn_client/__init__.py +53 -0
  9. pbn_client-0.1.0/src/pbn_client/auth.py +96 -0
  10. pbn_client-0.1.0/src/pbn_client/client.py +113 -0
  11. pbn_client-0.1.0/src/pbn_client/conf/__init__.py +0 -0
  12. pbn_client-0.1.0/src/pbn_client/conf/settings.py +46 -0
  13. pbn_client-0.1.0/src/pbn_client/const.py +33 -0
  14. pbn_client-0.1.0/src/pbn_client/dict_utils.py +48 -0
  15. pbn_client-0.1.0/src/pbn_client/exceptions.py +141 -0
  16. pbn_client-0.1.0/src/pbn_client/mixins/__init__.py +22 -0
  17. pbn_client-0.1.0/src/pbn_client/mixins/conferences.py +20 -0
  18. pbn_client-0.1.0/src/pbn_client/mixins/dictionaries.py +16 -0
  19. pbn_client-0.1.0/src/pbn_client/mixins/institutions.py +156 -0
  20. pbn_client-0.1.0/src/pbn_client/mixins/journals.py +28 -0
  21. pbn_client-0.1.0/src/pbn_client/mixins/person.py +26 -0
  22. pbn_client-0.1.0/src/pbn_client/mixins/publications.py +32 -0
  23. pbn_client-0.1.0/src/pbn_client/mixins/publishers.py +25 -0
  24. pbn_client-0.1.0/src/pbn_client/mixins/search.py +10 -0
  25. pbn_client-0.1.0/src/pbn_client/pagination.py +44 -0
  26. pbn_client-0.1.0/src/pbn_client/reporting.py +136 -0
  27. pbn_client-0.1.0/src/pbn_client/statements.py +388 -0
  28. pbn_client-0.1.0/src/pbn_client/transport.py +377 -0
  29. pbn_client-0.1.0/src/pbn_client/utils.py +23 -0
  30. pbn_client-0.1.0/tests/fakes.py +31 -0
  31. pbn_client-0.1.0/tests/test_configuration.py +71 -0
  32. pbn_client-0.1.0/tests/test_institutions.py +26 -0
  33. pbn_client-0.1.0/tests/test_masking.py +28 -0
  34. pbn_client-0.1.0/tests/test_pbn_validation_error.py +154 -0
  35. pbn_client-0.1.0/tests/test_reporting.py +85 -0
  36. pbn_client-0.1.0/tests/test_transport_rollbar_scrub.py +56 -0
@@ -0,0 +1,31 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ concurrency:
12
+ group: ${{ github.workflow }}-${{ github.ref }}
13
+ cancel-in-progress: true
14
+
15
+ jobs:
16
+ test:
17
+ runs-on: ubuntu-latest
18
+ strategy:
19
+ fail-fast: false
20
+ matrix:
21
+ python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
22
+ steps:
23
+ - uses: actions/checkout@v4
24
+ - name: Install uv
25
+ uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
26
+ with:
27
+ python-version: ${{ matrix.python-version }}
28
+ - name: Run tests
29
+ run: uv run --isolated pytest -q
30
+ - name: Lint
31
+ run: uv run --isolated ruff check .
@@ -0,0 +1,27 @@
1
+ __pycache__/
2
+ *.pyc
3
+ .pytest_cache/
4
+ .ruff_cache/
5
+ .venv/
6
+ dist/
7
+ build/
8
+ *.egg-info/
9
+
10
+ # Secrets / local env
11
+ .env
12
+ .env.*
13
+ !.env.example
14
+ !.env.sample
15
+ *.pem
16
+ *.key
17
+
18
+ # IDE / editor
19
+ .idea/
20
+ .vscode/
21
+ *.sublime-workspace
22
+
23
+ # OS turds
24
+ .DS_Store
25
+ Thumbs.db
26
+ desktop.ini
27
+ ._*
@@ -0,0 +1,18 @@
1
+ repos:
2
+ - repo: https://github.com/pre-commit/pre-commit-hooks
3
+ rev: v5.0.0
4
+ hooks:
5
+ - id: trailing-whitespace
6
+ - id: end-of-file-fixer
7
+ - id: check-yaml
8
+ - id: check-toml
9
+ - id: check-added-large-files
10
+ - id: check-merge-conflict
11
+ - id: detect-private-key
12
+
13
+ - repo: https://github.com/astral-sh/ruff-pre-commit
14
+ rev: v0.14.0
15
+ hooks:
16
+ - id: ruff-check
17
+ args: [--fix]
18
+ - id: ruff-format
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2017-2026 Michal Pasternak
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.
@@ -0,0 +1,108 @@
1
+ Metadata-Version: 2.4
2
+ Name: pbn-client
3
+ Version: 0.1.0
4
+ Summary: Framework-independent client for the Polish Bibliography Network API
5
+ Project-URL: Homepage, https://github.com/iplweb/pbn-client
6
+ Project-URL: Repository, https://github.com/iplweb/pbn-client
7
+ Project-URL: Issues, https://github.com/iplweb/pbn-client/issues
8
+ Author-email: Michał Pasternak <michal.dtz@gmail.com>
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: api-client,bibliography,pbn,polish-bibliography-network
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Programming Language :: Python :: 3.14
22
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
23
+ Requires-Python: <3.15,>=3.10
24
+ Requires-Dist: requests<3,>=2.28
25
+ Requires-Dist: simplejson<5,>=3.19
26
+ Description-Content-Type: text/markdown
27
+
28
+ # pbn-client
29
+
30
+ [![CI](https://github.com/iplweb/pbn-client/actions/workflows/ci.yml/badge.svg)](https://github.com/iplweb/pbn-client/actions/workflows/ci.yml)
31
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
32
+
33
+ `pbn-client` is a framework-independent Python client for communication with
34
+ the Polish Bibliography Network (PBN) API. It provides HTTP authentication,
35
+ pagination, dictionary and publication endpoints, and institution-profile
36
+ statement operations.
37
+
38
+ The package does not depend on Django or an external error-reporting service.
39
+ Projects that persist downloaded PBN data in Django should use the companion
40
+ `django-pbn-client` package.
41
+
42
+ ## Installation
43
+
44
+ ```console
45
+ pip install pbn-client
46
+ ```
47
+
48
+ Python 3.10 through 3.14 is supported.
49
+
50
+ ## Basic usage
51
+
52
+ ```python
53
+ from pbn_client import PBNClient, RequestsTransport
54
+
55
+ transport = RequestsTransport(
56
+ app_id="application-id",
57
+ app_token="application-token",
58
+ base_url="https://pbn-micro-alpha.opi.org.pl",
59
+ user_token="user-token",
60
+ timeout=(30, 120),
61
+ )
62
+ client = PBNClient(transport)
63
+
64
+ languages = list(client.get_languages())
65
+ ```
66
+
67
+ Credentials should normally be supplied explicitly. The interactive command
68
+ helpers also understand `PBN_CLIENT_APP_ID`, `PBN_CLIENT_APP_TOKEN`,
69
+ `PBN_CLIENT_BASE_URL`, `PBN_CLIENT_USER_TOKEN`, and
70
+ `PBN_CLIENT_HTTP_TIMEOUT`. A timeout may be a single number or a
71
+ `connect,read` pair.
72
+
73
+ ## Error reporting
74
+
75
+ The standalone default reporter is a no-op. An application can inject any
76
+ object implementing `ErrorReporter` when it constructs a transport:
77
+
78
+ ```python
79
+ import rollbar
80
+
81
+ from pbn_client import RequestsTransport
82
+
83
+ transport = RequestsTransport(
84
+ "application-id",
85
+ "application-token",
86
+ "https://pbn-micro-alpha.opi.org.pl",
87
+ reporter=rollbar,
88
+ )
89
+ ```
90
+
91
+ The reporter receives only scrubbed diagnostic metadata. Response bodies and
92
+ authentication header values are not sent to it. `LoggingReporter`,
93
+ `NullReporter`, and `set_default_reporter()` are also available for applications
94
+ that prefer a process-wide policy.
95
+
96
+ ## Development
97
+
98
+ From the repository root:
99
+
100
+ ```console
101
+ uv run --isolated pytest
102
+ uv run ruff check .
103
+ uv build
104
+ ```
105
+
106
+ ## License
107
+
108
+ `pbn-client` is released under the [MIT License](LICENSE).
@@ -0,0 +1,81 @@
1
+ # pbn-client
2
+
3
+ [![CI](https://github.com/iplweb/pbn-client/actions/workflows/ci.yml/badge.svg)](https://github.com/iplweb/pbn-client/actions/workflows/ci.yml)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+
6
+ `pbn-client` is a framework-independent Python client for communication with
7
+ the Polish Bibliography Network (PBN) API. It provides HTTP authentication,
8
+ pagination, dictionary and publication endpoints, and institution-profile
9
+ statement operations.
10
+
11
+ The package does not depend on Django or an external error-reporting service.
12
+ Projects that persist downloaded PBN data in Django should use the companion
13
+ `django-pbn-client` package.
14
+
15
+ ## Installation
16
+
17
+ ```console
18
+ pip install pbn-client
19
+ ```
20
+
21
+ Python 3.10 through 3.14 is supported.
22
+
23
+ ## Basic usage
24
+
25
+ ```python
26
+ from pbn_client import PBNClient, RequestsTransport
27
+
28
+ transport = RequestsTransport(
29
+ app_id="application-id",
30
+ app_token="application-token",
31
+ base_url="https://pbn-micro-alpha.opi.org.pl",
32
+ user_token="user-token",
33
+ timeout=(30, 120),
34
+ )
35
+ client = PBNClient(transport)
36
+
37
+ languages = list(client.get_languages())
38
+ ```
39
+
40
+ Credentials should normally be supplied explicitly. The interactive command
41
+ helpers also understand `PBN_CLIENT_APP_ID`, `PBN_CLIENT_APP_TOKEN`,
42
+ `PBN_CLIENT_BASE_URL`, `PBN_CLIENT_USER_TOKEN`, and
43
+ `PBN_CLIENT_HTTP_TIMEOUT`. A timeout may be a single number or a
44
+ `connect,read` pair.
45
+
46
+ ## Error reporting
47
+
48
+ The standalone default reporter is a no-op. An application can inject any
49
+ object implementing `ErrorReporter` when it constructs a transport:
50
+
51
+ ```python
52
+ import rollbar
53
+
54
+ from pbn_client import RequestsTransport
55
+
56
+ transport = RequestsTransport(
57
+ "application-id",
58
+ "application-token",
59
+ "https://pbn-micro-alpha.opi.org.pl",
60
+ reporter=rollbar,
61
+ )
62
+ ```
63
+
64
+ The reporter receives only scrubbed diagnostic metadata. Response bodies and
65
+ authentication header values are not sent to it. `LoggingReporter`,
66
+ `NullReporter`, and `set_default_reporter()` are also available for applications
67
+ that prefer a process-wide policy.
68
+
69
+ ## Development
70
+
71
+ From the repository root:
72
+
73
+ ```console
74
+ uv run --isolated pytest
75
+ uv run ruff check .
76
+ uv build
77
+ ```
78
+
79
+ ## License
80
+
81
+ `pbn-client` is released under the [MIT License](LICENSE).
@@ -0,0 +1,59 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "pbn-client"
7
+ version = "0.1.0"
8
+ description = "Framework-independent client for the Polish Bibliography Network API"
9
+ readme = "README.md"
10
+ requires-python = ">=3.10,<3.15"
11
+ license = "MIT"
12
+ license-files = ["LICENSE"]
13
+ authors = [
14
+ { name = "Michał Pasternak", email = "michal.dtz@gmail.com" },
15
+ ]
16
+ keywords = ["pbn", "bibliography", "polish-bibliography-network", "api-client"]
17
+ classifiers = [
18
+ "Development Status :: 4 - Beta",
19
+ "Intended Audience :: Developers",
20
+ "License :: OSI Approved :: MIT License",
21
+ "Operating System :: OS Independent",
22
+ "Programming Language :: Python :: 3",
23
+ "Programming Language :: Python :: 3.10",
24
+ "Programming Language :: Python :: 3.11",
25
+ "Programming Language :: Python :: 3.12",
26
+ "Programming Language :: Python :: 3.13",
27
+ "Programming Language :: Python :: 3.14",
28
+ "Topic :: Software Development :: Libraries :: Python Modules",
29
+ ]
30
+ dependencies = [
31
+ "requests>=2.28,<3",
32
+ "simplejson>=3.19,<5",
33
+ ]
34
+
35
+ [project.urls]
36
+ Homepage = "https://github.com/iplweb/pbn-client"
37
+ Repository = "https://github.com/iplweb/pbn-client"
38
+ Issues = "https://github.com/iplweb/pbn-client/issues"
39
+
40
+ [dependency-groups]
41
+ dev = [
42
+ "pytest>=9.1.1,<10",
43
+ "ruff>=0.14,<1",
44
+ ]
45
+
46
+ [tool.hatch.build.targets.wheel]
47
+ packages = ["src/pbn_client"]
48
+
49
+ [tool.pytest.ini_options]
50
+ addopts = "-ra"
51
+ pythonpath = ["src", "tests"]
52
+ testpaths = ["tests"]
53
+
54
+ [tool.ruff]
55
+ line-length = 88
56
+ target-version = "py310"
57
+
58
+ [tool.ruff.lint]
59
+ select = ["E", "F", "I"]
@@ -0,0 +1,53 @@
1
+ """Klient API PBN — czysta, niezależna od frameworka warstwa protokołu.
2
+
3
+ Pakiet operuje wyłącznie na pojęciach PBN: tokeny, URL-e, PBN UID-y, JSON-y
4
+ i flagi bool. Nie zna modelu domenowego żadnej aplikacji-hosta, więc może
5
+ być używany przez dowolny projekt integrujący się z PBN.
6
+ """
7
+
8
+ from .auth import OAuthMixin
9
+ from .client import PBNClient
10
+ from .mixins import (
11
+ ConferencesMixin,
12
+ DictionariesMixin,
13
+ InstitutionsMixin,
14
+ InstitutionsProfileMixin,
15
+ JournalsMixin,
16
+ PersonMixin,
17
+ PublicationsMixin,
18
+ PublishersMixin,
19
+ SearchMixin,
20
+ )
21
+ from .pagination import PageableResource
22
+ from .reporting import (
23
+ ErrorReporter,
24
+ LoggingReporter,
25
+ NullReporter,
26
+ get_default_reporter,
27
+ set_default_reporter,
28
+ )
29
+ from .transport import PBNClientTransport, RequestsTransport
30
+ from .utils import smart_content
31
+
32
+ __all__ = [
33
+ "PBNClient",
34
+ "OAuthMixin",
35
+ "ConferencesMixin",
36
+ "DictionariesMixin",
37
+ "InstitutionsMixin",
38
+ "InstitutionsProfileMixin",
39
+ "JournalsMixin",
40
+ "PersonMixin",
41
+ "PublicationsMixin",
42
+ "PublishersMixin",
43
+ "SearchMixin",
44
+ "PageableResource",
45
+ "ErrorReporter",
46
+ "LoggingReporter",
47
+ "NullReporter",
48
+ "get_default_reporter",
49
+ "set_default_reporter",
50
+ "PBNClientTransport",
51
+ "RequestsTransport",
52
+ "smart_content",
53
+ ]
@@ -0,0 +1,96 @@
1
+ """OAuth authentication mixin for PBN API."""
2
+
3
+ from urllib.parse import parse_qs, urlparse
4
+
5
+ import requests
6
+
7
+ from pbn_client.conf import settings as pbn_settings
8
+ from pbn_client.exceptions import (
9
+ AuthenticationConfigurationError,
10
+ AuthenticationResponseError,
11
+ )
12
+
13
+
14
+ class OAuthMixin:
15
+ """Mixin providing OAuth authentication functionality."""
16
+
17
+ @classmethod
18
+ def get_auth_url(klass, base_url, app_id, state=None):
19
+ url = f"{base_url}/auth/pbn/api/registration/user/token/{app_id}"
20
+ if state:
21
+ from urllib.parse import quote
22
+
23
+ url += f"?state={quote(state)}"
24
+ return url
25
+
26
+ @classmethod
27
+ def get_user_token(
28
+ klass,
29
+ base_url,
30
+ app_id,
31
+ app_token,
32
+ one_time_token,
33
+ *,
34
+ timeout=None,
35
+ ):
36
+ headers = {
37
+ "X-App-Id": app_id,
38
+ "X-App-Token": app_token,
39
+ }
40
+ body = {"oneTimeToken": one_time_token}
41
+ url = f"{base_url}/auth/pbn/api/user/token"
42
+ response = requests.post(
43
+ url=url,
44
+ json=body,
45
+ headers=headers,
46
+ timeout=(
47
+ pbn_settings.PBN_CLIENT_HTTP_TIMEOUT
48
+ if timeout is None
49
+ else pbn_settings.parse_timeout(timeout)
50
+ ),
51
+ )
52
+ try:
53
+ response.json()
54
+ except ValueError as e:
55
+ if response.content.startswith(b"Mismatched X-APP-TOKEN: "):
56
+ raise AuthenticationConfigurationError(
57
+ "Token aplikacji PBN nieprawidłowy. Poproś administratora "
58
+ "o skonfigurowanie prawidłowego tokena aplikacji PBN w "
59
+ "konfiguracji aplikacji. "
60
+ ) from e
61
+
62
+ raise AuthenticationResponseError(response.content) from e
63
+
64
+ return response.json().get("X-User-Token")
65
+
66
+ def authorize(self, base_url, app_id, app_token):
67
+ if self.access_token:
68
+ return True
69
+
70
+ self.access_token = pbn_settings.PBN_CLIENT_USER_TOKEN
71
+ if self.access_token:
72
+ return True
73
+
74
+ auth_url = OAuthMixin.get_auth_url(base_url, app_id)
75
+
76
+ print(
77
+ f"""I have launched a web browser with {auth_url} ,\nplease log-in,
78
+ then paste the redirected URL below. \n"""
79
+ )
80
+ import webbrowser
81
+
82
+ webbrowser.open(auth_url)
83
+ redirect_response = input("Paste the full redirect URL here:")
84
+ one_time_token = parse_qs(urlparse(redirect_response).query).get("ott")[0]
85
+
86
+ # NIE wypisujemy one_time_token ani access_token — to aktywne sekrety,
87
+ # które zostawałyby w terminalu/CI/przechwyconych logach (uwaga #4).
88
+ self.access_token = OAuthMixin.get_user_token(
89
+ base_url,
90
+ app_id,
91
+ app_token,
92
+ one_time_token,
93
+ timeout=getattr(self, "timeout", None),
94
+ )
95
+
96
+ return True
@@ -0,0 +1,113 @@
1
+ """Czysty klient protokołu PBN.
2
+
3
+ ``PBNClient`` to kompozycja mixinów protokołu (słownikowo-CRUD + silnik
4
+ oświadczeń ``StatementsMixin``). Operuje na tokenach, PBN UID-ach, JSON-ach
5
+ i flagach bool — nie zna modelu domenowego aplikacji-hosta.
6
+
7
+ Orkiestracja synchronizacji specyficzna dla aplikacji (znająca jej rekordy
8
+ i konfigurację instytucji) powinna żyć w warstwie aplikacji, jako klasa
9
+ dziedzicząca po ``PBNClient``.
10
+ """
11
+
12
+ import sys
13
+ from collections.abc import Iterable
14
+ from pprint import pprint
15
+
16
+ from .mixins import (
17
+ ConferencesMixin,
18
+ DictionariesMixin,
19
+ InstitutionsMixin,
20
+ InstitutionsProfileMixin,
21
+ JournalsMixin,
22
+ PersonMixin,
23
+ PublicationsMixin,
24
+ PublishersMixin,
25
+ SearchMixin,
26
+ )
27
+ from .statements import StatementsMixin
28
+ from .transport import RequestsTransport
29
+
30
+
31
+ class PBNClient(
32
+ ConferencesMixin,
33
+ DictionariesMixin,
34
+ InstitutionsMixin,
35
+ InstitutionsProfileMixin,
36
+ JournalsMixin,
37
+ PersonMixin,
38
+ PublicationsMixin,
39
+ PublishersMixin,
40
+ SearchMixin,
41
+ StatementsMixin,
42
+ ):
43
+ """Czysty klient protokołu PBN (bez orkiestracji specyficznej dla aplikacji)."""
44
+
45
+ _interactive = False
46
+
47
+ def __init__(self, transport: RequestsTransport):
48
+ self.transport = transport
49
+
50
+ def _get_command_function(self, cmd):
51
+ """Get function to execute from command name."""
52
+ try:
53
+ return getattr(self, cmd[0])
54
+ except AttributeError as e:
55
+ if self._interactive:
56
+ print(f"No such command: {cmd}")
57
+ return None
58
+ raise e
59
+
60
+ def _extract_arguments(self, lst):
61
+ """Extract positional and keyword arguments from command list."""
62
+ args = ()
63
+ kw = {}
64
+ for elem in lst:
65
+ if elem.find(":") >= 1:
66
+ k, n = elem.split(":", 1)
67
+ kw[k] = n
68
+ else:
69
+ args += (elem,)
70
+ return args, kw
71
+
72
+ def _print_non_interactive_result(self, res):
73
+ """Print result in non-interactive mode."""
74
+ import json
75
+
76
+ print(json.dumps(res))
77
+
78
+ def _print_interactive_result(self, res):
79
+ """Print result in interactive mode."""
80
+ if type(res) is dict:
81
+ pprint(res)
82
+ elif isinstance(res, Iterable):
83
+ if self._interactive and hasattr(res, "total_elements"):
84
+ print(
85
+ "Incoming data: no_elements=",
86
+ res.total_elements,
87
+ "no_pages=",
88
+ res.total_pages,
89
+ )
90
+ input("Press ENTER to continue> ")
91
+ for elem in res:
92
+ pprint(elem)
93
+
94
+ def exec(self, cmd):
95
+ fun = self._get_command_function(cmd)
96
+ if fun is None:
97
+ return
98
+
99
+ args, kw = self._extract_arguments(cmd[1:])
100
+ res = fun(*args, **kw)
101
+
102
+ if not sys.stdout.isatty():
103
+ self._print_non_interactive_result(res)
104
+ else:
105
+ self._print_interactive_result(res)
106
+
107
+ def interactive(self):
108
+ self._interactive = True
109
+ while True:
110
+ cmd = input("cmd> ")
111
+ if cmd == "exit":
112
+ break
113
+ self.exec(cmd.split(" "))
File without changes
@@ -0,0 +1,46 @@
1
+ """Environment defaults for the standalone PBN client.
2
+
3
+ Applications should normally pass credentials and timeouts to the transport
4
+ constructor. These variables remain available for command-line compatibility.
5
+ No framework settings object is imported here.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import os
11
+ from collections.abc import Sequence
12
+
13
+ from pbn_client.const import DEFAULT_BASE_URL
14
+
15
+ Timeout = float | tuple[float, float]
16
+ DEFAULT_HTTP_TIMEOUT: Timeout = (30.0, 120.0)
17
+
18
+
19
+ def parse_timeout(
20
+ raw: str | float | Sequence[float] | None,
21
+ default: Timeout = DEFAULT_HTTP_TIMEOUT,
22
+ ) -> Timeout:
23
+ """Parse a requests timeout from a scalar or ``connect,read`` value."""
24
+
25
+ if raw is None:
26
+ return default
27
+ if isinstance(raw, (int, float)):
28
+ return float(raw)
29
+ if isinstance(raw, Sequence) and not isinstance(raw, str):
30
+ values = tuple(float(value) for value in raw)
31
+ return values if len(values) == 2 else default
32
+
33
+ parts = [part.strip() for part in str(raw).split(",") if part.strip()]
34
+ if len(parts) == 1:
35
+ return float(parts[0])
36
+ if len(parts) == 2:
37
+ return (float(parts[0]), float(parts[1]))
38
+ return default
39
+
40
+
41
+ PBN_CLIENT_DEFAULT_BASE_URL = DEFAULT_BASE_URL
42
+ PBN_CLIENT_APP_ID = os.getenv("PBN_CLIENT_APP_ID")
43
+ PBN_CLIENT_APP_TOKEN = os.getenv("PBN_CLIENT_APP_TOKEN")
44
+ PBN_CLIENT_BASE_URL = os.getenv("PBN_CLIENT_BASE_URL", DEFAULT_BASE_URL)
45
+ PBN_CLIENT_USER_TOKEN = os.getenv("PBN_CLIENT_USER_TOKEN")
46
+ PBN_CLIENT_HTTP_TIMEOUT = parse_timeout(os.getenv("PBN_CLIENT_HTTP_TIMEOUT"))
@@ -0,0 +1,33 @@
1
+ DELETED = "DELETED"
2
+ ACTIVE = "ACTIVE"
3
+
4
+ PBN_POST_PUBLICATIONS_URL = "/api/v1/publications"
5
+
6
+ PBN_POST_PUBLICATION_NO_STATEMENTS_URL = "/api/v1/repositorium/publications"
7
+
8
+ PBN_POST_PUBLICATION_FEE_URL = "/api/v1/institutionProfile/publications/fees/{id}"
9
+
10
+ PBN_GET_LANGUAGES_URL = "/api/v1/dictionary/languages"
11
+ PBN_SEARCH_PUBLICATIONS_URL = "/api/v1/search/publications"
12
+
13
+ PBN_GET_JOURNAL_BY_ID = "/api/v1/journals/{id}"
14
+
15
+ DEFAULT_BASE_URL = "https://pbn-micro-alpha.opi.org.pl"
16
+
17
+ NEEDS_PBN_AUTH_MSG = (
18
+ "W celu poprawnej autentykacji należy podać poprawny token użytkownika aplikacji."
19
+ )
20
+ PBN_DELETE_PUBLICATION_STATEMENT = (
21
+ "/api/v1/institutionProfile/publications/{publicationId}"
22
+ )
23
+ PBN_GET_PUBLICATION_BY_ID_URL = "/api/v1/publications/id/{id}"
24
+
25
+ PBN_GET_INSTITUTION_STATEMENTS = (
26
+ "/api/v1/institutionProfile/publications/page/statements"
27
+ )
28
+
29
+ PBN_GET_DISCIPLINES_URL = "/api/v2/dictionary/disciplines"
30
+
31
+ PBN_POST_INSTITUTION_STATEMENTS_URL = "/api/v2/institution-profile/statements"
32
+
33
+ PBN_GET_INSTITUTION_PUBLICATIONS_V2 = "/api/v2/institution-profile/publications"