python-terminusgps 1.15.1__tar.gz → 1.16.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of python-terminusgps might be problematic. Click here for more details.

Files changed (37) hide show
  1. {python_terminusgps-1.15.1 → python_terminusgps-1.16.0}/PKG-INFO +1 -1
  2. {python_terminusgps-1.15.1 → python_terminusgps-1.16.0}/pyproject.toml +1 -1
  3. {python_terminusgps-1.15.1 → python_terminusgps-1.16.0}/terminusgps/wialon/items/base.py +23 -18
  4. {python_terminusgps-1.15.1 → python_terminusgps-1.16.0}/terminusgps/wialon/items/resource.py +31 -4
  5. {python_terminusgps-1.15.1 → python_terminusgps-1.16.0}/terminusgps/wialon/items/unit_group.py +10 -43
  6. {python_terminusgps-1.15.1 → python_terminusgps-1.16.0}/terminusgps/wialon/session.py +8 -9
  7. python_terminusgps-1.16.0/terminusgps/wialon/utils.py +185 -0
  8. {python_terminusgps-1.15.1 → python_terminusgps-1.16.0}/uv.lock +1 -1
  9. python_terminusgps-1.15.1/terminusgps/wialon/utils.py +0 -113
  10. {python_terminusgps-1.15.1 → python_terminusgps-1.16.0}/.gitignore +0 -0
  11. {python_terminusgps-1.15.1 → python_terminusgps-1.16.0}/COPYING +0 -0
  12. {python_terminusgps-1.15.1 → python_terminusgps-1.16.0}/README.md +0 -0
  13. {python_terminusgps-1.15.1 → python_terminusgps-1.16.0}/terminusgps/__init__.py +0 -0
  14. {python_terminusgps-1.15.1 → python_terminusgps-1.16.0}/terminusgps/authorizenet/__init__.py +0 -0
  15. {python_terminusgps-1.15.1 → python_terminusgps-1.16.0}/terminusgps/authorizenet/auth.py +0 -0
  16. {python_terminusgps-1.15.1 → python_terminusgps-1.16.0}/terminusgps/authorizenet/profiles/__init__.py +0 -0
  17. {python_terminusgps-1.15.1 → python_terminusgps-1.16.0}/terminusgps/authorizenet/profiles/addresses.py +0 -0
  18. {python_terminusgps-1.15.1 → python_terminusgps-1.16.0}/terminusgps/authorizenet/profiles/base.py +0 -0
  19. {python_terminusgps-1.15.1 → python_terminusgps-1.16.0}/terminusgps/authorizenet/profiles/customers.py +0 -0
  20. {python_terminusgps-1.15.1 → python_terminusgps-1.16.0}/terminusgps/authorizenet/profiles/payments.py +0 -0
  21. {python_terminusgps-1.15.1 → python_terminusgps-1.16.0}/terminusgps/authorizenet/subscriptions.py +0 -0
  22. {python_terminusgps-1.15.1 → python_terminusgps-1.16.0}/terminusgps/aws/__init__.py +0 -0
  23. {python_terminusgps-1.15.1 → python_terminusgps-1.16.0}/terminusgps/aws/secrets.py +0 -0
  24. {python_terminusgps-1.15.1 → python_terminusgps-1.16.0}/terminusgps/aws/ses.py +0 -0
  25. {python_terminusgps-1.15.1 → python_terminusgps-1.16.0}/terminusgps/settings.py +0 -0
  26. {python_terminusgps-1.15.1 → python_terminusgps-1.16.0}/terminusgps/twilio/__init__.py +0 -0
  27. {python_terminusgps-1.15.1 → python_terminusgps-1.16.0}/terminusgps/twilio/caller.py +0 -0
  28. {python_terminusgps-1.15.1 → python_terminusgps-1.16.0}/terminusgps/wialon/__init__.py +0 -0
  29. {python_terminusgps-1.15.1 → python_terminusgps-1.16.0}/terminusgps/wialon/constants.py +0 -0
  30. {python_terminusgps-1.15.1 → python_terminusgps-1.16.0}/terminusgps/wialon/errors.py +0 -0
  31. {python_terminusgps-1.15.1 → python_terminusgps-1.16.0}/terminusgps/wialon/flags.py +0 -0
  32. {python_terminusgps-1.15.1 → python_terminusgps-1.16.0}/terminusgps/wialon/items/__init__.py +0 -0
  33. {python_terminusgps-1.15.1 → python_terminusgps-1.16.0}/terminusgps/wialon/items/notification.py +0 -0
  34. {python_terminusgps-1.15.1 → python_terminusgps-1.16.0}/terminusgps/wialon/items/retranslator.py +0 -0
  35. {python_terminusgps-1.15.1 → python_terminusgps-1.16.0}/terminusgps/wialon/items/route.py +0 -0
  36. {python_terminusgps-1.15.1 → python_terminusgps-1.16.0}/terminusgps/wialon/items/unit.py +0 -0
  37. {python_terminusgps-1.15.1 → python_terminusgps-1.16.0}/terminusgps/wialon/items/user.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-terminusgps
3
- Version: 1.15.1
3
+ Version: 1.16.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
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "python-terminusgps"
3
- version = "1.15.1"
3
+ version = "1.16.0"
4
4
  description = "Provides abstractions/utilities for working with Wialon API, Authorize.NET API, AWS API, and more."
5
5
  readme = "README.md"
6
6
  authors = [ {name = "Blake Nall", email = "blake@terminusgps.com" } ]
@@ -1,4 +1,5 @@
1
1
  from abc import abstractmethod
2
+ from typing import Type
2
3
 
3
4
  from terminusgps.wialon import flags
4
5
  from terminusgps.wialon.session import WialonSession
@@ -24,6 +25,7 @@ class WialonBase:
24
25
  return str(self.id)
25
26
 
26
27
  def populate(self) -> None:
28
+ """Retrieves and saves the latest data for the item from Wialon."""
27
29
  response = self.session.wialon_api.core_search_item(
28
30
  **{"id": str(self.id), "flags": 0x1}
29
31
  ).get("item", {})
@@ -53,7 +55,7 @@ class WialonBase:
53
55
 
54
56
  return int(self._id) if self._id else None
55
57
 
56
- def has_access(self, other: "WialonBase") -> bool:
58
+ def has_access(self, other: Type["WialonBase"]) -> bool:
57
59
  """
58
60
  Checks if this Wialon object has access to ``other``.
59
61
 
@@ -74,7 +76,7 @@ class WialonBase:
74
76
  """
75
77
  Renames the Wialon object to the new name.
76
78
 
77
- :param new_name: A new name for this object.
79
+ :param new_name: A new name.
78
80
  :type new_name: :py:obj:`str`
79
81
  :returns: Nothing.
80
82
  :rtype: :py:obj:`None`
@@ -215,24 +217,27 @@ class WialonBase:
215
217
 
216
218
  self.session.wialon_api.item_delete_item(**{"itemId": self.id})
217
219
 
218
- def _get_cfields(self) -> dict:
219
- response = self.session.wialon_api.core_search_item(
220
- **{"id": self.id, "flags": flags.DATAFLAG_UNIT_CUSTOM_FIELDS}
221
- )
222
- return response["item"]["flds"]
223
-
224
- def _get_afields(self) -> dict:
225
- response = self.session.wialon_api.core_search_item(
226
- **{"id": self.id, "flags": flags.DATAFLAG_UNIT_ADMIN_FIELDS}
227
- )
228
- return response["item"]["aflds"]
229
-
230
220
  @property
231
221
  def cfields(self) -> dict:
232
- fields = self._get_cfields()
233
- return {field["n"]: field["v"] for _, field in fields.items()}
222
+ """Custom fields associated with the Wialon object."""
223
+ fields = (
224
+ self.session.wialon_api.core_search_item(
225
+ **{"id": self.id, "flags": flags.DATAFLAG_UNIT_CUSTOM_FIELDS}
226
+ )
227
+ .get("item", {})
228
+ .get("cfields")
229
+ )
230
+
231
+ return {field["n"]: field["v"] for _, field in fields.items()} if fields else {}
234
232
 
235
233
  @property
236
234
  def afields(self) -> dict:
237
- fields = self._get_afields()
238
- return {field["n"]: field["v"] for _, field in fields.items()}
235
+ """Admin fields associated with the Wialon object."""
236
+ fields = (
237
+ self.session.wialon_api.core_search_item(
238
+ **{"id": self.id, "flags": flags.DATAFLAG_UNIT_ADMIN_FIELDS}
239
+ )
240
+ .get("item", {})
241
+ .get("afields")
242
+ )
243
+ return {field["n"]: field["v"] for _, field in fields.items()} if fields else {}
@@ -21,10 +21,34 @@ class WialonResource(WialonBase):
21
21
  return response.get("item", {}).get("id")
22
22
 
23
23
  def delete(self) -> None:
24
- if self.is_account:
25
- self.delete_account()
26
- return
27
- super().delete()
24
+ """
25
+ Deletes all micro-objects assigned to the resource.
26
+
27
+ If the resource is an account, instead deletes all macro-objects and micro-objects assigned to the account.
28
+
29
+ :returns: Nothing.
30
+ :rtype: :py:obj:`None`
31
+
32
+ """
33
+ self.delete_account() if self.is_account else super().delete()
34
+
35
+ @property
36
+ def is_dealer(self) -> bool:
37
+ """
38
+ Whether or not the resource/account has dealer rights.
39
+
40
+ If the resource is **not** an account, this always returns :py:obj:`False`.
41
+
42
+ :type: :py:obj:`bool`
43
+
44
+ """
45
+ if not self.is_account:
46
+ return False # Resources cannot have dealer rights
47
+ return bool(
48
+ self.session.wialon_api.get_account_data(
49
+ **{"itemId": self.id, "type": 1}
50
+ ).get("dealerRights")
51
+ )
28
52
 
29
53
  @property
30
54
  def is_account(self) -> bool:
@@ -60,6 +84,8 @@ class WialonResource(WialonBase):
60
84
  """
61
85
  Sets dealer rights on the account.
62
86
 
87
+ You **probably don't** need to use this method.
88
+
63
89
  :param enabled: :py:obj:`True` to enable dealer rights, :py:obj:`False` to disable dealer rights. Default is :py:obj:`False`.
64
90
  :type enabled: :py:obj:`bool`
65
91
  :raises AssertionError: If the resource is not an account.
@@ -123,6 +149,7 @@ class WialonResource(WialonBase):
123
149
  self.session.wialon_api.account_create_account(
124
150
  **{"itemId": self.id, "plan": billing_plan}
125
151
  )
152
+ self.set_settings_flags()
126
153
 
127
154
  def delete_account(self) -> None:
128
155
  """
@@ -1,4 +1,5 @@
1
- from terminusgps.wialon import constants, flags
1
+ from typing import Type
2
+ from terminusgps.wialon import flags
2
3
  from terminusgps.wialon.items.base import WialonBase
3
4
 
4
5
 
@@ -31,7 +32,7 @@ class WialonUnitGroup(WialonBase):
31
32
  )
32
33
  return response.get("item", {}).get("id")
33
34
 
34
- def _update_items(self, new_items: list[str]) -> None:
35
+ def set_items(self, new_items: list[str]) -> None:
35
36
  """
36
37
  Sets this group's members to a list of Wialon unit ids.
37
38
 
@@ -47,7 +48,7 @@ class WialonUnitGroup(WialonBase):
47
48
  **{"itemId": self.id, "units": new_items}
48
49
  )
49
50
 
50
- def is_member(self, item: WialonBase) -> bool:
51
+ def is_member(self, item: Type[WialonBase]) -> bool:
51
52
  """
52
53
  Determines whether or not ``item`` is a member of the group.
53
54
 
@@ -60,43 +61,9 @@ class WialonUnitGroup(WialonBase):
60
61
  """
61
62
  return True if str(item.id) in self.items else False
62
63
 
63
- def grant_access(
64
- self, item: WialonBase, access_mask: int = constants.ACCESSMASK_UNIT_BASIC
65
- ) -> None:
64
+ def add_item(self, item: Type[WialonBase]) -> None:
66
65
  """
67
- Grants ``item`` access to the group, if it didn't already have access.
68
-
69
- :param item: A Wialon object.
70
- :type item: :py:obj:`~terminusgps.wialon.items.base.WialonBase`
71
- :param access_mask: A Wialon access mask.
72
- :type access_mask: :py:obj:`int`
73
- :raises WialonError: If something goes wrong with Wialon.
74
- :returns: Nothing.
75
- :rtype: :py:obj:`None`
76
-
77
- """
78
- self.session.wialon_api.user_update_item_access(
79
- **{"userId": item.id, "itemId": self.id, "accessMask": access_mask}
80
- )
81
-
82
- def revoke_access(self, item: WialonBase) -> None:
83
- """
84
- Revokes ``item``'s access from the group, if it had access.
85
-
86
- :param item: A Wialon object.
87
- :type item: :py:obj:`~terminusgps.wialon.items.base.WialonBase`
88
- :raises WialonError: If something goes wrong with Wialon.
89
- :returns: Nothing.
90
- :rtype: :py:obj:`None`
91
-
92
- """
93
- self.session.wialon_api.user_update_item_access(
94
- **{"userId": item.id, "itemId": self.id, "accessMask": 0}
95
- )
96
-
97
- def add_item(self, item: WialonBase) -> None:
98
- """
99
- Adds a Wialon unit to the group.
66
+ Adds a Wialon item to the group.
100
67
 
101
68
  :param item: A Wialon object.
102
69
  :type item: :py:obj:`~terminusgps.wialon.items.base.WialonBase`
@@ -106,9 +73,9 @@ class WialonUnitGroup(WialonBase):
106
73
 
107
74
  """
108
75
  new_items: list[str] = self.items.copy() + [str(item.id)]
109
- self._update_items(new_items)
76
+ self.set_items(new_items)
110
77
 
111
- def rm_item(self, item: WialonBase) -> None:
78
+ def rm_item(self, item: Type[WialonBase]) -> None:
112
79
  """
113
80
  Removes a Wialon unit from the group, if it's a member of the group.
114
81
 
@@ -123,7 +90,7 @@ class WialonUnitGroup(WialonBase):
123
90
  assert self.is_member(item), f"Cannot remove {item}, it's not in the group"
124
91
  new_items: list[str] = self.items.copy()
125
92
  new_items.remove(str(item.id))
126
- self._update_items(new_items)
93
+ self.set_items(new_items)
127
94
 
128
95
  @property
129
96
  def items(self) -> list[str]:
@@ -149,4 +116,4 @@ class WialonUnitGroup(WialonBase):
149
116
  "to": 0,
150
117
  }
151
118
  )
152
- return [str(unit_id) for unit_id in response.get("items")[0].get("u", [])]
119
+ return [unit_id for unit_id in response.get("items")[0].get("u", [])]
@@ -1,25 +1,24 @@
1
1
  import threading
2
2
  import logging
3
-
4
- from typing import Any
5
- from dataclasses import dataclass
6
- from datetime import datetime
3
+ import typing
4
+ import dataclasses
5
+ import datetime
7
6
 
8
7
  from wialon.api import WialonError
9
8
  from wialon.api import Wialon as WialonAPI
10
9
  from django.conf import settings
11
10
  from django.utils import timezone
12
11
 
13
- from .errors import WialonLogoutError, WialonLoginError
12
+ from terminusgps.wialon.errors import WialonLogoutError, WialonLoginError
14
13
 
15
14
 
16
- @dataclass
15
+ @dataclasses.dataclass
17
16
  class WialonAPICall:
18
17
  action: str
19
- timestamp: datetime
18
+ timestamp: datetime.datetime
20
19
  args: tuple
21
20
  kwargs: dict
22
- result: Any = None
21
+ result: typing.Any = None
23
22
  error: Exception | None = None
24
23
 
25
24
 
@@ -45,7 +44,7 @@ class Wialon(WialonAPI):
45
44
  def failure_rate(self) -> float:
46
45
  return len(self.failed_calls) / self.total_calls
47
46
 
48
- def call(self, action_name: str, *argc, **kwargs) -> Any:
47
+ def call(self, action_name: str, *argc, **kwargs) -> typing.Any:
49
48
  self.logger.info(f"Executing '{action_name}'...")
50
49
  self.logger.debug(f"Executing '{action_name}' using: '{kwargs}'")
51
50
  call_record = WialonAPICall(
@@ -0,0 +1,185 @@
1
+ import secrets
2
+ import string
3
+ import warnings
4
+ import typing
5
+
6
+ from terminusgps.wialon import items, flags
7
+ from terminusgps.wialon.items.base import WialonBase
8
+ from terminusgps.wialon.session import WialonSession
9
+
10
+
11
+ def get_hw_type_id(name: str, session: WialonSession) -> int | None:
12
+ """
13
+ Takes a Wialon hardware type name and returns its id, if it exists.
14
+
15
+ :param name: The name of a Wialon hardware type.
16
+ :type name: :py:obj:`str`
17
+ :param session: A valid Wialon API session.
18
+ :type session: :py:obj:`~terminusgps.wialon.session.WialonSession`
19
+ :raises WialonError: If something goes wrong with Wialon.
20
+ :returns: A Wialon hardware type id, if it was found.
21
+ :rtype: :py:obj:`int` | :py:obj:`None`
22
+
23
+ """
24
+ response = session.wialon_api.core_get_hw_types(
25
+ **{
26
+ "filterType": "id",
27
+ "filterValue": "name,id",
28
+ "includeType": "true",
29
+ "ignoreRename": "true",
30
+ }
31
+ )
32
+ hw_types = {item.get("name"): item.get("id") for item in response}
33
+ return int(hw_types.get(name)) if name in hw_types.keys() else None
34
+
35
+
36
+ def get_id_from_iccid(iccid: str, session: WialonSession) -> str | None:
37
+ """
38
+ Takes a Wialon unit's IMEI # and returns its unit id, if it exists.
39
+
40
+ :param iccid: A unique id.
41
+ :type iccid: :py:obj:`str`
42
+ :param session: A valid Wialon API session.
43
+ :type session: :py:obj:`~terminusgps.wialon.session.WialonSession`
44
+ :raises WialonError: If something goes wrong with Wialon.
45
+ :returns: A Wialon object id, if it was found.
46
+ :rtype: :py:obj:`str` | :py:obj:`None`
47
+
48
+ """
49
+ response = session.wialon_api.core_search_items(
50
+ **{
51
+ "spec": {
52
+ "itemsType": "avl_unit",
53
+ "propName": "rel_admin_field_value",
54
+ "propValueMask": str(iccid),
55
+ "sortType": "admin_fields",
56
+ "propType": "adminfield",
57
+ "or_logic": 0,
58
+ },
59
+ "force": 0,
60
+ "flags": flags.DATAFLAG_UNIT_BASE,
61
+ "from": 0,
62
+ "to": 0,
63
+ }
64
+ )
65
+
66
+ if response.get("totalItemsCount", 0) == 1:
67
+ return response["items"][0].get("id")
68
+
69
+
70
+ def get_wialon_cls(items_type: str) -> typing.Type[WialonBase]:
71
+ """
72
+ Returns a Wialon object class based on items_type.
73
+
74
+ :param items_type: A Wialon object type.
75
+ :type items_type: :py:obj:`str`
76
+ :returns: A subclass of :py:obj:`~terminusgps.wialon.items.base.WialonBase`.
77
+ :rtype: :py:obj:`~terminusgps.wialon.items.base.WialonBase`
78
+
79
+ """
80
+ match items_type:
81
+ case "user":
82
+ wialon_cls = items.WialonUser
83
+ case "avl_unit":
84
+ wialon_cls = items.WialonUnit
85
+ case "avl_unit_group":
86
+ wialon_cls = items.WialonUnitGroup
87
+ case "avl_resource":
88
+ wialon_cls = items.WialonResource
89
+ case _:
90
+ raise ValueError(f"Invalid items_type '{items_type}'")
91
+ return wialon_cls
92
+
93
+
94
+ def is_unique(value: str, session: WialonSession, items_type: str = "avl_unit") -> bool:
95
+ """
96
+ Determines if the value is unique among Wialon objects of type 'items_type'.
97
+
98
+ :param value: A value whose uniqueness is unknown.
99
+ :type value: :py:obj:`str`
100
+ :param session: A valid Wialon API session.
101
+ :type session: :py:obj:`~terminusgps.wialon.session.WialonSession`
102
+ :param items_type: The type of Wialon objects to validate the value against. Default is ``"avl_unit"``.
103
+ :type items_type: :py:obj:`str`
104
+ :returns: Whether or not the value is unique among 'items_type'.
105
+ :rtype: :py:obj:`bool`
106
+
107
+
108
+ """
109
+ result = session.wialon_api.core_check_unique(
110
+ **{"type": items_type, "value": value.strip()}
111
+ ).get("result")
112
+ return not bool(result)
113
+
114
+
115
+ def generate_wialon_password(length: int = 32) -> str:
116
+ """
117
+ Generates a Wialon compliant password of random characters.
118
+
119
+ Password length can be between ``8`` and ``64`` characters.
120
+
121
+ :param length: Length of the generated password. Default is ``32``.
122
+ :type length: :py:obj:`int`
123
+ :raises ValueError: If the provided length is invalid.
124
+ :returns: A Wialon compliant password.
125
+ :rtype: :py:obj:`str`
126
+
127
+ """
128
+ min_length, max_length = 8, 64
129
+ if length > max_length:
130
+ raise ValueError(
131
+ f"Password cannot be greater than {max_length} characters in length. Got {length}."
132
+ )
133
+ elif length < min_length:
134
+ raise ValueError(
135
+ f"Password cannot be less than {min_length} characters in length. Got {length}."
136
+ )
137
+
138
+ s0 = list(string.ascii_uppercase)
139
+ s1 = list(string.ascii_lowercase)
140
+ s2 = list(string.digits)
141
+ s3 = list("!@#$%^*()[]-_+")
142
+
143
+ while True:
144
+ password = "".join([secrets.choice(s0 + s1 + s2 + s3) for _ in range(length)])
145
+ if (
146
+ any(c.islower() for c in password)
147
+ and any(c.isupper() for c in password)
148
+ and sum(c.isdigit() for c in password) >= 3
149
+ and any(c in s3 for c in password)
150
+ ):
151
+ break
152
+ return password
153
+
154
+
155
+ def gen_wialon_password(length: int = 32) -> str:
156
+ """
157
+ DEPRECATED: Use :py:func:`~terminusgps.wialon.utils.generate_wialon_password` instead.
158
+
159
+ Generates a Wialon compliant password of random characters.
160
+
161
+ Password length can be between ``8`` and ``64`` characters.
162
+
163
+ :param length: Length of the generated password. Default is ``32``.
164
+ :type length: :py:obj:`int`
165
+ :raises ValueError: If the provided length is invalid.
166
+ :returns: A Wialon compliant password.
167
+ :rtype: :py:obj:`str`
168
+
169
+ """
170
+ warnings.warn(
171
+ "gen_wialon_password is deprecated and will be removed in a future version. Use generate_wialon_password instead.",
172
+ DeprecationWarning,
173
+ stacklevel=2,
174
+ )
175
+ return generate_wialon_password(length)
176
+
177
+
178
+ __all__ = [
179
+ "get_hw_type_id",
180
+ "get_id_from_iccid",
181
+ "get_wialon_cls",
182
+ "is_unique",
183
+ "generate_wialon_password",
184
+ "gen_wialon_password",
185
+ ]
@@ -504,7 +504,7 @@ wheels = [
504
504
 
505
505
  [[package]]
506
506
  name = "python-terminusgps"
507
- version = "1.15.1"
507
+ version = "1.16.0"
508
508
  source = { editable = "." }
509
509
  dependencies = [
510
510
  { name = "argparse" },
@@ -1,113 +0,0 @@
1
- import secrets
2
- import string
3
-
4
- from typing import Any
5
- from .session import WialonSession
6
- from .flags import DATAFLAG_UNIT_BASE
7
-
8
- from terminusgps.wialon.items import (
9
- WialonUser,
10
- WialonUnit,
11
- WialonUnitGroup,
12
- WialonResource,
13
- )
14
-
15
-
16
- def get_hw_type_id(name: str, session: WialonSession) -> int | None:
17
- response = session.wialon_api.core_get_hw_types(
18
- **{
19
- "filterType": "id",
20
- "filterValue": "name,id",
21
- "includeType": "true",
22
- "ignoreRename": "true",
23
- }
24
- )
25
- hw_types = {item.get("name"): item.get("id") for item in response}
26
- return int(hw_types.get(name)) if name in hw_types.keys() else None
27
-
28
-
29
- def get_wialon_cls(items_type: str) -> Any:
30
- """Returns a Wialon object class based on items_type."""
31
- match items_type:
32
- case "user":
33
- wialon_cls = WialonUser
34
- case "avl_unit":
35
- wialon_cls = WialonUnit
36
- case "avl_unit_group":
37
- wialon_cls = WialonUnitGroup
38
- case "avl_resource":
39
- wialon_cls = WialonResource
40
- case _:
41
- raise ValueError(f"Invalid items_type '{items_type}'")
42
- return wialon_cls
43
-
44
-
45
- def is_unique(value: str, session: WialonSession, items_type: str = "avl_unit") -> bool:
46
- """Determines if the value is unique among Wialon objects of type 'items_type'."""
47
- result = session.wialon_api.core_check_unique(
48
- **{"type": items_type, "value": value.strip()}
49
- ).get("result")
50
- return not bool(result)
51
-
52
-
53
- def gen_wialon_password(length: int = 32) -> str:
54
- """Generates a Wialon compliant password."""
55
- min_length, max_length = 4, 64
56
- if length > max_length:
57
- raise ValueError(
58
- f"Password cannot be greater than {max_length} characters in length. Got {length}."
59
- )
60
- elif length < min_length:
61
- raise ValueError(
62
- f"Password cannot be less than {min_length} characters in length. Got {length}."
63
- )
64
-
65
- s0 = list(string.ascii_uppercase)
66
- s1 = list(string.ascii_lowercase)
67
- s2 = list(string.digits)
68
- s3 = list("!@#$%^*()[]-_+")
69
-
70
- while True:
71
- password = "".join([secrets.choice(s0 + s1 + s2 + s3) for _ in range(length)])
72
- if (
73
- any(c.islower() for c in password)
74
- and any(c.isupper() for c in password)
75
- and sum(c.isdigit() for c in password) >= 3
76
- and any(c in s3 for c in password)
77
- ):
78
- break
79
- return password
80
-
81
-
82
- def get_id_from_iccid(iccid: str, session: WialonSession) -> str | None:
83
- """Takes a Wialon unit's IMEI # and returns its unit id, if it exists."""
84
- response = session.wialon_api.core_search_items(
85
- **{
86
- "spec": {
87
- "itemsType": "avl_unit",
88
- "propName": "rel_admin_field_value",
89
- "propValueMask": str(iccid),
90
- "sortType": "admin_fields",
91
- "propType": "adminfield",
92
- "or_logic": 0,
93
- },
94
- "force": 0,
95
- "flags": DATAFLAG_UNIT_BASE,
96
- "from": 0,
97
- "to": 0,
98
- }
99
- )
100
-
101
- if response.get("totalItemsCount", 0) == 1:
102
- return response["items"][0].get("id")
103
-
104
-
105
- def main() -> None:
106
- with WialonSession() as session:
107
- wialon_id = get_id_from_iccid("89015809000307608963", session)
108
- print(f"{wialon_id = }")
109
- return
110
-
111
-
112
- if __name__ == "__main__":
113
- main()