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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-terminusgps
3
- Version: 35.1.0
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=i6BR4G-uw5m9i4FaHzjmLZb3ufdqSpeFfg3MwTXILVI,7296
19
- terminusgps/aws/secrets.py,sha256=MxQEmmBLpMUQ2tYAsHdCYf-7RZ84LiogdKcTsACX5dw,361
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=JWHZ7VEeo8VNBH67gjOAQk5ITKuPllWDlJ4dVSQ5blA,11002
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=7J5B-EPj997GJV7TbZP7pAfjl2G4nTcKmP-RMxmuzoY,10959
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=0XPLfpFh7djiVOcuZE-lUdB_-_LRSO8C2PvfzBFh3HQ,10744
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.1.0.dist-info/METADATA,sha256=uIku-n8_UI-SA-M-ojn5NpxbNoP9Yjmn6G-bKMJ9WO8,1616
58
- python_terminusgps-35.1.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
59
- python_terminusgps-35.1.0.dist-info/licenses/COPYING,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
60
- python_terminusgps-35.1.0.dist-info/RECORD,,
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,,
@@ -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 = os.getenv("AWS_MESSAGING_ORIGIN_POOL"),
13
- configuration_set: str | None = os.getenv("AWS_MESSAGING_CONFIGURATION"),
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:`None`
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 is None:
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 is None:
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 ``300``.
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 voice message as a dry run. Default is :py:obj:`False`.
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: A voice message response.
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 ``300``.
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 voice message as a dry run. Default is :py:obj:`False`.
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
+ )
@@ -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
- """Logs into the default AWS CLI session and returns a secret value."""
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)
@@ -155,13 +155,24 @@ class WialonBase:
155
155
  )
156
156
  self._name = new_name
157
157
 
158
- def get_cfield_id(self, key: str) -> int | None:
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
- Returns a custom field id by its key, if it exists.
167
+ self.session.wialon_api.item_delete_item(**{"itemId": self.id})
161
168
 
162
- :param key: A custom field key.
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: A custom field id, if any.
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 get_afield_id(self, key: str) -> int | None:
192
+ def _get_afield_id(self, key: str) -> int | None:
181
193
  """
182
- Returns an admin field id by its key, if it exists.
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: An admin field id, if any.
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 add_afield(self, key: str, value: str) -> None:
215
+ def update_afield(self, key: str, value: str) -> None:
203
216
  """
204
- Adds an admin field to the Wialon object.
217
+ Updates an admin field by key.
205
218
 
206
- :param key: A key (name) for the admin field.
219
+ :param key: An admin field key (name).
207
220
  :type key: :py:obj:`str`
208
- :param value: A value for the admin field.
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.session.wialon_api.item_update_admin_field(
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
- def update_afield(self, id: int, key: str, value: str) -> None:
220
- """
221
- Updates an admin field by id.
222
-
223
- :param id: The admin field id.
224
- :type id: :py:obj:`int`
225
- :param key: A key (name) for the admin field.
226
- :type key: :py:obj:`str`
227
- :param value: A value for the admin field.
228
- :type value: :py:obj:`str`
229
- :raises WialonError: If something goes wrong with Wialon.
230
- :returns: Nothing.
231
- :rtype: :py:obj:`None`
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, id: int, key: str, value: str) -> None:
245
+ def update_cfield(self, key: str, value: str) -> None:
256
246
  """
257
247
  Updates a custom field by id.
258
248
 
259
- :param id: The admin field id.
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 value for the custom property.
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.session.wialon_api.item_update_custom_property(
288
- **{"itemId": self.id, "name": key, "value": value}
289
- )
258
+ field_id: int | None = self._get_cfield_id(key)
290
259
 
291
- def add_profile_field(self, key: str, value: str) -> None:
292
- """
293
- Adds a profile field to the Wialon object.
294
-
295
- :param key: A key (name) for the profile field.
296
- :type key: :py:obj:`str`
297
- :param value: A value for the profile field.
298
- :type value: :py:obj:`str`
299
- :raises WialonError: If something goes wrong with Wialon.
300
- :returns: Nothing.
301
- :rtype: :py:obj:`None`
302
-
303
- """
304
- self.session.wialon_api.item_update_profile_field(
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
- def delete(self) -> None:
275
+ @property
276
+ def cfields(self) -> dict[str, str]:
309
277
  """
310
- Deletes the Wialon object.
278
+ Custom fields for the Wialon object.
311
279
 
312
- :raises WialonError: If something goes wrong with Wialon.
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
- """Admin fields for the Wialon object."""
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
  )
@@ -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(self) -> list[str]:
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
- phone_numbers: list[str] = []
264
- phones_0: list[str] | None = self._get_driver_phone_numbers()
265
- phones_1: list[str] | None = self._get_cfield_phone_numbers()
266
- phones_2: list[str] | None = self._get_afield_phone_numbers()
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 = "to_number") -> list[str] | None:
282
+ def _get_afield_phone_numbers(self, key: str) -> list[str]:
285
283
  """
286
- Retrives any phone numbers saved in an admin field by key.
284
+ Retrives phone numbers saved in an admin field by key.
287
285
 
288
- :param key: An admin field key. Default is ``"to_number"``.
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, if the unit has admin phone number fields.
292
- :rtype: :py:obj:`list` | :py:obj:`None`
289
+ :returns: A list of phone numbers.
290
+ :rtype: :py:obj:`list`
293
291
 
294
292
  """
295
- for field_name, field_val in self.afields.items():
296
- if field_name == key:
297
- return self.clean_phone_numbers([field_val])
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 = "to_number") -> list[str] | None:
297
+ def _get_cfield_phone_numbers(self, key: str) -> list[str]:
300
298
  """
301
- Retrives any phone numbers saved in a custom field by key.
299
+ Retrives phone numbers saved in a custom field by key.
302
300
 
303
- :param key: A custom field key. Default is ``"to_number"``.
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, if the unit has custom phone number fields.
307
- :rtype: :py:obj:`list` | :py:obj:`None`
304
+ :returns: A list of phone numbers.
305
+ :rtype: :py:obj:`list`
308
306
 
309
307
  """
310
- for field_name, field_val in self.cfields.items():
311
- if field_name == key:
312
- return self.clean_phone_numbers([field_val])
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] | None:
312
+ def _get_driver_phone_numbers(self) -> list[str]:
315
313
  """
316
- Retrieves any phone numbers assigned to drivers attached to the unit.
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, if the unit has attached drivers with phone numbers.
320
- :rtype: :py:obj:`list` | :py:obj:`None`
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 []
@@ -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 ``32``.
297
+ :param length: Length of the generated password. Default is :py:obj:`32`.
298
298
  :type length: :py:obj:`int`
299
- :raises ValueError: If :py:length is less than ``8`` or greater than ``64``.
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
@@ -1,5 +0,0 @@
1
- import requests
2
-
3
-
4
- def get_public_ip() -> str:
5
- return requests.get("https://checkip.amazonaws.com").text.strip()