python-terminusgps 1.10.2__py3-none-any.whl → 1.11.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: 1.10.2
3
+ Version: 1.11.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
@@ -9,8 +9,9 @@ terminusgps/authorizenet/profiles/customers.py,sha256=iEuNFP6PHuGdFQftgxxKpQFYaG
9
9
  terminusgps/authorizenet/profiles/payments.py,sha256=qOWp-gO6qBCADP98WjoNCHm-jtq7DY5-moiPsOnT5yI,5263
10
10
  terminusgps/aws/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
11
  terminusgps/aws/secrets.py,sha256=kgRA77rs0mD4berWRLkPtJLOe4Grf--o5f8NzwQqXCk,360
12
+ terminusgps/aws/ses.py,sha256=sDca2GjMaZvUt3kUPtGCpcqHiOkij9lMV1ogZofgYUU,2372
12
13
  terminusgps/twilio/__init__.py,sha256=dYo41F2jft_eHDWSUtSpKGSRG1bewq_qClqilUJZkYM,33
13
- terminusgps/twilio/caller.py,sha256=XLMONHnojYZ0QVS0UlJlBZkL2qw3LO2b53jWJ4uv6oM,1832
14
+ terminusgps/twilio/caller.py,sha256=nHCIwUulo-C_v2orsp6OY0nuJA6YetSkxpMTwrRQ-uA,1831
14
15
  terminusgps/wialon/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
16
  terminusgps/wialon/constants.py,sha256=g06sNgWtSvY1pZljbH9KMsArhs8jFZRgJM9GT4Gluf0,3079
16
17
  terminusgps/wialon/errors.py,sha256=SV9pVwU6FZ_zGlGP6zJN6aLE2auF4TfHFISQLiPMKrI,1758
@@ -18,14 +19,15 @@ terminusgps/wialon/flags.py,sha256=-OoH-eZM54yjRqxsAXL8VImdQu_7C0OAxCrFqaCEuko,1
18
19
  terminusgps/wialon/session.py,sha256=YClz5dMQ4vshznngOYkW-kVPh4YAGDx4QHyOGpPwd-k,9474
19
20
  terminusgps/wialon/utils.py,sha256=9xJq5eAWm-vu0nJVJ7eUcwhJhMCrGjS1p__4CIBXYm4,3407
20
21
  terminusgps/wialon/items/__init__.py,sha256=3BVthghekMvhMQSzQgBMlD_phxmKSmp3Zvmo-z0O8Bs,211
21
- terminusgps/wialon/items/base.py,sha256=r0cDn7c1vKVARPmODbGPkeP2F-sktCqVofIb1Q_gsCQ,6694
22
- terminusgps/wialon/items/resource.py,sha256=xElMYSMyKA5y8rIP0EeF429MBV2hbLZcS6SokftvrOs,5641
22
+ terminusgps/wialon/items/base.py,sha256=sWM8Kg3eJ008M51q42OREHmeifOrFbV1JS9ELqE7coA,6878
23
+ terminusgps/wialon/items/notification.py,sha256=wC-KXxW4LhjSLJVzBGPfu5hz1vcIGKnYFMxQQ1JzX0g,1058
24
+ terminusgps/wialon/items/resource.py,sha256=KH5U9eizT6hpxC3r0JGhon-wbV_MiyU3eE57rCaznw8,5913
23
25
  terminusgps/wialon/items/retranslator.py,sha256=zsQXqOhH1zFe5_e8DROPu60z3v6xPWOEPyMCi2IqJAo,3861
24
26
  terminusgps/wialon/items/route.py,sha256=qOHPN_rejwiGqvwWzlwmUIAIyc3lKavCalf8ki66rx0,760
25
- terminusgps/wialon/items/unit.py,sha256=AUPoECgjzxnBxTAjPCbSVL_OzqCP7Oa4vjprFT3gJJ8,7275
27
+ terminusgps/wialon/items/unit.py,sha256=3fp9HXxW8e7PvMXwRBurNUrDstEmGyLBVRu3Q9kNcAM,7275
26
28
  terminusgps/wialon/items/unit_group.py,sha256=1yc7ldPIgGbtiTj0djRDWmt6Q9K_VT9sxOaioasbbLY,5212
27
29
  terminusgps/wialon/items/user.py,sha256=p_NA4iXLappr44RKwt9EHv_v0jc1XOQU_mJWe61s_qI,6077
28
- python_terminusgps-1.10.2.dist-info/METADATA,sha256=mOx5v7vNDP2-65IJpMHgC2epvklGGClRyb2FGrjPEjE,912
29
- python_terminusgps-1.10.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
30
- python_terminusgps-1.10.2.dist-info/licenses/COPYING,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
31
- python_terminusgps-1.10.2.dist-info/RECORD,,
30
+ python_terminusgps-1.11.0.dist-info/METADATA,sha256=oLYm0XWa_83TL-87BxqWU1yvdRrc3JqzrshOxq7SQ98,912
31
+ python_terminusgps-1.11.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
32
+ python_terminusgps-1.11.0.dist-info/licenses/COPYING,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
33
+ python_terminusgps-1.11.0.dist-info/RECORD,,
terminusgps/aws/ses.py ADDED
@@ -0,0 +1,72 @@
1
+ import boto3
2
+ import json
3
+
4
+
5
+ class SESEmailTemplate:
6
+ def __init__(self, template_name: str, region_name: str = "us-east-1") -> None:
7
+ self.name = template_name
8
+ self.region = region_name
9
+ self.client = boto3.Session().client(service_name="ses")
10
+
11
+ def send(
12
+ self,
13
+ from_addr: str,
14
+ recipients: list[str],
15
+ replyto_list: list[str],
16
+ data: dict[str, str],
17
+ cc_list: list[str] | None = None,
18
+ bcc_list: list[str] | None = None,
19
+ ) -> None:
20
+ destination = self._get_destination(recipients, cc_list, bcc_list)
21
+ self.client.send_templated_email(
22
+ **{
23
+ "Source": from_addr,
24
+ "Destination": destination,
25
+ "ReplyToAddresses": replyto_list,
26
+ "Template": self.name,
27
+ "TemplateData": json.dumps(data),
28
+ }
29
+ )
30
+
31
+ def create(self, subject: str, text_content: str, html_content: str) -> None:
32
+ self.client.create_template(
33
+ **{
34
+ "Template": {
35
+ "TemplateName": self.name,
36
+ "SubjectPart": subject,
37
+ "TextPart": text_content,
38
+ "HtmlPart": html_content,
39
+ }
40
+ }
41
+ )
42
+
43
+ def update(self, subject: str, text_content: str, html_content: str) -> None:
44
+ self.client.update_template(
45
+ **{
46
+ "Template": {
47
+ "TemplateName": self.name,
48
+ "SubjectPart": subject,
49
+ "TextPart": text_content,
50
+ "HtmlPart": html_content,
51
+ }
52
+ }
53
+ )
54
+
55
+ def test_render(self, data: dict[str, str]) -> str:
56
+ response = self.client.test_render_template(
57
+ **{"TemplateName": self.name, "TemplateData": json.dumps(data)}
58
+ )
59
+ return response.get("RenderedTemplate")
60
+
61
+ def _get_destination(
62
+ self,
63
+ recipients: list[str],
64
+ cc_list: list[str] | None,
65
+ bcc_list: list[str] | None,
66
+ ) -> dict[str, list[str]]:
67
+ destination: dict[str, list[str]] = {"ToAddresses": recipients}
68
+ if cc_list is not None:
69
+ destination.update({"CcAddresses": cc_list})
70
+ if bcc_list is not None:
71
+ destination.update({"BccAddresses": bcc_list})
72
+ return destination
@@ -37,7 +37,6 @@ class TwilioCaller:
37
37
  )
38
38
  case _:
39
39
  raise ValueError(f"Unsupported TwilioCaller method '{method}'.")
40
-
41
40
  return task
42
41
 
43
42
  async def create_call(self, to_number: str, message: str) -> None:
@@ -5,7 +5,14 @@ from terminusgps.wialon.session import WialonSession
5
5
 
6
6
 
7
7
  class WialonBase:
8
- def __init__(self, id: str | int, session: WialonSession, **kwargs) -> None:
8
+ def __init__(
9
+ self,
10
+ id: str | int | None = None,
11
+ session: WialonSession | None = None,
12
+ **kwargs,
13
+ ) -> None:
14
+ if session is None:
15
+ raise ValueError("A valid Wialon API session is required.")
9
16
  if isinstance(id, str) and not id.isdigit():
10
17
  raise ValueError(f"'id' must be a digit. Got '{id}'.")
11
18
 
@@ -59,7 +66,7 @@ class WialonBase:
59
66
  return True if self.id in response.keys() else False
60
67
 
61
68
  @abstractmethod
62
- def create(self) -> int | None:
69
+ def create(self, *args, **kwargs) -> int | None:
63
70
  """Creates a Wialon object and returns its id."""
64
71
  raise NotImplementedError("Subclasses must implement this method.")
65
72
 
@@ -0,0 +1,33 @@
1
+ from terminusgps.wialon.items.base import WialonBase
2
+
3
+
4
+ class WialonNotification(WialonBase):
5
+ def create(
6
+ self,
7
+ resource_id: str | int,
8
+ name: str,
9
+ activation_time: int,
10
+ deactivation_time: int,
11
+ text: str | None = None,
12
+ max_alarms: int = 0,
13
+ interval: int = 0,
14
+ lang: str = "en",
15
+ ) -> int | None:
16
+ if isinstance(resource_id, str) and not resource_id.isdigit():
17
+ raise ValueError(f"'resource_id' must be a digit, got '{resource_id}'.")
18
+
19
+ response = self.session.wialon_api.resource_update_notification(
20
+ **{
21
+ "itemId": resource_id,
22
+ "id": 0,
23
+ "callMode": "create",
24
+ "n": name,
25
+ "txt": text if text else name,
26
+ "ta": activation_time,
27
+ "td": deactivation_time,
28
+ "ma": max_alarms,
29
+ "mmtd": interval,
30
+ "la": lang,
31
+ }
32
+ )
33
+ return int(response[0].get("id")) if response else None
@@ -27,6 +27,13 @@ class WialonResource(WialonBase):
27
27
  )
28
28
  return response.get("item", {}).get("bact") == self.id
29
29
 
30
+ def is_migrated(self, unit: WialonBase) -> bool:
31
+ response = self.session.wialon_api.account_list_change_accounts(
32
+ **{"units": [unit.id]}
33
+ )
34
+ results = [unit.id == int(item.get("id")) for item in response]
35
+ return any(results)
36
+
30
37
  def migrate_unit(self, unit: WialonBase) -> None:
31
38
  """
32
39
  Migrates a :py:obj:`WialonUnit` into the account.
@@ -133,15 +133,6 @@ class WialonUnit(WialonBase):
133
133
  **{"itemId": self.id, "phoneNumber": quote_plus(phone)}
134
134
  )
135
135
 
136
- def clean_phone_numbers(self, phones: list[str]) -> list[str]:
137
- cleaned_phones = []
138
- for num in phones:
139
- if "," in num:
140
- cleaned_phones.extend(num.split(","))
141
- else:
142
- cleaned_phones.append(num)
143
- return cleaned_phones
144
-
145
136
  def get_phone_numbers(self) -> list[str]:
146
137
  """
147
138
  Retrieves all phone numbers assigned to the unit.
@@ -167,6 +158,15 @@ class WialonUnit(WialonBase):
167
158
  phone_numbers.extend(phones_2)
168
159
  return list(dict.fromkeys(phone_numbers)) # Removes duplicate phone numbers
169
160
 
161
+ def clean_phone_numbers(self, phones: list[str]) -> list[str]:
162
+ cleaned_phones = []
163
+ for num in phones:
164
+ if "," in num:
165
+ cleaned_phones.extend(num.split(","))
166
+ else:
167
+ cleaned_phones.append(num)
168
+ return cleaned_phones
169
+
170
170
  def _get_afield_phone_numbers(self, key: str = "to_number") -> list[str] | None:
171
171
  """
172
172
  Retrives any phone numbers saved in an admin field by key.