python-terminusgps 31.4.0__py3-none-any.whl → 31.5.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: 31.4.0
3
+ Version: 31.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
@@ -21,22 +21,24 @@ terminusgps/twilio/__init__.py,sha256=dYo41F2jft_eHDWSUtSpKGSRG1bewq_qClqilUJZkY
21
21
  terminusgps/twilio/caller.py,sha256=Q0wcJ5aaNDGvKfht7ARwPTFdqpFoB81DmZ_K2oZixyA,5077
22
22
  terminusgps/twilio/validators.py,sha256=HWCpzgDXTedofty4Zhrm2OenbgtkzrDUC08B1l15gqc,1112
23
23
  terminusgps/wialon/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
24
- terminusgps/wialon/constants.py,sha256=l67mdjTpnU9uA9zb3jKCdPaKEPGCcrN_iN_lm5mK_Pg,5697
24
+ terminusgps/wialon/constants.py,sha256=pMWqXkZK7bbA_KOdBdJ8hs9MnUUEk-MMygJ3fLNA4pQ,5783
25
25
  terminusgps/wialon/flags.py,sha256=M50EdhxQ8IMnJnU0mrHK7-h8Asc6tvNiTOOfd1dBW6A,12815
26
26
  terminusgps/wialon/logger.py,sha256=nUBsaLMaffSaAHGHAM6mFXDzix9hbNecVtA3INVtii8,968
27
- terminusgps/wialon/session.py,sha256=21ZRBUnsuK5zhkUHeccQW1lR39A4kQ_0qkLlRT1hsS8,10044
27
+ terminusgps/wialon/session.py,sha256=SGiHH58h8d3n4dQqKrLjWKlZvNHjBqpWqK6GKofMCBo,10079
28
28
  terminusgps/wialon/utils.py,sha256=JWHZ7VEeo8VNBH67gjOAQk5ITKuPllWDlJ4dVSQ5blA,11002
29
29
  terminusgps/wialon/validators.py,sha256=CVEpFsxqplEcpv4U2ipA6v9y5cSrMKFUXLvze-3vs4U,4223
30
30
  terminusgps/wialon/items/__init__.py,sha256=3BVthghekMvhMQSzQgBMlD_phxmKSmp3Zvmo-z0O8Bs,211
31
31
  terminusgps/wialon/items/base.py,sha256=7J5B-EPj997GJV7TbZP7pAfjl2G4nTcKmP-RMxmuzoY,10959
32
- terminusgps/wialon/items/resource.py,sha256=FmuY0thel8aIThSWKJXur72Sptful3MuwBfIn9bdeaY,14616
32
+ terminusgps/wialon/items/resource.py,sha256=DDnyL_Y5P3DxiirUkQb9y4cokMXZVq3xnfn16Vx8sxk,16992
33
33
  terminusgps/wialon/items/retranslator.py,sha256=sVEWrp8q1I-tO_rMTCH7TeQDDbZryIq8d9KVnhYyhH0,3795
34
34
  terminusgps/wialon/items/route.py,sha256=amNPKZqgl8pEzKA_XJudnvBPzEsfHTmrC7uF15sZVI4,1216
35
- terminusgps/wialon/items/unit.py,sha256=jq4S8EV_yukRTTE8rr2oXpu_9AdXDFYA6-GCjkR6shw,12282
35
+ terminusgps/wialon/items/unit.py,sha256=iwHM4AfUED2blKkP940RbUxFFPD_4pTDe4Dut853cfA,12302
36
36
  terminusgps/wialon/items/unit_group.py,sha256=QV2ZZgICaN3n6W1pFIyMLyNKB6KYBsLyKG64i-qXoOg,4113
37
37
  terminusgps/wialon/items/user.py,sha256=5LgwalGO8KS_7psmAVmAlnC0S6SPBDZMOuT5ZqPKRmo,7758
38
- terminusgps/wialon/tests/test_session.py,sha256=Dnm4ZZr31jTg2WHyRrYwiU7_1dQHokThyuO_OYiXpEg,642
39
- python_terminusgps-31.4.0.dist-info/METADATA,sha256=XF4L6nr7eF8K2zbaD8l8LP1Ue37jhvVw0_zhS2VDINM,1648
40
- python_terminusgps-31.4.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
41
- python_terminusgps-31.4.0.dist-info/licenses/COPYING,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
42
- python_terminusgps-31.4.0.dist-info/RECORD,,
38
+ terminusgps/wialon/tests/test_items.py,sha256=P2LmBYx_63-bfSmsrIdVegLUcet-OBIjjJo0kJAJ-lE,1300
39
+ terminusgps/wialon/tests/test_session.py,sha256=4H_TeIMrC4dyaDAy11a0o1GMn6PIZaRF8soPaoaowQg,1318
40
+ terminusgps/wialon/tests/test_utils.py,sha256=vYnOKCPPYqUWcQ2nTJTOu7NhNKMiTGNBkvX9H2APxL0,1670
41
+ python_terminusgps-31.5.0.dist-info/METADATA,sha256=Dcha0-mD7REBsNgTK-DTBoXBOTGZGUyfkjzicxVQ4ec,1648
42
+ python_terminusgps-31.5.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
43
+ python_terminusgps-31.5.0.dist-info/licenses/COPYING,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
44
+ python_terminusgps-31.5.0.dist-info/RECORD,,
@@ -4,6 +4,12 @@ from typing import override
4
4
  from . import flags
5
5
 
6
6
 
7
+ class WialonGeofenceType(enum.IntEnum):
8
+ LINE = 1
9
+ POLYGON = 2
10
+ CIRCLE = 3
11
+
12
+
7
13
  class WialonMeasurementUnit(enum.IntEnum):
8
14
  METRIC = 0
9
15
  US = 1
@@ -1,6 +1,6 @@
1
1
  from urllib.parse import quote_plus
2
2
 
3
- from terminusgps.wialon import flags
3
+ from terminusgps.wialon import constants, flags
4
4
  from terminusgps.wialon.items.base import WialonBase
5
5
 
6
6
 
@@ -85,6 +85,73 @@ class WialonResource(WialonBase):
85
85
  )
86
86
  return int(response.get("item", {}).get("bact")) == self.id
87
87
 
88
+ def create_geofence(
89
+ self,
90
+ name: str,
91
+ x: float,
92
+ y: float,
93
+ _type: constants.WialonGeofenceType = constants.WialonGeofenceType.CIRCLE,
94
+ desc: str = "",
95
+ flags: int = 0x10,
96
+ color: int = int("6d0204", 16),
97
+ text_color: int = int("21130d", 16),
98
+ text_size: int = 12,
99
+ width: int = 100,
100
+ min_zoom: int = 4,
101
+ max_zoom: int = 19,
102
+ ) -> None:
103
+ """
104
+ Creates a geofence in Wialon for the resource.
105
+
106
+ :param name: Name of the geofence.
107
+ :type name: :py:obj:`str`
108
+ :param x: X-coordinate for the geofence.
109
+ :type x: :py:obj:`float`
110
+ :param y: Y-coordinate for the geofence.
111
+ :type y: :py:obj:`float`
112
+ :param _type: *Optional.* Type of Wialon geofence.
113
+ :type _type: :py:obj:`int`
114
+ :param desc: *Optional.* Description of the geofence.
115
+ :type desc: :py:obj:`str`
116
+ :param flags: *Optional.* Flags to use on the creation API call.
117
+ :type flags: :py:obj:`int`
118
+ :param color: *Optional.* Color of the geofence.
119
+ :type color: :py:obj:`int`
120
+ :param text_color: *Optional.* Text color of the geofence.
121
+ :type text_color: :py:obj:`int`
122
+ :param text_size: *Optional.* Text size of the geofence.
123
+ :type text_size: :py:obj:`int`
124
+ :param width: *Optional.* Width of the geofence.
125
+ :type width: :py:obj:`int`
126
+ :param min_zoom: *Optional.* Minimum zoom level the geofence will be visible at.
127
+ :type min_zoom: :py:obj:`int`
128
+ :param max_zoom: *Optional* Maximum zoom level the geofence will be visible at.
129
+ :type max_zoom: :py:obj:`int`
130
+ :returns: Nothing.
131
+ :rtype: :py:obj:`None`
132
+
133
+ """
134
+ self.session.wialon_api.resource_update_zone(
135
+ **{
136
+ "itemId": self.id,
137
+ "id": 0,
138
+ "callMode": "create",
139
+ "n": name,
140
+ "d": desc,
141
+ "t": _type,
142
+ "w": width,
143
+ "f": flags,
144
+ "c": color,
145
+ "tc": text_color,
146
+ "ts": text_size,
147
+ "min": min_zoom,
148
+ "max": max_zoom,
149
+ "path": "",
150
+ "libId": 0,
151
+ "p": [{"x": x, "y": y, "r": width}],
152
+ }
153
+ )
154
+
88
155
  def is_migrated(self, unit: WialonBase) -> bool:
89
156
  """
90
157
  Checks if a unit is migrated into the account.
@@ -38,10 +38,10 @@ class WialonUnit(WialonBase):
38
38
 
39
39
  response = self.session.wialon_api.core_create_unit(
40
40
  **{
41
- "creatorId": creator_id,
42
- "name": name,
43
- "hwTypeId": hw_type_id,
44
- "dataFlags": flags.DataFlag.UNIT_BASE,
41
+ "creatorId": str(creator_id),
42
+ "name": str(name),
43
+ "hwTypeId": str(hw_type_id),
44
+ "dataFlags": str(flags.DataFlag.UNIT_BASE),
45
45
  }
46
46
  )
47
47
  return (
@@ -307,6 +307,7 @@ class WialonSession:
307
307
  """
308
308
  self.logger.debug("Logging out of Wialon API session...")
309
309
  response: dict = self.wialon_api.core_logout({})
310
+ self.wialon_api.sid = None
310
311
 
311
312
  if response.get("error") != 0:
312
313
  self.logger.warning(
@@ -0,0 +1,37 @@
1
+ import unittest
2
+ from datetime import datetime
3
+
4
+ from django.conf import settings
5
+
6
+ from .. import items
7
+ from ..session import WialonSession
8
+
9
+
10
+ class WialonResourceTestCase(unittest.TestCase):
11
+ def setUp(self) -> None:
12
+ if not hasattr(settings, "WIALON_TOKEN"):
13
+ self.fail("'WIALON_TOKEN' setting is required.")
14
+ if not hasattr(settings, "WIALON_ADMIN_ID"):
15
+ self.fail("'WIALON_ADMIN_ID' setting is required.")
16
+
17
+ self.session = WialonSession()
18
+ self.session.login(settings.WIALON_TOKEN)
19
+ self.test_timestamp = f"{datetime.now():%Y-%m-%d-%H:%M:%S}"
20
+ self.test_resource = items.WialonResource(
21
+ id=None,
22
+ creator_id=str(settings.WIALON_ADMIN_ID),
23
+ name=f"test_resource_{self.test_timestamp}",
24
+ skip_creator_check=True,
25
+ session=self.session,
26
+ )
27
+
28
+ def tearDown(self) -> None:
29
+ self.test_resource.delete()
30
+
31
+ def test_resource_is_not_account(self) -> None:
32
+ """Tests whether or not a new resource is an account after creation."""
33
+ self.assertFalse(self.test_resource.is_account)
34
+
35
+ def test_resource_create_account(self) -> None:
36
+ self.test_resource.create_account("terminusgps_ext_hist")
37
+ self.assertTrue(self.test_resource.is_account)
@@ -1,4 +1,3 @@
1
- import socket
2
1
  import unittest
3
2
 
4
3
  from django.conf import settings
@@ -8,15 +7,34 @@ from ..session import WialonSession
8
7
 
9
8
  class WialonSessionTestCase(unittest.TestCase):
10
9
  def setUp(self) -> None:
10
+ if not hasattr(settings, "WIALON_TOKEN"):
11
+ self.fail("'WIALON_TOKEN' setting is required.")
12
+
13
+ self.token = settings.WIALON_TOKEN
11
14
  self.session = WialonSession()
12
15
 
13
16
  def test_session_init(self) -> None:
14
- self.assertEqual(self.session.token, settings.WIALON_TOKEN)
15
17
  self.assertIsNone(self.session.id)
16
18
  self.assertIsNone(self.session.username)
17
19
  self.assertIsNone(self.session.uid)
18
20
 
19
21
  def test_session_login(self) -> None:
20
- mock_login_response = {"host": socket.gethostbyname(socket.gethostname())}
22
+ sid: str = self.session.login(self.token)
23
+ self.assertEqual(self.session.id, sid)
24
+
25
+ def test_session_logout(self) -> None:
26
+ self.session.login(self.token)
27
+ self.session.logout()
28
+ self.assertEqual(self.session.id, None)
29
+
30
+ def test_session_bad_call(self) -> None:
31
+ self.session.login(self.token)
32
+ response = self.session.wialon_api.core_bad_call({})
33
+ self.assertEqual(response, None)
34
+ self.session.logout()
21
35
 
22
- self.session.login(settings.WIALON_TOKEN)
36
+ def test_session_valid_call_count(self) -> None:
37
+ self.session.login(self.token) # Login/logout does not count
38
+ self.session.wialon_api.avl_evts()
39
+ self.assertEqual(self.session.wialon_api.total_calls, 1)
40
+ self.session.logout()
@@ -0,0 +1,41 @@
1
+ import string
2
+ import unittest
3
+
4
+
5
+ from .. import utils
6
+
7
+
8
+ class WialonUtilsTestCase(unittest.TestCase):
9
+ def test_generate_wialon_password_min_length(self) -> None:
10
+ with self.assertRaises(ValueError) as context:
11
+ utils.generate_wialon_password(length=7)
12
+ self.assertTrue(
13
+ "Password cannot be less than 8 characters in length, got 7."
14
+ in str(context.exception)
15
+ )
16
+
17
+ def test_generate_wialon_password_max_length(self) -> None:
18
+ with self.assertRaises(ValueError) as context:
19
+ utils.generate_wialon_password(length=65)
20
+ self.assertTrue(
21
+ "Password cannot be greater than 64 characters in length, got 65."
22
+ in str(context.exception)
23
+ )
24
+
25
+ def test_generate_wialon_password_valid(self) -> None:
26
+ password = utils.generate_wialon_password()
27
+ s0 = list(string.ascii_uppercase)
28
+ s1 = list(string.ascii_lowercase)
29
+ s2 = list(string.digits)
30
+ s3 = list("!@#$%^*()[]-_+")
31
+
32
+ if len(password) < 8 or len(password) > 64:
33
+ self.fail(f"Generated password was {len(password)} chars in length.")
34
+ if not any([c for c in password if c in s0]):
35
+ self.fail("Generated password did not contain an uppercase letter.")
36
+ if not any([c for c in password if c in s1]):
37
+ self.fail("Generated password did not contain a lowercase letter.")
38
+ if not any([c for c in password if c in s2]):
39
+ self.fail("Generated password did not contain a digit.")
40
+ if not any([c for c in password if c in s3]):
41
+ self.fail("Generated password did not contain a special symbol.")