python-terminusgps 31.3.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.3.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
@@ -12,6 +12,7 @@ terminusgps/authorizenet/profiles/subscriptions.py,sha256=N4_0kbBBJ0FtKdIydN8FLn
12
12
  terminusgps/aws/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
13
  terminusgps/aws/connect.py,sha256=TWf2bVz6LqSNQfVD7FKRn3K71VuLOwBj05Gs-9BsROg,693
14
14
  terminusgps/aws/secrets.py,sha256=MxQEmmBLpMUQ2tYAsHdCYf-7RZ84LiogdKcTsACX5dw,361
15
+ terminusgps/aws/utils.py,sha256=68GWcllu4h41qzFiKdUkWOAA4LDsTPqRY446VRMxYsY,116
15
16
  terminusgps/django/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
17
  terminusgps/django/mixins.py,sha256=Q9ZJuzpk3d9lDnlVA8ZTVvnZWxB13p08EQ8yVJcztn4,1034
17
18
  terminusgps/django/settings.py,sha256=cKUpFXH-uYWStvT6vzclczJAKwCGNx_2wY9-fqsbmIs,624
@@ -20,21 +21,24 @@ terminusgps/twilio/__init__.py,sha256=dYo41F2jft_eHDWSUtSpKGSRG1bewq_qClqilUJZkY
20
21
  terminusgps/twilio/caller.py,sha256=Q0wcJ5aaNDGvKfht7ARwPTFdqpFoB81DmZ_K2oZixyA,5077
21
22
  terminusgps/twilio/validators.py,sha256=HWCpzgDXTedofty4Zhrm2OenbgtkzrDUC08B1l15gqc,1112
22
23
  terminusgps/wialon/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
23
- terminusgps/wialon/constants.py,sha256=l67mdjTpnU9uA9zb3jKCdPaKEPGCcrN_iN_lm5mK_Pg,5697
24
+ terminusgps/wialon/constants.py,sha256=pMWqXkZK7bbA_KOdBdJ8hs9MnUUEk-MMygJ3fLNA4pQ,5783
24
25
  terminusgps/wialon/flags.py,sha256=M50EdhxQ8IMnJnU0mrHK7-h8Asc6tvNiTOOfd1dBW6A,12815
25
26
  terminusgps/wialon/logger.py,sha256=nUBsaLMaffSaAHGHAM6mFXDzix9hbNecVtA3INVtii8,968
26
- terminusgps/wialon/session.py,sha256=21ZRBUnsuK5zhkUHeccQW1lR39A4kQ_0qkLlRT1hsS8,10044
27
+ terminusgps/wialon/session.py,sha256=SGiHH58h8d3n4dQqKrLjWKlZvNHjBqpWqK6GKofMCBo,10079
27
28
  terminusgps/wialon/utils.py,sha256=JWHZ7VEeo8VNBH67gjOAQk5ITKuPllWDlJ4dVSQ5blA,11002
28
29
  terminusgps/wialon/validators.py,sha256=CVEpFsxqplEcpv4U2ipA6v9y5cSrMKFUXLvze-3vs4U,4223
29
30
  terminusgps/wialon/items/__init__.py,sha256=3BVthghekMvhMQSzQgBMlD_phxmKSmp3Zvmo-z0O8Bs,211
30
31
  terminusgps/wialon/items/base.py,sha256=7J5B-EPj997GJV7TbZP7pAfjl2G4nTcKmP-RMxmuzoY,10959
31
- terminusgps/wialon/items/resource.py,sha256=FmuY0thel8aIThSWKJXur72Sptful3MuwBfIn9bdeaY,14616
32
+ terminusgps/wialon/items/resource.py,sha256=DDnyL_Y5P3DxiirUkQb9y4cokMXZVq3xnfn16Vx8sxk,16992
32
33
  terminusgps/wialon/items/retranslator.py,sha256=sVEWrp8q1I-tO_rMTCH7TeQDDbZryIq8d9KVnhYyhH0,3795
33
34
  terminusgps/wialon/items/route.py,sha256=amNPKZqgl8pEzKA_XJudnvBPzEsfHTmrC7uF15sZVI4,1216
34
- terminusgps/wialon/items/unit.py,sha256=jq4S8EV_yukRTTE8rr2oXpu_9AdXDFYA6-GCjkR6shw,12282
35
+ terminusgps/wialon/items/unit.py,sha256=iwHM4AfUED2blKkP940RbUxFFPD_4pTDe4Dut853cfA,12302
35
36
  terminusgps/wialon/items/unit_group.py,sha256=QV2ZZgICaN3n6W1pFIyMLyNKB6KYBsLyKG64i-qXoOg,4113
36
37
  terminusgps/wialon/items/user.py,sha256=5LgwalGO8KS_7psmAVmAlnC0S6SPBDZMOuT5ZqPKRmo,7758
37
- python_terminusgps-31.3.0.dist-info/METADATA,sha256=n-d09k232ROHl_dbQ-Ky0SyWY-_ajnFxhPVSo9Ta2LY,1648
38
- python_terminusgps-31.3.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
39
- python_terminusgps-31.3.0.dist-info/licenses/COPYING,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
40
- python_terminusgps-31.3.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,,
@@ -0,0 +1,5 @@
1
+ import requests
2
+
3
+
4
+ def get_public_ip() -> str:
5
+ return requests.get("https://checkip.amazonaws.com").text.strip()
@@ -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)
@@ -0,0 +1,40 @@
1
+ import unittest
2
+
3
+ from django.conf import settings
4
+
5
+ from ..session import WialonSession
6
+
7
+
8
+ class WialonSessionTestCase(unittest.TestCase):
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
14
+ self.session = WialonSession()
15
+
16
+ def test_session_init(self) -> None:
17
+ self.assertIsNone(self.session.id)
18
+ self.assertIsNone(self.session.username)
19
+ self.assertIsNone(self.session.uid)
20
+
21
+ def test_session_login(self) -> None:
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()
35
+
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.")