carbonarc 1.0.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.
- carbonarc-1.0.0/LICENSE +21 -0
- carbonarc-1.0.0/PKG-INFO +56 -0
- carbonarc-1.0.0/README.md +28 -0
- carbonarc-1.0.0/pyproject.toml +35 -0
- carbonarc-1.0.0/src/carbonarc/__init__.py +13 -0
- carbonarc-1.0.0/src/carbonarc/base/__init__.py +0 -0
- carbonarc-1.0.0/src/carbonarc/base/auth.py +22 -0
- carbonarc-1.0.0/src/carbonarc/base/client.py +44 -0
- carbonarc-1.0.0/src/carbonarc/base/exceptions.py +32 -0
- carbonarc-1.0.0/src/carbonarc/base/manager.py +85 -0
- carbonarc-1.0.0/src/carbonarc/base/utils.py +36 -0
- carbonarc-1.0.0/src/carbonarc/client.py +31 -0
- carbonarc-1.0.0/src/carbonarc/data.py +545 -0
- carbonarc-1.0.0/src/carbonarc/explorer.py +223 -0
- carbonarc-1.0.0/src/carbonarc/hub.py +45 -0
- carbonarc-1.0.0/src/carbonarc/ontology.py +238 -0
- carbonarc-1.0.0/src/carbonarc/platform.py +62 -0
- carbonarc-1.0.0/src/carbonarc_cli/__init__.py +0 -0
- carbonarc-1.0.0/src/carbonarc_cli/cli.py +65 -0
carbonarc-1.0.0/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2025 Carbon Arc
|
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.
|
carbonarc-1.0.0/PKG-INFO
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
Metadata-Version: 2.3
|
2
|
+
Name: carbonarc
|
3
|
+
Version: 1.0.0
|
4
|
+
Summary: Carbon Arc - Python Package
|
5
|
+
License: MIT
|
6
|
+
Author: Carbon Arc
|
7
|
+
Author-email: support@carbonarc.co
|
8
|
+
Classifier: License :: OSI Approved :: MIT License
|
9
|
+
Classifier: Programming Language :: Python :: 2
|
10
|
+
Classifier: Programming Language :: Python :: 2.7
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
12
|
+
Classifier: Programming Language :: Python :: 3.4
|
13
|
+
Classifier: Programming Language :: Python :: 3.5
|
14
|
+
Classifier: Programming Language :: Python :: 3.6
|
15
|
+
Classifier: Programming Language :: Python :: 3.7
|
16
|
+
Classifier: Programming Language :: Python :: 3.8
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
22
|
+
Requires-Dist: Click (>=8.1.7,<9.0.0)
|
23
|
+
Requires-Dist: beautifulsoup4 (>=4.12.2,<5.0.0)
|
24
|
+
Requires-Dist: pandas (>=2.2.3,<3.0.0)
|
25
|
+
Requires-Dist: requests (>=2.31.0,<3.0.0)
|
26
|
+
Project-URL: Repository, https://github.com/Carbon-Arc/carbonarc.git
|
27
|
+
Description-Content-Type: text/markdown
|
28
|
+
|
29
|
+
# Carbon Arc - Python Package
|
30
|
+
|
31
|
+
Client for [Carbon Arc](https://carbonarc.co/), an Insights Exchange Platform.
|
32
|
+
|
33
|
+
## Usage
|
34
|
+
|
35
|
+
**Installation**
|
36
|
+
|
37
|
+
```bash
|
38
|
+
pip install carbonarc
|
39
|
+
```
|
40
|
+
|
41
|
+
**Quick Start**
|
42
|
+
|
43
|
+
Initialize the API client with authentication.
|
44
|
+
|
45
|
+
```python
|
46
|
+
from carbonarc import CarbonArcClient
|
47
|
+
|
48
|
+
client = CarbonArcClient(token="<token>") # retrieve token from account
|
49
|
+
```
|
50
|
+
|
51
|
+
## Resources
|
52
|
+
|
53
|
+
- [Tutorials](https://github.com/Carbon-Arc/carbonarc-tutorials)
|
54
|
+
- [Docs](https://docs.carbonarc.co/)
|
55
|
+
- [App](https://app.carbonarc.co/)
|
56
|
+
- [API](https://api.carbonarc.co/)
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# Carbon Arc - Python Package
|
2
|
+
|
3
|
+
Client for [Carbon Arc](https://carbonarc.co/), an Insights Exchange Platform.
|
4
|
+
|
5
|
+
## Usage
|
6
|
+
|
7
|
+
**Installation**
|
8
|
+
|
9
|
+
```bash
|
10
|
+
pip install carbonarc
|
11
|
+
```
|
12
|
+
|
13
|
+
**Quick Start**
|
14
|
+
|
15
|
+
Initialize the API client with authentication.
|
16
|
+
|
17
|
+
```python
|
18
|
+
from carbonarc import CarbonArcClient
|
19
|
+
|
20
|
+
client = CarbonArcClient(token="<token>") # retrieve token from account
|
21
|
+
```
|
22
|
+
|
23
|
+
## Resources
|
24
|
+
|
25
|
+
- [Tutorials](https://github.com/Carbon-Arc/carbonarc-tutorials)
|
26
|
+
- [Docs](https://docs.carbonarc.co/)
|
27
|
+
- [App](https://app.carbonarc.co/)
|
28
|
+
- [API](https://api.carbonarc.co/)
|
@@ -0,0 +1,35 @@
|
|
1
|
+
[tool.poetry]
|
2
|
+
name = "carbonarc"
|
3
|
+
version = "1.0.0"
|
4
|
+
description = "Carbon Arc - Python Package"
|
5
|
+
authors = ["Carbon Arc <support@carbonarc.co>"]
|
6
|
+
readme = "README.md"
|
7
|
+
packages = [{include = "carbonarc", from = "src"}, {include = "carbonarc_cli", from = "src"}]
|
8
|
+
license = "MIT License"
|
9
|
+
repository = "https://github.com/Carbon-Arc/carbonarc.git"
|
10
|
+
python = "^3.9"
|
11
|
+
|
12
|
+
[tool.poetry.dependencies]
|
13
|
+
requests = "^2.31.0"
|
14
|
+
pandas = "^2.2.3"
|
15
|
+
beautifulsoup4 = "^4.12.2"
|
16
|
+
Click = "^8.1.7"
|
17
|
+
|
18
|
+
[tool.poetry.scripts]
|
19
|
+
carbonarc = "carbonarc_cli.cli:cli"
|
20
|
+
|
21
|
+
[tool.setuptools.package-data]
|
22
|
+
carbonarc = ["carbonarc/*"]
|
23
|
+
carbonarc_cli = ["carbonarc_cli/*"]
|
24
|
+
|
25
|
+
[tool.poetry.group.dev.dependencies]
|
26
|
+
pytest = "^7.4.0"
|
27
|
+
pytest-mock = "^3.11.1"
|
28
|
+
black = "^23.7.0"
|
29
|
+
isort = "^5.12.0"
|
30
|
+
mypy = "^1.5.0"
|
31
|
+
types-requests = "^2.31.0"
|
32
|
+
|
33
|
+
[build-system]
|
34
|
+
requires = ["poetry-core"]
|
35
|
+
build-backend = "poetry.core.masonry.api"
|
@@ -0,0 +1,13 @@
|
|
1
|
+
try:
|
2
|
+
from importlib.metadata import version, PackageNotFoundError
|
3
|
+
try:
|
4
|
+
__version__ = version("carbonarc")
|
5
|
+
except PackageNotFoundError:
|
6
|
+
__version__ = "unkown"
|
7
|
+
except ImportError:
|
8
|
+
__version__ = "unkown"
|
9
|
+
|
10
|
+
from carbonarc.client import CarbonArcClient
|
11
|
+
|
12
|
+
|
13
|
+
__all__ = ["CarbonArcClient"]
|
File without changes
|
@@ -0,0 +1,22 @@
|
|
1
|
+
from requests.auth import AuthBase
|
2
|
+
|
3
|
+
|
4
|
+
class TokenAuth(AuthBase):
|
5
|
+
"""
|
6
|
+
Class for authenticating requests by user supplied token.
|
7
|
+
"""
|
8
|
+
|
9
|
+
def __init__(self, token: str):
|
10
|
+
assert token, "Token must be a non-empty string."
|
11
|
+
self.auth_token = token
|
12
|
+
|
13
|
+
def __call__(self, r):
|
14
|
+
"""
|
15
|
+
Override the default __call__ method for the AuthBase base class
|
16
|
+
|
17
|
+
More more info, see:
|
18
|
+
https://docs.python-requests.org/en/master/user/advanced/
|
19
|
+
"""
|
20
|
+
auth_token = self.auth_token
|
21
|
+
r.headers["Authorization"] = "Bearer " + auth_token
|
22
|
+
return r
|
@@ -0,0 +1,44 @@
|
|
1
|
+
import logging
|
2
|
+
from typing import Literal
|
3
|
+
|
4
|
+
from carbonarc.base.auth import TokenAuth
|
5
|
+
from carbonarc.base.manager import HttpRequestManager
|
6
|
+
|
7
|
+
|
8
|
+
class BaseAPIClient:
|
9
|
+
"""
|
10
|
+
A client for interacting with the Carbon Arc API.
|
11
|
+
"""
|
12
|
+
|
13
|
+
def __init__(
|
14
|
+
self,
|
15
|
+
token: str,
|
16
|
+
host: str = "https://platform.carbonarc.co",
|
17
|
+
version: str = "v2"
|
18
|
+
):
|
19
|
+
"""
|
20
|
+
Initialize APIClient with an authentication token and user agent.
|
21
|
+
:param auth_token: The authentication token to be used for requests.
|
22
|
+
:param host: The base URL of the Carbon Arc API.
|
23
|
+
:param version: The API version to use.
|
24
|
+
"""
|
25
|
+
|
26
|
+
self.host = host
|
27
|
+
self.version = version
|
28
|
+
|
29
|
+
self._logger = logging.getLogger(__name__)
|
30
|
+
|
31
|
+
self.auth_token = TokenAuth(token)
|
32
|
+
self.request_manager = HttpRequestManager(auth_token=self.auth_token)
|
33
|
+
|
34
|
+
def _build_base_url(
|
35
|
+
self,
|
36
|
+
product: Literal["clients", "framework", "library", "ontology", "hub"],
|
37
|
+
) -> str:
|
38
|
+
return self.host + f"/api/{self.version}/" + product
|
39
|
+
|
40
|
+
def _get(self, url: str, **kwargs) -> dict:
|
41
|
+
return self.request_manager.get(url, **kwargs).json()
|
42
|
+
|
43
|
+
def _post(self, url: str, **kwargs) -> dict:
|
44
|
+
return self.request_manager.post(url, **kwargs).json()
|
@@ -0,0 +1,32 @@
|
|
1
|
+
class CarbonArcException(Exception):
|
2
|
+
"""Base exception for all errors."""
|
3
|
+
|
4
|
+
def __init__(self, message, status_code=None, response=None):
|
5
|
+
self.message = message
|
6
|
+
self.status_code = status_code
|
7
|
+
self.response = response
|
8
|
+
super().__init__(self.message)
|
9
|
+
|
10
|
+
|
11
|
+
class AuthenticationError(CarbonArcException):
|
12
|
+
"""Raised when authentication fails."""
|
13
|
+
pass
|
14
|
+
|
15
|
+
|
16
|
+
class NotFoundError(CarbonArcException):
|
17
|
+
"""Raised when a resource is not found."""
|
18
|
+
pass
|
19
|
+
|
20
|
+
|
21
|
+
class ValidationError(CarbonArcException):
|
22
|
+
"""Raised when request validation fails."""
|
23
|
+
pass
|
24
|
+
|
25
|
+
|
26
|
+
class RateLimitError(CarbonArcException):
|
27
|
+
"""Raised when API rate limit is exceeded."""
|
28
|
+
pass
|
29
|
+
|
30
|
+
class InvalidConfigurationError(CarbonArcException):
|
31
|
+
"""Raised when the configuration is invalid."""
|
32
|
+
pass
|
@@ -0,0 +1,85 @@
|
|
1
|
+
import logging
|
2
|
+
from http import HTTPStatus
|
3
|
+
|
4
|
+
import requests
|
5
|
+
from bs4 import BeautifulSoup
|
6
|
+
from requests.auth import AuthBase
|
7
|
+
|
8
|
+
from carbonarc import __version__
|
9
|
+
from carbonarc.base.exceptions import AuthenticationError
|
10
|
+
|
11
|
+
|
12
|
+
class HttpRequestManager:
|
13
|
+
"""
|
14
|
+
This class is responsible for
|
15
|
+
making Http request calls
|
16
|
+
"""
|
17
|
+
|
18
|
+
def __init__(
|
19
|
+
self, auth_token: AuthBase, user_agent: str = f"Python-APIClient/{__version__}"
|
20
|
+
):
|
21
|
+
"""
|
22
|
+
Initialize the HttpRequestManager with an authentication token and user agent.
|
23
|
+
:param auth_token: The authentication token to be used for requests.
|
24
|
+
:param user_agent: The user agent string to be used for requests.
|
25
|
+
"""
|
26
|
+
if not isinstance(auth_token, AuthBase):
|
27
|
+
raise ValueError("auth_token must be an instance of requests.auth.AuthBase")
|
28
|
+
|
29
|
+
self.auth_token = auth_token
|
30
|
+
self._logger = logging.getLogger(__name__)
|
31
|
+
self.request_session = requests.Session()
|
32
|
+
self.request_session.headers.update(
|
33
|
+
{
|
34
|
+
"User-Agent": user_agent,
|
35
|
+
"Accept": "application/json",
|
36
|
+
}
|
37
|
+
)
|
38
|
+
|
39
|
+
def post(self, url, data=None, json=None, **kwargs) -> requests.Response:
|
40
|
+
return self._raise_for_status(
|
41
|
+
self.request_session.post(
|
42
|
+
url, auth=self.auth_token, data=data, json=json, **kwargs
|
43
|
+
)
|
44
|
+
)
|
45
|
+
|
46
|
+
def patch(self, url, data=None, json=None, **kwargs) -> requests.Response:
|
47
|
+
return self._raise_for_status(
|
48
|
+
self.request_session.patch(
|
49
|
+
url, auth=self.auth_token, data=data, json=json, **kwargs
|
50
|
+
)
|
51
|
+
)
|
52
|
+
|
53
|
+
def get(self, url, **kwargs) -> requests.Response:
|
54
|
+
return self._raise_for_status(
|
55
|
+
self.request_session.get(url, auth=self.auth_token, **kwargs)
|
56
|
+
)
|
57
|
+
|
58
|
+
def put(self, url, data=None, **kwargs) -> requests.Response:
|
59
|
+
return self._raise_for_status(
|
60
|
+
self.request_session.put(url, auth=self.auth_token, data=data, **kwargs)
|
61
|
+
)
|
62
|
+
|
63
|
+
def delete(self, url, **kwargs) -> requests.Response:
|
64
|
+
return self._raise_for_status(
|
65
|
+
self.request_session.delete(url, auth=self.auth_token, **kwargs)
|
66
|
+
)
|
67
|
+
|
68
|
+
def get_stream(self, url, **kwargs) -> requests.Response:
|
69
|
+
self.request_session.headers.update({"Accept": "application/octet-stream"})
|
70
|
+
return self._raise_for_status(
|
71
|
+
self.request_session.get(url, auth=self.auth_token, stream=True, **kwargs)
|
72
|
+
)
|
73
|
+
|
74
|
+
def _raise_for_status(self, response: requests.Response) -> requests.Response:
|
75
|
+
try:
|
76
|
+
response.raise_for_status()
|
77
|
+
except requests.exceptions.HTTPError as e:
|
78
|
+
if e.response.status_code == HTTPStatus.CONFLICT:
|
79
|
+
raise AuthenticationError("Conflict error")
|
80
|
+
if not bool(BeautifulSoup(e.response.text, "html.parser").find()):
|
81
|
+
self._logger.error(e.response.text)
|
82
|
+
else:
|
83
|
+
self._logger.debug(e.response.text)
|
84
|
+
raise
|
85
|
+
return response
|
@@ -0,0 +1,36 @@
|
|
1
|
+
import pandas as pd
|
2
|
+
import datetime
|
3
|
+
from typing import Union
|
4
|
+
|
5
|
+
def timeseries_response_to_pandas(response: Union[dict, pd.DataFrame]) -> pd.DataFrame:
|
6
|
+
"""
|
7
|
+
Convert a timeseries response to a pandas DataFrame.
|
8
|
+
:param response: The response object from the API.
|
9
|
+
:return: A pandas DataFrame containing the timeseries data.
|
10
|
+
"""
|
11
|
+
if isinstance(response, pd.DataFrame):
|
12
|
+
response["date"] = pd.to_datetime(response["date"])
|
13
|
+
return response
|
14
|
+
elif isinstance(response, dict):
|
15
|
+
current_page_data = response["data"]
|
16
|
+
df = pd.DataFrame(current_page_data)
|
17
|
+
df["date"] = pd.to_datetime(df["date"])
|
18
|
+
return df
|
19
|
+
else:
|
20
|
+
raise ValueError("Response must be a dictionary or a pandas DataFrame")
|
21
|
+
|
22
|
+
|
23
|
+
def is_valid_date(date_string: str) -> bool:
|
24
|
+
"""
|
25
|
+
Checks if a string is a valid date in YYYY-MM-DD or YYYY-MM-DDTHH:MM:SS format.
|
26
|
+
:param date_string: The date string to check.
|
27
|
+
:return: True if the date string is valid, False otherwise.
|
28
|
+
"""
|
29
|
+
formats = ['%Y-%m-%d', '%Y-%m-%dT%H:%M:%S']
|
30
|
+
for fmt in formats:
|
31
|
+
try:
|
32
|
+
datetime.datetime.strptime(date_string, fmt)
|
33
|
+
return True
|
34
|
+
except ValueError:
|
35
|
+
continue
|
36
|
+
return False
|
@@ -0,0 +1,31 @@
|
|
1
|
+
from carbonarc.data import DataAPIClient
|
2
|
+
from carbonarc.explorer import ExplorerAPIClient
|
3
|
+
from carbonarc.hub import HubAPIClient
|
4
|
+
from carbonarc.platform import PlatformAPIClient
|
5
|
+
from carbonarc.ontology import OntologyAPIClient
|
6
|
+
|
7
|
+
|
8
|
+
class CarbonArcClient:
|
9
|
+
"""
|
10
|
+
A client for interacting with the Carbon Arc API.
|
11
|
+
"""
|
12
|
+
|
13
|
+
def __init__(
|
14
|
+
self,
|
15
|
+
token: str,
|
16
|
+
host: str = "https://platform.carbonarc.co",
|
17
|
+
version: str = "v2",
|
18
|
+
):
|
19
|
+
"""
|
20
|
+
Initialize CarbonArcClient with an authentication token and user agent.
|
21
|
+
|
22
|
+
Args:
|
23
|
+
token (str): The authentication token to be used for requests.
|
24
|
+
host (str): The base URL of the Carbon Arc API.
|
25
|
+
version (str): The API version to use.
|
26
|
+
"""
|
27
|
+
self.data = DataAPIClient(token=token, host=host, version=version)
|
28
|
+
self.explorer = ExplorerAPIClient(token=token, host=host, version=version)
|
29
|
+
self.hub = HubAPIClient(token=token, host=host, version=version)
|
30
|
+
self.platform = PlatformAPIClient(token=token, host=host, version=version)
|
31
|
+
self.ontology = OntologyAPIClient(token=token, host=host, version=version)
|