python-terminusgps 25.0.3__py3-none-any.whl → 26.0.0__py3-none-any.whl

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-terminusgps
3
- Version: 25.0.3
3
+ Version: 26.0.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
@@ -16,21 +16,22 @@ terminusgps/aws/secrets.py,sha256=MxQEmmBLpMUQ2tYAsHdCYf-7RZ84LiogdKcTsACX5dw,36
16
16
  terminusgps/aws/ses.py,sha256=sDca2GjMaZvUt3kUPtGCpcqHiOkij9lMV1ogZofgYUU,2372
17
17
  terminusgps/twilio/__init__.py,sha256=dYo41F2jft_eHDWSUtSpKGSRG1bewq_qClqilUJZkYM,33
18
18
  terminusgps/twilio/caller.py,sha256=TiQ4f0TDDC35he1M1iSsBM2vutdGriwtV4pc17T--6I,3552
19
+ terminusgps/twilio/validators.py,sha256=nn7CCBTktNjcln6M4MR0ACxEsClgioSu89LRLgIiBZk,1112
19
20
  terminusgps/wialon/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
21
  terminusgps/wialon/constants.py,sha256=n1ux68oWYWnzeZdN811Iw_Lk78KBM0YMJMoonn67uZY,1723
21
22
  terminusgps/wialon/flags.py,sha256=_NIL3mrEGhvI5YISVjimjYtUatdgOqhZPJX368MtKSU,13959
22
23
  terminusgps/wialon/logger.py,sha256=nUBsaLMaffSaAHGHAM6mFXDzix9hbNecVtA3INVtii8,968
23
- terminusgps/wialon/session.py,sha256=t3d7trBQRxnXFnex73alSSD5n2LDTZQ9VAtzUKIERto,9670
24
- terminusgps/wialon/utils.py,sha256=Y7fo9a81w84b3N39h1AmVLS-AnC3aRT5R137hIsqCHU,6723
24
+ terminusgps/wialon/session.py,sha256=THl88yfVBfRH8daKh1lmLSR8ro8TbyOokHx1Cev-En8,9638
25
+ terminusgps/wialon/utils.py,sha256=QXBksDJlC0_khuJ6n9HEYlUQB9ArM-Pvh-K0xHORvDs,6917
25
26
  terminusgps/wialon/items/__init__.py,sha256=3BVthghekMvhMQSzQgBMlD_phxmKSmp3Zvmo-z0O8Bs,211
26
- terminusgps/wialon/items/base.py,sha256=zINd7NWeq4EGQgRhq6bNfEymeyq3uojn8HD71HvhJaQ,6998
27
+ terminusgps/wialon/items/base.py,sha256=5UssVqdAJEReITjh0RKjQ419IS1QA3htjWZAxLXmRbQ,6915
27
28
  terminusgps/wialon/items/resource.py,sha256=qmQKEakJa9LH5YkI9-iBDA0hsotc5ctcwdb7kZuXdfQ,14549
28
29
  terminusgps/wialon/items/retranslator.py,sha256=8q9EDc92w92MElnHIeEzH3Ra4kiPXrx4t9V5nPDZuRU,3728
29
30
  terminusgps/wialon/items/route.py,sha256=PTJx1gmT_PGz7nlSSUS2VzjYD-aAuvkEOIdIFneUSSQ,1148
30
- terminusgps/wialon/items/unit.py,sha256=uHQrauVkh2556kZp0Dh3WLZvQ6WJIWkibGgPYW1ljPE,9177
31
+ terminusgps/wialon/items/unit.py,sha256=Kn2lq6SJNcvlIjKeRIfZfvGNsHZ1xVVwiyI9WKMzE1M,9164
31
32
  terminusgps/wialon/items/unit_group.py,sha256=Stp-WfCGbOqcnxV6FU1AKtV7KIxIwCHa0NwhNJfgRoM,5032
32
33
  terminusgps/wialon/items/user.py,sha256=INwAibQVmjNNelepQXMfDevgJqMvIjHHgEjAMLRvhB0,7082
33
- python_terminusgps-25.0.3.dist-info/METADATA,sha256=wUbbi7rTr9yWRuwoo-MVWZLXE41Dk1QEt3npcZfvl28,946
34
- python_terminusgps-25.0.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
35
- python_terminusgps-25.0.3.dist-info/licenses/COPYING,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
36
- python_terminusgps-25.0.3.dist-info/RECORD,,
34
+ python_terminusgps-26.0.0.dist-info/METADATA,sha256=4sdznR0fg1dw8e55_cdmsmmREF9Ul3KzG3Wggo-yBVo,946
35
+ python_terminusgps-26.0.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
36
+ python_terminusgps-26.0.0.dist-info/licenses/COPYING,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
37
+ python_terminusgps-26.0.0.dist-info/RECORD,,
@@ -0,0 +1,31 @@
1
+ from django.core.exceptions import ValidationError
2
+ from django.utils.translation import gettext_lazy as _
3
+
4
+
5
+ def validate_phone_number(value: str) -> None:
6
+ min_length, max_length = 12, 19
7
+
8
+ if not value.startswith("+"):
9
+ raise ValidationError(
10
+ _("Phone number must begin with a '+', got '%(value)s'."),
11
+ code="invalid",
12
+ params={"value": value},
13
+ )
14
+ if not value[1:].isdigit():
15
+ raise ValidationError(
16
+ _("Phone number must be '+' followed by a digit, got '%(value)s'"),
17
+ code="invalid",
18
+ params={"value": value},
19
+ )
20
+ if len(value) < min_length:
21
+ raise ValidationError(
22
+ _("Phone number cannot be less than %(min)s characters, got %(len)s."),
23
+ code="invalid",
24
+ params={"min": min_length, "len": len(value)},
25
+ )
26
+ if len(value) > max_length:
27
+ raise ValidationError(
28
+ _("Phone number cannot be greater than %(max)s characters, got %(len)s."),
29
+ code="invalid",
30
+ params={"max": min_length, "len": len(value)},
31
+ )
@@ -128,78 +128,71 @@ class WialonBase:
128
128
  }
129
129
  )
130
130
 
131
- def add_cfield(self, field: tuple[str, str]) -> None:
131
+ def add_cfield(self, key: str, value: str) -> None:
132
132
  """
133
133
  Adds a custom field to the Wialon object.
134
134
 
135
- :param field: A tuple containing the name of the field and the value of the field.
136
- :type field: :py:obj:`tuple`
135
+ :param key: A key (name) for the custom field.
136
+ :type key: :py:obj:`str`
137
+ :param value: A value for the custom field.
138
+ :type value: :py:obj:`str`
137
139
  :returns: Nothing.
138
140
  :rtype: :py:obj:`None`
139
141
 
140
142
  """
141
143
 
142
144
  self.session.wialon_api.item_update_custom_field(
143
- **{
144
- "itemId": self.id,
145
- "id": 0,
146
- "callMode": "create",
147
- "n": field[0],
148
- "v": field[1],
149
- }
145
+ **{"itemId": self.id, "id": 0, "callMode": "create", "n": key, "v": value}
150
146
  )
151
147
 
152
- def update_cfield(self, field_id: int, field: tuple[str, str]) -> None:
148
+ def update_cfield(self, id: int, key: str, value: str) -> None:
153
149
  """
154
150
  Updates a custom field by id.
155
151
 
156
- :param field_id: The admin field id.
157
- :type field_id: :py:obj:`int`
158
- :param field: A tuple containing the name of the field and the value of the field.
159
- :type field: :py:obj:`tuple`
152
+ :param id: The admin field id.
153
+ :type id: :py:obj:`int`
154
+ :param field: A key (name) for the custom field.
155
+ :type field: :py:obj:`str`
156
+ :param field: A value for the custom field.
157
+ :type field: :py:obj:`str`
160
158
  :returns: Nothing.
161
159
  :rtype: :py:obj:`None`
162
160
 
163
161
  """
164
-
165
162
  self.session.wialon_api.item_update_custom_field(
166
- **{
167
- "itemId": self.id,
168
- "id": field_id,
169
- "callMode": "update",
170
- "n": field[0],
171
- "v": field[1],
172
- }
163
+ **{"itemId": self.id, "id": id, "callMode": "update", "n": key, "v": value}
173
164
  )
174
165
 
175
- def add_cproperty(self, property: tuple[str, str]) -> None:
166
+ def add_cproperty(self, key: str, value: str) -> None:
176
167
  """
177
168
  Adds a custom property to the Wialon object.
178
169
 
179
- :param property: A tuple containing the name of the property and the value of the property.
180
- :type property: :py:obj:`tuple`
170
+ :param key: A key (name) for the custom property.
171
+ :type key: :py:obj:`str`
172
+ :param value: A value for the custom property.
173
+ :type value: :py:obj:`str`
181
174
  :returns: Nothing.
182
175
  :rtype: :py:obj:`None`
183
176
 
184
177
  """
185
-
186
178
  self.session.wialon_api.item_update_custom_property(
187
- **{"itemId": self.id, "name": property[0], "value": property[1]}
179
+ **{"itemId": self.id, "name": key, "value": value}
188
180
  )
189
181
 
190
- def add_profile_field(self, field: tuple[str, str]) -> None:
182
+ def add_profile_field(self, key: str, value: str) -> None:
191
183
  """
192
184
  Adds a profile field to the Wialon object.
193
185
 
194
- :param field: A tuple containing the name of the field and the value of the field.
195
- :type field: :py:obj:`tuple`
186
+ :param key: A key (name) for the profile field.
187
+ :type key: :py:obj:`str`
188
+ :param value: A value for the profile field.
189
+ :type value: :py:obj:`str`
196
190
  :returns: Nothing.
197
191
  :rtype: :py:obj:`None`
198
192
 
199
193
  """
200
-
201
194
  self.session.wialon_api.item_update_profile_field(
202
- **{"itemId": self.id, "n": field[0], "v": field[1]}
195
+ **{"itemId": self.id, "n": key, "v": value}
203
196
  )
204
197
 
205
198
  def delete(self) -> None:
@@ -211,12 +204,11 @@ class WialonBase:
211
204
  :rtype: :py:obj:`None`
212
205
 
213
206
  """
214
-
215
207
  self.session.wialon_api.item_delete_item(**{"itemId": self.id})
216
208
 
217
209
  @property
218
- def cfields(self) -> dict:
219
- """Custom fields associated with the Wialon object."""
210
+ def cfields(self) -> dict[str, str]:
211
+ """Custom fields for the Wialon object."""
220
212
  response = self.session.wialon_api.core_search_item(
221
213
  **{"id": self.id, "flags": flags.DATAFLAG_UNIT_CUSTOM_FIELDS}
222
214
  )
@@ -224,8 +216,8 @@ class WialonBase:
224
216
  return {field["n"]: field["v"] for field in fields.values()} if fields else {}
225
217
 
226
218
  @property
227
- def afields(self) -> dict:
228
- """Admin fields associated with the Wialon object."""
219
+ def afields(self) -> dict[str, str]:
220
+ """Admin fields for the Wialon object."""
229
221
  response = self.session.wialon_api.core_search_item(
230
222
  **{"id": self.id, "flags": flags.DATAFLAG_UNIT_ADMIN_FIELDS}
231
223
  )
@@ -48,7 +48,7 @@ class WialonUnit(WialonBase):
48
48
  ),
49
49
  }
50
50
  )
51
- if response is not None:
51
+ if response:
52
52
  item = response.get("item", {})
53
53
  self._imei_number = item.get("uid")
54
54
  self._active = item.get("act", False)
@@ -194,7 +194,7 @@ class WialonUnit(WialonBase):
194
194
  """
195
195
  Retrieves all phone numbers assigned to the unit.
196
196
 
197
- This includes any attached drivers, custom/admin fields, or otherwise assigned phone numbers.
197
+ This includes any attached drivers, custom/admin fields or otherwise assigned phone numbers.
198
198
 
199
199
  :raises WialonError: If something goes wrong with Wialon.
200
200
  :returns: A list of phone numbers.
@@ -3,10 +3,9 @@ import datetime
3
3
  import logging
4
4
  import typing
5
5
 
6
+ import wialon.api
6
7
  from django.conf import settings
7
8
  from django.utils import timezone
8
- from wialon.api import Wialon as WialonBase
9
- from wialon.api import WialonError
10
9
 
11
10
  from terminusgps.wialon.logger import WialonLogger
12
11
 
@@ -21,7 +20,7 @@ class WialonAPICall:
21
20
  error: Exception | None = None
22
21
 
23
22
 
24
- class Wialon(WialonBase):
23
+ class Wialon(wialon.api.Wialon):
25
24
  def __init__(self, log_level: int = logging.INFO, *args, **kwargs) -> None:
26
25
  super().__init__(*args, **kwargs)
27
26
  self.call_history: list[WialonAPICall] = []
@@ -48,7 +47,7 @@ class Wialon(WialonBase):
48
47
  call_record.result = result
49
48
  self.logger.debug(f"Executed '{action_name}' successfully.")
50
49
  return result
51
- except WialonError as e:
50
+ except wialon.api.WialonError as e:
52
51
  call_record.error = e
53
52
  self.logger.warning(f"Failed to execute '{action_name}': '{e}'")
54
53
  return
@@ -275,7 +274,7 @@ class WialonSession:
275
274
  self._set_login_response(response)
276
275
  self.logger.debug(f"Logged into Wialon API session '{response.get('eid')}'")
277
276
  return response.get("eid")
278
- except (WialonError, AssertionError) as e:
277
+ except (wialon.api.WialonError, AssertionError) as e:
279
278
  self.logger.critical(f"Failed to login to Wialon API: '{e}'")
280
279
  raise
281
280
 
@@ -49,7 +49,7 @@ def get_id_from_imei(imei: str, session: WialonSession) -> str | None:
49
49
  if not imei.isdigit():
50
50
  raise ValueError(f"'imei' must be a digit, got '{imei}'.")
51
51
 
52
- results = session.wialon_api.core_search_items(
52
+ results: dict[str, typing.Any] | None = session.wialon_api.core_search_items(
53
53
  **{
54
54
  "spec": {
55
55
  "itemsType": "avl_unit",
@@ -66,7 +66,7 @@ def get_id_from_imei(imei: str, session: WialonSession) -> str | None:
66
66
  }
67
67
  )
68
68
 
69
- if results.get("totalItemsCount", 0) == 1:
69
+ if results and results.get("totalItemsCount", 0) == 1:
70
70
  return results["items"][0].get("id")
71
71
 
72
72
 
@@ -88,27 +88,25 @@ def get_id_from_iccid(iccid: str, session: WialonSession) -> str | None:
88
88
  return get_id_from_imei(imei=iccid, session=session)
89
89
 
90
90
 
91
- def get_wialon_cls(items_type: str) -> typing.Type[WialonBase]:
91
+ def get_wialon_cls(items_type: str) -> typing.Type[WialonBase] | None:
92
92
  """
93
- Returns a Wialon object class based on items_type.
93
+ Returns a Wialon object class based on ``items_type``.
94
+
95
+ Valid ``items_type`` are ``"user"``, ``"avl_unit"``, ``"avl_unit_group"`` and ``"avl_resource"``.
94
96
 
95
97
  :param items_type: A Wialon object type.
96
98
  :type items_type: :py:obj:`str`
97
99
  :returns: A subclass of :py:obj:`~terminusgps.wialon.items.base.WialonBase`.
98
- :rtype: :py:obj:`~terminusgps.wialon.items.base.WialonBase`
100
+ :rtype: :py:obj:`~terminusgps.wialon.items.base.WialonBase` | :py:obj:`None`
99
101
 
100
102
  """
101
- match items_type:
102
- case "user":
103
- return items.WialonUser
104
- case "avl_unit":
105
- return items.WialonUnit
106
- case "avl_unit_group":
107
- return items.WialonUnitGroup
108
- case "avl_resource":
109
- return items.WialonResource
110
- case _:
111
- raise ValueError(f"Invalid items_type, got '{items_type}'.")
103
+ items_map: dict[str, typing.Type[WialonBase]] = {
104
+ "user": items.WialonUser,
105
+ "avl_unit": items.WialonUnit,
106
+ "avl_unit_group": items.WialonUnitGroup,
107
+ "avl_resource": items.WialonResource,
108
+ }
109
+ return items_map.get(items_type)
112
110
 
113
111
 
114
112
  def get_vin_info(vin_number: str, session: WialonSession) -> dict:
@@ -123,15 +121,18 @@ def get_vin_info(vin_number: str, session: WialonSession) -> dict:
123
121
  :rtype: :py:obj:`dict`
124
122
 
125
123
  """
126
- response = session.wialon_api.unit_get_vin_info(**{"vin": vin_number}).get(
127
- "vin_lookup_result"
128
- )
129
- return (
130
- {field.get("n"): field.get("v") for field in response.get("pflds")}
131
- if "error" not in response.keys()
132
- else {}
124
+ response: dict[str, typing.Any] | None = session.wialon_api.unit_get_vin_info(
125
+ **{"vin": vin_number}
133
126
  )
134
127
 
128
+ if response is None or "error" in response.get("vin_lookup_result", {}).keys():
129
+ return {}
130
+
131
+ return {
132
+ field.get("n"): field.get("v")
133
+ for field in response.get("vin_lookup_result", {}).get("pflds")
134
+ }
135
+
135
136
 
136
137
  def is_unique(value: str, session: WialonSession, items_type: str = "avl_unit") -> bool:
137
138
  """
@@ -148,12 +149,14 @@ def is_unique(value: str, session: WialonSession, items_type: str = "avl_unit")
148
149
  :rtype: :py:obj:`bool`
149
150
 
150
151
  """
151
- return not bool(
152
- session.wialon_api.core_check_unique(
153
- **{"type": items_type, "value": value.strip()}
154
- ).get("result")
152
+ response = session.wialon_api.core_check_unique(
153
+ **{"type": items_type, "value": value.strip()}
155
154
  )
156
155
 
156
+ if not response:
157
+ return False
158
+ return bool(response.get("result"))
159
+
157
160
 
158
161
  def generate_wialon_password(length: int = 32) -> str:
159
162
  """