python-terminusgps 46.1.0__tar.gz → 47.1.0__tar.gz

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.
Files changed (55) hide show
  1. {python_terminusgps-46.1.0 → python_terminusgps-47.1.0}/PKG-INFO +1 -1
  2. {python_terminusgps-46.1.0 → python_terminusgps-47.1.0}/pyproject.toml +1 -1
  3. {python_terminusgps-46.1.0 → python_terminusgps-47.1.0}/terminusgps/default_settings.py +1 -0
  4. python_terminusgps-47.1.0/terminusgps/wialon/session.py +205 -0
  5. {python_terminusgps-46.1.0 → python_terminusgps-47.1.0}/terminusgps/wialon/utils.py +23 -10
  6. {python_terminusgps-46.1.0 → python_terminusgps-47.1.0}/uv.lock +109 -84
  7. python_terminusgps-46.1.0/terminusgps/wialon/session.py +0 -201
  8. python_terminusgps-46.1.0/terminusgps/wialon/validators.py +0 -114
  9. {python_terminusgps-46.1.0 → python_terminusgps-47.1.0}/.github/workflows/sphinx.yml +0 -0
  10. {python_terminusgps-46.1.0 → python_terminusgps-47.1.0}/.gitignore +0 -0
  11. {python_terminusgps-46.1.0 → python_terminusgps-47.1.0}/.python-version +0 -0
  12. {python_terminusgps-46.1.0 → python_terminusgps-47.1.0}/COPYING +0 -0
  13. {python_terminusgps-46.1.0 → python_terminusgps-47.1.0}/README.md +0 -0
  14. {python_terminusgps-46.1.0 → python_terminusgps-47.1.0}/docs/Makefile +0 -0
  15. {python_terminusgps-46.1.0 → python_terminusgps-47.1.0}/docs/make.bat +0 -0
  16. {python_terminusgps-46.1.0 → python_terminusgps-47.1.0}/docs/requirements.txt +0 -0
  17. {python_terminusgps-46.1.0 → python_terminusgps-47.1.0}/docs/source/authorizenet/api.rst +0 -0
  18. {python_terminusgps-46.1.0 → python_terminusgps-47.1.0}/docs/source/authorizenet/constants.rst +0 -0
  19. {python_terminusgps-46.1.0 → python_terminusgps-47.1.0}/docs/source/authorizenet/index.rst +0 -0
  20. {python_terminusgps-46.1.0 → python_terminusgps-47.1.0}/docs/source/authorizenet/service.rst +0 -0
  21. {python_terminusgps-46.1.0 → python_terminusgps-47.1.0}/docs/source/conf.py +0 -0
  22. {python_terminusgps-46.1.0 → python_terminusgps-47.1.0}/docs/source/index.rst +0 -0
  23. {python_terminusgps-46.1.0 → python_terminusgps-47.1.0}/docs/source/mixins.rst +0 -0
  24. {python_terminusgps-46.1.0 → python_terminusgps-47.1.0}/docs/source/validators.rst +0 -0
  25. {python_terminusgps-46.1.0 → python_terminusgps-47.1.0}/docs/source/wialon/constants.rst +0 -0
  26. {python_terminusgps-46.1.0 → python_terminusgps-47.1.0}/docs/source/wialon/exceptions.rst +0 -0
  27. {python_terminusgps-46.1.0 → python_terminusgps-47.1.0}/docs/source/wialon/index.rst +0 -0
  28. {python_terminusgps-46.1.0 → python_terminusgps-47.1.0}/docs/source/wialon/items.rst +0 -0
  29. {python_terminusgps-46.1.0 → python_terminusgps-47.1.0}/docs/source/wialon/session.rst +0 -0
  30. {python_terminusgps-46.1.0 → python_terminusgps-47.1.0}/docs/source/wialon/usage.rst +0 -0
  31. {python_terminusgps-46.1.0 → python_terminusgps-47.1.0}/terminusgps/__init__.py +0 -0
  32. {python_terminusgps-46.1.0 → python_terminusgps-47.1.0}/terminusgps/authorizenet/__init__.py +0 -0
  33. {python_terminusgps-46.1.0 → python_terminusgps-47.1.0}/terminusgps/authorizenet/api/__init__.py +0 -0
  34. {python_terminusgps-46.1.0 → python_terminusgps-47.1.0}/terminusgps/authorizenet/api/address_profiles.py +0 -0
  35. {python_terminusgps-46.1.0 → python_terminusgps-47.1.0}/terminusgps/authorizenet/api/customer_profiles.py +0 -0
  36. {python_terminusgps-46.1.0 → python_terminusgps-47.1.0}/terminusgps/authorizenet/api/payment_profiles.py +0 -0
  37. {python_terminusgps-46.1.0 → python_terminusgps-47.1.0}/terminusgps/authorizenet/api/subscriptions.py +0 -0
  38. {python_terminusgps-46.1.0 → python_terminusgps-47.1.0}/terminusgps/authorizenet/api/transactions.py +0 -0
  39. {python_terminusgps-46.1.0 → python_terminusgps-47.1.0}/terminusgps/authorizenet/constants.py +0 -0
  40. {python_terminusgps-46.1.0 → python_terminusgps-47.1.0}/terminusgps/authorizenet/service.py +0 -0
  41. {python_terminusgps-46.1.0 → python_terminusgps-47.1.0}/terminusgps/mixins.py +0 -0
  42. {python_terminusgps-46.1.0 → python_terminusgps-47.1.0}/terminusgps/validators.py +0 -0
  43. {python_terminusgps-46.1.0 → python_terminusgps-47.1.0}/terminusgps/wialon/__init__.py +0 -0
  44. {python_terminusgps-46.1.0 → python_terminusgps-47.1.0}/terminusgps/wialon/constants.py +0 -0
  45. {python_terminusgps-46.1.0 → python_terminusgps-47.1.0}/terminusgps/wialon/flags.py +0 -0
  46. {python_terminusgps-46.1.0 → python_terminusgps-47.1.0}/terminusgps/wialon/items/__init__.py +0 -0
  47. {python_terminusgps-46.1.0 → python_terminusgps-47.1.0}/terminusgps/wialon/items/account.py +0 -0
  48. {python_terminusgps-46.1.0 → python_terminusgps-47.1.0}/terminusgps/wialon/items/base.py +0 -0
  49. {python_terminusgps-46.1.0 → python_terminusgps-47.1.0}/terminusgps/wialon/items/factory.py +0 -0
  50. {python_terminusgps-46.1.0 → python_terminusgps-47.1.0}/terminusgps/wialon/items/resource.py +0 -0
  51. {python_terminusgps-46.1.0 → python_terminusgps-47.1.0}/terminusgps/wialon/items/retranslator.py +0 -0
  52. {python_terminusgps-46.1.0 → python_terminusgps-47.1.0}/terminusgps/wialon/items/route.py +0 -0
  53. {python_terminusgps-46.1.0 → python_terminusgps-47.1.0}/terminusgps/wialon/items/unit.py +0 -0
  54. {python_terminusgps-46.1.0 → python_terminusgps-47.1.0}/terminusgps/wialon/items/unit_group.py +0 -0
  55. {python_terminusgps-46.1.0 → python_terminusgps-47.1.0}/terminusgps/wialon/items/user.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-terminusgps
3
- Version: 46.1.0
3
+ Version: 47.1.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://terminusgps.github.io/python-terminusgps
6
6
  Project-URL: Repository, https://github.com/terminusgps/python-terminusgps
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "python-terminusgps"
3
- version = "46.1.0"
3
+ version = "47.1.0"
4
4
  description = "Provides abstractions/utilities for working with Wialon API, Authorize.NET API, AWS API, and more."
5
5
  readme = "README.md"
6
6
  authors = [ {name = "Blake Nall", email = "blake@terminusgps.com" } ]
@@ -16,3 +16,4 @@ MERCHANT_AUTH_VALIDATION_MODE = ValidationMode.TEST
16
16
  MERCHANT_AUTH_LOGIN_ID = os.getenv("MERCHANT_AUTH_LOGIN_ID")
17
17
  MERCHANT_AUTH_TRANSACTION_KEY = os.getenv("MERCHANT_AUTH_TRANSACTION_KEY")
18
18
  WIALON_TOKEN = os.getenv("WIALON_TOKEN")
19
+ WIALON_USERNAME = os.getenv("WIALON_USERNAME")
@@ -0,0 +1,205 @@
1
+ import logging
2
+ import os
3
+ import typing
4
+
5
+ import wialon.api
6
+
7
+ logger = logging.getLogger(__name__)
8
+
9
+
10
+ class WialonAPIError(Exception):
11
+ """Raised when a Wialon API call fails."""
12
+
13
+ def __init__(self, message, *args, **kwargs) -> None:
14
+ self.message = message
15
+ self._code = int(message._code)
16
+ super().__init__(message, *args, **kwargs)
17
+
18
+ @property
19
+ def code(self) -> int:
20
+ """Wialon API error code."""
21
+ return self._code
22
+
23
+
24
+ class Wialon(wialon.api.Wialon):
25
+ def call(self, action_name, *argc, **kwargs) -> dict[str, typing.Any]:
26
+ try:
27
+ return super().call(action_name, *argc, **kwargs)
28
+ except wialon.api.WialonError as e:
29
+ logger.warning(f"Failed to execute '{action_name}': '{e}'")
30
+ raise WialonAPIError(e)
31
+
32
+
33
+ class WialonSession:
34
+ def __init__(
35
+ self,
36
+ scheme: str = "https",
37
+ host: str = "hst-api.wialon.com",
38
+ port: int = 443,
39
+ sid: str | None = None,
40
+ token: str | None = None,
41
+ auth_hash: str | None = None,
42
+ username: str | None = None,
43
+ check_service: str | None = None,
44
+ ) -> None:
45
+ """
46
+ Starts or continues a Wialon API session.
47
+
48
+ :param scheme: HTTP request scheme to use. Default is ``"https"``.
49
+ :type scheme: str
50
+ :param host: Wialon API host url. Default is ``"hst-api.wialon.com"``.
51
+ :type host: str
52
+ :param port: Wialon API port. Default is ``443``.
53
+ :type port: int
54
+ :param sid: A Wialon API session id. Default is :py:obj:`None`.
55
+ :type sid: str | None
56
+ :param token: A Wialon API token. Default is :py:obj:`None`.
57
+ :type token: str | None
58
+ :param auth_hash: A Wialon API authentication hash. Default is environment variable ``"WIALON_TOKEN"``.
59
+ :type auth_hash: str | None
60
+ :param username: A Wialon user id to operate as during the session. Default is environment variable ``"WIALON_USERNAME"``.
61
+ :type username: str | None
62
+ :param check_service: A Wialon service name to check before calling the Wialon API. Default is :py:obj:`None`.
63
+ :type check_service: str | None
64
+ :returns: Nothing.
65
+ :rtype: None
66
+
67
+ """
68
+ self._uid = None
69
+ self._wialon_api = Wialon(scheme=scheme, host=host, port=port, sid=sid)
70
+
71
+ self._token = token if token else os.getenv("WIALON_TOKEN")
72
+ self._username = username if username else os.getenv("WIALON_USERNAME")
73
+ self._auth_hash = auth_hash
74
+ self._check_service = check_service
75
+
76
+ def __str__(self) -> str:
77
+ return f"Session #{self.id}"
78
+
79
+ def __repr__(self) -> str:
80
+ return f"{self.__class__}(sid={self.id})"
81
+
82
+ def __enter__(self) -> "WialonSession":
83
+ """Logs into the Wialon API session if it wasn't already active before returning it."""
84
+ if self.id is None:
85
+ if self._token:
86
+ self.token_login(token=self._token, username=self._username)
87
+ elif self._auth_hash and self._username:
88
+ self.auth_hash_login(
89
+ auth_hash=self._auth_hash,
90
+ username=self._username,
91
+ check_service=self._check_service,
92
+ )
93
+ else:
94
+ raise WialonAPIError(
95
+ message="Failed to login to the Wialon API", code=9001
96
+ )
97
+ return self
98
+
99
+ def __exit__(self, *args, **kwargs) -> None:
100
+ """Logs out of the Wialon API session if :py:attr:`id` was set."""
101
+ if self.id is not None:
102
+ self.logout()
103
+
104
+ def token_login(self, token: str, username: str | None = None) -> None:
105
+ """
106
+ Logs in to a Wialon API session using a token.
107
+
108
+ :param token: A Wialon API token.
109
+ :type token: str
110
+ :param username: Wialon user to operate as during the Wialon API session. Default is :py:obj:`None`.
111
+ :type username: str
112
+ :returns: Nothing.
113
+ :rtype: None
114
+
115
+ """
116
+ params = {"token": token, "flags": 0x3 if username else 0x1}
117
+ if username is not None:
118
+ params.update({"operateAs": username})
119
+ response = self.wialon_api.token_login(**params)
120
+ self.wialon_api.sid = response.get("eid")
121
+ self._username = response.get("au")
122
+ self._uid = response.get("user", {}).get("id")
123
+
124
+ def auth_hash_login(
125
+ self, auth_hash: str, username: str, check_service: str | None = None
126
+ ) -> None:
127
+ """
128
+ Logs in to a Wialon API session using an auth hash.
129
+
130
+ :param auth_hash: An authorization hash.
131
+ :type auth_hash: str
132
+ :param username: Wialon user to operate as during the Wialon API session.
133
+ :type username: str
134
+ :param check_service: Name of a Wialon service to check if the user has access to. Default is :py:obj:`None` (no service check).
135
+ :type check_service: str | None
136
+ :returns: Nothing.
137
+ :rtype: None
138
+
139
+ """
140
+ params = {"authHash": auth_hash, "operateAs": username}
141
+ if check_service is not None:
142
+ params.update({"checkService": check_service})
143
+ response = self.wialon_api.core_use_auth_hash(**params)
144
+ self.wialon_api.sid = response.get("eid")
145
+ self._username = response.get("au")
146
+ self._uid = response.get("user", {}).get("id")
147
+
148
+ def logout(self) -> None:
149
+ """
150
+ Logs out of the Wialon API session.
151
+
152
+ :raises WialonAPIError: If the Wialon API session logout failed.
153
+ :returns: Nothing.
154
+ :rtype: None
155
+
156
+ """
157
+ session_id = self.wialon_api.sid
158
+ if session_id is not None:
159
+ response = self.wialon_api.core_logout({})
160
+ if not int(response.get("error")) == 0:
161
+ raise WialonAPIError(
162
+ message=f"Failed to logout of the Wialon API session #{session_id}",
163
+ code=int(response.get("error")),
164
+ )
165
+ self.wialon_api.sid = None
166
+
167
+ @property
168
+ def wialon_api(self) -> Wialon:
169
+ return self._wialon_api
170
+
171
+ @property
172
+ def uid(self) -> str | None:
173
+ """
174
+ User id of the session's authenticated Wialon user.
175
+
176
+ :type: str | None
177
+ :value: None
178
+ """
179
+ return self._uid
180
+
181
+ @property
182
+ def username(self) -> str | None:
183
+ """
184
+ Username of the session's authenticated Wialon user.
185
+
186
+ :type: str | None
187
+ :value: None
188
+
189
+ """
190
+ return self._username
191
+
192
+ @property
193
+ def id(self) -> str | None:
194
+ """
195
+ Wialon API session id.
196
+
197
+ Shortcut property for :py:attr:`WialonSession.wialon_api.sid`.
198
+
199
+ Returns :py:obj:`None` if the session wasn't logged in.
200
+
201
+ :type: str | None
202
+ :value: None
203
+
204
+ """
205
+ return self.wialon_api.sid
@@ -64,9 +64,9 @@ def get_user_by_name(name: str, session: WialonSession) -> WialonUser:
64
64
  raise ValueError(f"Multiple users returned for '{name}'.")
65
65
  elif num_items <= 0:
66
66
  raise ValueError(f"Couldn't find a Wialon user named '{name}'.")
67
-
68
67
  factory = WialonObjectFactory(session)
69
- return factory.get("user", id=int(response.get("items")[0].get("id")))
68
+ user_id = response.get("items")[0].get("id")
69
+ return factory.get("user", id=int(user_id))
70
70
 
71
71
 
72
72
  def get_carrier_names(session: WialonSession) -> list[str]:
@@ -89,7 +89,8 @@ def get_carrier_names(session: WialonSession) -> list[str]:
89
89
  "propType": "propitemname",
90
90
  },
91
91
  "force": 0,
92
- "flags": flags.DataFlag.UNIT_BASE | flags.DataFlag.UNIT_ADMIN_FIELDS,
92
+ "flags": flags.DataFlag.UNIT_BASE
93
+ | flags.DataFlag.UNIT_ADMIN_FIELDS,
93
94
  "from": 0,
94
95
  "to": 0,
95
96
  }
@@ -103,7 +104,9 @@ def get_carrier_names(session: WialonSession) -> list[str]:
103
104
  return sorted(list(frozenset(carrier_names)))
104
105
 
105
106
 
106
- def get_units_by_carrier(carrier_name: str, session: WialonSession) -> list[WialonUnit]:
107
+ def get_units_by_carrier(
108
+ carrier_name: str, session: WialonSession
109
+ ) -> list[WialonUnit]:
107
110
  """
108
111
  Returns a list of all units by telecommunications carrier company name.
109
112
 
@@ -125,14 +128,17 @@ def get_units_by_carrier(carrier_name: str, session: WialonSession) -> list[Wial
125
128
  "propType": "propitemname",
126
129
  },
127
130
  "force": 0,
128
- "flags": flags.DataFlag.UNIT_BASE | flags.DataFlag.UNIT_ADMIN_FIELDS,
131
+ "flags": flags.DataFlag.UNIT_BASE
132
+ | flags.DataFlag.UNIT_ADMIN_FIELDS,
129
133
  "from": 0,
130
134
  "to": 0,
131
135
  }
132
136
  )
133
137
  num_items: int = int(response.get("totalItemsCount"))
134
138
  if num_items <= 0:
135
- raise ValueError(f"Couldn't find any units by carrier '{carrier_name}'.")
139
+ raise ValueError(
140
+ f"Couldn't find any units by carrier '{carrier_name}'."
141
+ )
136
142
 
137
143
  factory = WialonObjectFactory(session)
138
144
  unit_ids = [int(unit.get("id")) for unit in response.get("items")]
@@ -161,7 +167,8 @@ def get_unit_by_iccid(iccid: str, session: WialonSession) -> WialonUnit:
161
167
  "propType": "propitemname",
162
168
  },
163
169
  "force": 0,
164
- "flags": flags.DataFlag.UNIT_BASE | flags.DataFlag.UNIT_ADMIN_FIELDS,
170
+ "flags": flags.DataFlag.UNIT_BASE
171
+ | flags.DataFlag.UNIT_ADMIN_FIELDS,
165
172
  "from": 0,
166
173
  "to": 0,
167
174
  }
@@ -177,7 +184,9 @@ def get_unit_by_iccid(iccid: str, session: WialonSession) -> WialonUnit:
177
184
  return factory.get("avl_unit", id=int(response.get("items")[0].get("id")))
178
185
 
179
186
 
180
- def get_unit_by_imei(imei: int | str, session: WialonSession) -> WialonUnit | None:
187
+ def get_unit_by_imei(
188
+ imei: int | str, session: WialonSession
189
+ ) -> WialonUnit | None:
181
190
  """
182
191
  Takes a Wialon unit's IMEI # and returns its unit id.
183
192
 
@@ -218,7 +227,9 @@ def get_unit_by_imei(imei: int | str, session: WialonSession) -> WialonUnit | No
218
227
  return factory.get("avl_unit", id=int(response.get("items")[0].get("id")))
219
228
 
220
229
 
221
- def get_vin_info(vin_number: str, session: WialonSession) -> dict[str, typing.Any]:
230
+ def get_vin_info(
231
+ vin_number: str, session: WialonSession
232
+ ) -> dict[str, typing.Any]:
222
233
  """
223
234
  Retrieves vehicle data from a VIN number.
224
235
 
@@ -279,7 +290,9 @@ def generate_wialon_password(length: int = 32) -> str:
279
290
  s3 = list("!@#$%^*()[]-_+")
280
291
 
281
292
  while True:
282
- password = "".join([secrets.choice(s0 + s1 + s2 + s3) for _ in range(length)])
293
+ password = "".join(
294
+ [secrets.choice(s0 + s1 + s2 + s3) for _ in range(length)]
295
+ )
283
296
  if (
284
297
  any(c.islower() for c in password)
285
298
  and any(c.isupper() for c in password)
@@ -12,11 +12,11 @@ wheels = [
12
12
 
13
13
  [[package]]
14
14
  name = "asgiref"
15
- version = "3.9.2"
15
+ version = "3.10.0"
16
16
  source = { registry = "https://pypi.org/simple" }
17
- sdist = { url = "https://files.pythonhosted.org/packages/7f/bf/0f3ecda32f1cb3bf1dca480aca08a7a8a3bdc4bed2343a103f30731565c9/asgiref-3.9.2.tar.gz", hash = "sha256:a0249afacb66688ef258ffe503528360443e2b9a8d8c4581b6ebefa58c841ef1", size = 36894 }
17
+ sdist = { url = "https://files.pythonhosted.org/packages/46/08/4dfec9b90758a59acc6be32ac82e98d1fbfc321cb5cfa410436dbacf821c/asgiref-3.10.0.tar.gz", hash = "sha256:d89f2d8cd8b56dada7d52fa7dc8075baa08fb836560710d38c292a7a3f78c04e", size = 37483 }
18
18
  wheels = [
19
- { url = "https://files.pythonhosted.org/packages/c7/d1/69d02ce34caddb0a7ae088b84c356a625a93cd4ff57b2f97644c03fad905/asgiref-3.9.2-py3-none-any.whl", hash = "sha256:0b61526596219d70396548fc003635056856dba5d0d086f86476f10b33c75960", size = 23788 },
19
+ { url = "https://files.pythonhosted.org/packages/17/9c/fc2331f538fbf7eedba64b2052e99ccf9ba9d6888e2f41441ee28847004b/asgiref-3.10.0-py3-none-any.whl", hash = "sha256:aef8a81283a34d0ab31630c9b7dfe70c812c95eba78171367ca8745e88124734", size = 24050 },
20
20
  ]
21
21
 
22
22
  [[package]]
@@ -55,24 +55,24 @@ wheels = [
55
55
 
56
56
  [[package]]
57
57
  name = "beautifulsoup4"
58
- version = "4.13.5"
58
+ version = "4.14.2"
59
59
  source = { registry = "https://pypi.org/simple" }
60
60
  dependencies = [
61
61
  { name = "soupsieve" },
62
62
  { name = "typing-extensions" },
63
63
  ]
64
- sdist = { url = "https://files.pythonhosted.org/packages/85/2e/3e5079847e653b1f6dc647aa24549d68c6addb4c595cc0d902d1b19308ad/beautifulsoup4-4.13.5.tar.gz", hash = "sha256:5e70131382930e7c3de33450a2f54a63d5e4b19386eab43a5b34d594268f3695", size = 622954 }
64
+ sdist = { url = "https://files.pythonhosted.org/packages/77/e9/df2358efd7659577435e2177bfa69cba6c33216681af51a707193dec162a/beautifulsoup4-4.14.2.tar.gz", hash = "sha256:2a98ab9f944a11acee9cc848508ec28d9228abfd522ef0fad6a02a72e0ded69e", size = 625822 }
65
65
  wheels = [
66
- { url = "https://files.pythonhosted.org/packages/04/eb/f4151e0c7377a6e08a38108609ba5cede57986802757848688aeedd1b9e8/beautifulsoup4-4.13.5-py3-none-any.whl", hash = "sha256:642085eaa22233aceadff9c69651bc51e8bf3f874fb6d7104ece2beb24b47c4a", size = 105113 },
66
+ { url = "https://files.pythonhosted.org/packages/94/fe/3aed5d0be4d404d12d36ab97e2f1791424d9ca39c2f754a6285d59a3b01d/beautifulsoup4-4.14.2-py3-none-any.whl", hash = "sha256:5ef6fa3a8cbece8488d66985560f97ed091e22bbc4e9c2338508a9d5de6d4515", size = 106392 },
67
67
  ]
68
68
 
69
69
  [[package]]
70
70
  name = "certifi"
71
- version = "2025.8.3"
71
+ version = "2025.10.5"
72
72
  source = { registry = "https://pypi.org/simple" }
73
- sdist = { url = "https://files.pythonhosted.org/packages/dc/67/960ebe6bf230a96cda2e0abcf73af550ec4f090005363542f0765df162e0/certifi-2025.8.3.tar.gz", hash = "sha256:e564105f78ded564e3ae7c923924435e1daa7463faeab5bb932bc53ffae63407", size = 162386 }
73
+ sdist = { url = "https://files.pythonhosted.org/packages/4c/5b/b6ce21586237c77ce67d01dc5507039d444b630dd76611bbca2d8e5dcd91/certifi-2025.10.5.tar.gz", hash = "sha256:47c09d31ccf2acf0be3f701ea53595ee7e0b8fa08801c6624be771df09ae7b43", size = 164519 }
74
74
  wheels = [
75
- { url = "https://files.pythonhosted.org/packages/e5/48/1549795ba7742c948d2ad169c1c8cdbae65bc450d6cd753d124b17c8cd32/certifi-2025.8.3-py3-none-any.whl", hash = "sha256:f6c12493cfb1b06ba2ff328595af9350c65d6644968e5d3a2ffd78699af217a5", size = 161216 },
75
+ { url = "https://files.pythonhosted.org/packages/e4/37/af0d2ef3967ac0d6113837b44a4f0bfe1328c2b9763bd5b1744520e5cfed/certifi-2025.10.5-py3-none-any.whl", hash = "sha256:0f212c2744a9bb6de0c56639a6f68afe01ecd92d91f14ae897c4fe7bbeeef0de", size = 163286 },
76
76
  ]
77
77
 
78
78
  [[package]]
@@ -128,21 +128,21 @@ wheels = [
128
128
 
129
129
  [[package]]
130
130
  name = "django"
131
- version = "5.2.6"
131
+ version = "5.2.7"
132
132
  source = { registry = "https://pypi.org/simple" }
133
133
  dependencies = [
134
134
  { name = "asgiref" },
135
135
  { name = "sqlparse" },
136
136
  { name = "tzdata", marker = "sys_platform == 'win32'" },
137
137
  ]
138
- sdist = { url = "https://files.pythonhosted.org/packages/4c/8c/2a21594337250a171d45dda926caa96309d5136becd1f48017247f9cdea0/django-5.2.6.tar.gz", hash = "sha256:da5e00372763193d73cecbf71084a3848458cecf4cee36b9a1e8d318d114a87b", size = 10858861 }
138
+ sdist = { url = "https://files.pythonhosted.org/packages/b1/96/bd84e2bb997994de8bcda47ae4560991084e86536541d7214393880f01a8/django-5.2.7.tar.gz", hash = "sha256:e0f6f12e2551b1716a95a63a1366ca91bbcd7be059862c1b18f989b1da356cdd", size = 10865812 }
139
139
  wheels = [
140
- { url = "https://files.pythonhosted.org/packages/f5/af/6593f6d21404e842007b40fdeb81e73c20b6649b82d020bb0801b270174c/django-5.2.6-py3-none-any.whl", hash = "sha256:60549579b1174a304b77e24a93d8d9fafe6b6c03ac16311f3e25918ea5a20058", size = 8303111 },
140
+ { url = "https://files.pythonhosted.org/packages/8f/ef/81f3372b5dd35d8d354321155d1a38894b2b766f576d0abffac4d8ae78d9/django-5.2.7-py3-none-any.whl", hash = "sha256:59a13a6515f787dec9d97a0438cd2efac78c8aca1c80025244b0fe507fe0754b", size = 8307145 },
141
141
  ]
142
142
 
143
143
  [[package]]
144
144
  name = "django-stubs"
145
- version = "5.2.5"
145
+ version = "5.2.6"
146
146
  source = { registry = "https://pypi.org/simple" }
147
147
  dependencies = [
148
148
  { name = "django" },
@@ -150,22 +150,22 @@ dependencies = [
150
150
  { name = "types-pyyaml" },
151
151
  { name = "typing-extensions" },
152
152
  ]
153
- sdist = { url = "https://files.pythonhosted.org/packages/e1/8e/286150f593481c33f54d14efb58d72178f159d57d31043529d38bbc98e2f/django_stubs-5.2.5.tar.gz", hash = "sha256:fc78384e28d8c5292d60983075a5934f644f7c304c25ae2793fc57aa66d5018b", size = 247794 }
153
+ sdist = { url = "https://files.pythonhosted.org/packages/0c/ec/ab347204c6a13e6f1bbe1faefe5404a4ca3f2df72f568c53e32da4afc803/django_stubs-5.2.6.tar.gz", hash = "sha256:991dc36c10fefd6bf0cfd163db3dabd15787112f96cfedf6e8388ba1930040fb", size = 257279 }
154
154
  wheels = [
155
- { url = "https://files.pythonhosted.org/packages/2f/02/cdbf7652ef2c9a7a1fed7c279484b7f3806f15b1bb34aec9fef8e8cfacbf/django_stubs-5.2.5-py3-none-any.whl", hash = "sha256:223c1a3324cd4873b7629dec6e9adbe224a94508284c1926b25fddff7a92252b", size = 490196 },
155
+ { url = "https://files.pythonhosted.org/packages/90/fd/fa8c7740991599696412d00930641dc35eb047d1d70dc1f924e8ab999db3/django_stubs-5.2.6-py3-none-any.whl", hash = "sha256:bb517a60355474a2c8403a48d16ff906c8bc46d604f419206de10ac018a5f4bc", size = 507711 },
156
156
  ]
157
157
 
158
158
  [[package]]
159
159
  name = "django-stubs-ext"
160
- version = "5.2.5"
160
+ version = "5.2.6"
161
161
  source = { registry = "https://pypi.org/simple" }
162
162
  dependencies = [
163
163
  { name = "django" },
164
164
  { name = "typing-extensions" },
165
165
  ]
166
- sdist = { url = "https://files.pythonhosted.org/packages/16/94/c9b8f4c47084a0fa666da9066c36771098101932688adf2c17a40fab79c2/django_stubs_ext-5.2.5.tar.gz", hash = "sha256:ecc628df29d36cede638567c4e33ff485dd7a99f1552ad0cece8c60e9c3a8872", size = 6489 }
166
+ sdist = { url = "https://files.pythonhosted.org/packages/4c/13/d9d4fb0a797966b941f51ee2ac60eede308890488149985a91bacfc80ca2/django_stubs_ext-5.2.6.tar.gz", hash = "sha256:1165a596138a54edac12616af126332db03b812c8272ada724b4e89188a3c41f", size = 6524 }
167
167
  wheels = [
168
- { url = "https://files.pythonhosted.org/packages/e0/fe/a85a105fddffadb4a8d50e500caeee87d836b679d51a19d52dfa0cc6c660/django_stubs_ext-5.2.5-py3-none-any.whl", hash = "sha256:9b4b8ac9d32f7e6c304fd05477f8688fae6ed57f6a0f9f4d074f9e55b5a3da14", size = 9310 },
168
+ { url = "https://files.pythonhosted.org/packages/67/69/2ce28341493e35b7b182e2c0474288a350edf1cb27220517fb8abd4143c9/django_stubs_ext-5.2.6-py3-none-any.whl", hash = "sha256:c3736dcea49666140c92a1471dda83c31e8d5e71dc364ab3cedc7698dbf01cee", size = 9980 },
169
169
  ]
170
170
 
171
171
  [[package]]
@@ -233,40 +233,65 @@ wheels = [
233
233
 
234
234
  [[package]]
235
235
  name = "markupsafe"
236
- version = "3.0.2"
237
- source = { registry = "https://pypi.org/simple" }
238
- sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537 }
239
- wheels = [
240
- { url = "https://files.pythonhosted.org/packages/22/09/d1f21434c97fc42f09d290cbb6350d44eb12f09cc62c9476effdb33a18aa/MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf", size = 14274 },
241
- { url = "https://files.pythonhosted.org/packages/6b/b0/18f76bba336fa5aecf79d45dcd6c806c280ec44538b3c13671d49099fdd0/MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225", size = 12348 },
242
- { url = "https://files.pythonhosted.org/packages/e0/25/dd5c0f6ac1311e9b40f4af06c78efde0f3b5cbf02502f8ef9501294c425b/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028", size = 24149 },
243
- { url = "https://files.pythonhosted.org/packages/f3/f0/89e7aadfb3749d0f52234a0c8c7867877876e0a20b60e2188e9850794c17/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8", size = 23118 },
244
- { url = "https://files.pythonhosted.org/packages/d5/da/f2eeb64c723f5e3777bc081da884b414671982008c47dcc1873d81f625b6/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c", size = 22993 },
245
- { url = "https://files.pythonhosted.org/packages/da/0e/1f32af846df486dce7c227fe0f2398dc7e2e51d4a370508281f3c1c5cddc/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557", size = 24178 },
246
- { url = "https://files.pythonhosted.org/packages/c4/f6/bb3ca0532de8086cbff5f06d137064c8410d10779c4c127e0e47d17c0b71/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22", size = 23319 },
247
- { url = "https://files.pythonhosted.org/packages/a2/82/8be4c96ffee03c5b4a034e60a31294daf481e12c7c43ab8e34a1453ee48b/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48", size = 23352 },
248
- { url = "https://files.pythonhosted.org/packages/51/ae/97827349d3fcffee7e184bdf7f41cd6b88d9919c80f0263ba7acd1bbcb18/MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30", size = 15097 },
249
- { url = "https://files.pythonhosted.org/packages/c1/80/a61f99dc3a936413c3ee4e1eecac96c0da5ed07ad56fd975f1a9da5bc630/MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87", size = 15601 },
250
- { url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274 },
251
- { url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352 },
252
- { url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122 },
253
- { url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085 },
254
- { url = "https://files.pythonhosted.org/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978 },
255
- { url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208 },
256
- { url = "https://files.pythonhosted.org/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357 },
257
- { url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344 },
258
- { url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101 },
259
- { url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603 },
260
- { url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510 },
261
- { url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486 },
262
- { url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480 },
263
- { url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914 },
264
- { url = "https://files.pythonhosted.org/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796 },
265
- { url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473 },
266
- { url = "https://files.pythonhosted.org/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114 },
267
- { url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098 },
268
- { url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208 },
269
- { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739 },
236
+ version = "3.0.3"
237
+ source = { registry = "https://pypi.org/simple" }
238
+ sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313 }
239
+ wheels = [
240
+ { url = "https://files.pythonhosted.org/packages/5a/72/147da192e38635ada20e0a2e1a51cf8823d2119ce8883f7053879c2199b5/markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e", size = 11615 },
241
+ { url = "https://files.pythonhosted.org/packages/9a/81/7e4e08678a1f98521201c3079f77db69fb552acd56067661f8c2f534a718/markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce", size = 12020 },
242
+ { url = "https://files.pythonhosted.org/packages/1e/2c/799f4742efc39633a1b54a92eec4082e4f815314869865d876824c257c1e/markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d", size = 24332 },
243
+ { url = "https://files.pythonhosted.org/packages/3c/2e/8d0c2ab90a8c1d9a24f0399058ab8519a3279d1bd4289511d74e909f060e/markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d", size = 22947 },
244
+ { url = "https://files.pythonhosted.org/packages/2c/54/887f3092a85238093a0b2154bd629c89444f395618842e8b0c41783898ea/markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a", size = 21962 },
245
+ { url = "https://files.pythonhosted.org/packages/c9/2f/336b8c7b6f4a4d95e91119dc8521402461b74a485558d8f238a68312f11c/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b", size = 23760 },
246
+ { url = "https://files.pythonhosted.org/packages/32/43/67935f2b7e4982ffb50a4d169b724d74b62a3964bc1a9a527f5ac4f1ee2b/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f", size = 21529 },
247
+ { url = "https://files.pythonhosted.org/packages/89/e0/4486f11e51bbba8b0c041098859e869e304d1c261e59244baa3d295d47b7/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b", size = 23015 },
248
+ { url = "https://files.pythonhosted.org/packages/2f/e1/78ee7a023dac597a5825441ebd17170785a9dab23de95d2c7508ade94e0e/markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d", size = 14540 },
249
+ { url = "https://files.pythonhosted.org/packages/aa/5b/bec5aa9bbbb2c946ca2733ef9c4ca91c91b6a24580193e891b5f7dbe8e1e/markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c", size = 15105 },
250
+ { url = "https://files.pythonhosted.org/packages/e5/f1/216fc1bbfd74011693a4fd837e7026152e89c4bcf3e77b6692fba9923123/markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f", size = 13906 },
251
+ { url = "https://files.pythonhosted.org/packages/38/2f/907b9c7bbba283e68f20259574b13d005c121a0fa4c175f9bed27c4597ff/markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795", size = 11622 },
252
+ { url = "https://files.pythonhosted.org/packages/9c/d9/5f7756922cdd676869eca1c4e3c0cd0df60ed30199ffd775e319089cb3ed/markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219", size = 12029 },
253
+ { url = "https://files.pythonhosted.org/packages/00/07/575a68c754943058c78f30db02ee03a64b3c638586fba6a6dd56830b30a3/markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6", size = 24374 },
254
+ { url = "https://files.pythonhosted.org/packages/a9/21/9b05698b46f218fc0e118e1f8168395c65c8a2c750ae2bab54fc4bd4e0e8/markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676", size = 22980 },
255
+ { url = "https://files.pythonhosted.org/packages/7f/71/544260864f893f18b6827315b988c146b559391e6e7e8f7252839b1b846a/markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9", size = 21990 },
256
+ { url = "https://files.pythonhosted.org/packages/c2/28/b50fc2f74d1ad761af2f5dcce7492648b983d00a65b8c0e0cb457c82ebbe/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1", size = 23784 },
257
+ { url = "https://files.pythonhosted.org/packages/ed/76/104b2aa106a208da8b17a2fb72e033a5a9d7073c68f7e508b94916ed47a9/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc", size = 21588 },
258
+ { url = "https://files.pythonhosted.org/packages/b5/99/16a5eb2d140087ebd97180d95249b00a03aa87e29cc224056274f2e45fd6/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12", size = 23041 },
259
+ { url = "https://files.pythonhosted.org/packages/19/bc/e7140ed90c5d61d77cea142eed9f9c303f4c4806f60a1044c13e3f1471d0/markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed", size = 14543 },
260
+ { url = "https://files.pythonhosted.org/packages/05/73/c4abe620b841b6b791f2edc248f556900667a5a1cf023a6646967ae98335/markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5", size = 15113 },
261
+ { url = "https://files.pythonhosted.org/packages/f0/3a/fa34a0f7cfef23cf9500d68cb7c32dd64ffd58a12b09225fb03dd37d5b80/markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485", size = 13911 },
262
+ { url = "https://files.pythonhosted.org/packages/e4/d7/e05cd7efe43a88a17a37b3ae96e79a19e846f3f456fe79c57ca61356ef01/markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73", size = 11658 },
263
+ { url = "https://files.pythonhosted.org/packages/99/9e/e412117548182ce2148bdeacdda3bb494260c0b0184360fe0d56389b523b/markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37", size = 12066 },
264
+ { url = "https://files.pythonhosted.org/packages/bc/e6/fa0ffcda717ef64a5108eaa7b4f5ed28d56122c9a6d70ab8b72f9f715c80/markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19", size = 25639 },
265
+ { url = "https://files.pythonhosted.org/packages/96/ec/2102e881fe9d25fc16cb4b25d5f5cde50970967ffa5dddafdb771237062d/markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025", size = 23569 },
266
+ { url = "https://files.pythonhosted.org/packages/4b/30/6f2fce1f1f205fc9323255b216ca8a235b15860c34b6798f810f05828e32/markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6", size = 23284 },
267
+ { url = "https://files.pythonhosted.org/packages/58/47/4a0ccea4ab9f5dcb6f79c0236d954acb382202721e704223a8aafa38b5c8/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f", size = 24801 },
268
+ { url = "https://files.pythonhosted.org/packages/6a/70/3780e9b72180b6fecb83a4814d84c3bf4b4ae4bf0b19c27196104149734c/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb", size = 22769 },
269
+ { url = "https://files.pythonhosted.org/packages/98/c5/c03c7f4125180fc215220c035beac6b9cb684bc7a067c84fc69414d315f5/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009", size = 23642 },
270
+ { url = "https://files.pythonhosted.org/packages/80/d6/2d1b89f6ca4bff1036499b1e29a1d02d282259f3681540e16563f27ebc23/markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354", size = 14612 },
271
+ { url = "https://files.pythonhosted.org/packages/2b/98/e48a4bfba0a0ffcf9925fe2d69240bfaa19c6f7507b8cd09c70684a53c1e/markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218", size = 15200 },
272
+ { url = "https://files.pythonhosted.org/packages/0e/72/e3cc540f351f316e9ed0f092757459afbc595824ca724cbc5a5d4263713f/markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", size = 13973 },
273
+ { url = "https://files.pythonhosted.org/packages/33/8a/8e42d4838cd89b7dde187011e97fe6c3af66d8c044997d2183fbd6d31352/markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe", size = 11619 },
274
+ { url = "https://files.pythonhosted.org/packages/b5/64/7660f8a4a8e53c924d0fa05dc3a55c9cee10bbd82b11c5afb27d44b096ce/markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026", size = 12029 },
275
+ { url = "https://files.pythonhosted.org/packages/da/ef/e648bfd021127bef5fa12e1720ffed0c6cbb8310c8d9bea7266337ff06de/markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737", size = 24408 },
276
+ { url = "https://files.pythonhosted.org/packages/41/3c/a36c2450754618e62008bf7435ccb0f88053e07592e6028a34776213d877/markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97", size = 23005 },
277
+ { url = "https://files.pythonhosted.org/packages/bc/20/b7fdf89a8456b099837cd1dc21974632a02a999ec9bf7ca3e490aacd98e7/markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d", size = 22048 },
278
+ { url = "https://files.pythonhosted.org/packages/9a/a7/591f592afdc734f47db08a75793a55d7fbcc6902a723ae4cfbab61010cc5/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda", size = 23821 },
279
+ { url = "https://files.pythonhosted.org/packages/7d/33/45b24e4f44195b26521bc6f1a82197118f74df348556594bd2262bda1038/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf", size = 21606 },
280
+ { url = "https://files.pythonhosted.org/packages/ff/0e/53dfaca23a69fbfbbf17a4b64072090e70717344c52eaaaa9c5ddff1e5f0/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe", size = 23043 },
281
+ { url = "https://files.pythonhosted.org/packages/46/11/f333a06fc16236d5238bfe74daccbca41459dcd8d1fa952e8fbd5dccfb70/markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9", size = 14747 },
282
+ { url = "https://files.pythonhosted.org/packages/28/52/182836104b33b444e400b14f797212f720cbc9ed6ba34c800639d154e821/markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581", size = 15341 },
283
+ { url = "https://files.pythonhosted.org/packages/6f/18/acf23e91bd94fd7b3031558b1f013adfa21a8e407a3fdb32745538730382/markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4", size = 14073 },
284
+ { url = "https://files.pythonhosted.org/packages/3c/f0/57689aa4076e1b43b15fdfa646b04653969d50cf30c32a102762be2485da/markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab", size = 11661 },
285
+ { url = "https://files.pythonhosted.org/packages/89/c3/2e67a7ca217c6912985ec766c6393b636fb0c2344443ff9d91404dc4c79f/markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175", size = 12069 },
286
+ { url = "https://files.pythonhosted.org/packages/f0/00/be561dce4e6ca66b15276e184ce4b8aec61fe83662cce2f7d72bd3249d28/markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634", size = 25670 },
287
+ { url = "https://files.pythonhosted.org/packages/50/09/c419f6f5a92e5fadde27efd190eca90f05e1261b10dbd8cbcb39cd8ea1dc/markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50", size = 23598 },
288
+ { url = "https://files.pythonhosted.org/packages/22/44/a0681611106e0b2921b3033fc19bc53323e0b50bc70cffdd19f7d679bb66/markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e", size = 23261 },
289
+ { url = "https://files.pythonhosted.org/packages/5f/57/1b0b3f100259dc9fffe780cfb60d4be71375510e435efec3d116b6436d43/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5", size = 24835 },
290
+ { url = "https://files.pythonhosted.org/packages/26/6a/4bf6d0c97c4920f1597cc14dd720705eca0bf7c787aebc6bb4d1bead5388/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523", size = 22733 },
291
+ { url = "https://files.pythonhosted.org/packages/14/c7/ca723101509b518797fedc2fdf79ba57f886b4aca8a7d31857ba3ee8281f/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc", size = 23672 },
292
+ { url = "https://files.pythonhosted.org/packages/fb/df/5bd7a48c256faecd1d36edc13133e51397e41b73bb77e1a69deab746ebac/markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d", size = 14819 },
293
+ { url = "https://files.pythonhosted.org/packages/1a/8a/0402ba61a2f16038b48b39bccca271134be00c5c9f0f623208399333c448/markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9", size = 15426 },
294
+ { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146 },
270
295
  ]
271
296
 
272
297
  [[package]]
@@ -298,7 +323,7 @@ wheels = [
298
323
 
299
324
  [[package]]
300
325
  name = "python-terminusgps"
301
- version = "46.1.0"
326
+ version = "47.1.0"
302
327
  source = { editable = "." }
303
328
  dependencies = [
304
329
  { name = "authorizenet" },
@@ -379,37 +404,37 @@ wheels = [
379
404
 
380
405
  [[package]]
381
406
  name = "simplejson"
382
- version = "3.20.1"
383
- source = { registry = "https://pypi.org/simple" }
384
- sdist = { url = "https://files.pythonhosted.org/packages/af/92/51b417685abd96b31308b61b9acce7ec50d8e1de8fbc39a7fd4962c60689/simplejson-3.20.1.tar.gz", hash = "sha256:e64139b4ec4f1f24c142ff7dcafe55a22b811a74d86d66560c8815687143037d", size = 85591 }
385
- wheels = [
386
- { url = "https://files.pythonhosted.org/packages/8d/eb/34c16a1ac9ba265d024dc977ad84e1659d931c0a700967c3e59a98ed7514/simplejson-3.20.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f31c4a3a7ab18467ee73a27f3e59158255d1520f3aad74315edde7a940f1be23", size = 93100 },
387
- { url = "https://files.pythonhosted.org/packages/41/fc/2c2c007d135894971e6814e7c0806936e5bade28f8db4dd7e2a58b50debd/simplejson-3.20.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:884e6183d16b725e113b83a6fc0230152ab6627d4d36cb05c89c2c5bccfa7bc6", size = 75464 },
388
- { url = "https://files.pythonhosted.org/packages/0f/05/2b5ecb33b776c34bb5cace5de5d7669f9b60e3ca13c113037b2ca86edfbd/simplejson-3.20.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:03d7a426e416fe0d3337115f04164cd9427eb4256e843a6b8751cacf70abc832", size = 75112 },
389
- { url = "https://files.pythonhosted.org/packages/fe/36/1f3609a2792f06cd4b71030485f78e91eb09cfd57bebf3116bf2980a8bac/simplejson-3.20.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:000602141d0bddfcff60ea6a6e97d5e10c9db6b17fd2d6c66199fa481b6214bb", size = 150182 },
390
- { url = "https://files.pythonhosted.org/packages/2f/b0/053fbda38b8b602a77a4f7829def1b4f316cd8deb5440a6d3ee90790d2a4/simplejson-3.20.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:af8377a8af78226e82e3a4349efdde59ffa421ae88be67e18cef915e4023a595", size = 158363 },
391
- { url = "https://files.pythonhosted.org/packages/d1/4b/2eb84ae867539a80822e92f9be4a7200dffba609275faf99b24141839110/simplejson-3.20.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:15c7de4c88ab2fbcb8781a3b982ef883696736134e20b1210bca43fb42ff1acf", size = 148415 },
392
- { url = "https://files.pythonhosted.org/packages/e0/bd/400b0bd372a5666addf2540c7358bfc3841b9ce5cdbc5cc4ad2f61627ad8/simplejson-3.20.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:455a882ff3f97d810709f7b620007d4e0aca8da71d06fc5c18ba11daf1c4df49", size = 152213 },
393
- { url = "https://files.pythonhosted.org/packages/50/12/143f447bf6a827ee9472693768dc1a5eb96154f8feb140a88ce6973a3cfa/simplejson-3.20.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:fc0f523ce923e7f38eb67804bc80e0a028c76d7868500aa3f59225574b5d0453", size = 150048 },
394
- { url = "https://files.pythonhosted.org/packages/5e/ea/dd9b3e8e8ed710a66f24a22c16a907c9b539b6f5f45fd8586bd5c231444e/simplejson-3.20.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:76461ec929282dde4a08061071a47281ad939d0202dc4e63cdd135844e162fbc", size = 151668 },
395
- { url = "https://files.pythonhosted.org/packages/99/af/ee52a8045426a0c5b89d755a5a70cc821815ef3c333b56fbcad33c4435c0/simplejson-3.20.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:ab19c2da8c043607bde4d4ef3a6b633e668a7d2e3d56f40a476a74c5ea71949f", size = 158840 },
396
- { url = "https://files.pythonhosted.org/packages/68/db/ab32869acea6b5de7d75fa0dac07a112ded795d41eaa7e66c7813b17be95/simplejson-3.20.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b2578bedaedf6294415197b267d4ef678fea336dd78ee2a6d2f4b028e9d07be3", size = 154212 },
397
- { url = "https://files.pythonhosted.org/packages/fa/7a/e3132d454977d75a3bf9a6d541d730f76462ebf42a96fea2621498166f41/simplejson-3.20.1-cp312-cp312-win32.whl", hash = "sha256:339f407373325a36b7fd744b688ba5bae0666b5d340ec6d98aebc3014bf3d8ea", size = 74101 },
398
- { url = "https://files.pythonhosted.org/packages/bc/5d/4e243e937fa3560107c69f6f7c2eed8589163f5ed14324e864871daa2dd9/simplejson-3.20.1-cp312-cp312-win_amd64.whl", hash = "sha256:627d4486a1ea7edf1f66bb044ace1ce6b4c1698acd1b05353c97ba4864ea2e17", size = 75736 },
399
- { url = "https://files.pythonhosted.org/packages/c4/03/0f453a27877cb5a5fff16a975925f4119102cc8552f52536b9a98ef0431e/simplejson-3.20.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:71e849e7ceb2178344998cbe5ade101f1b329460243c79c27fbfc51c0447a7c3", size = 93109 },
400
- { url = "https://files.pythonhosted.org/packages/74/1f/a729f4026850cabeaff23e134646c3f455e86925d2533463420635ae54de/simplejson-3.20.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b63fdbab29dc3868d6f009a59797cefaba315fd43cd32ddd998ee1da28e50e29", size = 75475 },
401
- { url = "https://files.pythonhosted.org/packages/e2/14/50a2713fee8ff1f8d655b1a14f4a0f1c0c7246768a1b3b3d12964a4ed5aa/simplejson-3.20.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1190f9a3ce644fd50ec277ac4a98c0517f532cfebdcc4bd975c0979a9f05e1fb", size = 75112 },
402
- { url = "https://files.pythonhosted.org/packages/45/86/ea9835abb646755140e2d482edc9bc1e91997ed19a59fd77ae4c6a0facea/simplejson-3.20.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c1336ba7bcb722ad487cd265701ff0583c0bb6de638364ca947bb84ecc0015d1", size = 150245 },
403
- { url = "https://files.pythonhosted.org/packages/12/b4/53084809faede45da829fe571c65fbda8479d2a5b9c633f46b74124d56f5/simplejson-3.20.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e975aac6a5acd8b510eba58d5591e10a03e3d16c1cf8a8624ca177491f7230f0", size = 158465 },
404
- { url = "https://files.pythonhosted.org/packages/a9/7d/d56579468d1660b3841e1f21c14490d103e33cf911886b22652d6e9683ec/simplejson-3.20.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6a6dd11ee282937ad749da6f3b8d87952ad585b26e5edfa10da3ae2536c73078", size = 148514 },
405
- { url = "https://files.pythonhosted.org/packages/19/e3/874b1cca3d3897b486d3afdccc475eb3a09815bf1015b01cf7fcb52a55f0/simplejson-3.20.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ab980fcc446ab87ea0879edad41a5c28f2d86020014eb035cf5161e8de4474c6", size = 152262 },
406
- { url = "https://files.pythonhosted.org/packages/32/84/f0fdb3625292d945c2bd13a814584603aebdb38cfbe5fe9be6b46fe598c4/simplejson-3.20.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f5aee2a4cb6b146bd17333ac623610f069f34e8f31d2f4f0c1a2186e50c594f0", size = 150164 },
407
- { url = "https://files.pythonhosted.org/packages/95/51/6d625247224f01eaaeabace9aec75ac5603a42f8ebcce02c486fbda8b428/simplejson-3.20.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:652d8eecbb9a3b6461b21ec7cf11fd0acbab144e45e600c817ecf18e4580b99e", size = 151795 },
408
- { url = "https://files.pythonhosted.org/packages/7f/d9/bb921df6b35be8412f519e58e86d1060fddf3ad401b783e4862e0a74c4c1/simplejson-3.20.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:8c09948f1a486a89251ee3a67c9f8c969b379f6ffff1a6064b41fea3bce0a112", size = 159027 },
409
- { url = "https://files.pythonhosted.org/packages/03/c5/5950605e4ad023a6621cf4c931b29fd3d2a9c1f36be937230bfc83d7271d/simplejson-3.20.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:cbbd7b215ad4fc6f058b5dd4c26ee5c59f72e031dfda3ac183d7968a99e4ca3a", size = 154380 },
410
- { url = "https://files.pythonhosted.org/packages/66/ad/b74149557c5ec1e4e4d55758bda426f5d2ec0123cd01a53ae63b8de51fa3/simplejson-3.20.1-cp313-cp313-win32.whl", hash = "sha256:ae81e482476eaa088ef9d0120ae5345de924f23962c0c1e20abbdff597631f87", size = 74102 },
411
- { url = "https://files.pythonhosted.org/packages/db/a9/25282fdd24493e1022f30b7f5cdf804255c007218b2bfaa655bd7ad34b2d/simplejson-3.20.1-cp313-cp313-win_amd64.whl", hash = "sha256:1b9fd15853b90aec3b1739f4471efbf1ac05066a2c7041bf8db821bb73cd2ddc", size = 75736 },
412
- { url = "https://files.pythonhosted.org/packages/4b/30/00f02a0a921556dd5a6db1ef2926a1bc7a8bbbfb1c49cfed68a275b8ab2b/simplejson-3.20.1-py3-none-any.whl", hash = "sha256:8a6c1bbac39fa4a79f83cbf1df6ccd8ff7069582a9fd8db1e52cea073bc2c697", size = 57121 },
407
+ version = "3.20.2"
408
+ source = { registry = "https://pypi.org/simple" }
409
+ sdist = { url = "https://files.pythonhosted.org/packages/41/f4/a1ac5ed32f7ed9a088d62a59d410d4c204b3b3815722e2ccfb491fa8251b/simplejson-3.20.2.tar.gz", hash = "sha256:5fe7a6ce14d1c300d80d08695b7f7e633de6cd72c80644021874d985b3393649", size = 85784 }
410
+ wheels = [
411
+ { url = "https://files.pythonhosted.org/packages/9d/9e/1a91e7614db0416885eab4136d49b7303de20528860ffdd798ce04d054db/simplejson-3.20.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:4376d5acae0d1e91e78baeba4ee3cf22fbf6509d81539d01b94e0951d28ec2b6", size = 93523 },
412
+ { url = "https://files.pythonhosted.org/packages/5e/2b/d2413f5218fc25608739e3d63fe321dfa85c5f097aa6648dbe72513a5f12/simplejson-3.20.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f8fe6de652fcddae6dec8f281cc1e77e4e8f3575249e1800090aab48f73b4259", size = 75844 },
413
+ { url = "https://files.pythonhosted.org/packages/ad/f1/efd09efcc1e26629e120fef59be059ce7841cc6e1f949a4db94f1ae8a918/simplejson-3.20.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:25ca2663d99328d51e5a138f22018e54c9162438d831e26cfc3458688616eca8", size = 75655 },
414
+ { url = "https://files.pythonhosted.org/packages/97/ec/5c6db08e42f380f005d03944be1af1a6bd501cc641175429a1cbe7fb23b9/simplejson-3.20.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:12a6b2816b6cab6c3fd273d43b1948bc9acf708272074c8858f579c394f4cbc9", size = 150335 },
415
+ { url = "https://files.pythonhosted.org/packages/81/f5/808a907485876a9242ec67054da7cbebefe0ee1522ef1c0be3bfc90f96f6/simplejson-3.20.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac20dc3fcdfc7b8415bfc3d7d51beccd8695c3f4acb7f74e3a3b538e76672868", size = 158519 },
416
+ { url = "https://files.pythonhosted.org/packages/66/af/b8a158246834645ea890c36136584b0cc1c0e4b83a73b11ebd9c2a12877c/simplejson-3.20.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:db0804d04564e70862ef807f3e1ace2cc212ef0e22deb1b3d6f80c45e5882c6b", size = 148571 },
417
+ { url = "https://files.pythonhosted.org/packages/20/05/ed9b2571bbf38f1a2425391f18e3ac11cb1e91482c22d644a1640dea9da7/simplejson-3.20.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:979ce23ea663895ae39106946ef3d78527822d918a136dbc77b9e2b7f006237e", size = 152367 },
418
+ { url = "https://files.pythonhosted.org/packages/81/2c/bad68b05dd43e93f77994b920505634d31ed239418eb6a88997d06599983/simplejson-3.20.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a2ba921b047bb029805726800819675249ef25d2f65fd0edb90639c5b1c3033c", size = 150205 },
419
+ { url = "https://files.pythonhosted.org/packages/69/46/90c7fc878061adafcf298ce60cecdee17a027486e9dce507e87396d68255/simplejson-3.20.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:12d3d4dc33770069b780cc8f5abef909fe4a3f071f18f55f6d896a370fd0f970", size = 151823 },
420
+ { url = "https://files.pythonhosted.org/packages/ab/27/b85b03349f825ae0f5d4f780cdde0bbccd4f06c3d8433f6a3882df887481/simplejson-3.20.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:aff032a59a201b3683a34be1169e71ddda683d9c3b43b261599c12055349251e", size = 158997 },
421
+ { url = "https://files.pythonhosted.org/packages/71/ad/d7f3c331fb930638420ac6d236db68e9f4c28dab9c03164c3cd0e7967e15/simplejson-3.20.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:30e590e133b06773f0dc9c3f82e567463df40598b660b5adf53eb1c488202544", size = 154367 },
422
+ { url = "https://files.pythonhosted.org/packages/f0/46/5c67324addd40fa2966f6e886cacbbe0407c03a500db94fb8bb40333fcdf/simplejson-3.20.2-cp312-cp312-win32.whl", hash = "sha256:8d7be7c99939cc58e7c5bcf6bb52a842a58e6c65e1e9cdd2a94b697b24cddb54", size = 74285 },
423
+ { url = "https://files.pythonhosted.org/packages/fa/c9/5cc2189f4acd3a6e30ffa9775bf09b354302dbebab713ca914d7134d0f29/simplejson-3.20.2-cp312-cp312-win_amd64.whl", hash = "sha256:2c0b4a67e75b945489052af6590e7dca0ed473ead5d0f3aad61fa584afe814ab", size = 75969 },
424
+ { url = "https://files.pythonhosted.org/packages/5e/9e/f326d43f6bf47f4e7704a4426c36e044c6bedfd24e072fb8e27589a373a5/simplejson-3.20.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:90d311ba8fcd733a3677e0be21804827226a57144130ba01c3c6a325e887dd86", size = 93530 },
425
+ { url = "https://files.pythonhosted.org/packages/35/28/5a4b8f3483fbfb68f3f460bc002cef3a5735ef30950e7c4adce9c8da15c7/simplejson-3.20.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:feed6806f614bdf7f5cb6d0123cb0c1c5f40407ef103aa935cffaa694e2e0c74", size = 75846 },
426
+ { url = "https://files.pythonhosted.org/packages/7a/4d/30dfef83b9ac48afae1cf1ab19c2867e27b8d22b5d9f8ca7ce5a0a157d8c/simplejson-3.20.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6b1d8d7c3e1a205c49e1aee6ba907dcb8ccea83651e6c3e2cb2062f1e52b0726", size = 75661 },
427
+ { url = "https://files.pythonhosted.org/packages/09/1d/171009bd35c7099d72ef6afd4bb13527bab469965c968a17d69a203d62a6/simplejson-3.20.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:552f55745044a24c3cb7ec67e54234be56d5d6d0e054f2e4cf4fb3e297429be5", size = 150579 },
428
+ { url = "https://files.pythonhosted.org/packages/61/ae/229bbcf90a702adc6bfa476e9f0a37e21d8c58e1059043038797cbe75b8c/simplejson-3.20.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c2da97ac65165d66b0570c9e545786f0ac7b5de5854d3711a16cacbcaa8c472d", size = 158797 },
429
+ { url = "https://files.pythonhosted.org/packages/90/c5/fefc0ac6b86b9108e302e0af1cf57518f46da0baedd60a12170791d56959/simplejson-3.20.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f59a12966daa356bf68927fca5a67bebac0033cd18b96de9c2d426cd11756cd0", size = 148851 },
430
+ { url = "https://files.pythonhosted.org/packages/43/f1/b392952200f3393bb06fbc4dd975fc63a6843261705839355560b7264eb2/simplejson-3.20.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:133ae2098a8e162c71da97cdab1f383afdd91373b7ff5fe65169b04167da976b", size = 152598 },
431
+ { url = "https://files.pythonhosted.org/packages/f4/b4/d6b7279e52a3e9c0fa8c032ce6164e593e8d9cf390698ee981ed0864291b/simplejson-3.20.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7977640af7b7d5e6a852d26622057d428706a550f7f5083e7c4dd010a84d941f", size = 150498 },
432
+ { url = "https://files.pythonhosted.org/packages/62/22/ec2490dd859224326d10c2fac1353e8ad5c84121be4837a6dd6638ba4345/simplejson-3.20.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b530ad6d55e71fa9e93e1109cf8182f427a6355848a4ffa09f69cc44e1512522", size = 152129 },
433
+ { url = "https://files.pythonhosted.org/packages/33/ce/b60214d013e93dd9e5a705dcb2b88b6c72bada442a97f79828332217f3eb/simplejson-3.20.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:bd96a7d981bf64f0e42345584768da4435c05b24fd3c364663f5fbc8fabf82e3", size = 159359 },
434
+ { url = "https://files.pythonhosted.org/packages/99/21/603709455827cdf5b9d83abe726343f542491ca8dc6a2528eb08de0cf034/simplejson-3.20.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f28ee755fadb426ba2e464d6fcf25d3f152a05eb6b38e0b4f790352f5540c769", size = 154717 },
435
+ { url = "https://files.pythonhosted.org/packages/3c/f9/dc7f7a4bac16cf7eb55a4df03ad93190e11826d2a8950052949d3dfc11e2/simplejson-3.20.2-cp313-cp313-win32.whl", hash = "sha256:472785b52e48e3eed9b78b95e26a256f59bb1ee38339be3075dad799e2e1e661", size = 74289 },
436
+ { url = "https://files.pythonhosted.org/packages/87/10/d42ad61230436735c68af1120622b28a782877146a83d714da7b6a2a1c4e/simplejson-3.20.2-cp313-cp313-win_amd64.whl", hash = "sha256:a1a85013eb33e4820286139540accbe2c98d2da894b2dcefd280209db508e608", size = 75972 },
437
+ { url = "https://files.pythonhosted.org/packages/05/5b/83e1ff87eb60ca706972f7e02e15c0b33396e7bdbd080069a5d1b53cf0d8/simplejson-3.20.2-py3-none-any.whl", hash = "sha256:3b6bb7fb96efd673eac2e4235200bfffdc2353ad12c54117e1e4e2fc485ac017", size = 57309 },
413
438
  ]
414
439
 
415
440
  [[package]]
@@ -1,201 +0,0 @@
1
- import os
2
- import typing
3
-
4
- import wialon.api
5
-
6
-
7
- class WialonAPIError(Exception):
8
- """Raised when a Wialon API call fails."""
9
-
10
- def __init__(self, message, *args, **kwargs) -> None:
11
- self.message = message
12
- self._code = int(message._code)
13
- super().__init__(message, *args, **kwargs)
14
-
15
- @property
16
- def code(self) -> int:
17
- """Wialon API error code."""
18
- return self._code
19
-
20
-
21
- class Wialon(wialon.api.Wialon):
22
- def call(self, *argc, **kwargs) -> dict[str, typing.Any]:
23
- try:
24
- return super().call(*argc, **kwargs)
25
- except wialon.api.WialonError as e:
26
- raise WialonAPIError(e)
27
-
28
-
29
- class WialonSession:
30
- def __init__(
31
- self,
32
- token: str | None = None,
33
- sid: str | None = None,
34
- scheme: str = "https",
35
- host: str = "hst-api.wialon.com",
36
- port: int = 443,
37
- ) -> None:
38
- """
39
- Starts or continues a Wialon API session.
40
-
41
- :param token: A Wialon API token. If not provided, the environment variable ``"WIALON_TOKEN"`` is used.
42
- :type token: str | None
43
- :param sid: An optional Wialon API session id. If provided, the session is continued.
44
- :type sid: str | None
45
- :param scheme: HTTP request scheme to use. Default is ``"https"``.
46
- :type scheme: str
47
- :param host: Wialon API host url. Default is ``"hst-api.wialon.com"``.
48
- :type host: str
49
- :param port: Wialon API port. Default is ``443``.
50
- :type port: int
51
- :returns: Nothing.
52
- :rtype: None
53
-
54
- """
55
-
56
- self._token = token if token else os.getenv("WIALON_TOKEN")
57
- self._username = None
58
- self._uid = None
59
- self._wialon_api = Wialon(
60
- scheme=scheme, host=host, port=port, sid=sid, token=self.token
61
- )
62
-
63
- def __str__(self) -> str:
64
- return f"Session #{self.id}"
65
-
66
- def __repr__(self) -> str:
67
- return f"{self.__class__}(sid={self.id})"
68
-
69
- def __enter__(self) -> "WialonSession":
70
- """
71
- Logs into the Wialon API using :py:meth:`login`.
72
-
73
- :raises AssertionError: If the session's Wialon API :py:attr:`token` wasn't set.
74
- :returns: A valid Wialon API session.
75
- :rtype: ~terminusgps.wialon.session.WialonSession
76
-
77
- """
78
- assert self.token, "Wialon API token wasn't set."
79
- self.login(self.token)
80
- return self
81
-
82
- def __exit__(self, exc_type, exc_value, exc_traceback) -> None:
83
- """
84
- Logs out of the session by calling :py:meth:`logout`.
85
-
86
- :returns: Nothing.
87
- :rtype: None
88
-
89
- """
90
- self.logout()
91
-
92
- @property
93
- def wialon_api(self) -> Wialon:
94
- return self._wialon_api
95
-
96
- @property
97
- def uid(self) -> str | None:
98
- """
99
- User id of the session's authenticated Wialon user.
100
-
101
- :type: str | None
102
- :value: None
103
- """
104
- return self._uid
105
-
106
- @property
107
- def username(self) -> str | None:
108
- """
109
- Username of the session's authenticated Wialon user.
110
-
111
- :type: str | None
112
- :value: None
113
-
114
- """
115
- return self._username
116
-
117
- @property
118
- def id(self) -> str | None:
119
- """
120
- Wialon API session id.
121
-
122
- Shortcut property for :py:attr:`WialonSession.wialon_api.sid`.
123
-
124
- Returns :py:obj:`None` if the session wasn't logged in.
125
-
126
- :type: str | None
127
- :value: None
128
-
129
- """
130
- return self.wialon_api.sid
131
-
132
- @property
133
- def token(self) -> str:
134
- """
135
- Wialon API token set during :py:meth:`WialonSession.__init__`.
136
-
137
- :type: str
138
-
139
- """
140
- return str(self._token)
141
-
142
- def login(self, token: str, flags: int | None = None) -> str:
143
- """
144
- Logs into the Wialon API, starts a new session then returns its id.
145
-
146
- :param token: An active Wialon API token.
147
- :type token: str
148
- :param flags: A login response flag integer.
149
- :type flags: int
150
- :raises WialonError: If the login fails.
151
- :raises AssertionError: If the login token was not set.
152
- :returns: The new session id.
153
- :rtype: str
154
-
155
- """
156
- try:
157
- response = self.wialon_api.token_login(
158
- **{"token": token, "fl": flags if flags else 0x2}
159
- )
160
- self._set_login_response(response)
161
- return response.get("eid", "")
162
- except (wialon.api.WialonError, ValueError):
163
- print("Failed to login to the Wialon API.")
164
- raise
165
-
166
- def logout(self) -> None:
167
- """
168
- Logs out of the Wialon API session.
169
-
170
- :returns: Nothing.
171
- :rtype: None
172
-
173
- """
174
- sid = self.wialon_api.sid
175
- response = self.wialon_api.core_logout({})
176
-
177
- if response.get("error") != 0:
178
- print(
179
- f"Failed to properly logout of session #{sid}: '{response.get('message')}'"
180
- )
181
- self.wialon_api.sid = None
182
-
183
- def _set_login_response(self, login_response: dict | None = None) -> None:
184
- """
185
- Sets the Wialon API session's attributes based on a login response.
186
-
187
- :param login_response: A dictionary returned from :py:meth:`login`.
188
- :type login_response: dict
189
- :raises ValueError: If ``login_response`` wasn't provided.
190
- :returns: Nothing.
191
- :rtype: None
192
-
193
- """
194
- if login_response is None:
195
- raise ValueError(
196
- f"Login response is required, got '{login_response}'"
197
- )
198
-
199
- self.wialon_api.sid = login_response.get("eid")
200
- self._uid = login_response.get("user", {}).get("id")
201
- self._username = login_response.get("au")
@@ -1,114 +0,0 @@
1
- import string
2
-
3
- from django.conf import settings
4
- from django.core.exceptions import ImproperlyConfigured, ValidationError
5
- from django.utils.translation import gettext_lazy as _
6
-
7
- from .session import WialonSession
8
-
9
- if settings.configured and not hasattr(settings, "WIALON_TOKEN"):
10
- raise ImproperlyConfigured("'WIALON_TOKEN' setting is required.")
11
-
12
-
13
- class WialonValidatorBase:
14
- def __init__(self) -> None:
15
- self.session = WialonSession()
16
- self.session.login(self.session.token)
17
-
18
- def __call__(self, value: str) -> None:
19
- raise NotImplementedError("Subclasses must implement this method.")
20
-
21
-
22
- class WialonVinNumberValidator(WialonValidatorBase):
23
- def __call__(self, value: str) -> None:
24
- response = self.session.wialon_api.unit_get_vin_info(vin=value)
25
- if "error" in response["vin_lookup_result"].keys():
26
- raise ValidationError(
27
- _("Failed to get info for VIN # '%(value)s': '%(message)s'"),
28
- code="invalid",
29
- params={
30
- "value": value,
31
- "message": response["vin_lookup_result"].get("message", ""),
32
- },
33
- )
34
-
35
-
36
- class WialonImeiNumberValidator(WialonValidatorBase):
37
- def __call__(self, value: str) -> None:
38
- response = self.session.wialon_api.core_search_items(
39
- **{
40
- "spec": {
41
- "itemsType": "avl_unit",
42
- "propName": "sys_id,sys_unique_id",
43
- "propValueMask": f"*,*{value}*",
44
- "sortType": "sys_id,sys_unique_id",
45
- },
46
- "force": 0,
47
- "flags": 1,
48
- "from": 0,
49
- "to": 0,
50
- }
51
- )
52
- if response is None or not response.get("items"):
53
- raise ValidationError(
54
- _("IMEI # '%(value)s' wasn't found in Wialon."),
55
- code="invalid",
56
- params={"value": value},
57
- )
58
-
59
-
60
- def validate_imei_number(value: str) -> None:
61
- """Raises :py:exec:`ValidationError` if the value is an invalid IMEI number."""
62
- WialonImeiNumberValidator()(value)
63
-
64
-
65
- def validate_vin_number(value: str) -> None:
66
- """Raises :py:exec:`ValidationError` if the value is an invalid VIN number."""
67
- if len(value) != 17:
68
- raise ValidationError(
69
- _("Whoops! VIN # must be exactly 17 characters in length, got %(length)s."),
70
- code="invalid",
71
- params={"length": len(value)},
72
- )
73
- WialonVinNumberValidator()(value)
74
-
75
-
76
- def validate_wialon_password(value: str) -> None:
77
- """Raises :py:exec:`ValidationError` if the value represents an invalid Wialon password."""
78
- special_symbols_0: list[str] = ["!", "@", "#", "$", "%", "^", "*"]
79
- special_symbols_1: list[str] = ["(", ")", "[", "]", "-", "_", "+"]
80
- forbidden_symbols: list[str] = [",", ":", "&", "<", ">", "'"]
81
-
82
- if len(value) < 4:
83
- raise ValidationError(
84
- _("Password cannot be less than 4 characters in length, got %(len)s."),
85
- code="invalid",
86
- params={"len": len(value)},
87
- )
88
- if len(value) > 64:
89
- raise ValidationError(
90
- _("Password cannot be greater than 64 characters in length, got %(len)s."),
91
- code="invalid",
92
- params={"len": len(value)},
93
- )
94
- if not any([char for char in value if char in string.ascii_uppercase]):
95
- raise ValidationError(
96
- _("Password must contain at least one uppercase letter."), code="invalid"
97
- )
98
- if not any([char for char in value if char in string.ascii_lowercase]):
99
- raise ValidationError(
100
- _("Password must contain at least one lowercase letter."), code="invalid"
101
- )
102
- if not any(
103
- [char for char in value if char in special_symbols_0 + special_symbols_1]
104
- ):
105
- raise ValidationError(
106
- _("Password must contain at least one special symbol."), code="invalid"
107
- )
108
- for char in value:
109
- if char in forbidden_symbols:
110
- raise ValidationError(
111
- _("Password cannot contain forbidden character '%(char)s'."),
112
- code="invalid",
113
- params={"char": char},
114
- )