layrz-sdk 4.0.11__py3-none-any.whl → 4.1.8__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.
Files changed (104) hide show
  1. layrz_sdk/decorators/__init__.py +5 -0
  2. layrz_sdk/decorators/func_timing.py +103 -0
  3. layrz_sdk/entities/__init__.py +19 -0
  4. layrz_sdk/entities/action.py +28 -16
  5. layrz_sdk/entities/action_geofence_ownership.py +0 -2
  6. layrz_sdk/entities/action_kind.py +0 -2
  7. layrz_sdk/entities/action_subkind.py +0 -2
  8. layrz_sdk/entities/asset.py +20 -9
  9. layrz_sdk/entities/asset_constants.py +10 -9
  10. layrz_sdk/entities/asset_contact.py +7 -3
  11. layrz_sdk/entities/asset_operation_mode.py +0 -2
  12. layrz_sdk/entities/ats_entry.py +11 -4
  13. layrz_sdk/entities/ats_exit_history.py +21 -19
  14. layrz_sdk/entities/ats_operation.py +87 -0
  15. layrz_sdk/entities/ats_possible_entry.py +10 -7
  16. layrz_sdk/entities/ats_possible_exit.py +19 -9
  17. layrz_sdk/entities/ats_purchaseorder.py +106 -0
  18. layrz_sdk/entities/ats_reception.py +16 -4
  19. layrz_sdk/entities/broadcast/__init__.py +2 -1
  20. layrz_sdk/entities/broadcast/payload.py +40 -8
  21. layrz_sdk/entities/broadcast/result.py +72 -1
  22. layrz_sdk/entities/broadcast/service.py +12 -2
  23. layrz_sdk/entities/broadcast/status.py +2 -2
  24. layrz_sdk/entities/case.py +51 -8
  25. layrz_sdk/entities/case_ignored_status.py +1 -0
  26. layrz_sdk/entities/charts/axis_config.py +13 -5
  27. layrz_sdk/entities/charts/bar_chart.py +12 -1
  28. layrz_sdk/entities/charts/chart_data_serie.py +17 -1
  29. layrz_sdk/entities/charts/column_chart.py +12 -1
  30. layrz_sdk/entities/charts/html_chart.py +8 -4
  31. layrz_sdk/entities/charts/line_chart.py +12 -3
  32. layrz_sdk/entities/charts/map_chart.py +14 -5
  33. layrz_sdk/entities/charts/map_point.py +7 -1
  34. layrz_sdk/entities/charts/number_chart.py +8 -4
  35. layrz_sdk/entities/charts/pie_chart.py +11 -3
  36. layrz_sdk/entities/charts/radar_chart.py +11 -3
  37. layrz_sdk/entities/charts/radial_bar_chart.py +11 -3
  38. layrz_sdk/entities/charts/scatter_chart.py +12 -3
  39. layrz_sdk/entities/charts/scatter_serie.py +11 -1
  40. layrz_sdk/entities/charts/scatter_serie_item.py +7 -1
  41. layrz_sdk/entities/charts/table_chart.py +7 -3
  42. layrz_sdk/entities/charts/table_header.py +7 -1
  43. layrz_sdk/entities/charts/table_row.py +7 -1
  44. layrz_sdk/entities/charts/timeline_chart.py +11 -3
  45. layrz_sdk/entities/charts/timeline_serie.py +7 -1
  46. layrz_sdk/entities/charts/timeline_serie_item.py +7 -1
  47. layrz_sdk/entities/checkpoint.py +20 -13
  48. layrz_sdk/entities/command_series_ticket.py +29 -4
  49. layrz_sdk/entities/comment.py +22 -5
  50. layrz_sdk/entities/custom_field.py +12 -4
  51. layrz_sdk/entities/custom_report_page.py +3 -0
  52. layrz_sdk/entities/destination_phone.py +7 -1
  53. layrz_sdk/entities/device.py +11 -4
  54. layrz_sdk/entities/event.py +20 -4
  55. layrz_sdk/entities/exchange_service.py +12 -4
  56. layrz_sdk/entities/function.py +11 -4
  57. layrz_sdk/entities/geofence.py +20 -5
  58. layrz_sdk/entities/geofence_category.py +0 -2
  59. layrz_sdk/entities/last_message.py +7 -3
  60. layrz_sdk/entities/locator.py +95 -0
  61. layrz_sdk/entities/message.py +16 -9
  62. layrz_sdk/entities/modbus/config.py +7 -1
  63. layrz_sdk/entities/modbus/parameter.py +7 -1
  64. layrz_sdk/entities/modbus/wait.py +11 -1
  65. layrz_sdk/entities/notification_type.py +0 -2
  66. layrz_sdk/entities/operation.py +37 -16
  67. layrz_sdk/entities/operation_case_payload.py +39 -13
  68. layrz_sdk/entities/operation_payload.py +63 -26
  69. layrz_sdk/entities/operation_type.py +0 -2
  70. layrz_sdk/entities/outbound_service.py +11 -4
  71. layrz_sdk/entities/parameter_update.py +25 -0
  72. layrz_sdk/entities/platform.py +0 -2
  73. layrz_sdk/entities/position.py +7 -3
  74. layrz_sdk/entities/preset.py +15 -9
  75. layrz_sdk/entities/report.py +9 -5
  76. layrz_sdk/entities/report_col.py +17 -3
  77. layrz_sdk/entities/report_configuration.py +7 -1
  78. layrz_sdk/entities/report_data_type.py +1 -3
  79. layrz_sdk/entities/report_format.py +1 -5
  80. layrz_sdk/entities/report_header.py +12 -29
  81. layrz_sdk/entities/report_page.py +7 -3
  82. layrz_sdk/entities/report_row.py +7 -19
  83. layrz_sdk/entities/request_type.py +0 -2
  84. layrz_sdk/entities/sensor.py +21 -3
  85. layrz_sdk/entities/sensor_mask.py +28 -0
  86. layrz_sdk/entities/sound_effect.py +0 -2
  87. layrz_sdk/entities/static_position.py +6 -1
  88. layrz_sdk/entities/telemetry/assetmessage.py +14 -15
  89. layrz_sdk/entities/telemetry/devicemessage.py +19 -9
  90. layrz_sdk/entities/text_alignment.py +0 -2
  91. layrz_sdk/entities/timezone.py +12 -4
  92. layrz_sdk/entities/trigger.py +102 -17
  93. layrz_sdk/entities/trigger_kind.py +2 -5
  94. layrz_sdk/entities/user.py +11 -4
  95. layrz_sdk/entities/waypoint.py +38 -16
  96. layrz_sdk/entities/weekday.py +1 -3
  97. layrz_sdk/helpers/color.py +1 -6
  98. layrz_sdk/lcl/core.py +0 -1
  99. {layrz_sdk-4.0.11.dist-info → layrz_sdk-4.1.8.dist-info}/METADATA +2 -2
  100. layrz_sdk-4.1.8.dist-info/RECORD +123 -0
  101. layrz_sdk-4.0.11.dist-info/RECORD +0 -116
  102. {layrz_sdk-4.0.11.dist-info → layrz_sdk-4.1.8.dist-info}/WHEEL +0 -0
  103. {layrz_sdk-4.0.11.dist-info → layrz_sdk-4.1.8.dist-info}/licenses/LICENSE +0 -0
  104. {layrz_sdk-4.0.11.dist-info → layrz_sdk-4.1.8.dist-info}/top_level.txt +0 -0
@@ -1,13 +1,10 @@
1
- """Asset message"""
2
-
3
1
  from __future__ import annotations
4
2
 
5
- import json
6
3
  from datetime import datetime, timedelta
7
4
  from typing import Any, Self, cast
8
5
 
9
6
  from geopy.distance import geodesic
10
- from pydantic import BaseModel, Field, field_serializer
7
+ from pydantic import BaseModel, ConfigDict, Field, field_serializer
11
8
  from shapely.geometry import MultiPoint
12
9
 
13
10
  from layrz_sdk.constants import UTC
@@ -21,15 +18,17 @@ from layrz_sdk.entities.telemetry.devicemessage import DeviceMessage
21
18
  class AssetMessage(BaseModel):
22
19
  """Asset message model"""
23
20
 
24
- model_config = {
25
- 'json_encoders': {
26
- datetime: lambda v: v.timestamp(),
27
- timedelta: lambda v: v.total_seconds(),
28
- Position: lambda v: json.loads(v.model_dump_json(exclude_none=True)),
29
- }
30
- }
21
+ model_config = ConfigDict(
22
+ validate_by_name=False,
23
+ validate_by_alias=True,
24
+ serialize_by_alias=True,
25
+ )
31
26
 
32
- pk: int | None = Field(default=None, description='Message ID', alias='id')
27
+ pk: int | None = Field(
28
+ default=None,
29
+ description='Message ID',
30
+ alias='id',
31
+ )
33
32
  asset_id: int = Field(..., description='Asset ID')
34
33
 
35
34
  position: dict[str, float | int] = Field(
@@ -62,7 +61,7 @@ class AssetMessage(BaseModel):
62
61
  description='Timestamp when the message was received',
63
62
  )
64
63
 
65
- @field_serializer('received_at')
64
+ @field_serializer('received_at', when_used='always')
66
65
  def serialize_received_at(self: Self, value: datetime) -> float:
67
66
  """Serialize received_at to a timestamp."""
68
67
  return value.timestamp()
@@ -72,7 +71,7 @@ class AssetMessage(BaseModel):
72
71
  description='Elapsed time since the last message',
73
72
  )
74
73
 
75
- @field_serializer('elapsed_time')
74
+ @field_serializer('elapsed_time', when_used='always')
76
75
  def serialize_elapsed_time(self: Self, value: timedelta) -> float:
77
76
  """Serialize elapsed_time to total seconds."""
78
77
  return value.total_seconds()
@@ -153,7 +152,7 @@ class AssetMessage(BaseModel):
153
152
  def to_message(self: Self) -> Message:
154
153
  """Convert the asset message to a Message object."""
155
154
  return Message(
156
- id=self.pk if self.pk is not None else 0,
155
+ id=self.pk if self.pk is not None else 0, # ty: ignore
157
156
  asset_id=self.asset_id,
158
157
  position=Position.model_validate(self.position),
159
158
  payload=self.payload,
@@ -1,12 +1,9 @@
1
- """Device message"""
2
-
3
1
  from __future__ import annotations
4
2
 
5
- import json
6
3
  from datetime import datetime
7
4
  from typing import Any, Self
8
5
 
9
- from pydantic import BaseModel, Field, field_serializer
6
+ from pydantic import BaseModel, ConfigDict, Field, field_serializer
10
7
 
11
8
  from layrz_sdk.constants import REJECTED_KEYS, UTC
12
9
  from layrz_sdk.entities.device import Device
@@ -17,7 +14,16 @@ from layrz_sdk.entities.position import Position
17
14
  class DeviceMessage(BaseModel):
18
15
  """Device message model"""
19
16
 
20
- pk: int | None = Field(default=None, description='Device message ID', alias='id')
17
+ model_config = ConfigDict(
18
+ validate_by_name=False,
19
+ validate_by_alias=True,
20
+ serialize_by_alias=True,
21
+ )
22
+ pk: int | None = Field(
23
+ default=None,
24
+ description='Device message ID',
25
+ alias='id',
26
+ )
21
27
  ident: str = Field(..., description='Device identifier')
22
28
  device_id: int = Field(..., description='Device ID')
23
29
  protocol_id: int = Field(..., description='Protocol ID')
@@ -37,7 +43,7 @@ class DeviceMessage(BaseModel):
37
43
  description='Timestamp when the message was received',
38
44
  )
39
45
 
40
- @field_serializer('received_at')
46
+ @field_serializer('received_at', when_used='always')
41
47
  def serialize_received_at(self: Self, value: datetime) -> float:
42
48
  """Serialize received_at to a timestamp."""
43
49
  return value.timestamp()
@@ -69,6 +75,9 @@ class DeviceMessage(BaseModel):
69
75
  @classmethod
70
76
  def parse_from_dict(cls, *, raw_payload: dict[str, Any], device: Device) -> DeviceMessage:
71
77
  """Format a DeviceMessage from a dictionary."""
78
+ if not device.protocol_id:
79
+ raise ValueError('Device protocol_id is required to parse DeviceMessage')
80
+
72
81
  received_at: datetime
73
82
  position: dict[str, float | int] = {}
74
83
  payload: dict[str, Any] = {}
@@ -80,7 +89,8 @@ class DeviceMessage(BaseModel):
80
89
 
81
90
  for key, value in raw_payload.items():
82
91
  if key.startswith('position.'):
83
- position[key[9:]] = value
92
+ if isinstance(value, (float, int)):
93
+ position[key[9:]] = value
84
94
 
85
95
  if key not in REJECTED_KEYS:
86
96
  payload[key] = value
@@ -88,7 +98,7 @@ class DeviceMessage(BaseModel):
88
98
  return cls(
89
99
  ident=device.ident,
90
100
  device_id=device.pk,
91
- protocol_id=device.protocol_id, # type: ignore
101
+ protocol_id=device.protocol_id,
92
102
  position=position,
93
103
  payload=payload,
94
104
  received_at=received_at,
@@ -97,7 +107,7 @@ class DeviceMessage(BaseModel):
97
107
  def to_message(self: Self) -> Message:
98
108
  """Convert the asset message to a Message object."""
99
109
  return Message(
100
- id=self.pk if self.pk is not None else 0,
110
+ id=self.pk if self.pk is not None else 0, # ty: ignore
101
111
  asset_id=self.device_id if self.device_id is not None else 0,
102
112
  position=Position.model_validate(self.position),
103
113
  payload=self.payload,
@@ -1,5 +1,3 @@
1
- """Text alignment"""
2
-
3
1
  from enum import StrEnum
4
2
  from typing import Self
5
3
 
@@ -1,11 +1,19 @@
1
- """Timezone entity"""
2
-
3
- from pydantic import BaseModel, Field
1
+ from pydantic import BaseModel, ConfigDict, Field
4
2
 
5
3
 
6
4
  class Timezone(BaseModel):
7
5
  """Timezone entity"""
8
6
 
9
- pk: int = Field(..., description='Defines the primary key of the timezone', alias='id')
7
+ model_config = ConfigDict(
8
+ validate_by_name=False,
9
+ validate_by_alias=True,
10
+ serialize_by_alias=True,
11
+ )
12
+
13
+ pk: int = Field(
14
+ ...,
15
+ description='Defines the primary key of the timezone',
16
+ alias='id',
17
+ )
10
18
  name: str = Field(..., description='Defines the name of the timezone')
11
19
  offset: int = Field(..., description='Defines the offset of the timezone in seconds from UTC')
@@ -3,7 +3,7 @@
3
3
  from datetime import time, timedelta
4
4
  from typing import Any
5
5
 
6
- from pydantic import BaseModel, Field, field_validator
6
+ from pydantic import BaseModel, ConfigDict, Field, field_serializer, field_validator
7
7
 
8
8
  from .trigger_kind import TriggerCaseKind, TriggerCommentPattern, TriggerGeofenceKind, TriggerKind
9
9
  from .weekday import Weekday
@@ -12,18 +12,16 @@ from .weekday import Weekday
12
12
  class Trigger(BaseModel):
13
13
  """Trigger entity"""
14
14
 
15
- model_config = {
16
- 'json_encoders': {
17
- timedelta: lambda v: v.total_seconds(),
18
- TriggerCaseKind: lambda v: v.value,
19
- TriggerGeofenceKind: lambda v: v.value,
20
- TriggerKind: lambda v: v.value,
21
- TriggerCommentPattern: lambda v: v.value,
22
- Weekday: lambda v: v.value,
23
- },
24
- }
25
-
26
- pk: int = Field(description='Defines the primary key of the trigger', alias='id')
15
+ model_config = ConfigDict(
16
+ validate_by_name=False,
17
+ validate_by_alias=True,
18
+ serialize_by_alias=True,
19
+ )
20
+
21
+ pk: int = Field(
22
+ description='Defines the primary key of the trigger',
23
+ alias='id',
24
+ )
27
25
  name: str = Field(description='Defines the name of the trigger')
28
26
  code: str = Field(description='Defines the code of the trigger')
29
27
 
@@ -32,27 +30,48 @@ class Trigger(BaseModel):
32
30
  description='Defines the cooldown time of the trigger',
33
31
  )
34
32
 
33
+ @field_serializer('cooldown_time', when_used='always')
34
+ def serialize_cooldown_time(self, value: timedelta) -> float:
35
+ """Serialize cooldown_time to total seconds."""
36
+ return value.total_seconds()
37
+
35
38
  type_: TriggerKind | None = Field(
36
39
  default=None,
37
40
  description='Defines the kind of the trigger',
38
41
  alias='type',
39
42
  )
40
43
 
44
+ @field_serializer('type_', when_used='always')
45
+ def serialize_type(self, value: TriggerKind | None) -> str | None:
46
+ return value.value if value else None
47
+
41
48
  presence_type: TriggerGeofenceKind | None = Field(
42
49
  default=None,
43
50
  description='Defines the geofence kind of the trigger',
44
51
  )
45
52
 
53
+ @field_serializer('presence_type', when_used='always')
54
+ def serialize_presence_type(self, value: TriggerGeofenceKind | None) -> str | None:
55
+ return value.value if value else None
56
+
46
57
  case_type: TriggerCaseKind | None = Field(
47
58
  default=None,
48
59
  description='Defines the case kind of the trigger',
49
60
  )
50
61
 
62
+ @field_serializer('case_type', when_used='always')
63
+ def serialize_case_type(self, value: TriggerCaseKind | None) -> str | None:
64
+ return value.value if value else None
65
+
51
66
  case_comment_pattern: TriggerCommentPattern | None = Field(
52
67
  default=None,
53
68
  description='Defines the comment pattern of the trigger',
54
69
  )
55
70
 
71
+ @field_serializer('case_comment_pattern', when_used='always')
72
+ def serialize_case_comment_pattern(self, value: TriggerCommentPattern | None) -> str | None:
73
+ return value.value if value else None
74
+
56
75
  case_comment_value: str | None = Field(
57
76
  default=None,
58
77
  description='Defines the comment pattern value of the trigger',
@@ -72,6 +91,10 @@ class Trigger(BaseModel):
72
91
  description='Defines the weekdays of the trigger',
73
92
  )
74
93
 
94
+ @field_serializer('weekdays', when_used='always')
95
+ def serialize_weekdays(self, value: list[Weekday]) -> list[str]:
96
+ return [day.value for day in value]
97
+
75
98
  is_plain_crontab: bool = Field(
76
99
  default=False,
77
100
  description='Defines if the trigger is a plain crontab',
@@ -94,10 +117,7 @@ class Trigger(BaseModel):
94
117
 
95
118
  @field_validator('manual_action_fields', mode='before')
96
119
  def validate_manual_action_fields(cls, value: Any) -> list[dict[str, Any]]:
97
- """Ensure manual_action_fields is a list of dictionaries."""
98
- if isinstance(value, list):
99
- return value
100
- return []
120
+ return value if isinstance(value, list) else []
101
121
 
102
122
  formula: str | None = Field(
103
123
  default=None,
@@ -145,8 +165,73 @@ class Trigger(BaseModel):
145
165
  default=None,
146
166
  description='Defines the care protocol ID of the trigger',
147
167
  )
168
+ has_case_expirity: bool = Field(
169
+ default=False,
170
+ description='Defines if the trigger has case expiry',
171
+ )
172
+
173
+ when_case_expires_delta: timedelta | None = Field(
174
+ default=None,
175
+ description='Defines when the trigger expires delta',
176
+ )
177
+
178
+ @field_serializer('when_case_expires_delta', when_used='always')
179
+ def serialize_when_case_expires_delta(self, value: timedelta | None) -> float | None:
180
+ return value.total_seconds() if value else None
181
+
182
+ should_stack: bool = Field(
183
+ default=False,
184
+ description='Defines if the trigger cases should stack',
185
+ )
186
+ stack_upper_limit: int | None = Field(
187
+ default=None,
188
+ description='Defines the stack upper limit of the trigger cases. None means no limit',
189
+ )
148
190
 
149
191
  owner_id: int | None = Field(
150
192
  default=None,
151
193
  description='Owner ID',
152
194
  )
195
+
196
+ search_time_delta: timedelta | None = Field(
197
+ default=None,
198
+ description='Defines the search time delta of the trigger',
199
+ )
200
+
201
+ @field_serializer('search_time_delta', when_used='always')
202
+ def serialize_search_time_delta(self, value: timedelta | None) -> float | None:
203
+ return value.total_seconds() if value else None
204
+
205
+ is_paused: bool = Field(
206
+ default=False,
207
+ description='Defines if the trigger is paused',
208
+ )
209
+
210
+ should_generate_locator: bool = Field(
211
+ default=False,
212
+ description='Defines if the trigger should generate a locator',
213
+ )
214
+
215
+ locator_expires_delta: timedelta | None = Field(
216
+ default=None,
217
+ description='Defines the locator expires delta of the trigger',
218
+ )
219
+
220
+ @field_serializer('locator_expires_delta', when_used='always')
221
+ def serialize_locator_expires_delta(self, value: timedelta | None) -> float | None:
222
+ return value.total_seconds() if value else None
223
+
224
+ locator_expires_triggers_ids: list[int] = Field(
225
+ default_factory=list,
226
+ description='Defines the locator expires triggers IDs of the trigger',
227
+ )
228
+
229
+ locator_geofences_ids: list[int] = Field(
230
+ default_factory=list,
231
+ description='Defines the locator geofences IDs of the trigger',
232
+ )
233
+
234
+ locator_customization_id: int | None = Field(
235
+ default=None,
236
+ description='Defines the locator customization ID of the trigger',
237
+ )
@@ -1,13 +1,9 @@
1
- """Asset Operation Mode"""
2
-
3
1
  from enum import StrEnum
4
2
  from typing import Self
5
3
 
6
4
 
7
5
  class TriggerKind(StrEnum):
8
- """
9
- Trigger Kind definition
10
- """
6
+ """Trigger Kind definition"""
11
7
 
12
8
  PRESENCE_IN_GEOFENCE = 'PRESENCEINGEOFENCE'
13
9
  EXACT_TIME = 'EXACTTIME'
@@ -18,6 +14,7 @@ class TriggerKind(StrEnum):
18
14
  BHS_SPEEDING = 'BHS_SPEEDING'
19
15
  BHS_PRESENCE = 'BHS_PRESENCE'
20
16
  MANUAL_ACTION = 'MANUAL_ACTION'
17
+ NESTED = 'NESTED_TRIGGERS'
21
18
 
22
19
  def __str__(self: Self) -> str:
23
20
  """Readable property"""
@@ -1,10 +1,17 @@
1
- """User entity"""
2
-
3
- from pydantic import BaseModel, Field
1
+ from pydantic import BaseModel, ConfigDict, Field
4
2
 
5
3
 
6
4
  class User(BaseModel):
7
5
  """User entity"""
8
6
 
9
- pk: int = Field(description='Defines the primary key of the user', alias='id')
7
+ model_config = ConfigDict(
8
+ validate_by_name=False,
9
+ validate_by_alias=True,
10
+ serialize_by_alias=True,
11
+ )
12
+
13
+ pk: int = Field(
14
+ description='Defines the primary key of the user',
15
+ alias='id',
16
+ )
10
17
  name: str = Field(description='Defines the name of the user')
@@ -1,9 +1,7 @@
1
- """Waypoint entity"""
2
-
3
1
  from datetime import datetime, timedelta
4
2
  from enum import StrEnum
5
3
 
6
- from pydantic import BaseModel, Field
4
+ from pydantic import BaseModel, ConfigDict, Field, field_serializer
7
5
 
8
6
  from .geofence import Geofence
9
7
 
@@ -25,17 +23,30 @@ class WaypointKind(StrEnum):
25
23
  class Waypoint(BaseModel):
26
24
  """Waypoint entity definition"""
27
25
 
28
- model_config = {
29
- 'json_encoders': {
30
- datetime: lambda v: v.timestamp(),
31
- },
32
- }
26
+ model_config = ConfigDict(
27
+ validate_by_name=False,
28
+ validate_by_alias=True,
29
+ serialize_by_alias=True,
30
+ )
33
31
 
34
- pk: int = Field(description='Waypoint ID', alias='id')
32
+ pk: int = Field(
33
+ description='Waypoint ID',
34
+ alias='id',
35
+ )
35
36
  geofence: Geofence | None = Field(default=None, description='Geofence object')
36
37
  geofence_id: int | None = Field(default=None, description='Geofence ID')
37
38
  start_at: datetime | None = Field(default=None, description='Waypoint start date')
39
+
40
+ @field_serializer('start_at', when_used='always')
41
+ def serialize_start_at(self, value: datetime | None) -> float | None:
42
+ return value.timestamp() if value else None
43
+
38
44
  end_at: datetime | None = Field(default=None, description='Waypoint end date')
45
+
46
+ @field_serializer('end_at', when_used='always')
47
+ def serialize_end_at(self, value: datetime | None) -> float | None:
48
+ return value.timestamp() if value else None
49
+
39
50
  sequence_real: int = Field(..., description='Real sequence number')
40
51
  sequence_ideal: int = Field(..., description='Ideal sequence number')
41
52
 
@@ -43,20 +54,31 @@ class Waypoint(BaseModel):
43
54
  class WaypointRef(BaseModel):
44
55
  """Waypoint reference entity definition"""
45
56
 
46
- model_config = {
47
- 'json_encoders': {
48
- timedelta: lambda v: v.total_seconds(),
49
- datetime: lambda v: v.timestamp(),
50
- },
51
- }
57
+ model_config = ConfigDict(
58
+ validate_by_name=False,
59
+ validate_by_alias=True,
60
+ serialize_by_alias=True,
61
+ )
52
62
 
53
- pk: int = Field(description='Waypoint ID', alias='id')
63
+ pk: int = Field(
64
+ description='Waypoint ID',
65
+ alias='id',
66
+ )
54
67
  geofence_id: int = Field(description='Geofence ID')
55
68
  time: timedelta = Field(
56
69
  default_factory=lambda: timedelta(seconds=0),
57
70
  description='Time offset from the start of the checkpoint',
58
71
  )
72
+
73
+ @field_serializer('time', when_used='always')
74
+ def serialize_time(self, value: timedelta) -> float:
75
+ return value.total_seconds()
76
+
59
77
  kind: WaypointKind = Field(
60
78
  ...,
61
79
  description='Defines the kind of waypoint',
62
80
  )
81
+
82
+ @field_serializer('kind', when_used='always')
83
+ def serialize_kind(self, value: WaypointKind) -> str:
84
+ return value.value
@@ -3,9 +3,7 @@ from typing import Self
3
3
 
4
4
 
5
5
  class Weekday(StrEnum):
6
- """
7
- Weekday definition
8
- """
6
+ """Weekday definition"""
9
7
 
10
8
  MONDAY = 'MON'
11
9
  TUESDAY = 'TUE'
@@ -10,11 +10,7 @@ def convert_to_rgba(hex_color: str) -> Color:
10
10
  Convert Hex (or Hexa) color to RGB (or RGBA) color
11
11
 
12
12
  :param hex_color: Hex color
13
- :type hex_color: str
14
-
15
13
  :return: RGB or RGBA color
16
- :rtype: tuple
17
-
18
14
  :raises ValueError: If the color is invalid
19
15
  """
20
16
 
@@ -33,10 +29,9 @@ def use_black(color: str) -> bool:
33
29
  Use black
34
30
  Will return when the background color works well with black text color.
35
31
  Note: This method is not 100% accurate and will not work with alpha channel (Hexa color)
32
+
36
33
  :param color: Hex color
37
- :type color: str
38
34
  :return: True if the color works well with black text color
39
- :rtype: bool
40
35
  :raises ValueError: If the color is invalid
41
36
  """
42
37
  rgb = convert_to_rgba(color)
layrz_sdk/lcl/core.py CHANGED
@@ -1,6 +1,5 @@
1
1
  """Layrz Compute Language SDK"""
2
2
 
3
- # ruff: noqa: ANN401
4
3
  from typing import Any, Optional, Self, cast
5
4
 
6
5
  PATTERN_INVALID = 'Pattern should be string, received {received}'
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: layrz-sdk
3
- Version: 4.0.11
3
+ Version: 4.1.8
4
4
  Summary: Layrz SDK for Python
5
5
  Author-email: "Golden M, Inc." <software@goldenm.com>
6
6
  Maintainer-email: Kenny Mochizuki <kenny@goldenm.com>, Luis Reyes <lreyes@goldenm.com>, Kasen Li <kli@goldenm.com>, Miguel Zauzich <miguel@goldenm.com>, Angel Prieto <aprieto@goldenm.com>
@@ -18,7 +18,7 @@ Requires-Python: >=3.13
18
18
  Description-Content-Type: text/markdown
19
19
  License-File: LICENSE
20
20
  Requires-Dist: xlsxwriter<4.0.0,>=3.2.5
21
- Requires-Dist: pydantic>=2.10.6
21
+ Requires-Dist: pydantic>=2.12.3
22
22
  Requires-Dist: geopy>=2.4.1
23
23
  Requires-Dist: shapely>=2.1.1
24
24
  Requires-Dist: strenum>=0.4.15