python-terminusgps 1.11.0__tar.gz → 1.13.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 (35) hide show
  1. {python_terminusgps-1.11.0 → python_terminusgps-1.13.0}/PKG-INFO +1 -1
  2. {python_terminusgps-1.11.0 → python_terminusgps-1.13.0}/pyproject.toml +1 -1
  3. {python_terminusgps-1.11.0 → python_terminusgps-1.13.0}/terminusgps/wialon/errors.py +0 -24
  4. {python_terminusgps-1.11.0 → python_terminusgps-1.13.0}/terminusgps/wialon/items/resource.py +54 -2
  5. {python_terminusgps-1.11.0 → python_terminusgps-1.13.0}/terminusgps/wialon/session.py +68 -55
  6. {python_terminusgps-1.11.0 → python_terminusgps-1.13.0}/uv.lock +1 -1
  7. {python_terminusgps-1.11.0 → python_terminusgps-1.13.0}/.gitignore +0 -0
  8. {python_terminusgps-1.11.0 → python_terminusgps-1.13.0}/COPYING +0 -0
  9. {python_terminusgps-1.11.0 → python_terminusgps-1.13.0}/README.md +0 -0
  10. {python_terminusgps-1.11.0 → python_terminusgps-1.13.0}/terminusgps/__init__.py +0 -0
  11. {python_terminusgps-1.11.0 → python_terminusgps-1.13.0}/terminusgps/authorizenet/__init__.py +0 -0
  12. {python_terminusgps-1.11.0 → python_terminusgps-1.13.0}/terminusgps/authorizenet/auth.py +0 -0
  13. {python_terminusgps-1.11.0 → python_terminusgps-1.13.0}/terminusgps/authorizenet/profiles/__init__.py +0 -0
  14. {python_terminusgps-1.11.0 → python_terminusgps-1.13.0}/terminusgps/authorizenet/profiles/addresses.py +0 -0
  15. {python_terminusgps-1.11.0 → python_terminusgps-1.13.0}/terminusgps/authorizenet/profiles/base.py +0 -0
  16. {python_terminusgps-1.11.0 → python_terminusgps-1.13.0}/terminusgps/authorizenet/profiles/customers.py +0 -0
  17. {python_terminusgps-1.11.0 → python_terminusgps-1.13.0}/terminusgps/authorizenet/profiles/payments.py +0 -0
  18. {python_terminusgps-1.11.0 → python_terminusgps-1.13.0}/terminusgps/aws/__init__.py +0 -0
  19. {python_terminusgps-1.11.0 → python_terminusgps-1.13.0}/terminusgps/aws/secrets.py +0 -0
  20. {python_terminusgps-1.11.0 → python_terminusgps-1.13.0}/terminusgps/aws/ses.py +0 -0
  21. {python_terminusgps-1.11.0 → python_terminusgps-1.13.0}/terminusgps/settings.py +0 -0
  22. {python_terminusgps-1.11.0 → python_terminusgps-1.13.0}/terminusgps/twilio/__init__.py +0 -0
  23. {python_terminusgps-1.11.0 → python_terminusgps-1.13.0}/terminusgps/twilio/caller.py +0 -0
  24. {python_terminusgps-1.11.0 → python_terminusgps-1.13.0}/terminusgps/wialon/__init__.py +0 -0
  25. {python_terminusgps-1.11.0 → python_terminusgps-1.13.0}/terminusgps/wialon/constants.py +0 -0
  26. {python_terminusgps-1.11.0 → python_terminusgps-1.13.0}/terminusgps/wialon/flags.py +0 -0
  27. {python_terminusgps-1.11.0 → python_terminusgps-1.13.0}/terminusgps/wialon/items/__init__.py +0 -0
  28. {python_terminusgps-1.11.0 → python_terminusgps-1.13.0}/terminusgps/wialon/items/base.py +0 -0
  29. {python_terminusgps-1.11.0 → python_terminusgps-1.13.0}/terminusgps/wialon/items/notification.py +0 -0
  30. {python_terminusgps-1.11.0 → python_terminusgps-1.13.0}/terminusgps/wialon/items/retranslator.py +0 -0
  31. {python_terminusgps-1.11.0 → python_terminusgps-1.13.0}/terminusgps/wialon/items/route.py +0 -0
  32. {python_terminusgps-1.11.0 → python_terminusgps-1.13.0}/terminusgps/wialon/items/unit.py +0 -0
  33. {python_terminusgps-1.11.0 → python_terminusgps-1.13.0}/terminusgps/wialon/items/unit_group.py +0 -0
  34. {python_terminusgps-1.11.0 → python_terminusgps-1.13.0}/terminusgps/wialon/items/user.py +0 -0
  35. {python_terminusgps-1.11.0 → python_terminusgps-1.13.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.11.0
3
+ Version: 1.13.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.11.0"
3
+ version = "1.13.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" } ]
@@ -19,18 +19,6 @@ class WialonLoginError(WialonBaseError):
19
19
  return super().__init__(message, wialon_err)
20
20
 
21
21
 
22
- class WialonSessionDuplicationError(WialonBaseError):
23
- def __init__(
24
- self,
25
- session_id: str | None,
26
- wialon_err: WialonError | AssertionError | None = None,
27
- ) -> None:
28
- message = f"Failed to duplicate the Wialon session: '#{session_id}'\n"
29
- if wialon_err:
30
- message += str(wialon_err)
31
- return super().__init__(message, wialon_err)
32
-
33
-
34
22
  class WialonLogoutError(WialonBaseError):
35
23
  def __init__(
36
24
  self, session_id: str, wialon_err: WialonError | AssertionError | None = None
@@ -39,15 +27,3 @@ class WialonLogoutError(WialonBaseError):
39
27
  if wialon_err:
40
28
  message += str(wialon_err)
41
29
  return super().__init__(message, wialon_err)
42
-
43
-
44
- class WialonSessionInvalidError(WialonBaseError):
45
- def __init__(
46
- self,
47
- session_id: str | None,
48
- wialon_err: WialonError | AssertionError | None = None,
49
- ) -> None:
50
- message = f"The session was invalid/expired: '{session_id}'\n"
51
- if wialon_err:
52
- message += str(wialon_err)
53
- return super().__init__(message, wialon_err)
@@ -1,5 +1,6 @@
1
1
  from terminusgps.wialon import flags
2
2
  from terminusgps.wialon.items.base import WialonBase
3
+ from terminusgps.wialon.session import WialonSession
3
4
 
4
5
 
5
6
  class WialonResource(WialonBase):
@@ -22,24 +23,59 @@ class WialonResource(WialonBase):
22
23
 
23
24
  @property
24
25
  def is_account(self) -> bool:
26
+ """
27
+ Whether or not the resource is an account.
28
+
29
+ :type: :py:obj:`bool`
30
+
31
+ """
25
32
  response = self.session.wialon_api.core_search_item(
26
33
  **{"id": self.id, "flags": flags.DATAFLAG_RESOURCE_BILLING_PROPERTIES}
27
34
  )
28
35
  return response.get("item", {}).get("bact") == self.id
29
36
 
30
37
  def is_migrated(self, unit: WialonBase) -> bool:
38
+ """
39
+ Checks if a unit is migrated into the account.
40
+
41
+ :param unit: A Wialon object.
42
+ :type unit: :py:obj:`~terminusgps.wialon.items.base.WialonBase`
43
+ :returns: Whether or not the unit is migrated into the account.
44
+ :rtype: :py:obj:`bool`
45
+
46
+ """
47
+ assert self.is_account, "The resource is not an acccount"
31
48
  response = self.session.wialon_api.account_list_change_accounts(
32
49
  **{"units": [unit.id]}
33
50
  )
34
- results = [unit.id == int(item.get("id")) for item in response]
51
+ results = [self.id == int(item.get("id")) for item in response]
35
52
  return any(results)
36
53
 
54
+ def set_dealer_rights(self, enabled: bool = False) -> None:
55
+ """
56
+ Sets dealer rights on the account.
57
+
58
+ :param enabled: :py:obj:`True` to enable dealer rights, :py:obj:`False` to disable dealer rights. Default is :py:obj:`False`.
59
+ :type enabled: :py:obj:`bool`
60
+ :raises AssertionError: If the resource is not an account.
61
+ :raises WialonError: If something goes wrong with Wialon.
62
+ :returns: Nothing.
63
+ :rtype: :py:obj:`None`
64
+
65
+ """
66
+ assert self.is_account, "The resource is not an account"
67
+ self.session.wialon_api.account_update_dealer_rights(
68
+ **{"itemId": self.id, "enable": str(enabled).lower()}
69
+ )
70
+
37
71
  def migrate_unit(self, unit: WialonBase) -> None:
38
72
  """
39
73
  Migrates a :py:obj:`WialonUnit` into the account.
40
74
 
41
75
  :param unit: A Wialon object.
42
76
  :type unit: :py:obj:`~terminusgps.wialon.items.base.WialonBase`
77
+ :raises AssertionError: If the resource is not an account.
78
+ :raises WialonError: If something goes wrong with Wialon.
43
79
  :returns: Nothing.
44
80
  :rtype: :py:obj:`None`
45
81
 
@@ -55,6 +91,8 @@ class WialonResource(WialonBase):
55
91
 
56
92
  :param new_plan: The name of a billing plan.
57
93
  :type new_plan: :py:obj:`str`
94
+ :raises AssertionError: If the resource is not an account.
95
+ :raises WialonError: If something goes wrong with Wialon.
58
96
  :returns: Nothing.
59
97
  :rtype: :py:obj:`None`
60
98
 
@@ -70,11 +108,13 @@ class WialonResource(WialonBase):
70
108
 
71
109
  :param billing_plan: The name of a billing plan.
72
110
  :type billing_plan: :py:obj:`str`
111
+ :raises AssertionError: If the resource is already account.
112
+ :raises WialonError: If something goes wrong with Wialon.
73
113
  :returns: Nothing.
74
114
  :rtype: :py:obj:`None`
75
115
 
76
116
  """
77
- assert self.is_account, "The resource is already an account"
117
+ assert not self.is_account, "The resource is already an account"
78
118
  self.session.wialon_api.account_create_account(
79
119
  **{"itemId": self.id, "plan": billing_plan}
80
120
  )
@@ -83,6 +123,8 @@ class WialonResource(WialonBase):
83
123
  """
84
124
  Deletes the account if it exists, as well as any micro-objects and macro-objects it contains.
85
125
 
126
+ :raises AssertionError: If the resource is not an account.
127
+ :raises WialonError: If something goes wrong with Wialon.
86
128
  :returns: Nothing.
87
129
  :rtype: :py:obj:`None`
88
130
 
@@ -94,6 +136,8 @@ class WialonResource(WialonBase):
94
136
  """
95
137
  Enables the Wialon account.
96
138
 
139
+ :raises AssertionError: If the resource is not an account.
140
+ :raises WialonError: If something goes wrong with Wialon.
97
141
  :returns: Nothing.
98
142
  :rtype: :py:obj:`None`
99
143
 
@@ -107,6 +151,8 @@ class WialonResource(WialonBase):
107
151
  """
108
152
  Disables the Wialon account.
109
153
 
154
+ :raises AssertionError: If the resource is not an account.
155
+ :raises WialonError: If something goes wrong with Wialon.
110
156
  :returns: Nothing.
111
157
  :rtype: :py:obj:`None`
112
158
 
@@ -122,6 +168,8 @@ class WialonResource(WialonBase):
122
168
 
123
169
  :param days: Number of days to set the counter to. Default is ``0``.
124
170
  :type days: :py:obj:`int`
171
+ :raises AssertionError: If the resource is not an account.
172
+ :raises WialonError: If something goes wrong with Wialon.
125
173
  :returns: Nothing.
126
174
  :rtype: :py:obj:`None`
127
175
 
@@ -137,6 +185,8 @@ class WialonResource(WialonBase):
137
185
 
138
186
  :param days: Number of days to add to the account. Default is ``30``.
139
187
  :type days: :py:obj:`int`
188
+ :raises AssertionError: If the resource is not an account.
189
+ :raises WialonError: If something goes wrong with Wialon.
140
190
  :returns: Nothing.
141
191
  :rtype: :py:obj:`None`
142
192
 
@@ -166,6 +216,8 @@ class WialonResource(WialonBase):
166
216
  :type block_balance_val: :py:obj:`float`
167
217
  :param deny_balance_val: Minimum amount on the account's balance before denying the account.
168
218
  :type deny_balance_val: :py:obj:`float`
219
+ :raises AssertionError: If the resource is not an account.
220
+ :raises WialonError: If something goes wrong with Wialon.
169
221
  :returns: Nothing.
170
222
  :rtype: :py:obj:`None`
171
223
 
@@ -1,14 +1,40 @@
1
1
  import threading
2
+ import logging
3
+ from typing import Any
2
4
 
3
- from wialon.api import Wialon, WialonError
5
+ from wialon.api import WialonError
6
+ from wialon.api import Wialon as WialonAPI
4
7
  from django.conf import settings
5
8
 
6
- from .errors import (
7
- WialonLogoutError,
8
- WialonLoginError,
9
- WialonSessionDuplicationError,
10
- WialonSessionInvalidError,
11
- )
9
+ from .errors import WialonLogoutError, WialonLoginError
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+
14
+ class Wialon(WialonAPI):
15
+ def __init__(self, log_level: int = logging.INFO, *args, **kwargs) -> None:
16
+ super().__init__(*args, **kwargs)
17
+ self.logger = self.create_logger(log_level)
18
+
19
+ def call(self, action_name: str, *argc, **kwargs) -> Any:
20
+ self.logger.debug(f"Executing '{action_name}'...")
21
+ try:
22
+ return super().call(action_name, *argc, **kwargs)
23
+ except WialonError as e:
24
+ self.logger.critical(e)
25
+ raise
26
+
27
+ def create_logger(self, log_level: int) -> logging.Logger:
28
+ logger = logging.getLogger(self.__class__.__name__)
29
+ handler = logging.StreamHandler()
30
+ formatter = logging.Formatter(
31
+ "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
32
+ )
33
+
34
+ logger.setLevel(log_level)
35
+ handler.setFormatter(formatter)
36
+ logger.addHandler(handler)
37
+ return logger
12
38
 
13
39
 
14
40
  class WialonSession:
@@ -20,6 +46,7 @@ class WialonSession:
20
46
  scheme: str = "https",
21
47
  host: str = "hst-api.wialon.com",
22
48
  port: int = 443,
49
+ log_level: int = logging.INFO,
23
50
  ) -> None:
24
51
  """
25
52
  Starts or continues a Wialon API session.
@@ -34,7 +61,9 @@ class WialonSession:
34
61
 
35
62
  """
36
63
 
37
- self.wialon_api = Wialon(scheme=scheme, host=host, port=port, sid=sid)
64
+ self.wialon_api = Wialon(
65
+ scheme=scheme, host=host, port=port, sid=sid, log_level=log_level
66
+ )
38
67
  self.token = token
39
68
  self.login_id = uid
40
69
  self._username = None
@@ -42,6 +71,19 @@ class WialonSession:
42
71
  self._hw_gp_ip = None
43
72
  self._wsdk_version = None
44
73
  self._uid = None
74
+ self.logger = self.create_logger(log_level)
75
+
76
+ def create_logger(self, log_level: int) -> logging.Logger:
77
+ logger = logging.getLogger(self.__class__.__name__)
78
+ handler = logging.StreamHandler()
79
+ formatter = logging.Formatter(
80
+ "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
81
+ )
82
+
83
+ logger.setLevel(log_level)
84
+ handler.setFormatter(formatter)
85
+ logger.addHandler(handler)
86
+ return logger
45
87
 
46
88
  def __enter__(self) -> "WialonSession":
47
89
  assert self.token, "Wialon API token was not set"
@@ -51,24 +93,6 @@ class WialonSession:
51
93
  def __exit__(self, *args, **kwargs) -> None:
52
94
  self.logout()
53
95
 
54
- @property
55
- def active(self) -> bool:
56
- """
57
- Whether or not the Wialon session is currently active.
58
-
59
- :type: :py:obj:`bool`
60
- :value: :py:obj:`False`
61
- """
62
- is_active = False
63
-
64
- try:
65
- response = self.wialon_api.core_duplicate(**{"restore": 1})
66
- is_active = bool(response)
67
- except WialonError as e:
68
- raise WialonSessionInvalidError(self.id, e)
69
- finally:
70
- return is_active
71
-
72
96
  @property
73
97
  def gis_geocode(self) -> str | None:
74
98
  """
@@ -219,30 +243,6 @@ class WialonSession:
219
243
  def token(self, value: str | None = None) -> None:
220
244
  self._token = value if value else settings.WIALON_TOKEN
221
245
 
222
- def duplicate(self, username: str | None = None, continued: bool = False) -> str:
223
- """
224
- Duplicates the active Wialon API session.
225
-
226
- :param username: A Wialon user to operate as in the session.
227
- :type username: :py:obj:`str` | :py:obj:`None`
228
- :param continue_session: Whether or not the original session id should be valid after duplication.
229
- :type continue_session: :py:obj:`bool`
230
- :raises WialonSessionDuplicationError: If the Wialon session was not duplicated.
231
- :raises AssertionError: If the session was already active.
232
- :returns: The new session id.
233
- :rtype: :py:obj:`str`
234
-
235
- """
236
- try:
237
- assert self.active, "Cannot duplicate an inactive session."
238
- response = self.wialon_api.core_duplicate(
239
- **{"operateAs": username, "continueCurrentSession": continued}
240
- )
241
- self._set_login_response(response)
242
- return response.get("eid")
243
- except (WialonError, AssertionError) as e:
244
- raise WialonSessionDuplicationError(self.id, e)
245
-
246
246
  def login(self, token: str, flags: int = sum([0x1, 0x2, 0x20])) -> str:
247
247
  """
248
248
  Logs into the Wialon API and starts a new session.
@@ -252,17 +252,17 @@ class WialonSession:
252
252
  :param flags: A login response flag integer.
253
253
  :type flags: :py:obj:`int`
254
254
  :raises WialonLoginError: If the login fails.
255
- :raises AssertionError: If the session was already active.
256
255
  :returns: The new session id.
257
256
  :rtype: :py:obj:`str`
258
257
 
259
258
  """
259
+ self.logger.debug("Logging into Wialon API session...")
260
260
  try:
261
- assert not self.active, "Cannot login to an active session."
262
261
  response = self.wialon_api.token_login(**{"token": token, "fl": flags})
263
262
  self._set_login_response(response)
264
263
  return response.get("eid")
265
264
  except (WialonError, AssertionError) as e:
265
+ self.logger.critical(e)
266
266
  raise WialonLoginError(token, e)
267
267
 
268
268
  def logout(self) -> None:
@@ -274,8 +274,10 @@ class WialonSession:
274
274
  :rtype: :py:obj:`None`
275
275
 
276
276
  """
277
+ self.logger.debug(f"Logging out of Wialon API session '{self.id}'...")
277
278
  response: dict = self.wialon_api.core_logout({})
278
279
  if response.get("error") != 0:
280
+ self.logger.critical(response.get("error"))
279
281
  raise WialonLogoutError(str(self.id))
280
282
 
281
283
  def _set_login_response(self, login_response: dict) -> None:
@@ -317,8 +319,19 @@ class WialonSessionManager:
317
319
 
318
320
  def get_session(self, sid: str | None = None) -> WialonSession:
319
321
  with self._lock:
320
- if not self._session or not self._session.active:
322
+ if not self._session:
321
323
  self._session = WialonSession(sid=sid)
322
- if not self._session.active:
323
- self._session.login(settings.WIALON_TOKEN)
324
324
  return self._session
325
+
326
+
327
+ def main() -> None:
328
+ from terminusgps.wialon.items import WialonUnit
329
+
330
+ with WialonSession(log_level=logging.DEBUG) as session:
331
+ unit = WialonUnit(id="28082258", session=session)
332
+ unit.add_cfield(("to_number", "+17133049421"))
333
+ return
334
+
335
+
336
+ if __name__ == "__main__":
337
+ main()
@@ -504,7 +504,7 @@ wheels = [
504
504
 
505
505
  [[package]]
506
506
  name = "python-terminusgps"
507
- version = "1.10.3"
507
+ version = "1.13.0"
508
508
  source = { editable = "." }
509
509
  dependencies = [
510
510
  { name = "argparse" },