python-terminusgps 50.0.0__tar.gz → 50.1.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.
Files changed (44) hide show
  1. {python_terminusgps-50.0.0 → python_terminusgps-50.1.0}/PKG-INFO +1 -1
  2. {python_terminusgps-50.0.0 → python_terminusgps-50.1.0}/docs/source/conf.py +1 -1
  3. {python_terminusgps-50.0.0 → python_terminusgps-50.1.0}/pyproject.toml +1 -1
  4. {python_terminusgps-50.0.0 → python_terminusgps-50.1.0}/terminusgps/authorizenet/service.py +2 -3
  5. {python_terminusgps-50.0.0 → python_terminusgps-50.1.0}/terminusgps/wialon/utils.py +97 -68
  6. {python_terminusgps-50.0.0 → python_terminusgps-50.1.0}/uv.lock +16 -4
  7. {python_terminusgps-50.0.0 → python_terminusgps-50.1.0}/.github/workflows/sphinx.yml +0 -0
  8. {python_terminusgps-50.0.0 → python_terminusgps-50.1.0}/.gitignore +0 -0
  9. {python_terminusgps-50.0.0 → python_terminusgps-50.1.0}/.python-version +0 -0
  10. {python_terminusgps-50.0.0 → python_terminusgps-50.1.0}/COPYING +0 -0
  11. {python_terminusgps-50.0.0 → python_terminusgps-50.1.0}/README.md +0 -0
  12. {python_terminusgps-50.0.0 → python_terminusgps-50.1.0}/docs/Makefile +0 -0
  13. {python_terminusgps-50.0.0 → python_terminusgps-50.1.0}/docs/make.bat +0 -0
  14. {python_terminusgps-50.0.0 → python_terminusgps-50.1.0}/docs/requirements.txt +0 -0
  15. {python_terminusgps-50.0.0 → python_terminusgps-50.1.0}/docs/source/authorizenet/api.rst +0 -0
  16. {python_terminusgps-50.0.0 → python_terminusgps-50.1.0}/docs/source/authorizenet/constants.rst +0 -0
  17. {python_terminusgps-50.0.0 → python_terminusgps-50.1.0}/docs/source/authorizenet/index.rst +0 -0
  18. {python_terminusgps-50.0.0 → python_terminusgps-50.1.0}/docs/source/authorizenet/service.rst +0 -0
  19. {python_terminusgps-50.0.0 → python_terminusgps-50.1.0}/docs/source/index.rst +0 -0
  20. {python_terminusgps-50.0.0 → python_terminusgps-50.1.0}/docs/source/mixins.rst +0 -0
  21. {python_terminusgps-50.0.0 → python_terminusgps-50.1.0}/docs/source/validators.rst +0 -0
  22. {python_terminusgps-50.0.0 → python_terminusgps-50.1.0}/docs/source/wialon/constants.rst +0 -0
  23. {python_terminusgps-50.0.0 → python_terminusgps-50.1.0}/docs/source/wialon/exceptions.rst +0 -0
  24. {python_terminusgps-50.0.0 → python_terminusgps-50.1.0}/docs/source/wialon/index.rst +0 -0
  25. {python_terminusgps-50.0.0 → python_terminusgps-50.1.0}/docs/source/wialon/items.rst +0 -0
  26. {python_terminusgps-50.0.0 → python_terminusgps-50.1.0}/docs/source/wialon/session.rst +0 -0
  27. {python_terminusgps-50.0.0 → python_terminusgps-50.1.0}/docs/source/wialon/usage.rst +0 -0
  28. {python_terminusgps-50.0.0 → python_terminusgps-50.1.0}/docs/source/wialon/utils.rst +0 -0
  29. {python_terminusgps-50.0.0 → python_terminusgps-50.1.0}/terminusgps/__init__.py +0 -0
  30. {python_terminusgps-50.0.0 → python_terminusgps-50.1.0}/terminusgps/authorizenet/__init__.py +0 -0
  31. {python_terminusgps-50.0.0 → python_terminusgps-50.1.0}/terminusgps/authorizenet/api/__init__.py +0 -0
  32. {python_terminusgps-50.0.0 → python_terminusgps-50.1.0}/terminusgps/authorizenet/api/address_profiles.py +0 -0
  33. {python_terminusgps-50.0.0 → python_terminusgps-50.1.0}/terminusgps/authorizenet/api/customer_profiles.py +0 -0
  34. {python_terminusgps-50.0.0 → python_terminusgps-50.1.0}/terminusgps/authorizenet/api/payment_profiles.py +0 -0
  35. {python_terminusgps-50.0.0 → python_terminusgps-50.1.0}/terminusgps/authorizenet/api/subscriptions.py +0 -0
  36. {python_terminusgps-50.0.0 → python_terminusgps-50.1.0}/terminusgps/authorizenet/api/transactions.py +0 -0
  37. {python_terminusgps-50.0.0 → python_terminusgps-50.1.0}/terminusgps/authorizenet/constants.py +0 -0
  38. {python_terminusgps-50.0.0 → python_terminusgps-50.1.0}/terminusgps/default_settings.py +0 -0
  39. {python_terminusgps-50.0.0 → python_terminusgps-50.1.0}/terminusgps/mixins.py +0 -0
  40. {python_terminusgps-50.0.0 → python_terminusgps-50.1.0}/terminusgps/validators.py +0 -0
  41. {python_terminusgps-50.0.0 → python_terminusgps-50.1.0}/terminusgps/wialon/__init__.py +0 -0
  42. {python_terminusgps-50.0.0 → python_terminusgps-50.1.0}/terminusgps/wialon/constants.py +0 -0
  43. {python_terminusgps-50.0.0 → python_terminusgps-50.1.0}/terminusgps/wialon/flags.py +0 -0
  44. {python_terminusgps-50.0.0 → python_terminusgps-50.1.0}/terminusgps/wialon/session.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-terminusgps
3
- Version: 50.0.0
3
+ Version: 50.1.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://terminusgps.github.io/python-terminusgps
6
6
  Project-URL: Repository, https://github.com/terminusgps/python-terminusgps
@@ -12,7 +12,7 @@ sys.path.insert(0, os.path.abspath("../../"))
12
12
  project = "python-terminusgps"
13
13
  copyright = "2025, Terminus GPS, LLC"
14
14
  author = "Terminus GPS, LLC"
15
- release = "50.0.0"
15
+ release = "50.1.0"
16
16
 
17
17
  # -- General configuration ---------------------------------------------------
18
18
  # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "python-terminusgps"
3
- version = "50.0.0"
3
+ version = "50.1.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" } ]
@@ -1,4 +1,3 @@
1
- from abc import ABC
2
1
  from functools import cached_property
3
2
 
4
3
  from authorizenet.apicontractsv1 import merchantAuthenticationType
@@ -35,8 +34,8 @@ class AuthorizenetControllerExecutionError(Exception):
35
34
  return self._code
36
35
 
37
36
 
38
- class AuthorizenetService(ABC):
39
- """Base service for safely interacting with the Authorizenet API."""
37
+ class AuthorizenetService:
38
+ """Service for safely interacting with the Authorizenet API."""
40
39
 
41
40
  REQUIRED_SETTINGS = (
42
41
  "MERCHANT_AUTH_ENVIRONMENT",
@@ -1,31 +1,89 @@
1
1
  import secrets
2
2
  import string
3
+ import typing
3
4
 
4
5
  from terminusgps.wialon.session import WialonSession
5
6
 
7
+ __all__ = [
8
+ "generate_wialon_password",
9
+ "get_unit_from_iccid",
10
+ "get_unit_from_imei",
11
+ "get_units_from_carrier",
12
+ ]
6
13
 
7
- def get_id_from_iccid(
14
+
15
+ def generate_wialon_password(length: int = 32) -> str:
16
+ """
17
+ Generates a Wialon compliant password between ``8`` and ``64`` characters.
18
+
19
+ The generated password will contain:
20
+
21
+ - At least one uppercase letter.
22
+ - At least one lowercase letter.
23
+ - At least one special symbol.
24
+ - At least three digits.
25
+
26
+ :param length: Length of the generated password. Default is ``32``.
27
+ :type length: int
28
+ :raises ValueError: If ``length`` was less than ``8`` or greater than ``64``.
29
+ :returns: A Wialon compliant password.
30
+ :rtype: str
31
+
32
+ """
33
+ if length > 64:
34
+ raise ValueError(
35
+ f"Password cannot be greater than 64 characters in length, got {length}."
36
+ )
37
+ elif length < 8:
38
+ raise ValueError(
39
+ f"Password cannot be less than 8 characters in length, got {length}."
40
+ )
41
+
42
+ s0 = list(string.ascii_uppercase)
43
+ s1 = list(string.ascii_lowercase)
44
+ s2 = list(string.digits)
45
+ s3 = list("!@#$%^*()[]-_+")
46
+
47
+ while True:
48
+ password = "".join(
49
+ [secrets.choice(s0 + s1 + s2 + s3) for _ in range(length)]
50
+ )
51
+ if (
52
+ any(c.islower() for c in password)
53
+ and any(c.isupper() for c in password)
54
+ and sum(c.isdigit() for c in password) >= 3
55
+ and any(c in s3 for c in password)
56
+ ):
57
+ break
58
+ return password
59
+
60
+
61
+ def get_unit_from_iccid(
8
62
  iccid: str,
9
63
  session: WialonSession,
10
64
  *,
65
+ flags: int = 1,
11
66
  use_cache: bool = True,
12
67
  afield_key: str = "iccid",
13
- ) -> int | None:
68
+ ) -> dict[str, typing.Any]:
14
69
  """
15
- Returns a Wialon unit ID from a telecom iccid.
70
+ Returns a Wialon unit from a telecom iccid.
16
71
 
17
72
  :param iccid: A telecom iccid number.
18
73
  :type iccid: str
19
74
  :param session: A valid Wialon API session.
20
75
  :type session: ~terminusgps.wialon.session.WialonSession
76
+ :param flags: Wialon API response format flags. Default is ``1``.
77
+ :type flags: int
21
78
  :param use_cache: Whether to use a cached Wialon API response or force a Wialon API call. Default is :py:obj:`True`.
22
79
  :type use_cache: bool
23
80
  :param afield_key: Admin field key to search against. Default is ``"iccid"``.
24
81
  :type afield_key: str
25
82
  :raises ValueError: If multiple units were found with the provided iccid.
26
83
  :raises ValueError: If zero units were found with the provided iccid.
27
- :returns: A Wialon unit ID, if found.
28
- :rtype: int | None
84
+ :raises WialonAPIError: If something went wrong calling the Wialon API.
85
+ :returns: A Wialon unit dictionary.
86
+ :rtype: dict[str, ~typing.Any]
29
87
 
30
88
  """
31
89
  response = session.wialon_api.core_search_items(
@@ -38,7 +96,7 @@ def get_id_from_iccid(
38
96
  "propType": "admin_fields,admin_fields",
39
97
  },
40
98
  "force": int(not use_cache),
41
- "flags": 1,
99
+ "flags": flags,
42
100
  "from": 0,
43
101
  "to": 0,
44
102
  }
@@ -47,12 +105,16 @@ def get_id_from_iccid(
47
105
  raise ValueError(f"Multiple units found for iccid #{iccid}.")
48
106
  elif int(response["totalItemsCount"]) == 0:
49
107
  raise ValueError(f"No units found for iccid #{iccid}.")
50
- return int(response["items"][0]["id"])
108
+ return response["items"][0]
51
109
 
52
110
 
53
- def get_id_from_imei(
54
- imei: str, session: WialonSession, *, use_cache: bool = True
55
- ) -> int | None:
111
+ def get_unit_from_imei(
112
+ imei: str,
113
+ session: WialonSession,
114
+ *,
115
+ flags: int = 1,
116
+ use_cache: bool = True,
117
+ ) -> dict[str, typing.Any]:
56
118
  """
57
119
  Returns a Wialon unit ID from an IMEI (sys_unique_id) number.
58
120
 
@@ -60,12 +122,15 @@ def get_id_from_imei(
60
122
  :type imei: str
61
123
  :param session: A valid Wialon API session.
62
124
  :type session: ~terminusgps.wialon.session.WialonSession
125
+ :param flags: Wialon API response format flags. Default is ``1``.
126
+ :type flags: int
63
127
  :param use_cache: Whether to use a cached Wialon API response or force a Wialon API call. Default is :py:obj:`True`.
64
128
  :type use_cache: bool
65
129
  :raises ValueError: If multiple units were found with the provided IMEI number.
66
130
  :raises ValueError: If zero units were found with the provided IMEI number.
67
- :returns: A Wialon unit ID, if found.
68
- :rtype: int | None
131
+ :raises WialonAPIError: If something went wrong calling the Wialon API.
132
+ :returns: A Wialon unit dictionary.
133
+ :rtype: dict[str, ~typing.Any]
69
134
 
70
135
  """
71
136
  response = session.wialon_api.core_search_items(
@@ -78,7 +143,7 @@ def get_id_from_imei(
78
143
  "propType": "property",
79
144
  },
80
145
  "force": int(not use_cache),
81
- "flags": 1,
146
+ "flags": flags,
82
147
  "from": 0,
83
148
  "to": 0,
84
149
  }
@@ -87,16 +152,19 @@ def get_id_from_imei(
87
152
  raise ValueError(f"Multiple units found for IMEI #{imei}.")
88
153
  elif int(response["totalItemsCount"]) == 0:
89
154
  raise ValueError(f"No units found for IMEI #{imei}.")
90
- return int(response["items"][0]["id"])
155
+ return response["items"][0]
91
156
 
92
157
 
93
- def get_ids_from_carrier(
158
+ def get_units_from_carrier(
94
159
  carrier: str,
95
160
  session: WialonSession,
96
161
  *,
97
162
  use_cache: bool = True,
98
163
  afield_key: str = "carrier",
99
- ) -> list[int]:
164
+ flags: int = 1,
165
+ start: int = 0,
166
+ end: int = 0,
167
+ ) -> list[dict[str, typing.Any]]:
100
168
  """
101
169
  Returns a list of Wialon unit IDs by telecom carrier name.
102
170
 
@@ -110,8 +178,15 @@ def get_ids_from_carrier(
110
178
  :type use_cache: bool
111
179
  :param afield_key: Admin field key to search against. Default is ``"carrier"``.
112
180
  :type afield_key: str
113
- :returns: A list of Wialon unit IDs, if any were found.
114
- :rtype: list[int]
181
+ :param flags: Wialon API response flags. Default is ``1``.
182
+ :type flags: int
183
+ :param start: Start index. Default is ``0``.
184
+ :type start: int
185
+ :param end: End index. Default is ``0``.
186
+ :type end: int
187
+ :raises WialonAPIError: If something went wrong calling the Wialon API.
188
+ :returns: A list of Wialon unit dictionaries.
189
+ :rtype: list[dict[str, ~typing.Any]]
115
190
 
116
191
  """
117
192
  response = session.wialon_api.core_search_items(
@@ -124,57 +199,11 @@ def get_ids_from_carrier(
124
199
  "propType": "admin_fields,admin_fields",
125
200
  },
126
201
  "force": int(not use_cache),
127
- "flags": 1,
128
- "from": 0,
129
- "to": 0,
202
+ "flags": flags,
203
+ "from": start,
204
+ "to": end,
130
205
  }
131
206
  )
132
207
  if int(response["totalItemsCount"]) == 0:
133
208
  return []
134
- return [int(unit["id"]) for unit in response["items"]]
135
-
136
-
137
- def generate_wialon_password(length: int = 32) -> str:
138
- """
139
- Generates a Wialon compliant password between ``8`` and ``64`` characters.
140
-
141
- The generated password will contain:
142
-
143
- - At least one uppercase letter.
144
- - At least one lowercase letter.
145
- - At least one special symbol.
146
- - At least three digits.
147
-
148
- :param length: Length of the generated password. Default is ``32``.
149
- :type length: int
150
- :raises ValueError: If ``length`` was less than ``8`` or greater than ``64``.
151
- :returns: A Wialon compliant password.
152
- :rtype: str
153
-
154
- """
155
- if length > 64:
156
- raise ValueError(
157
- f"Password cannot be greater than 64 characters in length, got {length}."
158
- )
159
- elif length < 8:
160
- raise ValueError(
161
- f"Password cannot be less than 8 characters in length, got {length}."
162
- )
163
-
164
- s0 = list(string.ascii_uppercase)
165
- s1 = list(string.ascii_lowercase)
166
- s2 = list(string.digits)
167
- s3 = list("!@#$%^*()[]-_+")
168
-
169
- while True:
170
- password = "".join(
171
- [secrets.choice(s0 + s1 + s2 + s3) for _ in range(length)]
172
- )
173
- if (
174
- any(c.islower() for c in password)
175
- and any(c.isupper() for c in password)
176
- and sum(c.isdigit() for c in password) >= 3
177
- and any(c in s3 for c in password)
178
- ):
179
- break
180
- return password
209
+ return response["items"]
@@ -748,7 +748,7 @@ wheels = [
748
748
 
749
749
  [[package]]
750
750
  name = "python-terminusgps"
751
- version = "50.0.0"
751
+ version = "50.1.0"
752
752
  source = { editable = "." }
753
753
  dependencies = [
754
754
  { name = "authorizenet" },
@@ -820,13 +820,25 @@ wheels = [
820
820
  { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738 },
821
821
  ]
822
822
 
823
+ [[package]]
824
+ name = "roman-numerals"
825
+ version = "4.0.0"
826
+ source = { registry = "https://pypi.org/simple" }
827
+ sdist = { url = "https://files.pythonhosted.org/packages/6b/20/a6b20239f54814de5c34bf3f504e553b11780c2aad3677ad2daf989f1fb3/roman_numerals-4.0.0.tar.gz", hash = "sha256:231287018a8788bf8c0718482a08c15b90458523ea1d840a18a791a86d4583b3", size = 9027 }
828
+ wheels = [
829
+ { url = "https://files.pythonhosted.org/packages/ad/9d/ad950fd3b65cf0974c633862320829f3d461aef125b981504277c8409a93/roman_numerals-4.0.0-py3-none-any.whl", hash = "sha256:4131feb23ba1a542494873e4cee7844ec8d226a750134efc65ceb20939ed33c9", size = 7668 },
830
+ ]
831
+
823
832
  [[package]]
824
833
  name = "roman-numerals-py"
825
- version = "3.1.0"
834
+ version = "4.0.0"
826
835
  source = { registry = "https://pypi.org/simple" }
827
- sdist = { url = "https://files.pythonhosted.org/packages/30/76/48fd56d17c5bdbdf65609abbc67288728a98ed4c02919428d4f52d23b24b/roman_numerals_py-3.1.0.tar.gz", hash = "sha256:be4bf804f083a4ce001b5eb7e3c0862479d10f94c936f6c4e5f250aa5ff5bd2d", size = 9017 }
836
+ dependencies = [
837
+ { name = "roman-numerals" },
838
+ ]
839
+ sdist = { url = "https://files.pythonhosted.org/packages/7e/02/6cb667fc1872c5fe814971be2973317f0747d86f1c0aac37d0e1a1df1ecc/roman_numerals_py-4.0.0.tar.gz", hash = "sha256:f7fa8dff5b7b7251d3a7586b97c57a0698e2e28898fa42c23bcc0cf51b02aee9", size = 1119 }
828
840
  wheels = [
829
- { url = "https://files.pythonhosted.org/packages/53/97/d2cbbaa10c9b826af0e10fdf836e1bf344d9f0abb873ebc34d1f49642d3f/roman_numerals_py-3.1.0-py3-none-any.whl", hash = "sha256:9da2ad2fb670bcf24e81070ceb3be72f6c11c440d73bd579fbeca1e9f330954c", size = 7742 },
841
+ { url = "https://files.pythonhosted.org/packages/d2/b7/c0aa3b0154e022faa8e8b4eadda0c49f53b09b6b8d55ed3b7cd311abfd92/roman_numerals_py-4.0.0-py3-none-any.whl", hash = "sha256:dfcecf6e0cddbf2ee1112e7e2ebf58ba771984f075cb57a30e1811cee4f06332", size = 1244 },
830
842
  ]
831
843
 
832
844
  [[package]]