python-terminusgps 35.1.0__py3-none-any.whl → 35.4.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-35.1.0.dist-info → python_terminusgps-35.4.0.dist-info}/METADATA +1 -1
- {python_terminusgps-35.1.0.dist-info → python_terminusgps-35.4.0.dist-info}/RECORD +9 -11
- terminusgps/aws/notifications.py +127 -19
- terminusgps/aws/secrets.py +11 -1
- terminusgps/wialon/items/base.py +68 -102
- terminusgps/wialon/items/unit.py +32 -33
- terminusgps/wialon/utils.py +2 -2
- terminusgps/aws/tests/test_utils.py +0 -0
- terminusgps/aws/utils.py +0 -5
- {python_terminusgps-35.1.0.dist-info → python_terminusgps-35.4.0.dist-info}/WHEEL +0 -0
- {python_terminusgps-35.1.0.dist-info → python_terminusgps-35.4.0.dist-info}/licenses/COPYING +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-terminusgps
|
|
3
|
-
Version: 35.
|
|
3
|
+
Version: 35.4.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://docs.terminusgps.com
|
|
6
6
|
Project-URL: Repository, https://github.com/terminusgps/python-terminusgps
|
|
@@ -15,13 +15,11 @@ terminusgps/authorizenet/tests/test_auth.py,sha256=sdByYrcv8RKjcJckOHbyHH1z_M4qr
|
|
|
15
15
|
terminusgps/authorizenet/tests/test_profiles.py,sha256=tvLBY0iSAC33RlWiBP_NSnPskUQBjfrf0YHMpR-m22k,7566
|
|
16
16
|
terminusgps/authorizenet/tests/test_utils.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
17
17
|
terminusgps/aws/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
18
|
-
terminusgps/aws/notifications.py,sha256=
|
|
19
|
-
terminusgps/aws/secrets.py,sha256
|
|
20
|
-
terminusgps/aws/utils.py,sha256=68GWcllu4h41qzFiKdUkWOAA4LDsTPqRY446VRMxYsY,116
|
|
18
|
+
terminusgps/aws/notifications.py,sha256=76YnACuVIMs5EujEATqcBf8HcCFxs2649fnLx9nSauc,11103
|
|
19
|
+
terminusgps/aws/secrets.py,sha256=-9gNTaagJbWpeqTNXdxXsMVKUF2_mwzx5XvqTOFcqYU,670
|
|
21
20
|
terminusgps/aws/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
22
21
|
terminusgps/aws/tests/test_notifications.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
23
22
|
terminusgps/aws/tests/test_secrets.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
24
|
-
terminusgps/aws/tests/test_utils.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
25
23
|
terminusgps/django/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
26
24
|
terminusgps/django/mixins.py,sha256=Q9ZJuzpk3d9lDnlVA8ZTVvnZWxB13p08EQ8yVJcztn4,1034
|
|
27
25
|
terminusgps/django/settings.py,sha256=yJIcBE0xjY60AXF_gonY5hS3ofseqPW2k9uSoELgoho,639
|
|
@@ -40,21 +38,21 @@ terminusgps/wialon/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuF
|
|
|
40
38
|
terminusgps/wialon/constants.py,sha256=Ju2XL2A4DPFD-w8NjYqEfnnqregl1luPT0XGhGSUnO8,6316
|
|
41
39
|
terminusgps/wialon/flags.py,sha256=M50EdhxQ8IMnJnU0mrHK7-h8Asc6tvNiTOOfd1dBW6A,12815
|
|
42
40
|
terminusgps/wialon/session.py,sha256=09WYWvON_H844IFfdt7_x1we6tHhQSkt9K_BEedBjv0,10396
|
|
43
|
-
terminusgps/wialon/utils.py,sha256=
|
|
41
|
+
terminusgps/wialon/utils.py,sha256=SwpcxLPBLy0u3AT84G-Co4wxmhRrPFyW3jo_9O6ICvk,11008
|
|
44
42
|
terminusgps/wialon/validators.py,sha256=j5vrtcfClAzRdRkWTvRRAmrne1t0R6cKkmYdE7fAkSs,4247
|
|
45
43
|
terminusgps/wialon/items/__init__.py,sha256=3BVthghekMvhMQSzQgBMlD_phxmKSmp3Zvmo-z0O8Bs,211
|
|
46
|
-
terminusgps/wialon/items/base.py,sha256=
|
|
44
|
+
terminusgps/wialon/items/base.py,sha256=YpOPxghLYDZmWQZRb64CjwCq_0rTmm83cIYZm2oN5LY,9344
|
|
47
45
|
terminusgps/wialon/items/resource.py,sha256=DDnyL_Y5P3DxiirUkQb9y4cokMXZVq3xnfn16Vx8sxk,16992
|
|
48
46
|
terminusgps/wialon/items/retranslator.py,sha256=sVEWrp8q1I-tO_rMTCH7TeQDDbZryIq8d9KVnhYyhH0,3795
|
|
49
47
|
terminusgps/wialon/items/route.py,sha256=amNPKZqgl8pEzKA_XJudnvBPzEsfHTmrC7uF15sZVI4,1216
|
|
50
|
-
terminusgps/wialon/items/unit.py,sha256=
|
|
48
|
+
terminusgps/wialon/items/unit.py,sha256=G72CSpu5pcilGjXQDz8yTZ-xwWXm1rCYZUkG98lY-8w,10504
|
|
51
49
|
terminusgps/wialon/items/unit_group.py,sha256=QV2ZZgICaN3n6W1pFIyMLyNKB6KYBsLyKG64i-qXoOg,4113
|
|
52
50
|
terminusgps/wialon/items/user.py,sha256=5LgwalGO8KS_7psmAVmAlnC0S6SPBDZMOuT5ZqPKRmo,7758
|
|
53
51
|
terminusgps/wialon/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
54
52
|
terminusgps/wialon/tests/test_items.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
55
53
|
terminusgps/wialon/tests/test_session.py,sha256=9mBlYchMo9NeqRIZsmxYzrM1zBHorqu4ooxqSNppLpI,1336
|
|
56
54
|
terminusgps/wialon/tests/test_utils.py,sha256=SK4PxJQGECFnzx_EQeRAQfsQ5_3FLaVcis2W9u_ibuI,1730
|
|
57
|
-
python_terminusgps-35.
|
|
58
|
-
python_terminusgps-35.
|
|
59
|
-
python_terminusgps-35.
|
|
60
|
-
python_terminusgps-35.
|
|
55
|
+
python_terminusgps-35.4.0.dist-info/METADATA,sha256=yyOAC6wbzHngb7UQQuqNchUZeeikcsH43qdyJLwBQTg,1616
|
|
56
|
+
python_terminusgps-35.4.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
57
|
+
python_terminusgps-35.4.0.dist-info/licenses/COPYING,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
58
|
+
python_terminusgps-35.4.0.dist-info/RECORD,,
|
terminusgps/aws/notifications.py
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
import asyncio
|
|
1
2
|
import os
|
|
3
|
+
from collections.abc import Sequence
|
|
2
4
|
from contextlib import AsyncExitStack
|
|
3
5
|
|
|
4
6
|
from aiobotocore.session import AioSession
|
|
7
|
+
from loguru import logger
|
|
5
8
|
|
|
6
9
|
|
|
7
10
|
class AsyncNotificationManager:
|
|
@@ -9,12 +12,14 @@ class AsyncNotificationManager:
|
|
|
9
12
|
|
|
10
13
|
def __init__(
|
|
11
14
|
self,
|
|
12
|
-
origin_pool_arn: str | None =
|
|
13
|
-
configuration_set: str | None =
|
|
15
|
+
origin_pool_arn: str | None = None,
|
|
16
|
+
configuration_set: str | None = None,
|
|
14
17
|
max_sms_price: str = "0.20",
|
|
15
18
|
max_voice_price: str = "0.20",
|
|
16
19
|
region_name: str = "us-east-1",
|
|
17
20
|
debug_enabled: bool = False,
|
|
21
|
+
logger_level: int = 10,
|
|
22
|
+
logger_days: int = 10,
|
|
18
23
|
) -> None:
|
|
19
24
|
"""
|
|
20
25
|
Sets attributes on the notification manager.
|
|
@@ -23,35 +28,50 @@ class AsyncNotificationManager:
|
|
|
23
28
|
:type origin_pool_arn: :py:obj:`str` | :py:obj:`None`
|
|
24
29
|
:param configuration_set: An end-user messaging configuration set ARN. Default is :envvar:`AWS_MESSAGING_CONFIGURATION`.
|
|
25
30
|
:type configuration_set: :py:obj:`str` | :py:obj:`None`
|
|
26
|
-
:param max_sms_price: Max price to spend on a single SMS message.
|
|
31
|
+
:param max_sms_price: Max price to spend on a single SMS message. Default is :py:obj:`"0.20"`.
|
|
27
32
|
:type max_sms_price: :py:obj:`str`
|
|
28
|
-
:param max_voice_price: Max price to spend per minute on a single voice message.
|
|
33
|
+
:param max_voice_price: Max price to spend per minute on a single voice message. Default is :py:obj:`"0.20"`.
|
|
29
34
|
:type max_voice_price: :py:obj:`str`
|
|
30
|
-
:param region_name: An AWS region name used to open an AWS client.
|
|
35
|
+
:param region_name: An AWS region name used to open an AWS client. Default is :py:obj:`"us-east-1"`.
|
|
31
36
|
:type region_name: :py:obj:`str`
|
|
32
|
-
:param debug_enabled: Whether or not to enable debug mode.
|
|
33
|
-
:type debug_enabled: :py:obj:`
|
|
37
|
+
:param debug_enabled: Whether or not to enable debug mode. Default is :py:obj:`False`
|
|
38
|
+
:type debug_enabled: :py:obj:`False`
|
|
39
|
+
:param logger_level: A logger level. Default is :py:obj:`int`
|
|
40
|
+
:type logger_level: :py:obj:`int`
|
|
41
|
+
:param logger_days: How long in days logging data should be saved.
|
|
42
|
+
:type logger_days: :py:obj:`int`
|
|
34
43
|
:raises ValueError: If ``origin_pool_arn`` wasn't provided and :envvar:`AWS_MESSAGING_ORIGIN_POOL` wasn't set.
|
|
35
44
|
:raises ValueError: If ``configuration_set`` wasn't provided and :envvar:`AWS_MESSAGING_CONFIGURATION` wasn't set.
|
|
36
45
|
:returns: Nothing.
|
|
37
46
|
:rtype: :py:obj:`None`
|
|
38
47
|
|
|
39
48
|
"""
|
|
40
|
-
if origin_pool_arn
|
|
49
|
+
if not origin_pool_arn and not os.getenv("AWS_MESSAGING_ORIGIN_POOL"):
|
|
41
50
|
raise ValueError(f"'origin_pool_arn' is required, got '{origin_pool_arn}'.")
|
|
42
|
-
if configuration_set
|
|
51
|
+
if not configuration_set and not os.getenv("AWS_MESSAGING_CONFIGURATION"):
|
|
43
52
|
raise ValueError(
|
|
44
53
|
f"'configuration_set' is required, got '{configuration_set}'."
|
|
45
54
|
)
|
|
46
55
|
|
|
56
|
+
self._origin_pool_arn = origin_pool_arn or os.getenv(
|
|
57
|
+
"AWS_MESSAGING_ORIGIN_POOL", ""
|
|
58
|
+
)
|
|
59
|
+
self._configuration_set = configuration_set or os.getenv(
|
|
60
|
+
"AWS_MESSAGING_CONFIGURATION", ""
|
|
61
|
+
)
|
|
62
|
+
|
|
47
63
|
self._exit_stack = AsyncExitStack()
|
|
48
64
|
self._pinpoint_client = None
|
|
49
65
|
self._region_name = region_name
|
|
50
|
-
self._origin_pool_arn = origin_pool_arn
|
|
51
|
-
self._configuration_set = configuration_set
|
|
52
66
|
self._max_sms_price = max_sms_price
|
|
53
67
|
self._max_voice_price = max_voice_price
|
|
54
68
|
self._debug = debug_enabled
|
|
69
|
+
logger.add(
|
|
70
|
+
f"logs/{self.__class__.__name__}.log",
|
|
71
|
+
level=logger_level,
|
|
72
|
+
retention=f"{logger_days} days",
|
|
73
|
+
diagnose=debug_enabled,
|
|
74
|
+
)
|
|
55
75
|
|
|
56
76
|
async def __aenter__(self) -> "AsyncNotificationManager":
|
|
57
77
|
"""
|
|
@@ -92,22 +112,55 @@ class AsyncNotificationManager:
|
|
|
92
112
|
|
|
93
113
|
@property
|
|
94
114
|
def configuration_set(self) -> str:
|
|
115
|
+
"""
|
|
116
|
+
The configuration set to use for messaging.
|
|
117
|
+
|
|
118
|
+
:type: :py:obj:`str`
|
|
119
|
+
|
|
120
|
+
"""
|
|
95
121
|
return self._configuration_set
|
|
96
122
|
|
|
97
123
|
@property
|
|
98
124
|
def origin_pool(self) -> str:
|
|
125
|
+
"""
|
|
126
|
+
The origination pool to use for messaging.
|
|
127
|
+
|
|
128
|
+
:type: :py:obj:`str`
|
|
129
|
+
|
|
130
|
+
"""
|
|
99
131
|
return self._origin_pool_arn
|
|
100
132
|
|
|
101
133
|
@property
|
|
102
134
|
def max_sms_price(self) -> str:
|
|
135
|
+
"""
|
|
136
|
+
The max allowed price per sms message.
|
|
137
|
+
|
|
138
|
+
:type: :py:obj:`str`
|
|
139
|
+
:value: :py:obj:`"0.20"`
|
|
140
|
+
|
|
141
|
+
"""
|
|
103
142
|
return self._max_sms_price
|
|
104
143
|
|
|
105
144
|
@property
|
|
106
145
|
def max_voice_price(self) -> str:
|
|
146
|
+
"""
|
|
147
|
+
The max allowed price per voice message.
|
|
148
|
+
|
|
149
|
+
:type: :py:obj:`str`
|
|
150
|
+
:value: :py:obj:`"0.20"`
|
|
151
|
+
|
|
152
|
+
"""
|
|
107
153
|
return self._max_voice_price
|
|
108
154
|
|
|
109
155
|
@property
|
|
110
156
|
def debug(self) -> bool:
|
|
157
|
+
"""
|
|
158
|
+
Whether or not debug mode is enabled.
|
|
159
|
+
|
|
160
|
+
:type: :py:obj:`bool`
|
|
161
|
+
:value: :py:obj:`False`
|
|
162
|
+
|
|
163
|
+
"""
|
|
111
164
|
return self._debug
|
|
112
165
|
|
|
113
166
|
async def send_sms(
|
|
@@ -117,7 +170,7 @@ class AsyncNotificationManager:
|
|
|
117
170
|
ttl: int = 300,
|
|
118
171
|
dry_run: bool = False,
|
|
119
172
|
feedback: bool = False,
|
|
120
|
-
) -> dict:
|
|
173
|
+
) -> dict[str, str]:
|
|
121
174
|
"""
|
|
122
175
|
Texts ``message`` to ``phone`` via sms.
|
|
123
176
|
|
|
@@ -125,18 +178,20 @@ class AsyncNotificationManager:
|
|
|
125
178
|
:type phone: :py:obj:`str`
|
|
126
179
|
:param message: A message body.
|
|
127
180
|
:type message: :py:obj:`str`
|
|
128
|
-
:param ttl: Time to live in ms. Default is
|
|
181
|
+
:param ttl: Time to live in ms. Default is :py:obj:`300`.
|
|
129
182
|
:type ttl: :py:obj:`int`
|
|
130
|
-
:param dry_run: Whether or not to execute the
|
|
183
|
+
:param dry_run: Whether or not to execute the message as a dry run. Default is :py:obj:`False`.
|
|
131
184
|
:type dry_run: :py:obj:`bool`
|
|
132
185
|
:param feedback: Whether or not to include message feedback in the response. Default is :py:obj:`False`.
|
|
133
186
|
:type feedback: :py:obj:`bool`
|
|
134
187
|
:raises AssertionError: If :py:attr:`_pinpoint_client` wasn't set.
|
|
135
|
-
:returns:
|
|
188
|
+
:returns: An sms message response.
|
|
136
189
|
:rtype: :py:obj:`dict`
|
|
137
190
|
|
|
138
191
|
"""
|
|
139
192
|
assert self._pinpoint_client, "Asyncronous client wasn't set."
|
|
193
|
+
|
|
194
|
+
logger.debug(f"Texting '{message}' to '{phone}'...")
|
|
140
195
|
return await self._pinpoint_client.send_text_message(
|
|
141
196
|
**{
|
|
142
197
|
"DestinationPhoneNumber": phone,
|
|
@@ -159,7 +214,7 @@ class AsyncNotificationManager:
|
|
|
159
214
|
voice_id: str = "Joanna",
|
|
160
215
|
dry_run: bool = False,
|
|
161
216
|
feedback: bool = False,
|
|
162
|
-
) -> dict:
|
|
217
|
+
) -> dict[str, str]:
|
|
163
218
|
"""
|
|
164
219
|
Calls ``phone`` and reads ``message`` aloud.
|
|
165
220
|
|
|
@@ -167,11 +222,11 @@ class AsyncNotificationManager:
|
|
|
167
222
|
:type phone: :py:obj:`str`
|
|
168
223
|
:param message: A message body.
|
|
169
224
|
:type message: :py:obj:`str`
|
|
170
|
-
:param ttl: Time to live in ms. Default is
|
|
225
|
+
:param ttl: Time to live in ms. Default is :py:obj:`300`.
|
|
171
226
|
:type ttl: :py:obj:`int`
|
|
172
|
-
:param voice_id: A voice id to use for speech synthesis.
|
|
227
|
+
:param voice_id: A voice id to use for speech synthesis. Default is :py:obj:`"Joanna"`.
|
|
173
228
|
:type voice_id: :py:obj:`str`
|
|
174
|
-
:param dry_run: Whether or not to execute the
|
|
229
|
+
:param dry_run: Whether or not to execute the message as a dry run. Default is :py:obj:`False`.
|
|
175
230
|
:type dry_run: :py:obj:`bool`
|
|
176
231
|
:param feedback: Whether or not to include message feedback in the response. Default is :py:obj:`False`.
|
|
177
232
|
:type feedback: :py:obj:`bool`
|
|
@@ -181,6 +236,8 @@ class AsyncNotificationManager:
|
|
|
181
236
|
|
|
182
237
|
"""
|
|
183
238
|
assert self._pinpoint_client, "Asyncronous client wasn't set."
|
|
239
|
+
|
|
240
|
+
logger.debug(f"Reading '{message}' aloud to '{phone}'...")
|
|
184
241
|
return await self._pinpoint_client.send_voice_message(
|
|
185
242
|
**{
|
|
186
243
|
"DestinationPhoneNumber": phone,
|
|
@@ -195,3 +252,54 @@ class AsyncNotificationManager:
|
|
|
195
252
|
"MessageFeedbackEnabled": feedback,
|
|
196
253
|
}
|
|
197
254
|
)
|
|
255
|
+
|
|
256
|
+
async def batch_send_sms(
|
|
257
|
+
self, phones: Sequence[str], message: str, **kwargs
|
|
258
|
+
) -> list[dict[str, str]]:
|
|
259
|
+
"""
|
|
260
|
+
Sends ``message`` to all phone numbers in ``phones`` via sms.
|
|
261
|
+
|
|
262
|
+
:param phones: A sequence of phone numbers.
|
|
263
|
+
:type phones: :py:obj:`~collections.abc.Sequence`
|
|
264
|
+
:param message: A message body.
|
|
265
|
+
:type message: :py:obj:`str`
|
|
266
|
+
:param kwargs: Additional keyword arguments passed to :py:meth:`~terminusgps.aws.notifications.AsyncNotificationManager.send_sms`.
|
|
267
|
+
:raises AssertionError: If :py:attr:`_pinpoint_client` wasn't set.
|
|
268
|
+
:returns: A list of sms message responses.
|
|
269
|
+
:rtype: :py:obj:`dict`
|
|
270
|
+
|
|
271
|
+
.. seealso::
|
|
272
|
+
:py:meth:`~terminusgps.aws.notifications.AsyncNotificationManager.send_sms` for details on available keyword arguments.
|
|
273
|
+
|
|
274
|
+
"""
|
|
275
|
+
assert self._pinpoint_client, "Asyncronous client wasn't set."
|
|
276
|
+
return await asyncio.gather(
|
|
277
|
+
*[self.send_sms(phone=phone, message=message, **kwargs) for phone in phones]
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
async def batch_send_voice(
|
|
281
|
+
self, phones: Sequence[str], message: str, **kwargs
|
|
282
|
+
) -> list[dict[str, str]]:
|
|
283
|
+
"""
|
|
284
|
+
Calls each number in ``phones`` and reads ``message`` aloud.
|
|
285
|
+
|
|
286
|
+
:param phone: A sequence of phone numbers.
|
|
287
|
+
:type phone: :py:obj:`~collections.abc.Sequence`
|
|
288
|
+
:param message: A message body.
|
|
289
|
+
:type message: :py:obj:`str`
|
|
290
|
+
:param kwargs: Additional keyword arguments passed to :py:meth:`~terminusgps.aws.notifications.AsyncNotificationManager.send_voice`.
|
|
291
|
+
:raises AssertionError: If :py:attr:`_pinpoint_client` wasn't set.
|
|
292
|
+
:returns: A voice message response.
|
|
293
|
+
:rtype: :py:obj:`dict`
|
|
294
|
+
|
|
295
|
+
.. seealso::
|
|
296
|
+
:py:meth:`~terminusgps.aws.notifications.AsyncNotificationManager.send_voice` for details on available keyword arguments.
|
|
297
|
+
|
|
298
|
+
"""
|
|
299
|
+
assert self._pinpoint_client, "Asyncronous client wasn't set."
|
|
300
|
+
return await asyncio.gather(
|
|
301
|
+
*[
|
|
302
|
+
self.send_voice(phone=phone, message=message, **kwargs)
|
|
303
|
+
for phone in phones
|
|
304
|
+
]
|
|
305
|
+
)
|
terminusgps/aws/secrets.py
CHANGED
|
@@ -4,7 +4,17 @@ import boto3
|
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
def get_secret(name: str, region: str = "us-east-1") -> dict[str, str]:
|
|
7
|
-
"""
|
|
7
|
+
"""
|
|
8
|
+
Logs into the default AWS CLI session and returns a secret value.
|
|
9
|
+
|
|
10
|
+
:param name: An AWS `secretsmanager <https://docs.aws.amazon.com/secretsmanager/>`_ name.
|
|
11
|
+
:type name: :py:obj:`str`
|
|
12
|
+
:param region: An AWS region name. Default is :py:obj:`"us-east-1"`.
|
|
13
|
+
:type region: :py:obj:`str`
|
|
14
|
+
:returns: A secret value dictionary.
|
|
15
|
+
:rtype: :py:obj:`dict`
|
|
16
|
+
|
|
17
|
+
"""
|
|
8
18
|
client = boto3.Session().client(service_name="secretsmanager", region_name=region)
|
|
9
19
|
secret = client.get_secret_value(SecretId=name)["SecretString"]
|
|
10
20
|
return json.loads(secret)
|
terminusgps/wialon/items/base.py
CHANGED
|
@@ -155,13 +155,24 @@ class WialonBase:
|
|
|
155
155
|
)
|
|
156
156
|
self._name = new_name
|
|
157
157
|
|
|
158
|
-
def
|
|
158
|
+
def delete(self) -> None:
|
|
159
|
+
"""
|
|
160
|
+
Deletes the Wialon object.
|
|
161
|
+
|
|
162
|
+
:raises WialonError: If something goes wrong with Wialon.
|
|
163
|
+
:returns: Nothing.
|
|
164
|
+
:rtype: :py:obj:`None`
|
|
165
|
+
|
|
159
166
|
"""
|
|
160
|
-
|
|
167
|
+
self.session.wialon_api.item_delete_item(**{"itemId": self.id})
|
|
161
168
|
|
|
162
|
-
|
|
169
|
+
def _get_cfield_id(self, key: str) -> int | None:
|
|
170
|
+
"""
|
|
171
|
+
Returns a custom field id by key.
|
|
172
|
+
|
|
173
|
+
:param key: A custom field key (name).
|
|
163
174
|
:type key: :py:obj:`str`
|
|
164
|
-
:returns:
|
|
175
|
+
:returns: The custom field's id as an integer, if found.
|
|
165
176
|
:rtype: :py:obj:`int` | :py:obj:`None`
|
|
166
177
|
|
|
167
178
|
"""
|
|
@@ -173,17 +184,18 @@ class WialonBase:
|
|
|
173
184
|
.get("flds", {})
|
|
174
185
|
.values()
|
|
175
186
|
)
|
|
187
|
+
|
|
176
188
|
for field in cfields:
|
|
177
189
|
if field["n"] == key:
|
|
178
190
|
return int(field["id"])
|
|
179
191
|
|
|
180
|
-
def
|
|
192
|
+
def _get_afield_id(self, key: str) -> int | None:
|
|
181
193
|
"""
|
|
182
|
-
Returns an admin field id by
|
|
194
|
+
Returns an admin field id by key.
|
|
183
195
|
|
|
184
|
-
:param key: An admin field key.
|
|
196
|
+
:param key: An admin field key (name).
|
|
185
197
|
:type key: :py:obj:`str`
|
|
186
|
-
:returns:
|
|
198
|
+
:returns: The admin field's id as an integer, if found.
|
|
187
199
|
:rtype: :py:obj:`int` | :py:obj:`None`
|
|
188
200
|
|
|
189
201
|
"""
|
|
@@ -195,130 +207,79 @@ class WialonBase:
|
|
|
195
207
|
.get("aflds", {})
|
|
196
208
|
.values()
|
|
197
209
|
)
|
|
210
|
+
|
|
198
211
|
for field in afields:
|
|
199
212
|
if field["n"] == key:
|
|
200
213
|
return int(field["id"])
|
|
201
214
|
|
|
202
|
-
def
|
|
215
|
+
def update_afield(self, key: str, value: str) -> None:
|
|
203
216
|
"""
|
|
204
|
-
|
|
217
|
+
Updates an admin field by key.
|
|
205
218
|
|
|
206
|
-
:param key:
|
|
219
|
+
:param key: An admin field key (name).
|
|
207
220
|
:type key: :py:obj:`str`
|
|
208
|
-
:param value: A
|
|
221
|
+
:param value: A string.
|
|
209
222
|
:type value: :py:obj:`str`
|
|
210
223
|
:raises WialonError: If something goes wrong with Wialon.
|
|
211
224
|
:returns: Nothing.
|
|
212
225
|
:rtype: :py:obj:`None`
|
|
213
226
|
|
|
214
227
|
"""
|
|
215
|
-
self.
|
|
216
|
-
**{"itemId": self.id, "id": 0, "callMode": "create", "n": key, "v": value}
|
|
217
|
-
)
|
|
228
|
+
field_id: int | None = self._get_afield_id(key)
|
|
218
229
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
:
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
"""
|
|
234
|
-
self.session.wialon_api.item_update_admin_field(
|
|
235
|
-
**{"itemId": self.id, "id": id, "callMode": "update", "n": key, "v": value}
|
|
236
|
-
)
|
|
237
|
-
|
|
238
|
-
def add_cfield(self, key: str, value: str) -> None:
|
|
239
|
-
"""
|
|
240
|
-
Adds a custom field to the Wialon object.
|
|
241
|
-
|
|
242
|
-
:param key: A key (name) for the custom field.
|
|
243
|
-
:type key: :py:obj:`str`
|
|
244
|
-
:param value: A value for the custom field.
|
|
245
|
-
:type value: :py:obj:`str`
|
|
246
|
-
:raises WialonError: If something goes wrong with Wialon.
|
|
247
|
-
:returns: Nothing.
|
|
248
|
-
:rtype: :py:obj:`None`
|
|
249
|
-
|
|
250
|
-
"""
|
|
251
|
-
self.session.wialon_api.item_update_custom_field(
|
|
252
|
-
**{"itemId": self.id, "id": 0, "callMode": "create", "n": key, "v": value}
|
|
253
|
-
)
|
|
230
|
+
if field_id is not None:
|
|
231
|
+
self.session.wialon_api.item_update_admin_field(
|
|
232
|
+
**{
|
|
233
|
+
"itemId": self.id,
|
|
234
|
+
"id": field_id,
|
|
235
|
+
"callMode": "update",
|
|
236
|
+
"n": key,
|
|
237
|
+
"v": value,
|
|
238
|
+
}
|
|
239
|
+
)
|
|
240
|
+
else:
|
|
241
|
+
self.session.wialon_api.item_update_admin_field(
|
|
242
|
+
**{"itemId": self.id, "callMode": "create", "n": key, "v": value}
|
|
243
|
+
)
|
|
254
244
|
|
|
255
|
-
def update_cfield(self,
|
|
245
|
+
def update_cfield(self, key: str, value: str) -> None:
|
|
256
246
|
"""
|
|
257
247
|
Updates a custom field by id.
|
|
258
248
|
|
|
259
|
-
:param
|
|
260
|
-
:type id: :py:obj:`int`
|
|
261
|
-
:param field: A key (name) for the custom field.
|
|
262
|
-
:type field: :py:obj:`str`
|
|
263
|
-
:param field: A value for the custom field.
|
|
264
|
-
:type field: :py:obj:`str`
|
|
265
|
-
:raises WialonError: If something goes wrong with Wialon.
|
|
266
|
-
:returns: Nothing.
|
|
267
|
-
:rtype: :py:obj:`None`
|
|
268
|
-
|
|
269
|
-
"""
|
|
270
|
-
self.session.wialon_api.item_update_custom_field(
|
|
271
|
-
**{"itemId": self.id, "id": id, "callMode": "update", "n": key, "v": value}
|
|
272
|
-
)
|
|
273
|
-
|
|
274
|
-
def add_cproperty(self, key: str, value: str) -> None:
|
|
275
|
-
"""
|
|
276
|
-
Adds a custom property to the Wialon object.
|
|
277
|
-
|
|
278
|
-
:param key: A key (name) for the custom property.
|
|
249
|
+
:param key: A custom field key (name).
|
|
279
250
|
:type key: :py:obj:`str`
|
|
280
|
-
:param value: A
|
|
251
|
+
:param value: A string.
|
|
281
252
|
:type value: :py:obj:`str`
|
|
282
253
|
:raises WialonError: If something goes wrong with Wialon.
|
|
283
254
|
:returns: Nothing.
|
|
284
255
|
:rtype: :py:obj:`None`
|
|
285
256
|
|
|
286
257
|
"""
|
|
287
|
-
self.
|
|
288
|
-
**{"itemId": self.id, "name": key, "value": value}
|
|
289
|
-
)
|
|
258
|
+
field_id: int | None = self._get_cfield_id(key)
|
|
290
259
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
:
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
**{"itemId": self.id, "n": key, "v": value}
|
|
306
|
-
)
|
|
260
|
+
if field_id is not None:
|
|
261
|
+
self.session.wialon_api.item_update_custom_field(
|
|
262
|
+
**{
|
|
263
|
+
"itemId": self.id,
|
|
264
|
+
"id": field_id,
|
|
265
|
+
"callMode": "update",
|
|
266
|
+
"n": key,
|
|
267
|
+
"v": value,
|
|
268
|
+
}
|
|
269
|
+
)
|
|
270
|
+
else:
|
|
271
|
+
self.session.wialon_api.item_update_custom_field(
|
|
272
|
+
**{"itemId": self.id, "callMode": "create", "n": key, "v": value}
|
|
273
|
+
)
|
|
307
274
|
|
|
308
|
-
|
|
275
|
+
@property
|
|
276
|
+
def cfields(self) -> dict[str, str]:
|
|
309
277
|
"""
|
|
310
|
-
|
|
278
|
+
Custom fields for the Wialon object.
|
|
311
279
|
|
|
312
|
-
:
|
|
313
|
-
:returns: Nothing.
|
|
314
|
-
:rtype: :py:obj:`None`
|
|
280
|
+
:type: :py:obj:`dict`
|
|
315
281
|
|
|
316
282
|
"""
|
|
317
|
-
self.session.wialon_api.item_delete_item(**{"itemId": self.id})
|
|
318
|
-
|
|
319
|
-
@property
|
|
320
|
-
def cfields(self) -> dict[str, str]:
|
|
321
|
-
"""Custom fields for the Wialon object."""
|
|
322
283
|
response = self.session.wialon_api.core_search_item(
|
|
323
284
|
**{"id": self.id, "flags": flags.DataFlag.UNIT_CUSTOM_FIELDS}
|
|
324
285
|
)
|
|
@@ -327,7 +288,12 @@ class WialonBase:
|
|
|
327
288
|
|
|
328
289
|
@property
|
|
329
290
|
def afields(self) -> dict[str, str]:
|
|
330
|
-
"""
|
|
291
|
+
"""
|
|
292
|
+
Admin fields for the Wialon object.
|
|
293
|
+
|
|
294
|
+
:type: :py:obj:`dict`
|
|
295
|
+
|
|
296
|
+
"""
|
|
331
297
|
response = self.session.wialon_api.core_search_item(
|
|
332
298
|
**{"id": self.id, "flags": flags.DataFlag.UNIT_ADMIN_FIELDS}
|
|
333
299
|
)
|
terminusgps/wialon/items/unit.py
CHANGED
|
@@ -249,29 +249,27 @@ class WialonUnit(WialonBase):
|
|
|
249
249
|
**{"itemId": self.id, "phoneNumber": quote_plus(phone)}
|
|
250
250
|
)
|
|
251
251
|
|
|
252
|
-
def get_phone_numbers(
|
|
252
|
+
def get_phone_numbers(
|
|
253
|
+
self, cfield_key: str = "to_number", afield_key: str = "to_number"
|
|
254
|
+
) -> list[str]:
|
|
253
255
|
"""
|
|
254
256
|
Retrieves all phone numbers assigned to the unit.
|
|
255
257
|
|
|
256
258
|
This includes any attached drivers, custom/admin fields or otherwise assigned phone numbers.
|
|
257
259
|
|
|
260
|
+
:param cfield_key: A custom field key used to retrieve phone numbers. Default is :py:obj:`"to_number"`.
|
|
261
|
+
:type cfield_key: :py:obj:`str`
|
|
262
|
+
:param afield_key: An admin field key used to retrieve phone numbers. Default is :py:obj:`"to_number"`.
|
|
263
|
+
:type afield_key: :py:obj:`str`
|
|
258
264
|
:raises WialonError: If something goes wrong with Wialon.
|
|
259
265
|
:returns: A list of phone numbers.
|
|
260
266
|
:rtype: :py:obj:`list`
|
|
261
267
|
|
|
262
268
|
"""
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
if phones_0 is not None:
|
|
269
|
-
phone_numbers.extend(phones_0)
|
|
270
|
-
if phones_1 is not None:
|
|
271
|
-
phone_numbers.extend(phones_1)
|
|
272
|
-
if phones_2 is not None:
|
|
273
|
-
phone_numbers.extend(phones_2)
|
|
274
|
-
return list(dict.fromkeys(phone_numbers)) # Removes duplicate phone numbers
|
|
269
|
+
phones_0: list[str] = self._get_driver_phone_numbers()
|
|
270
|
+
phones_1: list[str] = self._get_cfield_phone_numbers(key=cfield_key)
|
|
271
|
+
phones_2: list[str] = self._get_afield_phone_numbers(key=afield_key)
|
|
272
|
+
return list(frozenset(phones_0 + phones_1 + phones_2))
|
|
275
273
|
|
|
276
274
|
def clean_phone_numbers(self, phones: list[str]) -> list[str]:
|
|
277
275
|
"""Takes a list of phone numbers and returns a list of clean phone numbers."""
|
|
@@ -281,43 +279,43 @@ class WialonUnit(WialonBase):
|
|
|
281
279
|
for clean_num in (num.split(",") if "," in num else [num])
|
|
282
280
|
]
|
|
283
281
|
|
|
284
|
-
def _get_afield_phone_numbers(self, key: str
|
|
282
|
+
def _get_afield_phone_numbers(self, key: str) -> list[str]:
|
|
285
283
|
"""
|
|
286
|
-
Retrives
|
|
284
|
+
Retrives phone numbers saved in an admin field by key.
|
|
287
285
|
|
|
288
|
-
:param key: An admin field key.
|
|
286
|
+
:param key: An admin field key.
|
|
289
287
|
:type key: :py:obj:`str`
|
|
290
288
|
:raises WialonError: If something goes wrong with Wialon.
|
|
291
|
-
:returns: A list of phone numbers
|
|
292
|
-
:rtype: :py:obj:`list`
|
|
289
|
+
:returns: A list of phone numbers.
|
|
290
|
+
:rtype: :py:obj:`list`
|
|
293
291
|
|
|
294
292
|
"""
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
293
|
+
if key not in self.afields.keys():
|
|
294
|
+
return []
|
|
295
|
+
return self.clean_phone_numbers([self.afields[key]])
|
|
298
296
|
|
|
299
|
-
def _get_cfield_phone_numbers(self, key: str
|
|
297
|
+
def _get_cfield_phone_numbers(self, key: str) -> list[str]:
|
|
300
298
|
"""
|
|
301
|
-
Retrives
|
|
299
|
+
Retrives phone numbers saved in a custom field by key.
|
|
302
300
|
|
|
303
|
-
:param key: A custom field key.
|
|
301
|
+
:param key: A custom field key.
|
|
304
302
|
:type key: :py:obj:`str`
|
|
305
303
|
:raises WialonError: If something goes wrong with Wialon.
|
|
306
|
-
:returns: A list of phone numbers
|
|
307
|
-
:rtype: :py:obj:`list`
|
|
304
|
+
:returns: A list of phone numbers.
|
|
305
|
+
:rtype: :py:obj:`list`
|
|
308
306
|
|
|
309
307
|
"""
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
308
|
+
if key not in self.cfields.keys():
|
|
309
|
+
return []
|
|
310
|
+
return self.clean_phone_numbers([self.cfields[key]])
|
|
313
311
|
|
|
314
|
-
def _get_driver_phone_numbers(self) -> list[str]
|
|
312
|
+
def _get_driver_phone_numbers(self) -> list[str]:
|
|
315
313
|
"""
|
|
316
|
-
|
|
314
|
+
Returns a list of phone numbers assigned to drivers attached to the unit.
|
|
317
315
|
|
|
318
316
|
:raises WialonError: If something goes wrong with Wialon.
|
|
319
|
-
:returns: A list of phone numbers
|
|
320
|
-
:rtype: :py:obj:`list`
|
|
317
|
+
:returns: A list of phone numbers.
|
|
318
|
+
:rtype: :py:obj:`list`
|
|
321
319
|
|
|
322
320
|
"""
|
|
323
321
|
response = self.session.wialon_api.resource_get_unit_drivers(
|
|
@@ -326,3 +324,4 @@ class WialonUnit(WialonBase):
|
|
|
326
324
|
if response:
|
|
327
325
|
dirty_phones = [driver[0].get("ph") for driver in response.values()]
|
|
328
326
|
return self.clean_phone_numbers(dirty_phones)
|
|
327
|
+
return []
|
terminusgps/wialon/utils.py
CHANGED
|
@@ -294,9 +294,9 @@ def generate_wialon_password(length: int = 32) -> str:
|
|
|
294
294
|
- At least one special symbol.
|
|
295
295
|
- At least three digits.
|
|
296
296
|
|
|
297
|
-
:param length: Length of the generated password. Default is
|
|
297
|
+
:param length: Length of the generated password. Default is :py:obj:`32`.
|
|
298
298
|
:type length: :py:obj:`int`
|
|
299
|
-
:raises ValueError: If
|
|
299
|
+
:raises ValueError: If ``length`` is less than ``8`` or greater than ``64``.
|
|
300
300
|
:returns: A Wialon compliant password.
|
|
301
301
|
:rtype: :py:obj:`str`
|
|
302
302
|
|
|
File without changes
|
terminusgps/aws/utils.py
DELETED
|
File without changes
|
{python_terminusgps-35.1.0.dist-info → python_terminusgps-35.4.0.dist-info}/licenses/COPYING
RENAMED
|
File without changes
|