python-terminusgps 22.0.2__py3-none-any.whl → 23.0.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.

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: 22.0.2
3
+ Version: 23.0.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
@@ -17,8 +17,5 @@ Requires-Dist: authorizenet>=1.1.5
17
17
  Requires-Dist: boto3>=1.34.144
18
18
  Requires-Dist: django>=5.1.5
19
19
  Requires-Dist: matplotlib>=3.10.1
20
- Requires-Dist: opencv-python>=4.11.0.86
21
- Requires-Dist: pyqt6>=6.8.1
22
20
  Requires-Dist: python-wialon>=1.2.4
23
- Requires-Dist: pyzbar>=0.1.9
24
21
  Requires-Dist: twilio>=9.4.5
@@ -1,26 +1,25 @@
1
1
  terminusgps/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- terminusgps/settings.py,sha256=JMcMFqNQRIcljIZrEsU9pm9AhxG-gcnDC7EONKd3ShE,502
2
+ terminusgps/settings.py,sha256=UBj2tvuBzQDEz05PeFqHG4_ise3Bl6Iu7pxcrtjNaYE,568
3
3
  terminusgps/authorizenet/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
- terminusgps/authorizenet/auth.py,sha256=JuP-l23PRnsnufda5f29Y61K9yhT6p6pRMEH5KGzmp0,564
5
- terminusgps/authorizenet/subscriptions.py,sha256=_SfaTYS_3rgHNqQuvZ59TK_zDFymU64sAzj9FCyqYyU,2217
4
+ terminusgps/authorizenet/auth.py,sha256=l3sPpLycqVO-gppU2sH7YAScuHmm3xXylxW6Gdu6a7Q,563
5
+ terminusgps/authorizenet/subscriptions.py,sha256=W2oIb1oWXEhtET4bHpUWzuLIl3XbAqMkghAXy0AyWAQ,1366
6
+ terminusgps/authorizenet/utils.py,sha256=q8ReDcYIk2eG6d36IM1vZorfeAxKhyQRLdtrIpKTNwA,2212
6
7
  terminusgps/authorizenet/profiles/__init__.py,sha256=QKjMKcXpCSCEhv6LBCz0yGP8t0VxAyKyYRS4_HLYnxQ,114
7
- terminusgps/authorizenet/profiles/addresses.py,sha256=ffAi3LcEOB3Xsr9f4N_PfjSZcN3I9dTKNamoAmnLpo4,3838
8
- terminusgps/authorizenet/profiles/base.py,sha256=G7lXIYrWk5m1_MLuZT0xBs5vabKAUVbk9eZHl8Rahl8,3378
9
- terminusgps/authorizenet/profiles/customers.py,sha256=iEuNFP6PHuGdFQftgxxKpQFYaGw8IWLjsOaugvEOA-g,4513
10
- terminusgps/authorizenet/profiles/payments.py,sha256=qOWp-gO6qBCADP98WjoNCHm-jtq7DY5-moiPsOnT5yI,5263
8
+ terminusgps/authorizenet/profiles/addresses.py,sha256=xAiqETexGXicYOd_M3a4SBuHweeZPDlfWhtpqdvls-g,5466
9
+ terminusgps/authorizenet/profiles/base.py,sha256=UTudVCju5-pLJQwoK0IfuT8OvxZVJCX3vApveUY3XE4,2286
10
+ terminusgps/authorizenet/profiles/customers.py,sha256=ehtsWhPt0zF8_3dFlgFBWHA4NbQb-6AvOhtjTy3uQDg,8639
11
+ terminusgps/authorizenet/profiles/payments.py,sha256=jcF_uJcaeU2JR5Sqza0LccsQGe9F-SUCjvg3o3QskIk,8808
11
12
  terminusgps/aws/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
- terminusgps/aws/secrets.py,sha256=kgRA77rs0mD4berWRLkPtJLOe4Grf--o5f8NzwQqXCk,360
13
+ terminusgps/aws/secrets.py,sha256=MxQEmmBLpMUQ2tYAsHdCYf-7RZ84LiogdKcTsACX5dw,361
13
14
  terminusgps/aws/ses.py,sha256=sDca2GjMaZvUt3kUPtGCpcqHiOkij9lMV1ogZofgYUU,2372
14
- terminusgps/opencv/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
- terminusgps/opencv/barcodes.py,sha256=NmdpUnixlGJI-6shbKGcb6-M_IZvgSZJRDVSsI7pcFg,669
16
15
  terminusgps/twilio/__init__.py,sha256=dYo41F2jft_eHDWSUtSpKGSRG1bewq_qClqilUJZkYM,33
17
16
  terminusgps/twilio/caller.py,sha256=nHCIwUulo-C_v2orsp6OY0nuJA6YetSkxpMTwrRQ-uA,1831
18
17
  terminusgps/wialon/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
18
  terminusgps/wialon/constants.py,sha256=n1ux68oWYWnzeZdN811Iw_Lk78KBM0YMJMoonn67uZY,1723
20
19
  terminusgps/wialon/flags.py,sha256=_NIL3mrEGhvI5YISVjimjYtUatdgOqhZPJX368MtKSU,13959
21
20
  terminusgps/wialon/logger.py,sha256=nUBsaLMaffSaAHGHAM6mFXDzix9hbNecVtA3INVtii8,968
22
- terminusgps/wialon/session.py,sha256=6xg65LZz6cebWclTQp1KRLvPss0aM-bWhAmmwiDOlMg,10267
23
- terminusgps/wialon/utils.py,sha256=HUuBgkqJzzsFHGKPgf_YAE0e5Oui8xdaYtb9fv_ibOc,6344
21
+ terminusgps/wialon/session.py,sha256=t3d7trBQRxnXFnex73alSSD5n2LDTZQ9VAtzUKIERto,9670
22
+ terminusgps/wialon/utils.py,sha256=Y7fo9a81w84b3N39h1AmVLS-AnC3aRT5R137hIsqCHU,6723
24
23
  terminusgps/wialon/items/__init__.py,sha256=3BVthghekMvhMQSzQgBMlD_phxmKSmp3Zvmo-z0O8Bs,211
25
24
  terminusgps/wialon/items/base.py,sha256=EAtzlnCvB7WTu3iX1bV1-S9TvwXc-Eez7e0X4TMiK9U,6943
26
25
  terminusgps/wialon/items/resource.py,sha256=qmQKEakJa9LH5YkI9-iBDA0hsotc5ctcwdb7kZuXdfQ,14549
@@ -29,7 +28,7 @@ terminusgps/wialon/items/route.py,sha256=PTJx1gmT_PGz7nlSSUS2VzjYD-aAuvkEOIdIFne
29
28
  terminusgps/wialon/items/unit.py,sha256=-jQL5Alvz77FZWteYNM9VNX15jtCufI-DWRRj3hRsgA,8539
30
29
  terminusgps/wialon/items/unit_group.py,sha256=vGVJMBY1BrKK0tP-C1DCIz4Bh6P-HKRnKY1g8g95UIs,5031
31
30
  terminusgps/wialon/items/user.py,sha256=INwAibQVmjNNelepQXMfDevgJqMvIjHHgEjAMLRvhB0,7082
32
- python_terminusgps-22.0.2.dist-info/METADATA,sha256=1YPz8oMH4ULPwmgyGVls3OiNRkzULUwCQLTMZ7746pA,1043
33
- python_terminusgps-22.0.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
34
- python_terminusgps-22.0.2.dist-info/licenses/COPYING,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
35
- python_terminusgps-22.0.2.dist-info/RECORD,,
31
+ python_terminusgps-23.0.0.dist-info/METADATA,sha256=mWo-bctKKFZdbhH0ROVcDsqvN9kC58jMl1o49TQkFCI,946
32
+ python_terminusgps-23.0.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
33
+ python_terminusgps-23.0.0.dist-info/licenses/COPYING,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
34
+ python_terminusgps-23.0.0.dist-info/RECORD,,
@@ -1,6 +1,5 @@
1
1
  from authorizenet.apicontractsv1 import merchantAuthenticationType
2
2
  from authorizenet.constants import constants
3
-
4
3
  from django.conf import settings
5
4
 
6
5
 
@@ -1,47 +1,59 @@
1
1
  from authorizenet import apicontractsv1, apicontrollers
2
- from authorizenet.apicontractsv1 import customerAddressType
3
2
 
4
3
  from terminusgps.authorizenet.profiles.base import AuthorizenetSubProfileBase
5
4
 
6
5
 
7
6
  class AddressProfile(AuthorizenetSubProfileBase):
8
- def create(self, **kwargs) -> int:
7
+ """An Authorizenet customer address profile."""
8
+
9
+ def create(self, shipping_addr: apicontractsv1.customerAddressType) -> int:
9
10
  """
10
- Creates an Authorize.NET address profile.
11
+ Creates an Authorizenet address profile.
11
12
 
12
- :param shipping_addr: An Authorize.NET customer address.
13
+ :param shipping_addr: An Authorizenet customer address.
13
14
  :type shipping_addr: :py:obj:`~authorizenet.apicontractsv1.customerAddressType`
14
- :raises ValueError: If no customer address was provided.
15
- :raises TypeError: If the customer address is not a :py:obj:`~authorizenet.apicontractsv1.customerAddressType` object.
16
- :returns: The new address profile's id.
15
+ :raises ControllerExecutionError: If something goes wrong during an Authorizenet API call.
16
+ :returns: A new address profile id.
17
17
  :rtype: :py:obj:`int`
18
18
 
19
19
  """
20
- shipping_addr = kwargs.get("shipping_addr")
21
-
22
- if not shipping_addr:
23
- raise ValueError("'shipping_addr' is required on creation.")
24
-
25
- if not isinstance(shipping_addr, customerAddressType):
26
- raise TypeError(
27
- f"'shipping_addr' must be customerAddressType, got '{type(shipping_addr)}'."
28
- )
29
-
30
20
  response = self._authorizenet_create_shipping_address(shipping_addr)
31
21
  return int(response.customerAddressId)
32
22
 
33
- def update(self, shipping_addr: customerAddressType) -> dict:
34
- """Updates the Authorize.NET payment profile."""
23
+ def update(self, shipping_addr: apicontractsv1.customerAddressType) -> dict:
24
+ """
25
+ Updates the Authorizenet address profile.
26
+
27
+ :param shipping_addr: A customer shipping address.
28
+ :type shipping_addr: :py:obj:`~authorizenet.apicontractsv1.customerAddressType`
29
+ :raises ControllerExecutionError: If something goes wrong during an Authorizenet API call.
30
+ :returns: An Authorizenet API response.
31
+ :rtype: :py:obj:`dict`
32
+
33
+ """
35
34
  return self._authorizenet_update_shipping_address(shipping_addr)
36
35
 
37
36
  def delete(self) -> dict:
38
- """Deletes the Authorize.NET payment profile."""
39
- return self._authorizenet_delete_shipping_address()
37
+ """
38
+ Deletes the Authorizenet address profile.
40
39
 
41
- def get_details(self) -> dict:
42
- return self._authorizenet_get_shipping_address()
40
+ :raises ControllerExecutionError: If something goes wrong during an Authorizenet API call.
41
+ :returns: An Authorizenet API response, if any.
42
+ :rtype: :py:obj:`dict`
43
+
44
+ """
45
+ return self._authorizenet_delete_shipping_address()
43
46
 
44
47
  def _authorizenet_get_shipping_address(self) -> dict:
48
+ """
49
+ Executes a :py:obj:`~authorizenet.apicontractsv1.getCustomerShippingAddressRequest` using the Authorizenet API.
50
+
51
+ :raises AssertionError: If :py:attr:`id` wasn't set.
52
+ :raises ControllerExecutionError: If something goes wrong during an Authorizenet API call.
53
+ :returns: An Authorizenet API response.
54
+ :rtype: :py:obj:`dict`
55
+
56
+ """
45
57
  assert self.id, "'id' was not set."
46
58
 
47
59
  request = apicontractsv1.getCustomerShippingAddressRequest(
@@ -53,7 +65,18 @@ class AddressProfile(AuthorizenetSubProfileBase):
53
65
  response = self.execute_controller(controller)
54
66
  return response
55
67
 
56
- def _authorizenet_create_shipping_address(self, addr: customerAddressType) -> dict:
68
+ def _authorizenet_create_shipping_address(
69
+ self, addr: apicontractsv1.customerAddressType
70
+ ) -> dict:
71
+ """
72
+ Executes a :py:obj:`~authorizenet.apicontractsv1.createCustomerShippingAddressRequest` using the Authorizenet API.
73
+
74
+ :raises AssertionError: If :py:attr:`id` wasn't set.
75
+ :raises ControllerExecutionError: If something goes wrong during an Authorizenet API call.
76
+ :returns: An Authorizenet API response.
77
+ :rtype: :py:obj:`dict`
78
+
79
+ """
57
80
  request = apicontractsv1.createCustomerShippingAddressRequest(
58
81
  merchantAuthentication=self.merchantAuthentication,
59
82
  customerProfileId=self.customerProfileId,
@@ -64,10 +87,21 @@ class AddressProfile(AuthorizenetSubProfileBase):
64
87
  response = self.execute_controller(controller)
65
88
  return response
66
89
 
67
- def _authorizenet_update_shipping_address(self, addr: customerAddressType) -> dict:
90
+ def _authorizenet_update_shipping_address(
91
+ self, addr: apicontractsv1.customerAddressType
92
+ ) -> dict:
93
+ """
94
+ Executes a :py:obj:`~authorizenet.apicontractsv1.updateCustomerShippingAddressRequest` using the Authorizenet API.
95
+
96
+ :raises AssertionError: If :py:attr:`id` wasn't set.
97
+ :raises ControllerExecutionError: If something goes wrong during an Authorizenet API call.
98
+ :returns: An Authorizenet API response.
99
+ :rtype: :py:obj:`dict`
100
+
101
+ """
68
102
  assert self.id, "'id' was not set."
69
- addr.customerAddressId = self.id
70
103
 
104
+ addr.customerAddressId = self.id
71
105
  request = apicontractsv1.updateCustomerShippingAddressRequest(
72
106
  merchantAuthentication=self.merchantAuthentication,
73
107
  customerProfileId=self.customerProfileId,
@@ -75,10 +109,18 @@ class AddressProfile(AuthorizenetSubProfileBase):
75
109
  default=self.default,
76
110
  )
77
111
  controller = apicontrollers.updateCustomerShippingAddressController(request)
78
- response = self.execute_controller(controller)
79
- return response
112
+ return self.execute_controller(controller)
80
113
 
81
114
  def _authorizenet_delete_shipping_address(self) -> dict:
115
+ """
116
+ Executes a :py:obj:`~authorizenet.apicontractsv1.deleteCustomerShippingAddressRequest` using the Authorizenet API.
117
+
118
+ :raises AssertionError: If :py:attr:`id` wasn't set.
119
+ :raises ControllerExecutionError: If something goes wrong during an Authorizenet API call.
120
+ :returns: An Authorizenet API response.
121
+ :rtype: :py:obj:`dict`
122
+
123
+ """
82
124
  assert self.id, "'id' was not set."
83
125
 
84
126
  request = apicontractsv1.deleteCustomerShippingAddressRequest(
@@ -1,81 +1,40 @@
1
1
  from abc import abstractmethod
2
+ from typing import override
2
3
 
3
- from authorizenet.apicontractsv1 import merchantAuthenticationType
4
- from authorizenet.apicontrollersbase import APIOperationBase
4
+ from authorizenet import apicontractsv1
5
5
 
6
- from ..auth import get_merchant_auth, get_environment, get_validation_mode
6
+ from terminusgps.authorizenet.auth import get_merchant_auth, get_validation_mode
7
+ from terminusgps.authorizenet.utils import ControllerExecutionMixin
7
8
 
8
9
 
9
- class AuthorizenetProfileBase:
10
+ class AuthorizenetProfileBase(ControllerExecutionMixin):
10
11
  def __init__(
11
- self, merchant_id: int | str, id: int | str | None = None, **kwargs
12
+ self, merchant_id: int | str, id: int | str | None = None, *args, **kwargs
12
13
  ) -> None:
13
14
  self._merchantCustomerId = merchant_id
14
- self._id = str(id) if id else self.create(**kwargs)
15
+ self._id = int(id) if id else self.create(*args, **kwargs)
15
16
 
17
+ @override
16
18
  def __str__(self) -> str:
17
19
  return f"#{self.id}"
18
20
 
19
- def execute_controller(self, controller: APIOperationBase) -> dict:
20
- """
21
- Executes an Authorize.NET controller and returns its response.
22
-
23
- :param controller: An Authorize.NET API controller.
24
- :raises ValueError: If the API call fails.
25
- :returns: The Authorize.NET API response.
26
- :rtype: :py:obj:`dict`
27
-
28
- """
29
- controller.setenvironment(self.environment)
30
- controller.execute()
31
- response = controller.getresponse()
32
-
33
- if response.messages.resultCode != "Ok":
34
- raise ValueError(response.messages.message[0]["text"].text)
35
- return response
36
-
37
21
  @property
38
22
  def merchantCustomerId(self) -> str:
39
- """
40
- An internally designated customer id.
41
-
42
- :type: :py:obj:`str`
43
-
44
- """
23
+ """An internally designated customer id."""
45
24
  return str(self._merchantCustomerId)
46
25
 
47
26
  @property
48
27
  def id(self) -> str:
49
- """
50
- An Authorizenet generated id.
51
-
52
- :type: :py:obj:`str`
53
-
54
- """
28
+ """An Authorizenet generated id."""
55
29
  return str(self._id)
56
30
 
57
31
  @property
58
- def merchantAuthentication(self) -> merchantAuthenticationType:
59
- """
60
- Merchant authentication for Authorizenet API calls.
61
-
62
- :type: :py:obj:`~authorizenet.apicontractsv1.merchantAuthenticationType`
63
-
64
- """
32
+ def merchantAuthentication(self) -> apicontractsv1.merchantAuthenticationType:
33
+ """Merchant authentication for Authorizenet API calls."""
65
34
  return get_merchant_auth()
66
35
 
67
- @property
68
- def environment(self) -> str:
69
- """
70
- Environment for Authorizenet API calls to execute within.
71
-
72
- :type: :py:obj:`str`
73
-
74
- """
75
- return get_environment()
76
-
77
36
  @abstractmethod
78
- def create(self, **kwargs) -> int:
37
+ def create(self, *args, **kwargs) -> int:
79
38
  raise NotImplementedError("Subclasses must implement this method.")
80
39
 
81
40
  @abstractmethod
@@ -93,34 +52,19 @@ class AuthorizenetSubProfileBase(AuthorizenetProfileBase):
93
52
  ) -> None:
94
53
  self._customerProfileId = customer_profile_id
95
54
  self._default = default
96
- return super().__init__(*args, **kwargs)
55
+ super().__init__(*args, **kwargs)
97
56
 
98
57
  @property
99
58
  def validationMode(self) -> str:
100
- """
101
- The validation mode for Authorizenet API calls.
102
-
103
- :type: :py:obj:`str`
104
-
105
- """
59
+ """The validation mode for Authorizenet API calls."""
106
60
  return get_validation_mode()
107
61
 
108
62
  @property
109
63
  def default(self) -> str:
110
- """
111
- Whether or not the sub profile is set as default in Authorizenet.
112
-
113
- :type: :py:obj:`str`
114
-
115
- """
64
+ """Whether or not the sub profile is set as default in Authorizenet."""
116
65
  return str(self._default).lower()
117
66
 
118
67
  @property
119
68
  def customerProfileId(self) -> str:
120
- """
121
- An Authorizenet generated customer profile id.
122
-
123
- :py:obj:`str`
124
-
125
- """
69
+ """An Authorizenet generated customer profile id."""
126
70
  return str(self._customerProfileId)
@@ -2,55 +2,119 @@ from authorizenet import apicontractsv1, apicontrollers
2
2
  from authorizenet.apicontractsv1 import customerProfileType
3
3
 
4
4
  from terminusgps.authorizenet.profiles.base import AuthorizenetProfileBase
5
+ from terminusgps.authorizenet.utils import get_customer_profile_ids
5
6
 
6
7
 
7
8
  class CustomerProfile(AuthorizenetProfileBase):
8
- def create(self, **kwargs) -> int:
9
+ """An Authorizenet customer profile."""
10
+
11
+ def create(self, email: str, desc: str | None = None) -> int:
9
12
  """
10
- Creates the customer profile.
13
+ Creates a customer profile using the Authorizenet API and returns its id.
11
14
 
12
- :param email: An email address for the customer profile.
15
+ :param email: An email address.
13
16
  :type email: :py:obj:`str`
14
- :param desc: A description describing the customer profile. Optional.
17
+ :param desc: An optional description.
15
18
  :type desc: :py:obj:`str` | :py:obj:`None`
19
+ :raises ControllerExecutionError: If something goes wrong during an Authorizenet API call.
16
20
  :returns: The new customer profile id.
17
21
  :rtype: :py:obj:`int`
18
22
 
19
23
  """
20
- if not kwargs.get("email"):
21
- raise ValueError("'email' is required for creation.")
22
-
23
- response = self._authorizenet_create_customer_profile(
24
- email=kwargs["email"], desc=kwargs.get("desc", "")
25
- )
24
+ response = self._authorizenet_create_customer_profile(email=email, desc=desc)
26
25
  return int(response.customerProfileId)
27
26
 
28
- def update(self, email: str, desc: str = "") -> dict:
29
- """Updates the customer profile."""
30
- return self._authorizenet_update_customer_profile(email, desc)
27
+ def update(self, email: str, desc: str = "") -> None:
28
+ """
29
+ Updates the customer profile.
31
30
 
32
- def delete(self) -> dict:
33
- """Deletes the customer profile."""
34
- return self._authorizenet_delete_customer_profile()
31
+ :param email: An email address.
32
+ :type email: :py:obj:`str`
33
+ :param desc: An optional description. Default is ``""``
34
+ :type desc: :py:obj:`str`
35
+ :raises ControllerExecutionError: If something goes wrong during an Authorizenet API call.
36
+ :returns: Nothing.
37
+ :rtype: :py:obj:`None`
38
+
39
+ """
40
+ self._authorizenet_update_customer_profile(email, desc)
41
+
42
+ def delete(self) -> None:
43
+ """
44
+ Deletes the customer profile.
45
+
46
+ :raises ControllerExecutionError: If something goes wrong during an Authorizenet API call.
47
+ :returns: Nothing.
48
+ :rtype: :py:obj:`None`
49
+
50
+ """
51
+ self._authorizenet_delete_customer_profile()
35
52
 
36
53
  def get_payment_profiles(self) -> list[dict] | None:
37
- """Retrieves the customer profile's payment profiles list."""
38
- profiles = self._authorizenet_get_customer_profile(issuer_info=False).get(
39
- "paymentProfiles"
40
- )
41
- return profiles if profiles else None
54
+ """
55
+ Returns a list of the customer's payment profiles.
56
+
57
+ :raises ControllerExecutionError: If something goes wrong during an Authorizenet API call.
58
+ :returns: A list of customer payment profiles, if they exist.
59
+ :rtype: :py:obj:`list` | :py:obj:`None`
60
+
61
+ """
62
+ response = self._authorizenet_get_customer_profile(issuer_info=False)
63
+ return response.get("paymentProfiles") if response else None
64
+
65
+ def get_addresses(self) -> list[dict] | None:
66
+ """
67
+ Returns a list of the customer's address profiles.
68
+
69
+ :raises ControllerExecutionError: If something goes wrong during an Authorizenet API call.
70
+ :returns: A list of customer shipping addresses, if they exist.
71
+ :rtype: :py:obj:`list` | :py:obj:`None`
72
+
73
+ """
74
+ response = self._authorizenet_get_customer_profile(issuer_info=False)
75
+ return response.get("shipToList") if response else None
76
+
77
+ @property
78
+ def exists(self) -> bool:
79
+ """Whether or not the customer profile exists in Authorizenet."""
80
+ profile_ids = get_customer_profile_ids()
81
+ return int(self.id) in profile_ids if self.id is not None else False
82
+
83
+ def _authorizenet_get_customer_profile_ids(self) -> dict | None:
84
+ """
85
+ Executes a :py:obj:`~authorizenet.apicontractsv1.getCustomerProfileIdsRequest` using the Authorizenet API.
86
+
87
+ `getCustomerProfileIdsRequest <https://developer.authorize.net/api/reference/index.html#customer-profiles-get-customer-profile-ids>`_
42
88
 
43
- def get_shipping_addresses(self) -> list[dict] | None:
44
- """Retrieves the customer profile's shipping address list."""
45
- addresses = self._authorizenet_get_customer_profile(issuer_info=False).get(
46
- "shipToList"
89
+ :raises ControllerExecutionError: If something goes wrong during an Authorizenet API call.
90
+ :returns: An Authorizenet API response, if any.
91
+ :rtype: :py:obj:`dict` | :py:obj:`None`
92
+
93
+ """
94
+ request = apicontractsv1.getCustomerProfileIdsRequest(
95
+ merchantAuthentication=self.merchantAuthentication
47
96
  )
48
- return addresses if addresses else None
97
+ controller = apicontrollers.getCustomerProfileIdsController(request)
98
+ return self.execute_controller(controller)
49
99
 
50
100
  def _authorizenet_create_customer_profile(
51
101
  self, email: str, desc: str = ""
52
- ) -> dict[str, str]:
53
- """Executes a :py:obj:`~authorizenet.apicontractsv1.createCustomerProfileRequest` using the Authorize.NET API."""
102
+ ) -> dict | None:
103
+ """
104
+ Executes a :py:obj:`~authorizenet.apicontractsv1.createCustomerProfileRequest` using the Authorizenet API.
105
+
106
+ `createCustomerProfileRequest <https://developer.authorize.net/api/reference/index.html#customer-profiles-create-customer-profile>`_
107
+
108
+ :param email: An email address.
109
+ :type email: :py:obj:`str`
110
+ :param desc: An optional description.
111
+ :type desc: :py:obj:`str`
112
+ :raises AssertionError: If :py:attr:`id` wasn't set.
113
+ :raises ControllerExecutionError: If something goes wrong during an Authorizenet API call.
114
+ :returns: An Authorizenet API response, if any.
115
+ :rtype: :py:obj:`dict` | :py:obj:`None`
116
+
117
+ """
54
118
  request = apicontractsv1.createCustomerProfileRequest(
55
119
  merchantAuthentication=self.merchantAuthentication,
56
120
  profile=customerProfileType(
@@ -60,11 +124,24 @@ class CustomerProfile(AuthorizenetProfileBase):
60
124
  ),
61
125
  )
62
126
  controller = apicontrollers.createCustomerProfileController(request)
63
- response = self.execute_controller(controller)
64
- return response
127
+ return self.execute_controller(controller)
128
+
129
+ def _authorizenet_get_customer_profile(
130
+ self, issuer_info: bool = True
131
+ ) -> dict | None:
132
+ """
133
+ Executes a :py:obj:`~authorizenet.apicontractsv1.getCustomerProfileRequest` using the Authorizenet API.
65
134
 
66
- def _authorizenet_get_customer_profile(self, issuer_info: bool = True) -> dict:
67
- """Executes a :py:obj:`~authorizenet.apicontractsv1.getCustomerProfileRequest` using the Authorize.NET API."""
135
+ `getCustomerProfileRequest <https://developer.authorize.net/api/reference/index.html#customer-profiles-get-customer-profile>`_
136
+
137
+ :param issuer_info: Whether or not to include issuer info in the response.
138
+ :type issuer_info: :py:obj:`bool`
139
+ :raises AssertionError: If :py:attr:`id` was not set.
140
+ :raises ControllerExecutionError: If something goes wrong during an Authorizenet API call.
141
+ :returns: An Authorizenet API response, if any.
142
+ :rtype: :py:obj:`dict` | :py:obj:`None`
143
+
144
+ """
68
145
  assert self.id, "'id' was not set."
69
146
 
70
147
  request = apicontractsv1.getCustomerProfileRequest(
@@ -73,11 +150,26 @@ class CustomerProfile(AuthorizenetProfileBase):
73
150
  includeIssuerInfo=str(issuer_info).lower(),
74
151
  )
75
152
  controller = apicontrollers.getCustomerProfileController(request)
76
- response = self.execute_controller(controller)
77
- return response
153
+ return self.execute_controller(controller)
78
154
 
79
- def _authorizenet_update_customer_profile(self, email: str, desc: str = "") -> dict:
80
- """Executes an :py:obj:`~authorizenet.apicontractsv1.updateCustomerProfileRequest` using the Authorize.NET API."""
155
+ def _authorizenet_update_customer_profile(
156
+ self, email: str, desc: str = ""
157
+ ) -> dict | None:
158
+ """
159
+ Executes an :py:obj:`~authorizenet.apicontractsv1.updateCustomerProfileRequest` using the Authorizenet API.
160
+
161
+ `updateCustomerProfileRequest <https://developer.authorize.net/api/reference/index.html#customer-profiles-update-customer-profile>`_
162
+
163
+ :param email: An email address.
164
+ :type email: :py:obj:`str`
165
+ :param desc: An optional description.
166
+ :type desc: :py:obj:`str`
167
+ :raises ControllerExecutionError: If something goes wrong during an Authorizenet API call.
168
+ :raises AssertionError: If :py:attr:`id` wasn't set.
169
+ :returns: An Authorizenet API response, if any.
170
+ :rtype: :py:obj:`dict` | :py:obj:`None`
171
+
172
+ """
81
173
  assert self.id, "'id' was not set."
82
174
 
83
175
  request = apicontractsv1.updateCustomerProfileRequest(
@@ -90,11 +182,20 @@ class CustomerProfile(AuthorizenetProfileBase):
90
182
  ),
91
183
  )
92
184
  controller = apicontrollers.updateCustomerProfileController(request)
93
- response = self.execute_controller(controller)
94
- return response
185
+ return self.execute_controller(controller)
186
+
187
+ def _authorizenet_delete_customer_profile(self) -> dict | None:
188
+ """
189
+ Executes a :py:obj:`~authorizenet.apicontractsv1.deleteCustomerProfileRequest` using the Authorizenet API.
95
190
 
96
- def _authorizenet_delete_customer_profile(self) -> dict:
97
- """Executes a :py:obj:`~authorizenet.apicontractsv1.deleteCustomerProfileRequest` using the Authorize.NET API."""
191
+ `deleteCustomerProfileRequest <https://developer.authorize.net/api/reference/index.html#customer-profiles-delete-customer-profile>`_
192
+
193
+ :raises AssertionError: If :py:attr:`id` wasn't set.
194
+ :raises ControllerExecutionError: If something goes wrong during an Authorizenet API call.
195
+ :returns: An Authorizenet API response, if any.
196
+ :rtype: :py:obj:`dict` | :py:obj:`None`
197
+
198
+ """
98
199
  assert self.id, "'id' was not set."
99
200
 
100
201
  request = apicontractsv1.deleteCustomerProfileRequest(
@@ -102,5 +203,4 @@ class CustomerProfile(AuthorizenetProfileBase):
102
203
  customerProfileId=self.id,
103
204
  )
104
205
  controller = apicontrollers.deleteCustomerProfileController(request)
105
- response = self.execute_controller(controller)
106
- return response
206
+ return self.execute_controller(controller)
@@ -1,63 +1,108 @@
1
1
  from authorizenet import apicontractsv1, apicontrollers
2
- from authorizenet.apicontractsv1 import (
3
- customerAddressType,
4
- customerPaymentProfileType,
5
- paymentType,
6
- )
7
2
 
8
3
  from terminusgps.authorizenet.profiles.base import AuthorizenetSubProfileBase
9
4
 
10
5
 
11
6
  class PaymentProfile(AuthorizenetSubProfileBase):
12
- def create(self, **kwargs) -> int:
7
+ """An Authorizenet customer payment profile."""
8
+
9
+ def create(
10
+ self,
11
+ billing_addr: apicontractsv1.customerAddressType,
12
+ payment: apicontractsv1.paymentType,
13
+ ) -> int:
13
14
  """
14
- Creates an Authorize.NET payment profile.
15
+ Creates an Authorizenet payment profile and returns its id.
15
16
 
16
- :param billing_addr: An Authorize.NET customer address.
17
+ :param billing_addr: A billing address.
17
18
  :type billing_addr: :py:obj:`~authorizenet.apicontractsv1.customerAddressType`
18
- :param payment: An Authorize.NET API payment.
19
+ :param payment: A payment object.
19
20
  :type payment: :py:obj:`~authorizenet.apicontractsv1.paymentType`
20
21
  :returns: The new payment profile's id.
21
22
  :rtype: :py:obj:`int`
22
23
 
23
24
  """
24
- billing_addr = kwargs.get("billing_addr")
25
- payment = kwargs.get("payment")
26
- if not billing_addr:
27
- raise ValueError("'billing_addr' is required on creation.")
28
- if not payment:
29
- raise ValueError("'payment' is required on creation.")
30
-
31
- if not isinstance(billing_addr, customerAddressType):
32
- raise TypeError(
33
- f"'billing_addr' must be customerAddressType, got '{type(billing_addr)}'"
34
- )
35
- if not isinstance(payment, paymentType):
36
- raise TypeError(
37
- f"'payment' must be customerAddressType, got '{type(payment)}'"
38
- )
39
-
40
25
  response = self._authorizenet_create_payment_profile(billing_addr, payment)
41
26
  return int(response.customerPaymentProfileId)
42
27
 
43
- def update(self, billing_addr: customerAddressType, payment: paymentType) -> dict:
44
- """Updates the Authorize.NET payment profile."""
28
+ def update(
29
+ self,
30
+ billing_addr: apicontractsv1.customerAddressType,
31
+ payment: apicontractsv1.paymentType,
32
+ ) -> dict:
33
+ """Updates the Authorizenet payment profile."""
45
34
  return self._authorizenet_update_payment_profile(billing_addr, payment)
46
35
 
47
36
  def delete(self) -> dict:
48
- """Deletes the Authorize.NET payment profile."""
37
+ """Deletes the Authorizenet payment profile."""
49
38
  return self._authorizenet_delete_payment_profile()
50
39
 
51
40
  def get_details(self, issuer_info: bool = False) -> dict:
52
41
  return self._authorizenet_get_payment_profile(issuer_info)
53
42
 
43
+ def _authorizenet_get_transaction_list_for_customer(
44
+ self,
45
+ limit: int = 100,
46
+ ordering: str = "submitTimeUTC",
47
+ offset: int = 1,
48
+ descending: bool = False,
49
+ ) -> dict:
50
+ """
51
+ Executes a :py:obj:`~authorizenet.apicontractsv1.getTransactionListForCustomerRequest` using the Authorizenet API.
52
+
53
+ :param limit: Total number of transactions to retrieve. Default is ``100``.
54
+ :type limit: :py:obj:`int`
55
+ :param ordering: A field to order the transactions by. Default is ``"submitTimeUTC"``.
56
+ :type ordering: :py:obj:`str`
57
+ :param offset: Page of the response to retrieve.
58
+ :type offset: :py:obj:`~authorizenet.apicontractsv1.customerAddressType`
59
+ :param descending: Sort the results in descending order.
60
+ :type descending: :py:obj:`bool`
61
+ :raises ValueError: If ``ordering`` is invalid.
62
+ :raises AssertionError: If :py:attr:`id` wasn't set.
63
+ :returns: An Authorizenet API response.
64
+ :rtype: :py:obj:`dict`
65
+
66
+ """
67
+ ordering_options: tuple[str, str] = ("id", "submitTimeUTC")
68
+ if ordering not in ordering_options:
69
+ raise ValueError(
70
+ f"'ordering' must be one of {ordering_options}, got '{ordering}'."
71
+ )
72
+
73
+ request = apicontractsv1.getTransactionListForCustomerRequest(
74
+ merchantAuthentication=self.merchantAuthentication,
75
+ customerProfileId=self.customerProfileId,
76
+ customerPaymentProfileId=self.id,
77
+ sorting={"orderBy": ordering, "orderDescending": str(descending).lower()},
78
+ paging={"limit": str(limit), "offset": str(offset)},
79
+ )
80
+ controller = apicontrollers.getTransactionListForCustomerController(request)
81
+ response = self.execute_controller(controller)
82
+ return response
83
+
54
84
  def _authorizenet_create_payment_profile(
55
- self, billing_addr: customerAddressType, payment: paymentType
85
+ self,
86
+ billing_addr: apicontractsv1.customerAddressType,
87
+ payment: apicontractsv1.paymentType,
56
88
  ) -> dict:
89
+ """
90
+ Executes a :py:obj:`~authorizenet.apicontractsv1.createCustomerPaymentProfileRequest` using the Authorizenet API.
91
+
92
+ :param billing_addr: A customer address.
93
+ :type billing_addr: :py:obj:`~authorizenet.apicontractsv1.customerAddressType`
94
+ :param payment: A payment method.
95
+ :type payment: :py:obj:`~authorizenet.apicontractsv1.paymentType`
96
+ :raises AssertionError: If :py:attr:`id` wasn't set.
97
+ :raises : If :py:attr:`id` wasn't set.
98
+ :returns: An Authorizenet API response.
99
+ :rtype: :py:obj:`dict`
100
+
101
+ """
57
102
  request = apicontractsv1.createCustomerPaymentProfileRequest(
58
103
  customerProfileId=self.customerProfileId,
59
104
  merchantAuthentication=self.merchantAuthentication,
60
- paymentProfile=customerPaymentProfileType(
105
+ paymentProfile=apicontractsv1.customerPaymentProfileType(
61
106
  billTo=billing_addr, payment=payment, defaultPaymentProfile=self.default
62
107
  ),
63
108
  validationMode=self.validationMode,
@@ -67,6 +112,16 @@ class PaymentProfile(AuthorizenetSubProfileBase):
67
112
  return response
68
113
 
69
114
  def _authorizenet_get_payment_profile(self, issuer_info: bool = False) -> dict:
115
+ """
116
+ Executes a :py:obj:`~authorizenet.apicontractsv1.getCustomerPaymentProfileRequest` using the Authorizenet API.
117
+
118
+ :param issuer_info: Whether or not to include issuer information in the response.
119
+ :type issuer_info: :py:obj:`bool`
120
+ :raises AssertionError: If :py:attr:`id` wasn't set.
121
+ :returns: An Authorizenet API response.
122
+ :rtype: :py:obj:`dict`
123
+
124
+ """
70
125
  assert self.id, "'id' was not set."
71
126
 
72
127
  request = apicontractsv1.getCustomerPaymentProfileRequest(
@@ -80,14 +135,28 @@ class PaymentProfile(AuthorizenetSubProfileBase):
80
135
  return response
81
136
 
82
137
  def _authorizenet_update_payment_profile(
83
- self, billing_addr: customerAddressType, payment: paymentType
138
+ self,
139
+ billing_addr: apicontractsv1.customerAddressType,
140
+ payment: apicontractsv1.paymentType,
84
141
  ) -> dict:
142
+ """
143
+ Executes a :py:obj:`~authorizenet.apicontractsv1.validateCustomerPaymentProfileRequest` using the Authorizenet API.
144
+
145
+ :param billing_addr: A customer address.
146
+ :type billing_addr: :py:obj:`~authorizenet.apicontractsv1.customerAddressType`
147
+ :param payment: A payment method.
148
+ :type payment: :py:obj:`~authorizenet.apicontractsv1.paymentType`
149
+ :raises AssertionError: If :py:attr:`id` wasn't set.
150
+ :returns: An Authorizenet API response.
151
+ :rtype: :py:obj:`dict`
152
+
153
+ """
85
154
  assert self.id, "'id' was not set."
86
155
 
87
156
  request = apicontractsv1.updateCustomerPaymentProfileRequest(
88
157
  merchantAuthentication=self.merchantAuthentication,
89
158
  customerProfileId=self.customerProfileId,
90
- paymentProfile=customerPaymentProfileType(
159
+ paymentProfile=apicontractsv1.customerPaymentProfileType(
91
160
  billTo=billing_addr,
92
161
  payment=payment,
93
162
  defaultPaymentProfile=self.default,
@@ -100,6 +169,14 @@ class PaymentProfile(AuthorizenetSubProfileBase):
100
169
  return response
101
170
 
102
171
  def _authorizenet_validate_payment_profile(self) -> dict:
172
+ """
173
+ Executes a :py:obj:`~authorizenet.apicontractsv1.validateCustomerPaymentProfileRequest` using the Authorizenet API.
174
+
175
+ :raises AssertionError: If :py:attr:`id` wasn't set.
176
+ :returns: An Authorizenet API response.
177
+ :rtype: :py:obj:`dict`
178
+
179
+ """
103
180
  assert self.id, "'id' was not set."
104
181
 
105
182
  request = apicontractsv1.validateCustomerPaymentProfileRequest(
@@ -113,6 +190,14 @@ class PaymentProfile(AuthorizenetSubProfileBase):
113
190
  return response
114
191
 
115
192
  def _authorizenet_delete_payment_profile(self) -> dict:
193
+ """
194
+ Executes a :py:obj:`~authorizenet.apicontractsv1.deleteCustomerPaymentProfileRequest` using the Authorizenet API.
195
+
196
+ :raises AssertionError: If :py:attr:`id` wasn't set.
197
+ :returns: An Authorizenet API response.
198
+ :rtype: :py:obj:`dict`
199
+
200
+ """
116
201
  assert self.id, "'id' was not set."
117
202
 
118
203
  request = apicontractsv1.deleteCustomerPaymentProfileRequest(
@@ -1,54 +1,28 @@
1
- from authorizenet import apicontractsv1, apicontrollers, apicontrollersbase
2
- from terminusgps.authorizenet.auth import get_merchant_auth, get_environment
1
+ from authorizenet import apicontractsv1, apicontrollers
3
2
 
3
+ from terminusgps.authorizenet.auth import get_merchant_auth
4
+ from terminusgps.authorizenet.utils import ControllerExecutionMixin
4
5
 
5
- class Subscription:
6
- def __init__(self, name: str, id: str | int | None = None, **kwargs) -> None:
7
- if id is not None and isinstance(id, str) and not id.isdigit():
8
- raise ValueError(f"'id' must be a digit, got '{id}'.")
9
- self.name = name
10
- self.id = str(id if id else self.create(**kwargs))
11
-
12
- @property
13
- def merchantAuthentication(self) -> apicontractsv1.merchantAuthenticationType:
14
- return get_merchant_auth()
15
-
16
- @property
17
- def environment(self) -> str:
18
- return get_environment()
19
-
20
- def execute_controller(
21
- self, controller: apicontrollersbase.APIOperationBase
22
- ) -> dict:
23
- """
24
- Executes an Authorize.NET controller and returns its response.
25
-
26
- :param controller: An Authorize.NET API controller.
27
- :raises ValueError: If the API call fails.
28
- :returns: The Authorize.NET API response.
29
- :rtype: :py:obj:`dict`
30
6
 
31
- """
32
- controller.setenvironment(self.environment)
33
- controller.execute()
34
- response = controller.getresponse()
35
-
36
- if response.messages.resultCode != "Ok":
37
- raise ValueError(response.messages.message[0]["text"].text)
38
- return response
7
+ class Subscription(ControllerExecutionMixin):
8
+ def __init__(self, id: str | int | None = None, *args, **kwargs) -> None:
9
+ if id and isinstance(id, str) and not id.isdigit():
10
+ raise ValueError(f"'id' must be a digit, got '{id}'.")
11
+ self.id = int(id) if id else self.create(*args, **kwargs)
39
12
 
40
13
  def create(
41
14
  self,
42
- schedule: apicontractsv1.paymentScheduleType,
15
+ name: str,
43
16
  amount: str,
17
+ schedule: apicontractsv1.paymentScheduleType,
44
18
  payment: apicontractsv1.paymentType,
45
19
  address: apicontractsv1.customerAddressType,
46
20
  trial_amount: str = "0.00",
47
21
  ) -> int:
48
22
  request = apicontractsv1.ARBCreateSubscriptionRequest(
49
- merchantAuthentication=self.merchantAuthentication,
23
+ merchantAuthentication=get_merchant_auth(),
50
24
  subscription=apicontractsv1.ARBSubscriptionType(
51
- name=self.name,
25
+ name=name,
52
26
  paymentSchedule=schedule,
53
27
  amount=amount,
54
28
  trialAmount=trial_amount,
@@ -0,0 +1,61 @@
1
+ from authorizenet import apicontractsv1, apicontrollers
2
+ from authorizenet.apicontrollersbase import APIOperationBase
3
+
4
+ from .auth import get_environment, get_merchant_auth
5
+
6
+
7
+ class ControllerExecutionError(Exception):
8
+ """Raised when an Authorizenet API controller fails to execute."""
9
+
10
+ def __init__(self, message: str, code: str, *args, **kwargs) -> None:
11
+ self.code = code
12
+ super().__init__(message, *args, **kwargs)
13
+
14
+ def __str__(self) -> str:
15
+ return f"{self.code}: {self.message}"
16
+
17
+
18
+ class ControllerExecutionMixin:
19
+ @staticmethod
20
+ def execute_controller(controller: APIOperationBase) -> dict | None:
21
+ """
22
+ Executes an Authorizenet controller and returns its response.
23
+
24
+ :param controller: An Authorizenet API controller.
25
+ :type controller: :py:obj:`~authorizenet.apicontrollersbase.APIOperationBase`
26
+ :raises ControllerExecutionError: If the API call fails.
27
+ :returns: An Authorizenet API response, if any.
28
+ :rtype: :py:obj:`dict` | :py:obj:`None`
29
+
30
+ """
31
+ controller.setenvironment(get_environment())
32
+ controller.execute()
33
+ response = controller.getresponse()
34
+
35
+ if response is not None and response.messages.resultCode != "Ok":
36
+ raise ControllerExecutionError(
37
+ message=response.messages.message[0]["text"].text,
38
+ code=response.messages.message[0]["code"].text,
39
+ )
40
+ return response
41
+
42
+
43
+ def get_customer_profile_ids() -> list[int]:
44
+ """
45
+ Returns a list of all customer profile ids in Authorizenet.
46
+
47
+ :raises ValueError: If something goes wrong calling the Authorizenet API.
48
+ :returns: A list of all customer profile ids in Authorizenet.
49
+ :rtype: :py:obj:`list`
50
+
51
+ """
52
+ request = apicontractsv1.getCustomerProfileIdsRequest(
53
+ merchantAuthentication=get_merchant_auth()
54
+ )
55
+ controller = apicontrollers.getCustomerProfileIdsController(request)
56
+ controller.execute()
57
+ response = controller.getresponse()
58
+
59
+ if response is not None and response.messages.resultCode != "Ok":
60
+ raise ValueError(response.messages.message["text"].text)
61
+ return [int(id) for id in response.ids.getchildren()]
@@ -1,6 +1,7 @@
1
- import boto3
2
1
  import json
3
2
 
3
+ import boto3
4
+
4
5
 
5
6
  def get_secret(name: str, region: str = "us-east-1") -> dict[str, str]:
6
7
  """Logs into the default AWS CLI session and returns a secret value."""
terminusgps/settings.py CHANGED
@@ -1,6 +1,8 @@
1
1
  import os
2
+ import pathlib
2
3
 
3
4
  DEBUG = True
5
+ BASE_DIR = pathlib.Path(__file__).resolve().parent
4
6
  MERCHANT_AUTH_LOGIN_ID = os.getenv("MERCHANT_AUTH_LOGIN_ID")
5
7
  MERCHANT_AUTH_TRANSACTION_KEY = os.getenv("MERCHANT_AUTH_TRANSACTION_KEY")
6
8
  SECRET_KEY = "xem*6no%8d9%^qzt2f3x3ar-uq4_+7h9myc$t0!+4%bj5us6f)"
@@ -1,7 +1,6 @@
1
1
  import dataclasses
2
2
  import datetime
3
3
  import logging
4
- import threading
5
4
  import typing
6
5
 
7
6
  from django.conf import settings
@@ -80,7 +79,7 @@ class WialonSession:
80
79
  :type host: :py:obj:`str`
81
80
  :param port: Wialon API host port. Default is ``443``.
82
81
  :type port: :py:obj:`int`
83
- :param log_level: Severity level at which log messages should be handled. Default is ``20`` (logging.INFO).
82
+ :param log_level: Logging severity level. Default is ``20`` (logging.INFO).
84
83
  :type log_level: :py:obj:`int`
85
84
  :returns: Nothing.
86
85
  :rtype: :py:obj:`None`
@@ -106,7 +105,7 @@ class WialonSession:
106
105
  self.login(self.token)
107
106
  return self
108
107
 
109
- def __exit__(self, *args, **kwargs) -> None:
108
+ def __exit__(self, exc_type, exc_value, exc_traceback) -> None:
110
109
  """Logs out of the session by calling :py:meth:`logout`."""
111
110
  self.logout()
112
111
 
@@ -324,23 +323,3 @@ class WialonSession:
324
323
  self._username = login_response.get("au")
325
324
  self._video_service_url = login_response.get("video_service_url")
326
325
  self._wsdk_version = login_response.get("wsdk_version")
327
-
328
-
329
- class WialonSessionManager:
330
- _instance = None
331
- _lock = threading.Lock()
332
- _session = None
333
-
334
- def __new__(cls) -> "WialonSessionManager":
335
- with cls._lock:
336
- if not cls._instance:
337
- cls._instance = super().__new__(cls)
338
- return cls._instance
339
-
340
- def get_session(
341
- self, sid: str | None = None, log_level: int = logging.INFO
342
- ) -> WialonSession:
343
- with self._lock:
344
- if not self._session:
345
- self._session = WialonSession(sid=sid, log_level=log_level)
346
- return self._session
@@ -2,7 +2,7 @@ import secrets
2
2
  import string
3
3
  import typing
4
4
 
5
- from terminusgps.wialon import items, flags
5
+ from terminusgps.wialon import flags, items
6
6
  from terminusgps.wialon.items.base import WialonBase
7
7
  from terminusgps.wialon.session import WialonSession
8
8
 
@@ -15,7 +15,7 @@ def get_hw_type_id(name: str, session: WialonSession) -> int | None:
15
15
  :type name: :py:obj:`str`
16
16
  :param session: A valid Wialon API session.
17
17
  :type session: :py:obj:`~terminusgps.wialon.session.WialonSession`
18
- :raises WialonError: If something goes wrong with Wialon.
18
+ :raises WialonError: If something goes wrong during a Wialon API call.
19
19
  :returns: A Wialon hardware type id, if it was found.
20
20
  :rtype: :py:obj:`int` | :py:obj:`None`
21
21
 
@@ -34,18 +34,22 @@ def get_hw_type_id(name: str, session: WialonSession) -> int | None:
34
34
 
35
35
  def get_id_from_imei(imei: str, session: WialonSession) -> str | None:
36
36
  """
37
- Takes a Wialon unit's IMEI # and returns its unit id, if it exists.
37
+ Takes a Wialon unit's IMEI # and returns its unit id.
38
38
 
39
- :param iccid: A unique id.
40
- :type iccid: :py:obj:`str`
39
+ :param imei: A Wialon unit's IMEI #.
40
+ :type imei: :py:obj:`str`
41
41
  :param session: A valid Wialon API session.
42
42
  :type session: :py:obj:`~terminusgps.wialon.session.WialonSession`
43
- :raises WialonError: If something goes wrong with Wialon.
43
+ :raises ValueError: If ``imei`` contains non-digit characters.
44
+ :raises WialonError: If something goes wrong during a Wialon API call.
44
45
  :returns: A Wialon object id, if it was found.
45
46
  :rtype: :py:obj:`str` | :py:obj:`None`
46
47
 
47
48
  """
48
- response = session.wialon_api.core_search_items(
49
+ if not imei.isdigit():
50
+ raise ValueError(f"'imei' must be a digit, got '{imei}'.")
51
+
52
+ results = session.wialon_api.core_search_items(
49
53
  **{
50
54
  "spec": {
51
55
  "itemsType": "avl_unit",
@@ -62,8 +66,8 @@ def get_id_from_imei(imei: str, session: WialonSession) -> str | None:
62
66
  }
63
67
  )
64
68
 
65
- if response.get("totalItemsCount", 0) == 1:
66
- return response["items"][0].get("id")
69
+ if results.get("totalItemsCount", 0) == 1:
70
+ return results["items"][0].get("id")
67
71
 
68
72
 
69
73
  def get_id_from_iccid(iccid: str, session: WialonSession) -> str | None:
@@ -76,7 +80,7 @@ def get_id_from_iccid(iccid: str, session: WialonSession) -> str | None:
76
80
  :type iccid: :py:obj:`str`
77
81
  :param session: A valid Wialon API session.
78
82
  :type session: :py:obj:`~terminusgps.wialon.session.WialonSession`
79
- :raises WialonError: If something goes wrong with Wialon.
83
+ :raises WialonError: If something goes wrong during a Wialon API call.
80
84
  :returns: A Wialon object id, if it was found.
81
85
  :rtype: :py:obj:`str` | :py:obj:`None`
82
86
 
@@ -96,16 +100,15 @@ def get_wialon_cls(items_type: str) -> typing.Type[WialonBase]:
96
100
  """
97
101
  match items_type:
98
102
  case "user":
99
- wialon_cls = items.WialonUser
103
+ return items.WialonUser
100
104
  case "avl_unit":
101
- wialon_cls = items.WialonUnit
105
+ return items.WialonUnit
102
106
  case "avl_unit_group":
103
- wialon_cls = items.WialonUnitGroup
107
+ return items.WialonUnitGroup
104
108
  case "avl_resource":
105
- wialon_cls = items.WialonResource
109
+ return items.WialonResource
106
110
  case _:
107
- raise ValueError(f"Invalid items_type '{items_type}'")
108
- return wialon_cls
111
+ raise ValueError(f"Invalid items_type, got '{items_type}'.")
109
112
 
110
113
 
111
114
  def get_vin_info(vin_number: str, session: WialonSession) -> dict:
@@ -123,7 +126,6 @@ def get_vin_info(vin_number: str, session: WialonSession) -> dict:
123
126
  response = session.wialon_api.unit_get_vin_info(**{"vin": vin_number}).get(
124
127
  "vin_lookup_result"
125
128
  )
126
-
127
129
  return (
128
130
  {field.get("n"): field.get("v") for field in response.get("pflds")}
129
131
  if "error" not in response.keys()
@@ -135,31 +137,38 @@ def is_unique(value: str, session: WialonSession, items_type: str = "avl_unit")
135
137
  """
136
138
  Determines if the value is unique among Wialon objects of type 'items_type'.
137
139
 
138
- :param value: A value whose uniqueness is unknown.
140
+ :param value: A Wialon object name.
139
141
  :type value: :py:obj:`str`
140
142
  :param session: A valid Wialon API session.
141
143
  :type session: :py:obj:`~terminusgps.wialon.session.WialonSession`
142
- :param items_type: The type of Wialon objects to validate the value against. Default is ``"avl_unit"``.
144
+ :param items_type: Type of Wialon objects to validate the value against. Default is ``"avl_unit"``.
143
145
  :type items_type: :py:obj:`str`
146
+ :raises WialonError: If something goes wrong during a Wialon API call.
144
147
  :returns: Whether or not the value is unique among 'items_type'.
145
148
  :rtype: :py:obj:`bool`
146
149
 
147
150
  """
148
- result = session.wialon_api.core_check_unique(
149
- **{"type": items_type, "value": value.strip()}
150
- ).get("result")
151
- return not bool(result)
151
+ return not bool(
152
+ session.wialon_api.core_check_unique(
153
+ **{"type": items_type, "value": value.strip()}
154
+ ).get("result")
155
+ )
152
156
 
153
157
 
154
158
  def generate_wialon_password(length: int = 32) -> str:
155
159
  """
156
- Generates a Wialon compliant password of random characters.
160
+ Generates a Wialon compliant password between ``8`` and ``64`` characters.
161
+
162
+ The generated password will contain:
157
163
 
158
- Password length can be between ``8`` and ``64`` characters.
164
+ - At least one uppercase letter.
165
+ - At least one lowercase letter.
166
+ - At least one special symbol.
167
+ - At least three digits.
159
168
 
160
169
  :param length: Length of the generated password. Default is ``32``.
161
170
  :type length: :py:obj:`int`
162
- :raises ValueError: If the provided length is invalid.
171
+ :raises ValueError: If :py:length is less than ``8`` or greater than ``64``.
163
172
  :returns: A Wialon compliant password.
164
173
  :rtype: :py:obj:`str`
165
174
 
File without changes
@@ -1,31 +0,0 @@
1
- import cv2 as cv
2
- from pyzbar.pyzbar import decode
3
-
4
-
5
- def read_barcode(filepath: str) -> None:
6
- img = cv.imread(filepath)
7
- barcodes = decode(img)
8
-
9
- if not barcodes:
10
- raise ValueError("No barcodes detected.")
11
-
12
- for barcode in barcodes:
13
- (x, y, w, h) = barcode.rect
14
- cv.rectangle(img, (x - 10, y - 10), (x + w + 10, y + h + 10), (255, 0, 0), 2)
15
-
16
- print(f"{barcode.data = }")
17
- print(f"{barcode.type = }")
18
-
19
- cv.imshow("Image", img)
20
- cv.waitKey(0)
21
- cv.destroyAllWindows()
22
-
23
-
24
- def main() -> None:
25
- filepath = "/home/blake/sift_keypoints.jpg"
26
- read_barcode(filepath)
27
- return
28
-
29
-
30
- if __name__ == "__main__":
31
- main()