python-terminusgps 1.4.7__tar.gz → 1.5.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.
Files changed (31) hide show
  1. {python_terminusgps-1.4.7 → python_terminusgps-1.5.0}/PKG-INFO +1 -1
  2. {python_terminusgps-1.4.7 → python_terminusgps-1.5.0}/pyproject.toml +1 -1
  3. {python_terminusgps-1.4.7 → python_terminusgps-1.5.0}/terminusgps/authorizenet/auth.py +7 -1
  4. python_terminusgps-1.5.0/terminusgps/wialon/__init__.py +0 -0
  5. {python_terminusgps-1.4.7 → python_terminusgps-1.5.0}/terminusgps/wialon/items/base.py +114 -15
  6. python_terminusgps-1.5.0/terminusgps/wialon/items/unit.py +152 -0
  7. python_terminusgps-1.5.0/terminusgps/wialon/items/unit_group.py +152 -0
  8. python_terminusgps-1.5.0/terminusgps/wialon/items/user.py +183 -0
  9. {python_terminusgps-1.4.7 → python_terminusgps-1.5.0}/terminusgps/wialon/session.py +8 -4
  10. {python_terminusgps-1.4.7 → python_terminusgps-1.5.0}/terminusgps/wialon/utils.py +0 -9
  11. {python_terminusgps-1.4.7 → python_terminusgps-1.5.0}/uv.lock +1 -1
  12. python_terminusgps-1.4.7/terminusgps/wialon/__init__.py +0 -5
  13. python_terminusgps-1.4.7/terminusgps/wialon/items/account.py +0 -15
  14. python_terminusgps-1.4.7/terminusgps/wialon/items/unit.py +0 -90
  15. python_terminusgps-1.4.7/terminusgps/wialon/items/unit_group.py +0 -69
  16. python_terminusgps-1.4.7/terminusgps/wialon/items/user.py +0 -79
  17. {python_terminusgps-1.4.7 → python_terminusgps-1.5.0}/.gitignore +0 -0
  18. {python_terminusgps-1.4.7 → python_terminusgps-1.5.0}/COPYING +0 -0
  19. {python_terminusgps-1.4.7 → python_terminusgps-1.5.0}/README.md +0 -0
  20. {python_terminusgps-1.4.7 → python_terminusgps-1.5.0}/requirements.txt +0 -0
  21. {python_terminusgps-1.4.7 → python_terminusgps-1.5.0}/terminusgps/__init__.py +0 -0
  22. {python_terminusgps-1.4.7 → python_terminusgps-1.5.0}/terminusgps/authorizenet/__init__.py +0 -0
  23. {python_terminusgps-1.4.7 → python_terminusgps-1.5.0}/terminusgps/aws/__init__.py +0 -0
  24. {python_terminusgps-1.4.7 → python_terminusgps-1.5.0}/terminusgps/aws/secrets.py +0 -0
  25. {python_terminusgps-1.4.7 → python_terminusgps-1.5.0}/terminusgps/wialon/constants.py +0 -0
  26. {python_terminusgps-1.4.7 → python_terminusgps-1.5.0}/terminusgps/wialon/errors.py +0 -0
  27. {python_terminusgps-1.4.7 → python_terminusgps-1.5.0}/terminusgps/wialon/flags.py +0 -0
  28. {python_terminusgps-1.4.7 → python_terminusgps-1.5.0}/terminusgps/wialon/items/__init__.py +0 -0
  29. {python_terminusgps-1.4.7 → python_terminusgps-1.5.0}/terminusgps/wialon/items/resource.py +0 -0
  30. {python_terminusgps-1.4.7 → python_terminusgps-1.5.0}/terminusgps/wialon/items/retranslator.py +0 -0
  31. {python_terminusgps-1.4.7 → python_terminusgps-1.5.0}/terminusgps/wialon/items/route.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-terminusgps
3
- Version: 1.4.7
3
+ Version: 1.5.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.4.7"
3
+ version = "1.5.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,10 +1,14 @@
1
1
  from authorizenet.apicontractsv1 import merchantAuthenticationType
2
2
  from authorizenet.constants import constants
3
3
 
4
- from django.conf import settings
4
+ from django.conf import ImproperlyConfigured, settings
5
5
 
6
6
 
7
7
  def get_merchant_auth() -> merchantAuthenticationType:
8
+ if not hasattr(settings, "MERCHANT_AUTH_LOGIN_ID"):
9
+ raise ImproperlyConfigured("'MERCHANT_AUTH_LOGIN_ID' is required.")
10
+ if not hasattr(settings, "MERCHANT_AUTH_TRANSACTION_KEY"):
11
+ raise ImproperlyConfigured("'MERCHANT_AUTH_TRANSACTION_KEY' is required.")
8
12
  return merchantAuthenticationType(
9
13
  name=str(settings.MERCHANT_AUTH_LOGIN_ID),
10
14
  transactionKey=str(settings.MERCHANT_AUTH_TRANSACTION_KEY),
@@ -12,4 +16,6 @@ def get_merchant_auth() -> merchantAuthenticationType:
12
16
 
13
17
 
14
18
  def get_environment() -> str:
19
+ if not hasattr(settings, "DEBUG"):
20
+ raise ImproperlyConfigured("'DEBUG' is required.")
15
21
  return constants.SANDBOX if settings.DEBUG else constants.PRODUCTION
@@ -1,10 +1,8 @@
1
1
  import terminusgps.wialon.flags as flags
2
2
  from terminusgps.wialon.session import WialonSession
3
- from terminusgps.wialon.utils import repopulate
4
3
 
5
4
 
6
5
  class WialonBase:
7
- @repopulate
8
6
  def __init__(
9
7
  self, *, id: str | None = None, session: WialonSession, **kwargs
10
8
  ) -> None:
@@ -16,17 +14,45 @@ class WialonBase:
16
14
  self._id = id
17
15
 
18
16
  def __str__(self) -> str:
19
- return f"{self.__class__}:{self.id}"
17
+ return str(self.id)
18
+
19
+ def populate(self) -> None:
20
+ response = self.session.wialon_api.core_search_item(
21
+ **{"id": str(self.id), "flags": 0x1}
22
+ )["item"]
23
+ self.name = response.get("nm")
24
+ self.hw_type = response.get("cls")
25
+ self.access_lvl = response.get("uacl")
20
26
 
21
27
  @property
22
28
  def session(self) -> WialonSession:
29
+ """
30
+ A valid Wialon API session.
31
+
32
+ :type: :py:obj:`~terminusgps.wialon.session.WialonSession`
33
+
34
+ """
35
+
23
36
  return self._session
24
37
 
25
38
  @property
26
39
  def id(self) -> int | None:
40
+ """
41
+ A unique Wialon ID.
42
+
43
+ :type: :py:obj:`int` | :py:obj:`None`
44
+
45
+ """
46
+
27
47
  return int(self._id) if self._id else None
28
48
 
29
49
  def has_access(self, other: "WialonBase") -> bool:
50
+ """
51
+ Checks if this Wialon object has access to ``other``.
52
+
53
+ :type: :py:obj:`bool`
54
+
55
+ """
30
56
  response = self.session.wialon_api.core_check_accessors(
31
57
  **{"items": [other.id], "flags": False}
32
58
  )
@@ -36,22 +62,32 @@ class WialonBase:
36
62
  """Creates a Wialon object and returns the newly created Wialon object's id."""
37
63
  raise NotImplementedError("Subclasses must implement this method.")
38
64
 
39
- def populate(self) -> None:
40
- """Retrieves and sets hw_type and name for this Wialon object."""
41
- item = self.session.wialon_api.core_search_item(
42
- **{"id": self.id, "flags": flags.DATAFLAG_UNIT_BASE}
43
- ).get("item", {})
44
- self.hw_type = item.get("cls", None)
45
- self.name = item.get("nm", None)
46
- self.uid = item.get("uid", None)
47
-
48
- @repopulate
49
65
  def rename(self, new_name: str) -> None:
66
+ """
67
+ Renames the Wialon object to the new name.
68
+
69
+ :param new_name: A new name for this object.
70
+ :type new_name: :py:obj:`str`
71
+ :returns: Nothing.
72
+ :rtype: :py:obj:`None`
73
+
74
+ """
75
+
50
76
  self.session.wialon_api.item_update_name(
51
77
  **{"itemId": self.id, "name": new_name}
52
78
  )
53
79
 
54
80
  def add_afield(self, field: tuple[str, str]) -> None:
81
+ """
82
+ Adds an admin field to the Wialon object.
83
+
84
+ :param field: A tuple containing the name of the field and the value of the field.
85
+ :type field: :py:obj:`tuple`
86
+ :returns: Nothing.
87
+ :rtype: :py:obj:`None`
88
+
89
+ """
90
+
55
91
  self.session.wialon_api.item_update_admin_field(
56
92
  **{
57
93
  "itemId": self.id,
@@ -63,6 +99,18 @@ class WialonBase:
63
99
  )
64
100
 
65
101
  def update_afield(self, field_id: int, field: tuple[str, str]) -> None:
102
+ """
103
+ Updates an admin field by id to the Wialon object.
104
+
105
+ :param field_id: The admin field id.
106
+ :type field_id: :py:obj:`int`
107
+ :param field: A tuple containing the name of the field and the value of the field.
108
+ :type field: :py:obj:`tuple`
109
+ :returns: Nothing.
110
+ :rtype: :py:obj:`None`
111
+
112
+ """
113
+
66
114
  self.session.wialon_api.item_update_admin_field(
67
115
  **{
68
116
  "itemId": self.id,
@@ -74,6 +122,16 @@ class WialonBase:
74
122
  )
75
123
 
76
124
  def add_cfield(self, field: tuple[str, str]) -> None:
125
+ """
126
+ Adds a custom field to the Wialon object.
127
+
128
+ :param field: A tuple containing the name of the field and the value of the field.
129
+ :type field: :py:obj:`tuple`
130
+ :returns: Nothing.
131
+ :rtype: :py:obj:`None`
132
+
133
+ """
134
+
77
135
  self.session.wialon_api.item_update_custom_field(
78
136
  **{
79
137
  "itemId": self.id,
@@ -85,6 +143,18 @@ class WialonBase:
85
143
  )
86
144
 
87
145
  def update_cfield(self, field_id: int, field: tuple[str, str]) -> None:
146
+ """
147
+ Updates a custom field by id.
148
+
149
+ :param field_id: The admin field id.
150
+ :type field_id: :py:obj:`int`
151
+ :param field: A tuple containing the name of the field and the value of the field.
152
+ :type field: :py:obj:`tuple`
153
+ :returns: Nothing.
154
+ :rtype: :py:obj:`None`
155
+
156
+ """
157
+
88
158
  self.session.wialon_api.item_update_custom_field(
89
159
  **{
90
160
  "itemId": self.id,
@@ -95,17 +165,46 @@ class WialonBase:
95
165
  }
96
166
  )
97
167
 
98
- def add_cproperty(self, field: tuple[str, str]) -> None:
168
+ def add_cproperty(self, property: tuple[str, str]) -> None:
169
+ """
170
+ Adds a custom property to the Wialon object.
171
+
172
+ :param property: A tuple containing the name of the property and the value of the property.
173
+ :type property: :py:obj:`tuple`
174
+ :returns: Nothing.
175
+ :rtype: :py:obj:`None`
176
+
177
+ """
178
+
99
179
  self.session.wialon_api.item_update_custom_property(
100
- **{"itemId": self.id, "name": field[0], "value": field[1]}
180
+ **{"itemId": self.id, "name": property[0], "value": property[1]}
101
181
  )
102
182
 
103
183
  def add_profile_field(self, field: tuple[str, str]) -> None:
184
+ """
185
+ Adds a profile field to the Wialon object.
186
+
187
+ :param field: A tuple containing the name of the field and the value of the field.
188
+ :type field: :py:obj:`tuple`
189
+ :returns: Nothing.
190
+ :rtype: :py:obj:`None`
191
+
192
+ """
193
+
104
194
  self.session.wialon_api.item_update_profile_field(
105
195
  **{"itemId": self.id, "n": field[0], "v": field[1]}
106
196
  )
107
197
 
108
198
  def delete(self) -> None:
199
+ """
200
+ Deletes the Wialon object.
201
+
202
+ :raises WialonError: If something goes wrong with Wialon.
203
+ :returns: Nothing.
204
+ :rtype: :py:obj:`None`
205
+
206
+ """
207
+
109
208
  self.session.wialon_api.item_delete_item(**{"itemId": self.id})
110
209
 
111
210
  def _get_cfields(self) -> dict:
@@ -0,0 +1,152 @@
1
+ from urllib.parse import quote_plus
2
+
3
+ from terminusgps.wialon import flags
4
+ from terminusgps.wialon.items.base import WialonBase
5
+
6
+
7
+ class WialonUnit(WialonBase):
8
+ def create(self, **kwargs) -> int | None:
9
+ """
10
+ Creates a new Wialon unit.
11
+
12
+ :param creator_id: A Wialon user id.
13
+ :type creator_id: :py:obj:`int`
14
+ :param name: A new name for the unit.
15
+ :type name: :py:obj:`str`
16
+ :param hw_type: A Wialon hardware type.
17
+ :type hw_type: :py:obj:`str`
18
+ :returns: The Wialon id for the new unit.
19
+ :rtype: :py:obj:`int` | :py:obj:`None`
20
+
21
+ """
22
+ if not kwargs.get("creator_id"):
23
+ raise ValueError("'creator_id' is required on creation.")
24
+ if not kwargs.get("name"):
25
+ raise ValueError("'name' is required on creation.")
26
+ if not kwargs.get("hw_type"):
27
+ raise ValueError("'hw_type' is required on creation.")
28
+
29
+ response = self.session.wialon_api.core_create_unit(
30
+ **{
31
+ "creatorId": kwargs["creator_id"],
32
+ "name": kwargs["name"],
33
+ "hwTypeId": kwargs["hw_type"],
34
+ "dataFlags": flags.DATAFLAG_UNIT_BASE,
35
+ }
36
+ )
37
+ return response.get("item", {}).get("id")
38
+
39
+ def execute_command(
40
+ self,
41
+ name: str,
42
+ link_type: str,
43
+ timeout: int = 5,
44
+ flags: int = 0,
45
+ param: dict | None = None,
46
+ ) -> None:
47
+ """
48
+ Executes a command on this Wialon unit.
49
+
50
+ :param name: A Wialon command name.
51
+ :type name: :py:obj:`str`
52
+ :param link_type: A protocol to use for the Wialon command.
53
+ :type link_type: :py:obj:`str`
54
+ :param timeout: How long (in seconds) to wait before timing out command execution. Default is ``5``.
55
+ :type timeout: :py:obj:`int`
56
+ :param flags: Flags to pass to the Wialon command execution.
57
+ :type flags: :py:obj:`int`
58
+ :param param: Additional parameters to execute the command with.
59
+ :type param: :py:obj:`dict` | :py:obj:`None`
60
+ :returns: Nothing.
61
+ :rtype: :py:obj:`None`
62
+
63
+ """
64
+
65
+ self.session.wialon_api.unit_exec_cmd(
66
+ **{
67
+ "itemId": self.id,
68
+ "commandName": name,
69
+ "linkType": link_type,
70
+ "timeout": timeout,
71
+ "flags": flags,
72
+ "param": param if param else {},
73
+ }
74
+ )
75
+
76
+ def set_access_password(self, password: str) -> None:
77
+ """
78
+ Sets a new access password for this Wialon unit.
79
+
80
+ :param password: A new access password.
81
+ :type name: :py:obj:`str`
82
+ :raises WialonError: If something goes wrong with Wialon.
83
+ :returns: Nothing.
84
+ :rtype: :py:obj:`None`
85
+
86
+ """
87
+
88
+ self.session.wialon_api.unit_update_access_password(
89
+ **{"itemId": self.id, "accessPassword": password}
90
+ )
91
+
92
+ def activate(self) -> None:
93
+ """
94
+ Activates this Wialon unit.
95
+
96
+ :raises WialonError: If something goes wrong with Wialon.
97
+ :returns: Nothing.
98
+ :rtype: :py:obj:`None`
99
+
100
+ """
101
+
102
+ self.session.wialon_api.unit_set_active(
103
+ **{"itemId": self.id, "active": int(True)}
104
+ )
105
+
106
+ def deactivate(self) -> None:
107
+ """
108
+ Deactivates this Wialon unit.
109
+
110
+ :raises WialonError: If something goes wrong with Wialon.
111
+ :returns: Nothing.
112
+ :rtype: :py:obj:`None`
113
+
114
+ """
115
+
116
+ self.session.wialon_api.unit_set_active(
117
+ **{"itemId": self.id, "active": int(False)}
118
+ )
119
+
120
+ def assign_phone(self, phone: str) -> None:
121
+ """
122
+ Assigns a phone number to this Wialon unit.
123
+
124
+ :param phone: A phone number beginning with a country code.
125
+ :type phone: :py:obj:`str`
126
+ :raises WialonError: If something goes wrong with Wialon.
127
+ :returns: Nothing.
128
+ :rtype: :py:obj:`None`
129
+
130
+ """
131
+
132
+ self.session.wialon_api.unit_update_phone(
133
+ **{"itemId": self.id, "phoneNumber": quote_plus(phone)}
134
+ )
135
+
136
+ def get_phone_numbers(self) -> list[str]:
137
+ """
138
+ Retrieves all phone numbers assigned to this Wialon unit.
139
+
140
+ This includes the usually assigned phone number + custom/admin fields labeled ``to_number``.
141
+
142
+ :raises WialonError: If something goes wrong with Wialon.
143
+ :returns: A list of phone numbers.
144
+ :rtype: :py:obj:`list`
145
+
146
+ """
147
+
148
+ phones = []
149
+ for field in self.cfields | self.afields:
150
+ if field["n"] == "to_number":
151
+ phones.append(field["v"])
152
+ return phones
@@ -0,0 +1,152 @@
1
+ from terminusgps.wialon import constants, flags
2
+ from terminusgps.wialon.items.base import WialonBase
3
+
4
+
5
+ class WialonUnitGroup(WialonBase):
6
+ def create(self, **kwargs) -> int | None:
7
+ """
8
+ Creates a new Wialon unit group.
9
+
10
+ :param creator_id: A Wialon user id.
11
+ :type creator_id: :py:obj:`int`
12
+ :param name: A name for the group.
13
+ :type name: :py:obj:`str`
14
+ :raises WialonError: If something goes wrong with Wialon.
15
+ :returns: The Wialon id for the new group.
16
+ :rtype: :py:obj:`int` | :py:obj:`None`
17
+
18
+ """
19
+
20
+ if not kwargs.get("creator_id"):
21
+ raise ValueError("'creator_id' is required on creation.")
22
+ if not kwargs.get("name"):
23
+ raise ValueError("'name' is required on creation.")
24
+
25
+ response = self.session.wialon_api.core_create_unit_group(
26
+ **{
27
+ "creatorId": kwargs["creator_id"],
28
+ "name": kwargs["name"],
29
+ "dataFlags": flags.DATAFLAG_UNIT_BASE,
30
+ }
31
+ )
32
+ return response.get("item", {}).get("id")
33
+
34
+ def _update_items(self, new_items: list[str]) -> None:
35
+ """
36
+ Sets this group's members to a list of Wialon unit ids.
37
+
38
+ :param new_items: A list of Wialon unit ids.
39
+ :type new_items: :py:obj:`list`
40
+ :raises WialonError: If something goes wrong with Wialon.
41
+ :returns: Nothing.
42
+ :rtype: :py:obj:`None`
43
+
44
+ """
45
+
46
+ self.session.wialon_api.unit_group_update_units(
47
+ **{"itemId": self.id, "units": new_items}
48
+ )
49
+
50
+ def is_member(self, item: WialonBase) -> bool:
51
+ """
52
+ Determines whether or not ``item`` is a member of the group.
53
+
54
+ :param item: A Wialon object.
55
+ :type item: :py:obj:`~terminusgps.wialon.items.base.WialonBase`
56
+ :raises WialonError: If something goes wrong with Wialon.
57
+ :returns: :py:obj:`True` if ``item`` is a member of the group, else :py:obj:`False`.
58
+ :rtype: :py:obj:`bool`
59
+
60
+ """
61
+ return True if str(item.id) in self.items else False
62
+
63
+ def grant_access(
64
+ self, item: WialonBase, access_mask: int = constants.ACCESSMASK_UNIT_BASIC
65
+ ) -> None:
66
+ """
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.
100
+
101
+ :param item: A Wialon object.
102
+ :type item: :py:obj:`~terminusgps.wialon.items.base.WialonBase`
103
+ :raises WialonError: If something goes wrong with Wialon.
104
+ :returns: Nothing.
105
+ :rtype: :py:obj:`None`
106
+
107
+ """
108
+ new_items: list[str] = self.items.copy() + [str(item.id)]
109
+ self._update_items(new_items)
110
+
111
+ def rm_item(self, item: WialonBase) -> None:
112
+ """
113
+ Removes a Wialon unit from the group, if it's a member of the group.
114
+
115
+ :param item: A Wialon object.
116
+ :type item: :py:obj:`~terminusgps.wialon.items.base.WialonBase`
117
+ :raises AssertionError: If the item wasn't in the group.
118
+ :raises WialonError: If something goes wrong with Wialon.
119
+ :returns: Nothing.
120
+ :rtype: :py:obj:`None`
121
+
122
+ """
123
+ assert self.is_member(item), f"Cannot remove {item}, it's not in the group"
124
+ new_items: list[str] = self.items.copy()
125
+ new_items.remove(str(item.id))
126
+ self._update_items(new_items)
127
+
128
+ @property
129
+ def items(self) -> list[str]:
130
+ """
131
+ Returns a list of the group's Wialon unit ids.
132
+
133
+ :type: :py:obj:`list`
134
+
135
+ """
136
+ response = self.session.wialon_api.core_search_items(
137
+ **{
138
+ "spec": {
139
+ "itemsType": "avl_unit_group",
140
+ "propName": "sys_id",
141
+ "propValueMask": str(self.id),
142
+ "sortType": "sys_id",
143
+ "propType": "property",
144
+ "or_logic": 0,
145
+ },
146
+ "force": 1,
147
+ "flags": flags.DATAFLAG_UNIT_BASE,
148
+ "from": 0,
149
+ "to": 0,
150
+ }
151
+ )
152
+ return [str(unit_id) for unit_id in response.get("items")[0].get("u", [])]
@@ -0,0 +1,183 @@
1
+ from urllib.parse import quote_plus
2
+
3
+ from terminusgps.wialon import flags
4
+ from terminusgps.wialon.items.base import WialonBase
5
+ from terminusgps.wialon import constants
6
+
7
+
8
+ class WialonUser(WialonBase):
9
+ def create(self, **kwargs) -> int | None:
10
+ """
11
+ Creates a new Wialon user.
12
+
13
+ :param creator_id: A Wialon user id.
14
+ :type creator_id: :py:obj:`int`
15
+ :param name: A name for the user.
16
+ :type name: :py:obj:`str`
17
+ :param name: A password for the user.
18
+ :type name: :py:obj:`str`
19
+ :raises WialonError: If something goes wrong with Wialon.
20
+ :returns: The Wialon id for the new user.
21
+ :rtype: :py:obj:`int` | :py:obj:`None`
22
+
23
+ """
24
+ if not kwargs.get("creator_id"):
25
+ raise ValueError("'creator_id' is required on creation.")
26
+ if not kwargs.get("name"):
27
+ raise ValueError("'name' is required on creation.")
28
+ if not kwargs.get("password"):
29
+ raise ValueError("'password' is required on creation.")
30
+
31
+ response = self.session.wialon_api.core_create_user(
32
+ **{
33
+ "creatorId": kwargs["creator_id"],
34
+ "name": kwargs["name"],
35
+ "password": kwargs["password"],
36
+ "dataFlags": flags.DATAFLAG_UNIT_BASE,
37
+ }
38
+ )
39
+ return response.get("item", {}).get("id")
40
+
41
+ def _get_access_response(self, hw_type: str) -> dict:
42
+ """
43
+ Returns a dict of the Wialon objects the user has access to.
44
+
45
+ :param hw_type: A hardware type of Wialon objects to generate a list for.
46
+ :type hw_type: :py:obj:`str`
47
+ :raises WialonError: If something goes wrong with Wialon.
48
+ :returns: The Wialon API response.
49
+ :rtype: :py:obj:`dict`
50
+
51
+ """
52
+ return self.session.wialon_api.user_get_items_access(
53
+ **{
54
+ "userId": self.id,
55
+ "directAccess": True,
56
+ "itemSuperclass": hw_type,
57
+ "flags": 0x2,
58
+ }
59
+ )
60
+
61
+ @property
62
+ def units(self) -> list[str]:
63
+ """
64
+ The user's units.
65
+
66
+ :raises WialonError: If something goes wrong with Wialon.
67
+ :returns: A list of unit ids the user has access to.
68
+ :rtype: :py:obj:`list`
69
+
70
+ """
71
+ response = self._get_access_response(hw_type="avl_unit")
72
+ return [key for key in response.keys()]
73
+
74
+ @property
75
+ def groups(self) -> list[str]:
76
+ """
77
+ The user's unit groups.
78
+
79
+ :raises WialonError: If something goes wrong with Wialon.
80
+ :returns: A list of group ids the user has access to.
81
+ :rtype: :py:obj:`list`
82
+
83
+ """
84
+ response = self._get_access_response(hw_type="avl_unit_group")
85
+ return [key for key in response.keys()]
86
+
87
+ def has_access(self, other: WialonBase) -> bool:
88
+ """
89
+ Checks if the user has access to ``other``.
90
+
91
+ :param other: A Wialon object.
92
+ :type phone: :py:obj:`~terminusgps.wialon.items.base.WialonBase`
93
+ :raises WialonError: If something goes wrong with Wialon.
94
+ :returns: :py:obj:`True` if the user can access ``other``, else :py:obj:`False`.
95
+ :rtype: :py:obj:`bool`
96
+
97
+ """
98
+ response: dict = self._get_access_response(hw_type=other.hw_type)
99
+ items: list[str] = [key for key in response.keys()]
100
+ return True if str(other.id) in items else False
101
+
102
+ def assign_phone(self, phone: str) -> None:
103
+ """
104
+ Assigns a phone number to the user.
105
+
106
+ :param phone: A phone number, including country code.
107
+ :type phone: :py:obj:`str`
108
+ :raises WialonError: If something goes wrong with Wialon.
109
+ :returns: Nothing.
110
+ :rtype: :py:obj:`None`
111
+
112
+ """
113
+ self.add_cproperty(("phone", quote_plus(phone)))
114
+
115
+ def assign_email(self, email: str) -> None:
116
+ """
117
+ Assigns an email address to the user.
118
+
119
+ :param phone: An email address.
120
+ :type phone: :py:obj:`str`
121
+ :raises WialonError: If something goes wrong with Wialon.
122
+ :returns: Nothing.
123
+ :rtype: :py:obj:`None`
124
+
125
+ """
126
+ self.add_cproperty(("email", email))
127
+
128
+ def grant_access(
129
+ self, item: WialonBase, access_mask: int = constants.ACCESSMASK_UNIT_BASIC
130
+ ) -> None:
131
+ """
132
+ Grants the user access to ``item``.
133
+
134
+ :param item: A Wialon object.
135
+ :type item: :py:obj:`~terminusgps.wialon.items.base.WialonBase`
136
+ :param access_mask: A Wialon access mask integer.
137
+ :type access_mask: :py:obj:`int`
138
+ :raises WialonError: If something goes wrong with Wialon.
139
+ :returns: Nothing.
140
+ :rtype: :py:obj:`None`
141
+
142
+ """
143
+ self.session.wialon_api.user_update_item_access(
144
+ **{"userId": self.id, "itemId": item.id, "accessMask": access_mask}
145
+ )
146
+
147
+ def set_settings_flags(self, flags: int, flags_mask: int) -> None:
148
+ """
149
+ Sets the user's settings flags.
150
+
151
+ :param flags: The new user settings flags.
152
+ :type flags: :py:obj:`int`
153
+ :param flags_mask: A user settings flag mask.
154
+ :type flags_mask: :py:obj:`int`
155
+ :raises WialonError: If something goes wrong with Wialon.
156
+ :returns: Nothing.
157
+ :rtype: :py:obj:`None`
158
+
159
+ """
160
+ self.session.wialon_api.user_update_user_flags(
161
+ **{"userId": self.id, "flags": flags, "flagsMask": flags_mask}
162
+ )
163
+
164
+ def update_password(self, old_password: str, new_password: str) -> None:
165
+ """
166
+ Updates the password of the user.
167
+
168
+ :param old_password: The user's original password.
169
+ :type old_password: :py:obj:`str`
170
+ :param new_password: A new password.
171
+ :type new_password: :py:obj:`str`
172
+ :raises WialonError: If something goes wrong with Wialon.
173
+ :returns: Nothing.
174
+ :rtype: :py:obj:`None`
175
+
176
+ """
177
+ self.session.wialon_api.user_update_password(
178
+ **{
179
+ "userId": self.id,
180
+ "oldPassword": old_password,
181
+ "newPassword": new_password,
182
+ }
183
+ )
@@ -1,12 +1,15 @@
1
- import os
2
-
3
1
  from wialon.api import Wialon, WialonError
2
+ from django.conf import settings
3
+ from django.core.exceptions import ImproperlyConfigured
4
4
 
5
5
  from .errors import WialonLogoutError, WialonLoginError
6
6
 
7
7
 
8
8
  class WialonSession:
9
9
  def __init__(self, token: str | None = None, sid: str | None = None) -> None:
10
+ if not hasattr(settings, "WIALON_TOKEN"):
11
+ raise ImproperlyConfigured("'WIALON_TOKEN' setting is required.")
12
+
10
13
  self.token = token
11
14
  self.wialon_api = Wialon(
12
15
  scheme="https", host="hst-api.wialon.com", port=443, sid=sid
@@ -22,10 +25,11 @@ class WialonSession:
22
25
 
23
26
  @token.setter
24
27
  def token(self, value: str | None = None) -> None:
25
- self._token = value if value else os.getenv("WIALON_TOKEN")
28
+ self._token = value if value else settings.WIALON_TOKEN
26
29
 
27
30
  def __enter__(self) -> "WialonSession":
28
- self.login(self.token)
31
+ if not self.wialon_api.sid:
32
+ self.login(self.token)
29
33
  return self
30
34
 
31
35
  def __exit__(self, exc_type, exc_value, exc_tb) -> None:
@@ -5,15 +5,6 @@ from .session import WialonSession
5
5
  from .flags import DATAFLAG_UNIT_BASE
6
6
 
7
7
 
8
- def repopulate(func):
9
- def wrapper(self, *args, **kwargs):
10
- result = func(self, *args, **kwargs)
11
- self.populate()
12
- return result
13
-
14
- return wrapper
15
-
16
-
17
8
  def is_unique(value: str, session: WialonSession, items_type: str = "avl_unit") -> bool:
18
9
  """Determines if the value is unique among Wialon objects of type 'items_type'."""
19
10
  result = session.wialon_api.core_check_unique(
@@ -176,7 +176,7 @@ wheels = [
176
176
 
177
177
  [[package]]
178
178
  name = "python-terminusgps"
179
- version = "1.4.6"
179
+ version = "1.4.7"
180
180
  source = { editable = "." }
181
181
  dependencies = [
182
182
  { name = "argparse" },
@@ -1,5 +0,0 @@
1
- from os import environ
2
-
3
- environ.setdefault("WIALON_HOST", "hst-api.wialon.com")
4
- environ.setdefault("WIALON_SCHEME", "https")
5
- environ.setdefault("WIALON_PORT", "443")
@@ -1,15 +0,0 @@
1
- from terminusgps.wialon.items.base import WialonBase
2
-
3
-
4
- class WialonAccount(WialonBase):
5
- def create(self, **kwargs) -> int | None:
6
- if not kwargs.get("resource_id"):
7
- raise ValueError("'resource_id' is required for creation.")
8
-
9
- self.session.wialon_api.account_create_account(
10
- **{
11
- "itemId": kwargs["resource_id"],
12
- "plan": kwargs.get("plan", "terminusgps_ext_hist"),
13
- }
14
- )
15
- return int(kwargs["resource_id"])
@@ -1,90 +0,0 @@
1
- from urllib.parse import quote_plus
2
-
3
- from terminusgps.wialon import flags
4
- from terminusgps.wialon.items.base import WialonBase, repopulate
5
-
6
-
7
- class WialonUnit(WialonBase):
8
- def create(self, **kwargs) -> int | None:
9
- if not kwargs.get("creator_id"):
10
- raise ValueError("'creator_id' is required on creation.")
11
- if not kwargs.get("name"):
12
- raise ValueError("'name' is required on creation.")
13
- if not kwargs.get("hw_type"):
14
- raise ValueError("'hw_type' is required on creation.")
15
-
16
- response = self.session.wialon_api.core_create_unit(
17
- **{
18
- "creatorId": kwargs["creator_id"],
19
- "name": kwargs["name"],
20
- "hwTypeId": kwargs["hw_type"],
21
- "dataFlags": flags.DATAFLAG_UNIT_BASE,
22
- }
23
- )
24
- return response.get("item", {}).get("id")
25
-
26
- def populate(self) -> None:
27
- super().populate()
28
- unit_data = self.session.wialon_api.core_search_item(
29
- **{"id": self.id, "flags": flags.DATAFLAG_UNIT_ADVANCED_PROPERTIES}
30
- )
31
- self.uid = unit_data.get("uid", "")
32
- self.phone = unit_data.get("ph", "")
33
- self.is_active = bool(unit_data.get("act", 0))
34
-
35
- def execute_command(
36
- self,
37
- name: str,
38
- link_type: str,
39
- timeout: int = 5,
40
- flags: int = 0,
41
- param: dict | None = None,
42
- ) -> None:
43
- self.session.wialon_api.unit_exec_cmd(
44
- **{
45
- "itemId": self.id,
46
- "commandName": name,
47
- "linkType": link_type,
48
- "timeout": timeout,
49
- "flags": flags,
50
- "param": param if param else {},
51
- }
52
- )
53
-
54
- def set_access_password(self, password: str) -> None:
55
- self.session.wialon_api.unit_update_access_password(
56
- **{"itemId": self.id, "accessPassword": password}
57
- )
58
-
59
- @repopulate
60
- def activate(self) -> None:
61
- if self.is_active:
62
- return
63
-
64
- self.session.wialon_api.unit_set_active(
65
- **{"itemId": self.id, "active": int(True)}
66
- )
67
-
68
- @repopulate
69
- def deactivate(self) -> None:
70
- if not self.is_active:
71
- return
72
-
73
- self.session.wialon_api.unit_set_active(
74
- **{"itemId": self.id, "active": int(False)}
75
- )
76
-
77
- @repopulate
78
- def assign_phone(self, phone: str) -> None:
79
- self.session.wialon_api.unit_update_phone(
80
- **{"itemId": self.id, "phoneNumber": quote_plus(phone)}
81
- )
82
-
83
- def get_phone_numbers(self) -> list[str]:
84
- phones = []
85
- if self.phone:
86
- phones.append(self.phone)
87
- for field in self.cfields | self.afields:
88
- if field["n"] == "to_number":
89
- phones.append(field["v"])
90
- return phones
@@ -1,69 +0,0 @@
1
- from terminusgps.wialon import constants, flags
2
- from terminusgps.wialon.items.base import WialonBase
3
-
4
-
5
- class WialonUnitGroup(WialonBase):
6
- def create(self, **kwargs) -> int | None:
7
- if not kwargs.get("creator_id"):
8
- raise ValueError("'creator_id' is required on creation.")
9
- if not kwargs.get("name"):
10
- raise ValueError("'name' is required on creation.")
11
-
12
- response = self.session.wialon_api.core_create_unit_group(
13
- **{
14
- "creatorId": kwargs["creator_id"],
15
- "name": kwargs["name"],
16
- "dataFlags": flags.DATAFLAG_UNIT_BASE,
17
- }
18
- )
19
- return response.get("item", {}).get("id")
20
-
21
- def _update_items(self, new_items: list[str]) -> None:
22
- self.session.wialon_api.unit_group_update_units(
23
- **{"itemId": self.id, "units": new_items}
24
- )
25
-
26
- def is_member(self, item: WialonBase) -> bool:
27
- return True if str(item.id) in self.items else False
28
-
29
- def grant_access(
30
- self, item: WialonBase, access_mask: int = constants.ACCESSMASK_UNIT_BASIC
31
- ) -> None:
32
- self.session.wialon_api.user_update_item_access(
33
- **{"userId": item.id, "itemId": self.id, "accessMask": access_mask}
34
- )
35
-
36
- def revoke_access(self, item: WialonBase) -> None:
37
- self.session.wialon_api.user_update_item_access(
38
- **{"userId": item.id, "itemId": self.id, "accessMask": 0}
39
- )
40
-
41
- def add_item(self, item: WialonBase) -> None:
42
- new_items: list[str] = self.items.copy() + [str(item.id)]
43
- self._update_items(new_items)
44
-
45
- def rm_item(self, item: WialonBase) -> None:
46
- assert self.is_member(item), f"Cannot remove {item.name}, it's not in the group"
47
- new_items: list[str] = self.items.copy()
48
- new_items.remove(str(item.id))
49
- self._update_items(new_items)
50
-
51
- @property
52
- def items(self) -> list[str]:
53
- response = self.session.wialon_api.core_search_items(
54
- **{
55
- "spec": {
56
- "itemsType": "avl_unit_group",
57
- "propName": "sys_id",
58
- "propValueMask": str(self.id),
59
- "sortType": "sys_id",
60
- "propType": "property",
61
- "or_logic": 0,
62
- },
63
- "force": 1,
64
- "flags": flags.DATAFLAG_UNIT_BASE,
65
- "from": 0,
66
- "to": 0,
67
- }
68
- )
69
- return [str(unit_id) for unit_id in response.get("items")[0].get("u", [])]
@@ -1,79 +0,0 @@
1
- from urllib.parse import quote_plus
2
-
3
- from terminusgps.wialon import flags
4
- from terminusgps.wialon.items.base import WialonBase
5
- from terminusgps.wialon import constants
6
-
7
-
8
- class WialonUser(WialonBase):
9
- def create(self, **kwargs) -> int | None:
10
- if not kwargs.get("creator_id"):
11
- raise ValueError("'creator_id' is required on creation.")
12
- if not kwargs.get("name"):
13
- raise ValueError("'name' is required on creation.")
14
- if not kwargs.get("password"):
15
- raise ValueError("'password' is required on creation.")
16
-
17
- response = self.session.wialon_api.core_create_user(
18
- **{
19
- "creatorId": kwargs["creator_id"],
20
- "name": kwargs["name"],
21
- "password": kwargs["password"],
22
- "dataFlags": flags.DATAFLAG_UNIT_BASE,
23
- }
24
- )
25
- return response.get("item", {}).get("id")
26
-
27
- def _get_access_response(self, hw_type: str) -> dict:
28
- return self.session.wialon_api.user_get_items_access(
29
- **{
30
- "userId": self.id,
31
- "directAccess": True,
32
- "itemSuperclass": hw_type,
33
- "flags": 0x2,
34
- }
35
- )
36
-
37
- @property
38
- def units(self) -> list[str]:
39
- response = self._get_access_response(hw_type="avl_unit")
40
- return [key for key in response.keys()]
41
-
42
- @property
43
- def groups(self) -> list[str]:
44
- response = self._get_access_response(hw_type="avl_unit_group")
45
- return [key for key in response.keys()]
46
-
47
- def has_access(self, other: WialonBase) -> bool:
48
- response: dict = self._get_access_response(hw_type=other.hw_type)
49
- items: list[str] = [key for key in response.keys()]
50
- return True if str(other.id) in items else False
51
-
52
- def assign_phone(self, phone: str) -> None:
53
- self.add_cproperty(("phone", quote_plus(phone)))
54
-
55
- def assign_email(self, email: str) -> None:
56
- """Assigns an email address to the Wialon user."""
57
- self.add_cproperty(("email", email))
58
-
59
- def grant_access(
60
- self, item: WialonBase, access_mask: int = constants.ACCESSMASK_UNIT_BASIC
61
- ) -> None:
62
- """Grants item access to the Wialon user according to the access mask integer."""
63
- self.session.wialon_api.user_update_item_access(
64
- **{"userId": self.id, "itemId": item.id, "accessMask": access_mask}
65
- )
66
-
67
- def set_settings_flags(self, flags: int, flags_mask: int) -> None:
68
- self.session.wialon_api.user_update_user_flags(
69
- **{"userId": self.id, "flags": flags, "flagsMask": flags_mask}
70
- )
71
-
72
- def update_password(self, old_password: str, new_password: str) -> None:
73
- self.session.wialon_api.user_update_password(
74
- **{
75
- "userId": self.id,
76
- "oldPassword": old_password,
77
- "newPassword": new_password,
78
- }
79
- )