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.
- {python_terminusgps-25.0.3.dist-info → python_terminusgps-26.0.0.dist-info}/METADATA +1 -1
- {python_terminusgps-25.0.3.dist-info → python_terminusgps-26.0.0.dist-info}/RECORD +9 -8
- terminusgps/twilio/validators.py +31 -0
- terminusgps/wialon/items/base.py +30 -38
- terminusgps/wialon/items/unit.py +2 -2
- terminusgps/wialon/session.py +4 -5
- terminusgps/wialon/utils.py +30 -27
- {python_terminusgps-25.0.3.dist-info → python_terminusgps-26.0.0.dist-info}/WHEEL +0 -0
- {python_terminusgps-25.0.3.dist-info → python_terminusgps-26.0.0.dist-info}/licenses/COPYING +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-terminusgps
|
|
3
|
-
Version:
|
|
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=
|
|
24
|
-
terminusgps/wialon/utils.py,sha256=
|
|
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=
|
|
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=
|
|
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-
|
|
34
|
-
python_terminusgps-
|
|
35
|
-
python_terminusgps-
|
|
36
|
-
python_terminusgps-
|
|
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
|
+
)
|
terminusgps/wialon/items/base.py
CHANGED
|
@@ -128,78 +128,71 @@ class WialonBase:
|
|
|
128
128
|
}
|
|
129
129
|
)
|
|
130
130
|
|
|
131
|
-
def add_cfield(self,
|
|
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
|
|
136
|
-
:type
|
|
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,
|
|
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
|
|
157
|
-
:type
|
|
158
|
-
:param field: A
|
|
159
|
-
:type field: :py:obj:`
|
|
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,
|
|
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
|
|
180
|
-
:type
|
|
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":
|
|
179
|
+
**{"itemId": self.id, "name": key, "value": value}
|
|
188
180
|
)
|
|
189
181
|
|
|
190
|
-
def add_profile_field(self,
|
|
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
|
|
195
|
-
:type
|
|
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":
|
|
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
|
|
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
|
|
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
|
)
|
terminusgps/wialon/items/unit.py
CHANGED
|
@@ -48,7 +48,7 @@ class WialonUnit(WialonBase):
|
|
|
48
48
|
),
|
|
49
49
|
}
|
|
50
50
|
)
|
|
51
|
-
if response
|
|
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
|
|
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.
|
terminusgps/wialon/session.py
CHANGED
|
@@ -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(
|
|
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
|
|
terminusgps/wialon/utils.py
CHANGED
|
@@ -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
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
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(
|
|
127
|
-
"
|
|
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
|
-
|
|
152
|
-
|
|
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
|
"""
|
|
File without changes
|
{python_terminusgps-25.0.3.dist-info → python_terminusgps-26.0.0.dist-info}/licenses/COPYING
RENAMED
|
File without changes
|