python-terminusgps 1.13.6__tar.gz → 1.15.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.
Potentially problematic release.
This version of python-terminusgps might be problematic. Click here for more details.
- {python_terminusgps-1.13.6 → python_terminusgps-1.15.0}/PKG-INFO +1 -1
- {python_terminusgps-1.13.6 → python_terminusgps-1.15.0}/pyproject.toml +1 -1
- python_terminusgps-1.15.0/terminusgps/authorizenet/subscriptions.py +61 -0
- {python_terminusgps-1.13.6 → python_terminusgps-1.15.0}/terminusgps/wialon/constants.py +1 -1
- {python_terminusgps-1.13.6 → python_terminusgps-1.15.0}/terminusgps/wialon/items/unit.py +6 -0
- {python_terminusgps-1.13.6 → python_terminusgps-1.15.0}/terminusgps/wialon/items/user.py +28 -0
- {python_terminusgps-1.13.6 → python_terminusgps-1.15.0}/terminusgps/wialon/session.py +46 -13
- {python_terminusgps-1.13.6 → python_terminusgps-1.15.0}/uv.lock +1 -1
- {python_terminusgps-1.13.6 → python_terminusgps-1.15.0}/.gitignore +0 -0
- {python_terminusgps-1.13.6 → python_terminusgps-1.15.0}/COPYING +0 -0
- {python_terminusgps-1.13.6 → python_terminusgps-1.15.0}/README.md +0 -0
- {python_terminusgps-1.13.6 → python_terminusgps-1.15.0}/terminusgps/__init__.py +0 -0
- {python_terminusgps-1.13.6 → python_terminusgps-1.15.0}/terminusgps/authorizenet/__init__.py +0 -0
- {python_terminusgps-1.13.6 → python_terminusgps-1.15.0}/terminusgps/authorizenet/auth.py +0 -0
- {python_terminusgps-1.13.6 → python_terminusgps-1.15.0}/terminusgps/authorizenet/profiles/__init__.py +0 -0
- {python_terminusgps-1.13.6 → python_terminusgps-1.15.0}/terminusgps/authorizenet/profiles/addresses.py +0 -0
- {python_terminusgps-1.13.6 → python_terminusgps-1.15.0}/terminusgps/authorizenet/profiles/base.py +0 -0
- {python_terminusgps-1.13.6 → python_terminusgps-1.15.0}/terminusgps/authorizenet/profiles/customers.py +0 -0
- {python_terminusgps-1.13.6 → python_terminusgps-1.15.0}/terminusgps/authorizenet/profiles/payments.py +0 -0
- {python_terminusgps-1.13.6 → python_terminusgps-1.15.0}/terminusgps/aws/__init__.py +0 -0
- {python_terminusgps-1.13.6 → python_terminusgps-1.15.0}/terminusgps/aws/secrets.py +0 -0
- {python_terminusgps-1.13.6 → python_terminusgps-1.15.0}/terminusgps/aws/ses.py +0 -0
- {python_terminusgps-1.13.6 → python_terminusgps-1.15.0}/terminusgps/settings.py +0 -0
- {python_terminusgps-1.13.6 → python_terminusgps-1.15.0}/terminusgps/twilio/__init__.py +0 -0
- {python_terminusgps-1.13.6 → python_terminusgps-1.15.0}/terminusgps/twilio/caller.py +0 -0
- {python_terminusgps-1.13.6 → python_terminusgps-1.15.0}/terminusgps/wialon/__init__.py +0 -0
- {python_terminusgps-1.13.6 → python_terminusgps-1.15.0}/terminusgps/wialon/errors.py +0 -0
- {python_terminusgps-1.13.6 → python_terminusgps-1.15.0}/terminusgps/wialon/flags.py +0 -0
- {python_terminusgps-1.13.6 → python_terminusgps-1.15.0}/terminusgps/wialon/items/__init__.py +0 -0
- {python_terminusgps-1.13.6 → python_terminusgps-1.15.0}/terminusgps/wialon/items/base.py +0 -0
- {python_terminusgps-1.13.6 → python_terminusgps-1.15.0}/terminusgps/wialon/items/notification.py +0 -0
- {python_terminusgps-1.13.6 → python_terminusgps-1.15.0}/terminusgps/wialon/items/resource.py +0 -0
- {python_terminusgps-1.13.6 → python_terminusgps-1.15.0}/terminusgps/wialon/items/retranslator.py +0 -0
- {python_terminusgps-1.13.6 → python_terminusgps-1.15.0}/terminusgps/wialon/items/route.py +0 -0
- {python_terminusgps-1.13.6 → python_terminusgps-1.15.0}/terminusgps/wialon/items/unit_group.py +0 -0
- {python_terminusgps-1.13.6 → python_terminusgps-1.15.0}/terminusgps/wialon/utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-terminusgps
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.15.0
|
|
4
4
|
Summary: Provides abstractions/utilities for working with Wialon API, Authorize.NET API, AWS API, and more.
|
|
5
5
|
Project-URL: Documentation, https://app.terminusgps.com/docs/apps/python-terminusgps/index.html
|
|
6
6
|
Project-URL: Repository, https://github.com/terminusgps/python-terminusgps
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "python-terminusgps"
|
|
3
|
-
version = "1.
|
|
3
|
+
version = "1.15.0"
|
|
4
4
|
description = "Provides abstractions/utilities for working with Wialon API, Authorize.NET API, AWS API, and more."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
authors = [ {name = "Blake Nall", email = "blake@terminusgps.com" } ]
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
from authorizenet import apicontractsv1, apicontrollers, apicontrollersbase
|
|
2
|
+
from terminusgps.authorizenet.auth import get_merchant_auth, get_environment
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class Subscription:
|
|
6
|
+
def __init__(self, name: str, id: str | int | None = None, **kwargs) -> None:
|
|
7
|
+
if id is not None and isinstance(id, str) and not id.isdigit():
|
|
8
|
+
raise ValueError(f"'id' must be a digit, got '{id}'.")
|
|
9
|
+
self.name = name
|
|
10
|
+
self.id = str(id if id else self.create(**kwargs))
|
|
11
|
+
|
|
12
|
+
@property
|
|
13
|
+
def merchantAuthentication(self) -> apicontractsv1.merchantAuthenticationType:
|
|
14
|
+
return get_merchant_auth()
|
|
15
|
+
|
|
16
|
+
@property
|
|
17
|
+
def environment(self) -> str:
|
|
18
|
+
return get_environment()
|
|
19
|
+
|
|
20
|
+
def execute_controller(
|
|
21
|
+
self, controller: apicontrollersbase.APIOperationBase
|
|
22
|
+
) -> dict:
|
|
23
|
+
"""
|
|
24
|
+
Executes an Authorize.NET controller and returns its response.
|
|
25
|
+
|
|
26
|
+
:param controller: An Authorize.NET API controller.
|
|
27
|
+
:raises ValueError: If the API call fails.
|
|
28
|
+
:returns: The Authorize.NET API response.
|
|
29
|
+
:rtype: :py:obj:`dict`
|
|
30
|
+
|
|
31
|
+
"""
|
|
32
|
+
controller.setenvironment(self.environment)
|
|
33
|
+
controller.execute()
|
|
34
|
+
response = controller.getresponse()
|
|
35
|
+
|
|
36
|
+
if response.messages.resultCode != "Ok":
|
|
37
|
+
raise ValueError(response.messages.message[0]["text"].text)
|
|
38
|
+
return response
|
|
39
|
+
|
|
40
|
+
def create(
|
|
41
|
+
self,
|
|
42
|
+
schedule: apicontractsv1.paymentScheduleType,
|
|
43
|
+
amount: str,
|
|
44
|
+
payment: apicontractsv1.paymentType,
|
|
45
|
+
address: apicontractsv1.customerAddressType,
|
|
46
|
+
trial_amount: str = "0.00",
|
|
47
|
+
) -> int:
|
|
48
|
+
request = apicontractsv1.ARBCreateSubscriptionRequest(
|
|
49
|
+
merchantAuthentication=self.merchantAuthentication,
|
|
50
|
+
subscription=apicontractsv1.ARBSubscriptionType(
|
|
51
|
+
name=self.name,
|
|
52
|
+
paymentSchedule=schedule,
|
|
53
|
+
amount=amount,
|
|
54
|
+
trialAmount=trial_amount,
|
|
55
|
+
payment=payment,
|
|
56
|
+
bill_to=address,
|
|
57
|
+
),
|
|
58
|
+
)
|
|
59
|
+
controller = apicontrollers.ARBCreateSubscriptionController(request)
|
|
60
|
+
response = self.execute_controller(controller)
|
|
61
|
+
return int(response.subscriptionId)
|
|
@@ -36,6 +36,12 @@ class WialonUnit(WialonBase):
|
|
|
36
36
|
)
|
|
37
37
|
return response.get("item", {}).get("id")
|
|
38
38
|
|
|
39
|
+
@property
|
|
40
|
+
def available_commands(self) -> dict:
|
|
41
|
+
return self.session.wialon_api.core_get_hw_cmds(
|
|
42
|
+
**{"deviceTypeId": 0, "unitId": self.id}
|
|
43
|
+
)
|
|
44
|
+
|
|
39
45
|
def execute_command(
|
|
40
46
|
self,
|
|
41
47
|
name: str,
|
|
@@ -180,3 +180,31 @@ class WialonUser(WialonBase):
|
|
|
180
180
|
"newPassword": new_password,
|
|
181
181
|
}
|
|
182
182
|
)
|
|
183
|
+
|
|
184
|
+
def verify_auth(self, destination: str, method: str = "email") -> str:
|
|
185
|
+
"""
|
|
186
|
+
Sends an authentication code to ``destination`` via ``method``.
|
|
187
|
+
|
|
188
|
+
``Method`` can be ``"email"`` or ``"sms"``.
|
|
189
|
+
|
|
190
|
+
:param destination: The email or phone number to send an auth code to.
|
|
191
|
+
:type destination: :py:obj:`str`
|
|
192
|
+
:param method: Email or sms. Default is ``"email"``.
|
|
193
|
+
:type method: :py:obj:`str`
|
|
194
|
+
:raises ValueError: If the method isn't ``"email"`` or ``"sms"``.
|
|
195
|
+
:raises WialonError: If something goes wrong with Wialon.
|
|
196
|
+
:returns: An auth code.
|
|
197
|
+
:rtype: :py:obj:`str`
|
|
198
|
+
|
|
199
|
+
"""
|
|
200
|
+
if method != "email" or "sms":
|
|
201
|
+
raise ValueError(f"Invalid method '{method}'.")
|
|
202
|
+
|
|
203
|
+
response = self.session.wialon_api.user_verify_auth(
|
|
204
|
+
**{
|
|
205
|
+
"userId": self.id,
|
|
206
|
+
"type": 1 if method == "email" else 0,
|
|
207
|
+
"destination": destination,
|
|
208
|
+
}
|
|
209
|
+
)
|
|
210
|
+
return response.get("code")
|
|
@@ -1,33 +1,64 @@
|
|
|
1
1
|
import threading
|
|
2
2
|
import logging
|
|
3
|
+
|
|
3
4
|
from typing import Any
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from datetime import datetime
|
|
4
7
|
|
|
5
8
|
from wialon.api import WialonError
|
|
6
9
|
from wialon.api import Wialon as WialonAPI
|
|
7
10
|
from django.conf import settings
|
|
11
|
+
from django.utils import timezone
|
|
8
12
|
|
|
9
13
|
from .errors import WialonLogoutError, WialonLoginError
|
|
10
14
|
|
|
11
|
-
|
|
15
|
+
|
|
16
|
+
@dataclass
|
|
17
|
+
class WialonAPICall:
|
|
18
|
+
action: str
|
|
19
|
+
timestamp: datetime
|
|
20
|
+
args: tuple
|
|
21
|
+
kwargs: dict
|
|
22
|
+
result: Any = None
|
|
23
|
+
error: Exception | None = None
|
|
12
24
|
|
|
13
25
|
|
|
14
26
|
class Wialon(WialonAPI):
|
|
15
27
|
def __init__(self, log_level: int = logging.INFO, *args, **kwargs) -> None:
|
|
16
28
|
super().__init__(*args, **kwargs)
|
|
29
|
+
self.call_history: list[WialonAPICall] = []
|
|
17
30
|
self.logger = self.create_logger(log_level)
|
|
18
|
-
self.callstack = []
|
|
19
31
|
|
|
20
32
|
@property
|
|
21
|
-
def
|
|
22
|
-
return len(self.
|
|
33
|
+
def total_calls(self) -> int:
|
|
34
|
+
return len(self.call_history)
|
|
35
|
+
|
|
36
|
+
@property
|
|
37
|
+
def successful_calls(self) -> list[WialonAPICall | None]:
|
|
38
|
+
return [call for call in self.call_history if not call.error]
|
|
39
|
+
|
|
40
|
+
@property
|
|
41
|
+
def failed_calls(self) -> list[WialonAPICall | None]:
|
|
42
|
+
return [call for call in self.call_history if call.error]
|
|
43
|
+
|
|
44
|
+
@property
|
|
45
|
+
def failure_rate(self) -> float:
|
|
46
|
+
return len(self.failed_calls) / self.total_calls
|
|
23
47
|
|
|
24
48
|
def call(self, action_name: str, *argc, **kwargs) -> Any:
|
|
25
|
-
self.logger.
|
|
26
|
-
self.
|
|
49
|
+
self.logger.info(f"Executing '{action_name}'...")
|
|
50
|
+
self.logger.debug(f"Executing '{action_name}' using: '{kwargs}'")
|
|
51
|
+
call_record = WialonAPICall(
|
|
52
|
+
action=action_name, timestamp=timezone.now(), args=argc, kwargs=kwargs
|
|
53
|
+
)
|
|
54
|
+
|
|
27
55
|
try:
|
|
28
|
-
|
|
56
|
+
result = super().call(action_name, *argc, **kwargs)
|
|
57
|
+
call_record.result = result
|
|
58
|
+
self.call_history.append(call_record)
|
|
59
|
+
return result
|
|
29
60
|
except WialonError as e:
|
|
30
|
-
self.logger.
|
|
61
|
+
self.logger.warning(f"Failed to execute '{action_name}': '{e}'")
|
|
31
62
|
raise
|
|
32
63
|
|
|
33
64
|
def create_logger(self, log_level: int) -> logging.Logger:
|
|
@@ -262,13 +293,13 @@ class WialonSession:
|
|
|
262
293
|
:rtype: :py:obj:`str`
|
|
263
294
|
|
|
264
295
|
"""
|
|
265
|
-
self.logger.debug("Logging into Wialon API session...")
|
|
266
296
|
try:
|
|
267
297
|
response = self.wialon_api.token_login(**{"token": token, "fl": flags})
|
|
268
298
|
self._set_login_response(response)
|
|
299
|
+
self.logger.info(f"Logged into Wialon API session '{response.get('eid')}'")
|
|
269
300
|
return response.get("eid")
|
|
270
301
|
except (WialonError, AssertionError) as e:
|
|
271
|
-
self.logger.critical(e)
|
|
302
|
+
self.logger.critical(f"Failed to login to Wialon API: '{e}'")
|
|
272
303
|
raise WialonLoginError(token, e)
|
|
273
304
|
|
|
274
305
|
def logout(self) -> None:
|
|
@@ -280,13 +311,15 @@ class WialonSession:
|
|
|
280
311
|
:rtype: :py:obj:`None`
|
|
281
312
|
|
|
282
313
|
"""
|
|
283
|
-
self.logger.debug(f"Logging out of Wialon API session '{self.id}'...")
|
|
284
314
|
response: dict = self.wialon_api.core_logout({})
|
|
285
315
|
self.logger.info(
|
|
286
|
-
f"
|
|
316
|
+
f"Logged out of session '{self.id}' after {self.wialon_api.total_calls} Wialon API calls."
|
|
287
317
|
)
|
|
318
|
+
self.logger.debug(f"Failure rate: {self.wialon_api.failure_rate}%")
|
|
288
319
|
if response.get("error") != 0:
|
|
289
|
-
self.logger.
|
|
320
|
+
self.logger.warning(
|
|
321
|
+
f"Failed to logout of session: '{response.get('message')}'"
|
|
322
|
+
)
|
|
290
323
|
raise WialonLogoutError(str(self.id))
|
|
291
324
|
|
|
292
325
|
def _set_login_response(self, login_response: dict) -> None:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_terminusgps-1.13.6 → python_terminusgps-1.15.0}/terminusgps/authorizenet/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_terminusgps-1.13.6 → python_terminusgps-1.15.0}/terminusgps/authorizenet/profiles/base.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_terminusgps-1.13.6 → python_terminusgps-1.15.0}/terminusgps/wialon/items/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_terminusgps-1.13.6 → python_terminusgps-1.15.0}/terminusgps/wialon/items/notification.py
RENAMED
|
File without changes
|
{python_terminusgps-1.13.6 → python_terminusgps-1.15.0}/terminusgps/wialon/items/resource.py
RENAMED
|
File without changes
|
{python_terminusgps-1.13.6 → python_terminusgps-1.15.0}/terminusgps/wialon/items/retranslator.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_terminusgps-1.13.6 → python_terminusgps-1.15.0}/terminusgps/wialon/items/unit_group.py
RENAMED
|
File without changes
|
|
File without changes
|