python-terminusgps 1.6.0__py3-none-any.whl → 1.7.1__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.

Potentially problematic release.


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

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-terminusgps
3
- Version: 1.6.0
3
+ Version: 1.7.1
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,24 +1,25 @@
1
1
  terminusgps/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
2
  terminusgps/settings.py,sha256=CIWif9AJYXk5nE-9Mqmqkeoum6-ON5MTuh0WJilgv3E,185
3
+ terminusgps/test.py,sha256=SmvDgRMqLnj_t29eWbckYjK7e9n-Yy-oWwlbN3IdhqE,2004
3
4
  terminusgps/authorizenet/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
5
  terminusgps/authorizenet/auth.py,sha256=zITSQMH8F-3vw-lDQRbuR2s9AVlGqbOyyFo0zqU00qU,870
5
6
  terminusgps/aws/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
7
  terminusgps/aws/secrets.py,sha256=kgRA77rs0mD4berWRLkPtJLOe4Grf--o5f8NzwQqXCk,360
7
8
  terminusgps/wialon/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
- terminusgps/wialon/constants.py,sha256=dsQaNt3TIjIZbr_m94KmBGFdrqe6HER2r6lol3X8QpU,2802
9
+ terminusgps/wialon/constants.py,sha256=aybYcNcHyar2lz_6BlQbMWpbcHW6eyJeq05QwuiqZQ0,3078
9
10
  terminusgps/wialon/errors.py,sha256=SV9pVwU6FZ_zGlGP6zJN6aLE2auF4TfHFISQLiPMKrI,1758
10
11
  terminusgps/wialon/flags.py,sha256=-OoH-eZM54yjRqxsAXL8VImdQu_7C0OAxCrFqaCEuko,13510
11
- terminusgps/wialon/session.py,sha256=PNtEooiJln3yZ_hSZXglciyN-H1W6_X5NbLFP5Ju0Ns,9987
12
- terminusgps/wialon/utils.py,sha256=cDz00d5Jopdh-Al-554-yD5LrKQ37hq4YBXvPunnmIg,2715
12
+ terminusgps/wialon/session.py,sha256=segEjyt-K9AAlMq_OYLVtxFBp_jE5c3KL-AJQhOVa9U,9936
13
+ terminusgps/wialon/utils.py,sha256=9xJq5eAWm-vu0nJVJ7eUcwhJhMCrGjS1p__4CIBXYm4,3407
13
14
  terminusgps/wialon/items/__init__.py,sha256=3BVthghekMvhMQSzQgBMlD_phxmKSmp3Zvmo-z0O8Bs,211
14
- terminusgps/wialon/items/base.py,sha256=MN4FG6U1nWkYAjNfXQJRVrWJoiW4Y64tyqxUO0mhv_w,6593
15
- terminusgps/wialon/items/resource.py,sha256=ADjGsQaCqD6Y3_AaD8vLgYzGaMK69MpxCZojnylS4GE,815
15
+ terminusgps/wialon/items/base.py,sha256=Z6aGEC2_m5CPHYCa1rgAvkGtSegbrvZ68mifCqo-07A,6617
16
+ terminusgps/wialon/items/resource.py,sha256=xElMYSMyKA5y8rIP0EeF429MBV2hbLZcS6SokftvrOs,5641
16
17
  terminusgps/wialon/items/retranslator.py,sha256=zsQXqOhH1zFe5_e8DROPu60z3v6xPWOEPyMCi2IqJAo,3861
17
18
  terminusgps/wialon/items/route.py,sha256=qOHPN_rejwiGqvwWzlwmUIAIyc3lKavCalf8ki66rx0,760
18
- terminusgps/wialon/items/unit.py,sha256=tLAWPfHUUS6_Feh1aYBMFPgyuGlSiAnfumJJmGt3bKw,4662
19
+ terminusgps/wialon/items/unit.py,sha256=wzcLa0adMTKX6rtO4Vz6ntaxgY50rLk2KK_VBXlW_lQ,4680
19
20
  terminusgps/wialon/items/unit_group.py,sha256=1yc7ldPIgGbtiTj0djRDWmt6Q9K_VT9sxOaioasbbLY,5212
20
21
  terminusgps/wialon/items/user.py,sha256=bkcMw_YiocaMuhP_pBfB7apjAZwnsOzGScnGkn8Ybbw,6107
21
- python_terminusgps-1.6.0.dist-info/METADATA,sha256=VBNWqHqXEN2Ig3nLeZrGOPrt_0Hrja2YPYfpMPdfrhA,882
22
- python_terminusgps-1.6.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
23
- python_terminusgps-1.6.0.dist-info/licenses/COPYING,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
24
- python_terminusgps-1.6.0.dist-info/RECORD,,
22
+ python_terminusgps-1.7.1.dist-info/METADATA,sha256=-KAv1c71Uia0KYMDRSjEdmoINaVnCQM64WI-bdebMCA,882
23
+ python_terminusgps-1.7.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
24
+ python_terminusgps-1.7.1.dist-info/licenses/COPYING,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
25
+ python_terminusgps-1.7.1.dist-info/RECORD,,
terminusgps/test.py ADDED
@@ -0,0 +1,55 @@
1
+ from django.utils import timezone
2
+ from terminusgps.wialon.session import WialonSession
3
+ from terminusgps.wialon.utils import get_hw_type_id, gen_wialon_password
4
+ from terminusgps.wialon.items import WialonUser, WialonResource, WialonUnit
5
+ from terminusgps.wialon import constants
6
+
7
+
8
+ def registration_test() -> None:
9
+ timestamp = f"{timezone.now():%y-%m-%d-%H:%M:%S}"
10
+
11
+ with WialonSession() as session:
12
+ print(f"Running test '{registration_test.__name__}' as '{session.username}'...")
13
+ hw_type_id = get_hw_type_id(name="Test HW", session=session)
14
+
15
+ super_user = WialonUser(
16
+ creator_id=session.uid,
17
+ name=f"super_user_{timestamp}",
18
+ password=gen_wialon_password(length=32),
19
+ session=session,
20
+ )
21
+ resource = WialonResource(
22
+ creator_id=super_user.id, name=f"resource_{timestamp}", session=session
23
+ )
24
+ unit = WialonUnit(
25
+ creator_id=session.uid,
26
+ name=f"test_unit_{timestamp}",
27
+ hw_type_id=hw_type_id,
28
+ session=session,
29
+ )
30
+ super_user.grant_access(unit, access_mask=constants.ACCESSMASK_UNIT_MIGRATION)
31
+ resource.create_account("terminusgps_ext_hist")
32
+ resource.migrate_unit(unit)
33
+ end_user = WialonUser(
34
+ creator_id=super_user.id,
35
+ name=f"end_user_{timestamp}",
36
+ password=gen_wialon_password(length=32),
37
+ session=session,
38
+ )
39
+ end_user.grant_access(unit, access_mask=constants.ACCESSMASK_UNIT_BASIC)
40
+
41
+
42
+ def account_days_test() -> None:
43
+ timestamp = f"{timezone.now():%y-%m-%d-%H:%M:%S}"
44
+
45
+ with WialonSession() as session:
46
+ print(f"Running test '{account_days_test.__name__}' as '{session.username}'...")
47
+ account = WialonResource(id="28990259", session=session)
48
+ account.set_settings_flags(0x20)
49
+ account.set_minimum_days(0)
50
+ account.add_days(30)
51
+
52
+
53
+ if __name__ == "__main__":
54
+ registration_test()
55
+ account_days_test()
@@ -74,3 +74,13 @@ ACCESSMASK_UNIT_SUPER: int = sum(
74
74
  )
75
75
 
76
76
  ACCESSMASK_UNIT_FULL: int = sum([flags.ACCESSFLAG_FULL_ACCESS])
77
+
78
+ ACCESSMASK_UNIT_MIGRATION: int = sum(
79
+ [
80
+ flags.ACCESSFLAG_DELETE_ITEM,
81
+ flags.ACCESSFLAG_MANAGE_ITEM_ACCESS,
82
+ flags.ACCESSFLAG_UNIT_DELETE_MESSAGES,
83
+ flags.ACCESSFLAG_UNIT_MANAGE_CONNECTIVITY,
84
+ flags.ACCESSFLAG_VIEW_ITEM_BASIC,
85
+ ]
86
+ )
@@ -12,6 +12,7 @@ class WialonBase:
12
12
  self._id = self.create(**kwargs)
13
13
  else:
14
14
  self._id = id
15
+ self.populate()
15
16
 
16
17
  def __str__(self) -> str:
17
18
  return str(self.id)
@@ -16,7 +16,159 @@ class WialonResource(WialonBase):
16
16
  "creatorId": kwargs["creator_id"],
17
17
  "name": kwargs["name"],
18
18
  "dataFlags": kwargs["flags"],
19
- "skipCreatorCheck": int(True),
20
19
  }
21
20
  )
22
21
  return response.get("item", {}).get("id")
22
+
23
+ @property
24
+ def is_account(self) -> bool:
25
+ response = self.session.wialon_api.core_search_item(
26
+ **{"id": self.id, "flags": flags.DATAFLAG_RESOURCE_BILLING_PROPERTIES}
27
+ )
28
+ return response.get("item", {}).get("bact") == self.id
29
+
30
+ def migrate_unit(self, unit: WialonBase) -> None:
31
+ """
32
+ Migrates a :py:obj:`WialonUnit` into the account.
33
+
34
+ :param unit: A Wialon object.
35
+ :type unit: :py:obj:`~terminusgps.wialon.items.base.WialonBase`
36
+ :returns: Nothing.
37
+ :rtype: :py:obj:`None`
38
+
39
+ """
40
+ assert self.is_account, "The resource is not an account"
41
+ self.session.wialon_api.account_change_account(
42
+ **{"itemId": unit.id, "resourceId": self.id}
43
+ )
44
+
45
+ def update_plan(self, new_plan: str) -> None:
46
+ """
47
+ Updates the account billing plan.
48
+
49
+ :param new_plan: The name of a billing plan.
50
+ :type new_plan: :py:obj:`str`
51
+ :returns: Nothing.
52
+ :rtype: :py:obj:`None`
53
+
54
+ """
55
+ assert self.is_account, "The resource is not an account"
56
+ self.session.wialon_api.account_update_plan(
57
+ **{"itemId": self.id, "plan": new_plan}
58
+ )
59
+
60
+ def create_account(self, billing_plan: str) -> None:
61
+ """
62
+ Transforms the resource into an account.
63
+
64
+ :param billing_plan: The name of a billing plan.
65
+ :type billing_plan: :py:obj:`str`
66
+ :returns: Nothing.
67
+ :rtype: :py:obj:`None`
68
+
69
+ """
70
+ assert self.is_account, "The resource is already an account"
71
+ self.session.wialon_api.account_create_account(
72
+ **{"itemId": self.id, "plan": billing_plan}
73
+ )
74
+
75
+ def delete_account(self) -> None:
76
+ """
77
+ Deletes the account if it exists, as well as any micro-objects and macro-objects it contains.
78
+
79
+ :returns: Nothing.
80
+ :rtype: :py:obj:`None`
81
+
82
+ """
83
+ assert self.is_account, "The resource is not an account"
84
+ self.session.wialon_api.account_delete_account(**{"itemId": self.id})
85
+
86
+ def enable_account(self) -> None:
87
+ """
88
+ Enables the Wialon account.
89
+
90
+ :returns: Nothing.
91
+ :rtype: :py:obj:`None`
92
+
93
+ """
94
+ assert self.is_account, "The resource is not an account"
95
+ self.session.wialon_api.account_enable_account(
96
+ **{"itemId": self.id, "enable": int(True)}
97
+ )
98
+
99
+ def disable_account(self) -> None:
100
+ """
101
+ Disables the Wialon account.
102
+
103
+ :returns: Nothing.
104
+ :rtype: :py:obj:`None`
105
+
106
+ """
107
+ assert self.is_account, "The resource is not an account"
108
+ self.session.wialon_api.account_enable_account(
109
+ **{"itemId": self.id, "enable": int(False)}
110
+ )
111
+
112
+ def set_minimum_days(self, days: int = 0) -> None:
113
+ """
114
+ Sets the minimum days counter value to ``days``.
115
+
116
+ :param days: Number of days to set the counter to. Default is ``0``.
117
+ :type days: :py:obj:`int`
118
+ :returns: Nothing.
119
+ :rtype: :py:obj:`None`
120
+
121
+ """
122
+ assert self.is_account, "The resource is not an account"
123
+ self.session.wialon_api.account_update_min_days(
124
+ **{"itemId": self.id, "minDays": days}
125
+ )
126
+
127
+ def add_days(self, days: int = 30) -> None:
128
+ """
129
+ Adds days to the account.
130
+
131
+ :param days: Number of days to add to the account. Default is ``30``.
132
+ :type days: :py:obj:`int`
133
+ :returns: Nothing.
134
+ :rtype: :py:obj:`None`
135
+
136
+ """
137
+ assert self.is_account, "The resource is not an account"
138
+ self.session.wialon_api.account_do_payment(
139
+ **{
140
+ "itemId": self.id,
141
+ "balanceUpdate": "0.00",
142
+ "daysUpdate": days,
143
+ "description": f"{self.session.id} - Added {days} days.",
144
+ }
145
+ )
146
+
147
+ def set_settings_flags(
148
+ self,
149
+ flags: int = 0x20,
150
+ block_balance_val: float = 0.00,
151
+ deny_balance_val: float = 0.00,
152
+ ) -> None:
153
+ """
154
+ Sets account settings flags.
155
+
156
+ :param flags: A flag integer to set on the account.
157
+ :type flags: :py:obj:`int`
158
+ :param block_balance_val: Minimum amount on the account's balance before blocking the account.
159
+ :type block_balance_val: :py:obj:`float`
160
+ :param deny_balance_val: Minimum amount on the account's balance before denying the account.
161
+ :type deny_balance_val: :py:obj:`float`
162
+ :returns: Nothing.
163
+ :rtype: :py:obj:`None`
164
+
165
+ """
166
+ assert self.is_account, "The resource is not an account"
167
+ self.session.wialon_api.account_update_flags(
168
+ **{
169
+ "itemId": self.id,
170
+ "flags": flags,
171
+ "blockBalance": block_balance_val,
172
+ "denyBalance": deny_balance_val,
173
+ }
174
+ )
@@ -13,8 +13,8 @@ class WialonUnit(WialonBase):
13
13
  :type creator_id: :py:obj:`int`
14
14
  :param name: A new name for the unit.
15
15
  :type name: :py:obj:`str`
16
- :param hw_type: A Wialon hardware type.
17
- :type hw_type: :py:obj:`str`
16
+ :param hw_type_id: A Wialon hardware type ID.
17
+ :type hw_type_id: :py:obj:`str`
18
18
  :returns: The Wialon id for the new unit.
19
19
  :rtype: :py:obj:`int` | :py:obj:`None`
20
20
 
@@ -23,14 +23,14 @@ class WialonUnit(WialonBase):
23
23
  raise ValueError("'creator_id' is required on creation.")
24
24
  if not kwargs.get("name"):
25
25
  raise ValueError("'name' is required on creation.")
26
- if not kwargs.get("hw_type"):
27
- raise ValueError("'hw_type' is required on creation.")
26
+ if not kwargs.get("hw_type_id"):
27
+ raise ValueError("'hw_type_id' is required on creation.")
28
28
 
29
29
  response = self.session.wialon_api.core_create_unit(
30
30
  **{
31
31
  "creatorId": kwargs["creator_id"],
32
32
  "name": kwargs["name"],
33
- "hwTypeId": kwargs["hw_type"],
33
+ "hwTypeId": kwargs["hw_type_id"],
34
34
  "dataFlags": flags.DATAFLAG_UNIT_BASE,
35
35
  }
36
36
  )
@@ -17,6 +17,7 @@ class WialonSession:
17
17
  self,
18
18
  token: str | None = None,
19
19
  sid: str | None = None,
20
+ uid: str | None = None,
20
21
  scheme: str = "https",
21
22
  host: str = "hst-api.wialon.com",
22
23
  port: int = 443,
@@ -40,6 +41,7 @@ class WialonSession:
40
41
 
41
42
  self.wialon_api = Wialon(scheme=scheme, host=host, port=port, sid=sid)
42
43
  self.token = token
44
+ self.login_id = uid
43
45
  self._username = None
44
46
  self._gis_sid = None
45
47
  self._hw_gp_ip = None
@@ -48,7 +50,7 @@ class WialonSession:
48
50
 
49
51
  def __enter__(self) -> "WialonSession":
50
52
  assert self.token, "Wialon API token was not set"
51
- self.login(self.token)
53
+ self.login(self.token, self.login_id)
52
54
  return self
53
55
 
54
56
  def __exit__(self, *args, **kwargs) -> None:
@@ -252,8 +254,6 @@ class WialonSession:
252
254
 
253
255
  :param token: An active Wialon API token.
254
256
  :type token: :py:obj:`str`
255
- :param user_id: A user to operate as in the Wialon API session.
256
- :type user_id: :py:obj:`str` | :py:obj:`None`
257
257
  :param flags: A login response flag integer.
258
258
  :type flags: :py:obj:`int`
259
259
  :raises WialonLoginError: If the login fails.
@@ -13,6 +13,19 @@ from terminusgps.wialon.items import (
13
13
  )
14
14
 
15
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
+
16
29
  def get_wialon_cls(items_type: str) -> Any:
17
30
  """Returns a Wialon object class based on items_type."""
18
31
  match items_type:
@@ -37,23 +50,33 @@ def is_unique(value: str, session: WialonSession, items_type: str = "avl_unit")
37
50
  return not bool(result)
38
51
 
39
52
 
40
- def gen_wialon_password(length: int = 16) -> str:
53
+ def gen_wialon_password(length: int = 32) -> str:
41
54
  """Generates a Wialon compliant password."""
42
- if length > 64:
43
- raise ValueError(f"Passwords cannot be greater than 64 chars. Got '{length}'.")
44
- if length < 4:
45
- raise ValueError(f"Password cannot be less than 4 chars. Got '{length}'.")
46
-
47
- symbols: str = "!@#$%^*()[]-_+"
48
- alphabet: str = string.ascii_letters + string.digits + symbols
49
- password: str = "".join(secrets.choice(alphabet) for _ in range(length - 4))
50
- return (
51
- password
52
- + secrets.choice(string.ascii_lowercase)
53
- + secrets.choice(string.ascii_uppercase)
54
- + secrets.choice(string.digits)
55
- + secrets.choice(symbols)
56
- )
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
57
80
 
58
81
 
59
82
  def get_id_from_iccid(iccid: str, session: WialonSession) -> str | None: