python-terminusgps 1.14.0__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.

Files changed (36) hide show
  1. {python_terminusgps-1.14.0 → python_terminusgps-1.15.0}/PKG-INFO +1 -1
  2. {python_terminusgps-1.14.0 → python_terminusgps-1.15.0}/pyproject.toml +1 -1
  3. {python_terminusgps-1.14.0 → python_terminusgps-1.15.0}/terminusgps/wialon/constants.py +1 -1
  4. {python_terminusgps-1.14.0 → python_terminusgps-1.15.0}/terminusgps/wialon/items/unit.py +6 -0
  5. {python_terminusgps-1.14.0 → python_terminusgps-1.15.0}/terminusgps/wialon/items/user.py +28 -0
  6. {python_terminusgps-1.14.0 → python_terminusgps-1.15.0}/terminusgps/wialon/session.py +46 -13
  7. {python_terminusgps-1.14.0 → python_terminusgps-1.15.0}/uv.lock +1 -1
  8. {python_terminusgps-1.14.0 → python_terminusgps-1.15.0}/.gitignore +0 -0
  9. {python_terminusgps-1.14.0 → python_terminusgps-1.15.0}/COPYING +0 -0
  10. {python_terminusgps-1.14.0 → python_terminusgps-1.15.0}/README.md +0 -0
  11. {python_terminusgps-1.14.0 → python_terminusgps-1.15.0}/terminusgps/__init__.py +0 -0
  12. {python_terminusgps-1.14.0 → python_terminusgps-1.15.0}/terminusgps/authorizenet/__init__.py +0 -0
  13. {python_terminusgps-1.14.0 → python_terminusgps-1.15.0}/terminusgps/authorizenet/auth.py +0 -0
  14. {python_terminusgps-1.14.0 → python_terminusgps-1.15.0}/terminusgps/authorizenet/profiles/__init__.py +0 -0
  15. {python_terminusgps-1.14.0 → python_terminusgps-1.15.0}/terminusgps/authorizenet/profiles/addresses.py +0 -0
  16. {python_terminusgps-1.14.0 → python_terminusgps-1.15.0}/terminusgps/authorizenet/profiles/base.py +0 -0
  17. {python_terminusgps-1.14.0 → python_terminusgps-1.15.0}/terminusgps/authorizenet/profiles/customers.py +0 -0
  18. {python_terminusgps-1.14.0 → python_terminusgps-1.15.0}/terminusgps/authorizenet/profiles/payments.py +0 -0
  19. {python_terminusgps-1.14.0 → python_terminusgps-1.15.0}/terminusgps/authorizenet/subscriptions.py +0 -0
  20. {python_terminusgps-1.14.0 → python_terminusgps-1.15.0}/terminusgps/aws/__init__.py +0 -0
  21. {python_terminusgps-1.14.0 → python_terminusgps-1.15.0}/terminusgps/aws/secrets.py +0 -0
  22. {python_terminusgps-1.14.0 → python_terminusgps-1.15.0}/terminusgps/aws/ses.py +0 -0
  23. {python_terminusgps-1.14.0 → python_terminusgps-1.15.0}/terminusgps/settings.py +0 -0
  24. {python_terminusgps-1.14.0 → python_terminusgps-1.15.0}/terminusgps/twilio/__init__.py +0 -0
  25. {python_terminusgps-1.14.0 → python_terminusgps-1.15.0}/terminusgps/twilio/caller.py +0 -0
  26. {python_terminusgps-1.14.0 → python_terminusgps-1.15.0}/terminusgps/wialon/__init__.py +0 -0
  27. {python_terminusgps-1.14.0 → python_terminusgps-1.15.0}/terminusgps/wialon/errors.py +0 -0
  28. {python_terminusgps-1.14.0 → python_terminusgps-1.15.0}/terminusgps/wialon/flags.py +0 -0
  29. {python_terminusgps-1.14.0 → python_terminusgps-1.15.0}/terminusgps/wialon/items/__init__.py +0 -0
  30. {python_terminusgps-1.14.0 → python_terminusgps-1.15.0}/terminusgps/wialon/items/base.py +0 -0
  31. {python_terminusgps-1.14.0 → python_terminusgps-1.15.0}/terminusgps/wialon/items/notification.py +0 -0
  32. {python_terminusgps-1.14.0 → python_terminusgps-1.15.0}/terminusgps/wialon/items/resource.py +0 -0
  33. {python_terminusgps-1.14.0 → python_terminusgps-1.15.0}/terminusgps/wialon/items/retranslator.py +0 -0
  34. {python_terminusgps-1.14.0 → python_terminusgps-1.15.0}/terminusgps/wialon/items/route.py +0 -0
  35. {python_terminusgps-1.14.0 → python_terminusgps-1.15.0}/terminusgps/wialon/items/unit_group.py +0 -0
  36. {python_terminusgps-1.14.0 → 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.14.0
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.14.0"
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" } ]
@@ -74,7 +74,7 @@ ACCESSMASK_UNIT_SUPER: int = sum(
74
74
  ]
75
75
  )
76
76
 
77
- ACCESSMASK_UNIT_FULL: int = sum([flags.ACCESSFLAG_FULL_ACCESS])
77
+ ACCESSMASK_UNIT_FULL: int = flags.ACCESSFLAG_FULL_ACCESS
78
78
 
79
79
  ACCESSMASK_UNIT_MIGRATION: int = sum(
80
80
  [
@@ -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
- logger = logging.getLogger(__name__)
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 num_calls(self) -> int:
22
- return len(self.callstack)
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.debug(f"Executing '{action_name}'...")
26
- self.callstack.append(action_name)
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
- return super().call(action_name, *argc, **kwargs)
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.critical(f"Failed to execute '{action_name}':'{e}'")
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"Called the Wialon API {self.wialon_api.num_calls} times during session '{self.id}'."
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.critical(response.get("error"))
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:
@@ -504,7 +504,7 @@ wheels = [
504
504
 
505
505
  [[package]]
506
506
  name = "python-terminusgps"
507
- version = "1.14.0"
507
+ version = "1.15.0"
508
508
  source = { editable = "." }
509
509
  dependencies = [
510
510
  { name = "argparse" },