act365 1.0.2__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.
act365-1.0.2/PKG-INFO ADDED
@@ -0,0 +1,15 @@
1
+ Metadata-Version: 2.1
2
+ Name: act365
3
+ Version: 1.0.2
4
+ Summary: Python Client for [ACT365](https://www.act365.eu/)
5
+ Author: Simon McCartney
6
+ Author-email: simon@mccartney.ie
7
+ Requires-Python: >=3.12,<4.0
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Python :: 3.12
10
+ Classifier: Programming Language :: Python :: 3.13
11
+ Requires-Dist: httpx (>=0.27.0,<0.28.0)
12
+ Requires-Dist: json5 (==0.9.25)
13
+ Description-Content-Type: text/markdown
14
+
15
+
act365-1.0.2/README.md ADDED
File without changes
@@ -0,0 +1,26 @@
1
+ [tool.poetry]
2
+ name = "act365"
3
+ version = "1.0.2"
4
+ description = "Python Client for [ACT365](https://www.act365.eu/)"
5
+ authors = ["Simon McCartney <simon@mccartney.ie>"]
6
+ readme = "README.md"
7
+ packages = [{include = "act365", from = "src"}]
8
+
9
+ [tool.poetry.scripts]
10
+ act365cli = "act365.client:run"
11
+
12
+ [tool.poetry.dependencies]
13
+ python = "^3.12"
14
+ httpx = "^0.27.0"
15
+ json5 = "0.9.25"
16
+
17
+ [tool.poetry.group.dev.dependencies]
18
+ pytest = "^8.3.1"
19
+ pytest-httpx = "^0.30.0"
20
+ httpx = {extras = ["cli"], version = "^0.27.0"}
21
+ flake8 = "^7.1.1"
22
+ isort = "^5.13.2"
23
+
24
+ [build-system]
25
+ requires = ["poetry-core"]
26
+ build-backend = "poetry.core.masonry.api"
File without changes
@@ -0,0 +1,74 @@
1
+ import logging
2
+ from time import sleep
3
+
4
+ import httpx
5
+
6
+ logging.basicConfig(filename="act365.log", filemode="w", level=logging.INFO)
7
+ LOG = logging.getLogger("act635")
8
+
9
+
10
+ class Act365Auth(httpx.Auth):
11
+ requires_response_body = True
12
+
13
+ def __init__(
14
+ self,
15
+ username,
16
+ password,
17
+ grant_type="password",
18
+ url="https://userapi.act365.eu/api",
19
+ ):
20
+
21
+ if username is None or password is None:
22
+ raise Exception
23
+ self.username = username
24
+ self.password = password
25
+ self.grant_type = grant_type
26
+ self.url = url
27
+
28
+ self.access_token = None
29
+
30
+ def get_token(self):
31
+ data = {
32
+ "grant_type": self.grant_type,
33
+ "username": self.username,
34
+ "password": self.password,
35
+ }
36
+
37
+ while self.access_token is None:
38
+ # Set Content-Type: application/x-www-form-urlencoded via headers
39
+ # as apiary complains about case mismatch
40
+ response = httpx.post(
41
+ self.url + "/account/login",
42
+ data=data,
43
+ timeout=90,
44
+ headers={"Content-Type": "application/x-www-form-urlencoded"},
45
+ )
46
+ LOG.debug(f"Response: {response.status_code} {response.text}")
47
+ LOG.info(f"Response Headers: {response.headers}")
48
+
49
+ if response.status_code == httpx.codes.OK:
50
+ # "token_type":"bearer","expires_in":86399,
51
+ # ".issued":"Wed, 24 Jul 2024 11:37:56 GMT",
52
+ # ".expires":"Thu, 25 Jul 2024 11:37:56 GMT"}'
53
+ self.access_token = response.json().get("access_token", None)
54
+ self.token_type = response.json().get("token_type", None)
55
+ self.expires_in = response.json().get("expires_in", None)
56
+ self.issued = response.json().get(".issued", None)
57
+ self.expires = response.json().get(".expires", None)
58
+ elif response.status_code == httpx.codes.TOO_MANY_REQUESTS:
59
+ sleep(65)
60
+ else:
61
+ raise Exception
62
+
63
+ def auth_flow(self, request):
64
+ if self.access_token is None:
65
+ self.get_token()
66
+
67
+ request.headers["Authorization"] = "Bearer " + self.access_token
68
+ response = yield request
69
+
70
+ if response.status_code == 401:
71
+ self.get_token()
72
+ request.headers["Authorization"] = "Bearer " + self.access_token
73
+
74
+ yield request