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
@@ -0,0 +1,5 @@
1
+ """decorators"""
2
+
3
+ from .func_timing import func_timing
4
+
5
+ __all__ = ['func_timing']
@@ -0,0 +1,103 @@
1
+ """Timing decorator"""
2
+
3
+ import asyncio
4
+ import logging
5
+ import os
6
+ from collections.abc import Callable, Coroutine
7
+ from functools import wraps
8
+ from typing import Any, ParamSpec, TypeVar, overload
9
+
10
+ T = TypeVar('T')
11
+ P = ParamSpec('P')
12
+
13
+ log = logging.getLogger(__name__)
14
+
15
+ SHOULD_DISPLAY = os.environ.get('LAYRZ_SDK_DISPLAY_TIMING', '1') == '1'
16
+ if raw_depth := os.environ.get('LAYRZ_SDK_TIMING_DEPTH'):
17
+ try:
18
+ MAX_DEPTH = int(raw_depth)
19
+ except ValueError:
20
+ MAX_DEPTH = 0
21
+ else:
22
+ MAX_DEPTH = 0
23
+
24
+
25
+ @overload
26
+ def func_timing(func: Callable[P, T]) -> Callable[P, T | Coroutine[Any, Any, T]]: ...
27
+
28
+
29
+ @overload
30
+ def func_timing(*, depth: int) -> Callable[[Callable[P, T]], Callable[P, T | Coroutine[Any, Any, T]]]: ...
31
+
32
+
33
+ def func_timing(
34
+ func: Callable[P, T] | None = None,
35
+ *,
36
+ depth: int = 0,
37
+ ) -> Any:
38
+ """
39
+ Decorator to time a function execution.
40
+
41
+ :param depth: The depth of the function call for logging indentation.
42
+ :return: The wrapped function with timing functionality.
43
+ """
44
+
45
+ def decorator(func: Callable[P, T]) -> Callable[P, T]:
46
+ """Decorator to time a function"""
47
+ import time
48
+
49
+ prefix = '\t' * depth
50
+
51
+ @wraps(func)
52
+ async def async_wrapper(*args: P.args, **kwargs: P.kwargs) -> T:
53
+ start_time = time.perf_counter_ns()
54
+ result: T = await func(*args, **kwargs) # type: ignore
55
+ diff = time.perf_counter_ns() - start_time
56
+
57
+ if SHOULD_DISPLAY and depth <= MAX_DEPTH:
58
+ log.info(f'{prefix}{func.__name__}() took {_readable_time(diff)}') # ty: ignore
59
+
60
+ return result
61
+
62
+ @wraps(func)
63
+ def sync_wrapper(*args: P.args, **kwargs: P.kwargs) -> T:
64
+ start_time = time.perf_counter_ns()
65
+ result = func(*args, **kwargs)
66
+ diff = time.perf_counter_ns() - start_time
67
+
68
+ if SHOULD_DISPLAY and depth <= MAX_DEPTH:
69
+ log.info(f'{prefix}{func.__name__}() took {_readable_time(diff)}') # ty: ignore
70
+
71
+ return result
72
+
73
+ if asyncio.iscoroutinefunction(func):
74
+ return async_wrapper # type: ignore
75
+ return sync_wrapper
76
+
77
+ if func is None:
78
+ return decorator
79
+ return decorator(func)
80
+
81
+
82
+ def _readable_time(diff: int) -> str:
83
+ """Convert nanoseconds to a readable format"""
84
+ if diff < 1_000:
85
+ return f'{diff} ns'
86
+
87
+ diff = diff // 1_000
88
+ if diff < 1_000:
89
+ return f'{diff} μs'
90
+
91
+ diff = diff // 1_000
92
+ if diff < 1_000:
93
+ return f'{diff} ms'
94
+
95
+ diff = diff // 1_000
96
+ if diff < 60:
97
+ return f'{diff} s'
98
+
99
+ diff = diff // 60
100
+ if diff < 60:
101
+ return f'{diff} m'
102
+
103
+ return f'{diff // 60} h'
@@ -10,8 +10,10 @@ from .asset_contact import AssetContact
10
10
  from .asset_operation_mode import AssetOperationMode
11
11
  from .ats_entry import AtsEntry
12
12
  from .ats_exit_history import AtsExitExecutionHistory
13
+ from .ats_operation import AtsOperation, AtsOperationMovement
13
14
  from .ats_possible_entry import AtsPossibleEntry
14
15
  from .ats_possible_exit import AtsPossibleExit
16
+ from .ats_purchaseorder import AtsPurchaseOrder, DeliveryCategories, OrderCategories, OrderStatus
15
17
  from .ats_reception import AtsReception
16
18
  from .broadcast import (
17
19
  BroadcastPayload,
@@ -20,6 +22,7 @@ from .broadcast import (
20
22
  BroadcastResult,
21
23
  BroadcastService,
22
24
  BroadcastStatus,
25
+ RawBroadcastResult,
23
26
  )
24
27
  from .case import Case
25
28
  from .case_ignored_status import CaseIgnoredStatus
@@ -65,6 +68,7 @@ from .function import Function
65
68
  from .geofence import Geofence
66
69
  from .geofence_category import GeofenceCategory
67
70
  from .last_message import LastMessage
71
+ from .locator import Locator, LocatorMqttConfig
68
72
  from .message import Message
69
73
  from .modbus import ModbusConfig, ModbusParameter, ModbusSchema, ModbusStatus, ModbusWait
70
74
  from .notification_type import TwilioNotificationType
@@ -73,6 +77,7 @@ from .operation_case_payload import OperationCaseCommentPayload, OperationCasePa
73
77
  from .operation_payload import OperationPayload
74
78
  from .operation_type import OperationType
75
79
  from .outbound_service import OutboundService
80
+ from .parameter_update import ParameterUpdate
76
81
  from .platform import Platform
77
82
  from .position import Position
78
83
  from .presence_type import PresenceType
@@ -87,7 +92,9 @@ from .report_page import ReportPage
87
92
  from .report_row import ReportRow
88
93
  from .request_type import HttpRequestType
89
94
  from .sensor import Sensor
95
+ from .sensor_mask import SensorMask
90
96
  from .sound_effect import SoundEffect
97
+ from .static_position import StaticPosition
91
98
  from .telemetry import AssetMessage, DeviceMessage
92
99
  from .text_alignment import TextAlignment
93
100
  from .timezone import Timezone
@@ -118,6 +125,7 @@ __all__ = [
118
125
  'BroadcastRequest',
119
126
  'BroadcastResponse',
120
127
  'BroadcastResult',
128
+ 'RawBroadcastResult',
121
129
  'BroadcastService',
122
130
  'BroadcastStatus',
123
131
  'Case',
@@ -150,6 +158,8 @@ __all__ = [
150
158
  'HttpRequestType',
151
159
  'LastMessage',
152
160
  'LineChart',
161
+ 'Locator',
162
+ 'LocatorMqttConfig',
153
163
  'MapCenterType',
154
164
  'MapChart',
155
165
  'MapPoint',
@@ -185,7 +195,9 @@ __all__ = [
185
195
  'ScatterSerie',
186
196
  'ScatterSerieItem',
187
197
  'Sensor',
198
+ 'SensorMask',
188
199
  'SoundEffect',
200
+ 'StaticPosition',
189
201
  'TableChart',
190
202
  'TableHeader',
191
203
  'TableRow',
@@ -206,4 +218,11 @@ __all__ = [
206
218
  'WaypointKind',
207
219
  'WaypointRef',
208
220
  'Weekday',
221
+ 'AtsPurchaseOrder',
222
+ 'DeliveryCategories',
223
+ 'OrderCategories',
224
+ 'OrderStatus',
225
+ 'AtsOperationMovement',
226
+ 'AtsOperation',
227
+ 'ParameterUpdate',
209
228
  ]
@@ -1,8 +1,4 @@
1
- """Action entity"""
2
-
3
- from datetime import timedelta
4
-
5
- from pydantic import BaseModel, Field
1
+ from pydantic import BaseModel, ConfigDict, Field, field_serializer
6
2
 
7
3
  from .action_geofence_ownership import ActionGeofenceOwnership
8
4
  from .action_kind import ActionKind
@@ -13,20 +9,24 @@ from .geofence_category import GeofenceCategory
13
9
  class Action(BaseModel):
14
10
  """Action entity"""
15
11
 
16
- model_config = {
17
- 'json_encoders': {
18
- timedelta: lambda v: v.total_seconds(),
19
- ActionKind: lambda v: v.value,
20
- ActionSubKind: lambda v: v.value,
21
- GeofenceCategory: lambda v: v.value,
22
- ActionGeofenceOwnership: lambda v: v.value,
23
- },
24
- }
25
-
26
- pk: int = Field(..., description='Primary key of the action entity', alias='id')
12
+ model_config = ConfigDict(
13
+ validate_by_name=False,
14
+ validate_by_alias=True,
15
+ serialize_by_alias=True,
16
+ )
17
+
18
+ pk: int = Field(
19
+ ...,
20
+ description='Primary key of the action entity',
21
+ alias='id',
22
+ )
27
23
  name: str = Field(..., description='Name of the action')
28
24
  kind: ActionKind = Field(..., description='Kind of the action')
29
25
 
26
+ @field_serializer('kind', when_used='always')
27
+ def serialize_kind(self, kind: ActionKind) -> str:
28
+ return kind.value
29
+
30
30
  command_id: int | None = Field(
31
31
  default=None,
32
32
  description='Tag ID associated with the action to send commands to primary devices',
@@ -34,6 +34,10 @@ class Action(BaseModel):
34
34
 
35
35
  subkind: ActionSubKind = Field(default=ActionSubKind.UNUSED, description='Subkind of the action')
36
36
 
37
+ @field_serializer('subkind', when_used='always')
38
+ def serialize_subkind(self, subkind: ActionSubKind) -> str:
39
+ return subkind.value
40
+
37
41
  wait_for_image: bool = Field(
38
42
  default=False,
39
43
  description='Whether to wait for an image to be taken before executing the action',
@@ -45,6 +49,10 @@ class Action(BaseModel):
45
49
  description='Geofence category of the action',
46
50
  )
47
51
 
52
+ @field_serializer('geofence_cateogry', when_used='always')
53
+ def serialize_geofence_category(self, geofence_category: GeofenceCategory) -> str:
54
+ return geofence_category.value
55
+
48
56
  geofence_name_formula: str | None = Field(
49
57
  default=None,
50
58
  description='Formula to generate the geofence name',
@@ -67,4 +75,8 @@ class Action(BaseModel):
67
75
  description='Ownership of the new geofence created by the action',
68
76
  )
69
77
 
78
+ @field_serializer('new_geofence_ownership', when_used='always')
79
+ def serialize_new_geofence_ownership(self, ownership: ActionGeofenceOwnership) -> str:
80
+ return ownership.value
81
+
70
82
  owner_id: int | None = Field(default=None, description='Owner ID')
@@ -1,5 +1,3 @@
1
- """Action geofence ownership"""
2
-
3
1
  from enum import StrEnum
4
2
  from typing import Self
5
3
 
@@ -1,5 +1,3 @@
1
- """Action kind"""
2
-
3
1
  from enum import StrEnum
4
2
  from typing import Self
5
3
 
@@ -1,5 +1,3 @@
1
- """Action sub kind"""
2
-
3
1
  from enum import StrEnum
4
2
  from typing import Self
5
3
 
@@ -1,8 +1,6 @@
1
- """Asset Entity"""
2
-
3
1
  from typing import Any, Self
4
2
 
5
- from pydantic import BaseModel, Field, field_validator, model_validator
3
+ from pydantic import BaseModel, ConfigDict, Field, field_serializer, field_validator, model_validator
6
4
 
7
5
  from .asset_contact import AssetContact
8
6
  from .asset_operation_mode import AssetOperationMode
@@ -15,13 +13,16 @@ from .static_position import StaticPosition
15
13
  class Asset(BaseModel):
16
14
  """Asset entity definition"""
17
15
 
18
- model_config = {
19
- 'json_encoders': {
20
- AssetOperationMode: lambda v: v.value,
21
- }
22
- }
16
+ model_config = ConfigDict(
17
+ validate_by_name=False,
18
+ validate_by_alias=True,
19
+ serialize_by_alias=True,
20
+ )
23
21
 
24
- pk: int = Field(description='Defines the primary key of the asset', alias='id')
22
+ pk: int = Field(
23
+ description='Defines the primary key of the asset',
24
+ alias='id',
25
+ )
25
26
  name: str = Field(description='Defines the name of the asset')
26
27
  vin: str | None = Field(
27
28
  default=None,
@@ -30,6 +31,11 @@ class Asset(BaseModel):
30
31
  plate: str | None = Field(default=None, description='Defines the plate number of the asset')
31
32
  kind_id: int | None = Field(description='Defines the type of the asset', default=None)
32
33
  operation_mode: AssetOperationMode = Field(description='Defines the operation mode of the asset')
34
+
35
+ @field_serializer('operation_mode', when_used='always')
36
+ def serialize_operation_mode(self, operation_mode: AssetOperationMode) -> str:
37
+ return operation_mode.value
38
+
33
39
  sensors: list[Sensor] = Field(default_factory=list, description='Defines the list of sensors of the asset')
34
40
  custom_fields: list[CustomField] = Field(
35
41
  default_factory=list, description='Defines the list of custom fields of the asset'
@@ -104,3 +110,8 @@ class Asset(BaseModel):
104
110
  def asset_type(self: Self) -> int | None:
105
111
  """Get asset type"""
106
112
  return self.kind_id
113
+
114
+ partition_number: int | None = Field(
115
+ default=None,
116
+ description='Partition number assigned for this Asset, if is None, will be auto-assigned by the system',
117
+ )
@@ -1,22 +1,23 @@
1
- """Asset constants"""
2
-
3
1
  from datetime import timedelta
4
2
 
5
- from pydantic import BaseModel, Field
3
+ from pydantic import BaseModel, ConfigDict, Field, field_serializer
6
4
 
7
5
 
8
6
  class AssetConstants(BaseModel):
9
7
  """Asset constants"""
10
8
 
11
- model_config = {
12
- 'json_encoders': {
13
- timedelta: lambda v: v.total_seconds(),
14
- },
15
- }
16
-
9
+ model_config = ConfigDict(
10
+ validate_by_name=False,
11
+ validate_by_alias=True,
12
+ serialize_by_alias=True,
13
+ )
17
14
  distance_traveled: float = Field(default=0.0, description='Total distance traveled by the asset in meters')
18
15
  primary_device: str = Field(default='N/A', description='Primary device associated with the asset')
19
16
  elapsed_time: timedelta = Field(
20
17
  default=timedelta(seconds=0),
21
18
  description='Total elapsed time for the asset in seconds',
22
19
  )
20
+
21
+ @field_serializer('elapsed_time', when_used='always')
22
+ def serialize_elapsed_time(self, elapsed_time: timedelta) -> float:
23
+ return elapsed_time.total_seconds()
@@ -1,11 +1,15 @@
1
- """Asset contact information"""
2
-
3
- from pydantic import BaseModel, Field
1
+ from pydantic import BaseModel, ConfigDict, Field
4
2
 
5
3
 
6
4
  class AssetContact(BaseModel):
7
5
  """Asset contact information"""
8
6
 
7
+ model_config = ConfigDict(
8
+ validate_by_name=False,
9
+ validate_by_alias=True,
10
+ serialize_by_alias=True,
11
+ )
12
+
9
13
  name: str = Field(default='', description='Name of the contact person for the asset')
10
14
  phone: str = Field(default='', description='Phone number of the contact person for the asset')
11
15
  email: str = Field(default='', description='Email address of the contact person for the asset')
@@ -1,5 +1,3 @@
1
- """Asset Operation Mode"""
2
-
3
1
  from enum import StrEnum
4
2
  from typing import Self
5
3
 
@@ -1,12 +1,19 @@
1
- """Entry entity"""
2
-
3
- from pydantic import BaseModel, Field
1
+ from pydantic import BaseModel, ConfigDict, Field
4
2
 
5
3
 
6
4
  class AtsEntry(BaseModel):
7
5
  """Entry entity"""
8
6
 
9
- pk: int = Field(description='Defines the primary key of the Function', 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 Function',
15
+ alias='id',
16
+ )
10
17
  old_tank_level: float = Field(description='Old tank level in liters', default=0.0)
11
18
  new_tank_level: float = Field(description='New tank level in liters', default=0.0)
12
19
  density: float | None = Field(description='Density of the fuel in kg/m3', default=None)
@@ -1,30 +1,24 @@
1
- """Exit Execution History"""
2
-
3
1
  from datetime import datetime
4
2
  from typing import Literal
5
3
 
6
- from pydantic import (
7
- BaseModel,
8
- ConfigDict,
9
- Field,
10
- )
4
+ from pydantic import BaseModel, ConfigDict, Field, field_serializer
11
5
 
12
6
 
13
7
  class AtsExitExecutionHistory(BaseModel):
14
- model_config = {
15
- 'json_encoders': {
16
- datetime: lambda v: v.timestamp(),
17
- }
18
- }
19
- pk: int = Field(description='Primary key of the Exit Execution History', alias='id')
20
-
21
- from_asset_id: int = Field(
22
- description='ID of the asset from which the exit is initiated',
8
+ model_config = ConfigDict(
9
+ validate_by_name=False,
10
+ validate_by_alias=True,
11
+ serialize_by_alias=True,
12
+ from_attributes=True,
23
13
  )
24
- to_asset_id: int = Field(
25
- description='ID of the asset to which the exit is directed',
14
+ pk: int = Field(
15
+ description='Primary key of the Exit Execution History',
16
+ alias='id',
26
17
  )
27
18
 
19
+ from_asset_id: int = Field(description='ID of the asset from which the exit is initiated')
20
+ to_asset_id: int = Field(description='ID of the asset to which the exit is directed')
21
+
28
22
  status: Literal['PENDING', 'FAILED', 'SUCCESS'] = Field(default='PENDING')
29
23
 
30
24
  from_app: Literal['ATSWEB', 'ATSMOBILE', 'NFC'] | None = Field(
@@ -38,5 +32,13 @@ class AtsExitExecutionHistory(BaseModel):
38
32
  to_asset_mileage: float | None = Field(default=None, description='Mileage of the asset to which the exit is directed')
39
33
 
40
34
  created_at: datetime = Field(description='Timestamp when the exit was created')
35
+
36
+ @field_serializer('created_at', when_used='always')
37
+ def serialize_created_at(self, created_at: datetime) -> float:
38
+ return created_at.timestamp()
39
+
41
40
  updated_at: datetime = Field(description='Timestamp when the exit was last updated')
42
- model_config = ConfigDict(from_attributes=True)
41
+
42
+ @field_serializer('updated_at', when_used='always')
43
+ def serialize_updated_at(self, updated_at: datetime) -> float:
44
+ return updated_at.timestamp()
@@ -0,0 +1,87 @@
1
+ """Entry entity"""
2
+
3
+ from datetime import datetime
4
+ from enum import StrEnum
5
+
6
+ from pydantic import BaseModel, ConfigDict, Field, field_serializer
7
+
8
+ from .ats_purchaseorder import AtsPurchaseOrder, DeliveryCategories, OrderCategories, OrderStatus
9
+
10
+
11
+ class AtsOperationMovement(BaseModel):
12
+ """Ats operation movement entity"""
13
+
14
+ model_config = ConfigDict(
15
+ validate_by_name=False,
16
+ validate_by_alias=True,
17
+ serialize_by_alias=True,
18
+ )
19
+
20
+ pk: int | None = Field(
21
+ description='Defines the primary key of the Function',
22
+ default=None,
23
+ alias='id',
24
+ )
25
+ status: OrderStatus | None = Field(description='Current status of the order', default=None)
26
+
27
+ @field_serializer('status', when_used='always')
28
+ def serialize_status(self, status: OrderStatus | None) -> str | None:
29
+ return status.value if status else None
30
+
31
+ created_at: datetime | None = Field(description='Timestamp when the operation movement was created', default=None)
32
+
33
+ @field_serializer('created_at', when_used='always')
34
+ def serialize_created_at(self, created_at: datetime | None) -> float | None:
35
+ return created_at.timestamp() if created_at else None
36
+
37
+ asset_id: int | None = Field(description='ID of the asset', default=None)
38
+ operation_id: int | None = Field(description='ID of the operation', default=None)
39
+
40
+
41
+ class AtsOperation(BaseModel):
42
+ """Entry entity"""
43
+
44
+ model_config = ConfigDict(
45
+ validate_by_name=False,
46
+ validate_by_alias=True,
47
+ serialize_by_alias=True,
48
+ )
49
+
50
+ pk: int = Field(
51
+ description='Defines the primary key of the Function',
52
+ alias='id',
53
+ )
54
+ purchased_at: datetime = Field(description='Timestamp when the operation was purchased')
55
+
56
+ @field_serializer('purchased_at', when_used='always')
57
+ def serialize_purchased_at(self, purchased_at: datetime) -> float:
58
+ return purchased_at.timestamp()
59
+
60
+ order_status: OrderStatus = Field(..., description='Current status of the order')
61
+
62
+ @field_serializer('order_status', when_used='always')
63
+ def serialize_order_status(self, order_status: OrderStatus) -> str:
64
+ return order_status.value
65
+
66
+ category: OrderCategories = Field(..., description='Category of the operation')
67
+
68
+ @field_serializer('category', when_used='always')
69
+ def serialize_category(self, category: OrderCategories) -> str:
70
+ return category.value
71
+
72
+ deliver_category: DeliveryCategories = Field(..., description='Delivery category of the operation')
73
+
74
+ @field_serializer('deliver_category', when_used='always')
75
+ def serialize_deliver_category(self, deliver_category: DeliveryCategories) -> str:
76
+ return deliver_category.value
77
+
78
+ seller_asset_id: int = Field(description='ID of the seller asset')
79
+ transport_asset_id: int = Field(description='ID of the transport asset')
80
+ finished_at: datetime | None = Field(description='Timestamp when the operation was finished', default=None)
81
+
82
+ @field_serializer('finished_at', when_used='always')
83
+ def serialize_finished_at(self, finished_at: datetime | None) -> float | None:
84
+ return finished_at.timestamp() if finished_at else None
85
+
86
+ history: list[AtsOperationMovement] = Field(description='List of operation movements')
87
+ purchase_orders: list[AtsPurchaseOrder] = Field(description='List of purchase orders')
@@ -1,13 +1,17 @@
1
- """Entry entity"""
2
-
3
1
  from datetime import datetime
4
2
 
5
- from pydantic import BaseModel, Field
3
+ from pydantic import BaseModel, ConfigDict, Field
6
4
 
7
5
 
8
6
  class AtsPossibleEntry(BaseModel):
9
7
  """Entry entity"""
10
8
 
9
+ model_config = ConfigDict(
10
+ validate_by_name=False,
11
+ validate_by_alias=True,
12
+ serialize_by_alias=True,
13
+ )
14
+
11
15
  initial_tank_level: float = Field(description='Initial tank level in liters', default=0.0)
12
16
  tank_accumulator: float = Field(description='Tank accumulator in liters', default=0.0)
13
17
  is_ready: bool = Field(description='Indicates if the entry is ready', default=False)
@@ -20,10 +24,9 @@ class AtsPossibleEntry(BaseModel):
20
24
  is_recalculated: bool = Field(description='Indicates if the entry is recalculated', default=False)
21
25
  is_blackbox: bool = Field(description='Indicates if the entry is a black box', default=False)
22
26
  is_executed_by_command: bool | None = Field(
23
- description='Indicates if the entry is executed by command', default=False
27
+ description='Indicates if the entry is executed by command',
28
+ default=False,
24
29
  )
25
30
  is_ready_by_reception: bool | None = Field(description='Indicates if the entry is ready by reception', default=False)
26
31
  false_positive_count: int = Field(description='Count of false positives for the entry', default=0)
27
- reception_id: int | None = Field(
28
- description='Reception ID associated with the entry', default=None, alias='receptionId'
29
- )
32
+ reception_id: int | None = Field(description='Reception ID associated with the entry', default=None)
@@ -1,20 +1,21 @@
1
- """Ats Exit entity"""
2
-
3
1
  from datetime import datetime
4
2
 
5
- from pydantic import BaseModel, Field
3
+ from pydantic import BaseModel, ConfigDict, Field, field_serializer
6
4
 
7
5
 
8
6
  class AtsPossibleExit(BaseModel):
9
7
  """AtsPossibleExit entity"""
10
8
 
11
- model_config = {
12
- 'json_encoders': {
13
- datetime: lambda v: v.timestamp(),
14
- }
15
- }
9
+ model_config = ConfigDict(
10
+ validate_by_name=False,
11
+ validate_by_alias=True,
12
+ serialize_by_alias=True,
13
+ )
16
14
 
17
- pk: int = Field(description='Defines the primary key of the AtsPossibleExit', alias='id')
15
+ pk: int = Field(
16
+ description='Defines the primary key of the AtsPossibleExit',
17
+ alias='id',
18
+ )
18
19
 
19
20
  identifier: int | None = Field(
20
21
  default=None,
@@ -54,11 +55,20 @@ class AtsPossibleExit(BaseModel):
54
55
  default_factory=datetime.now,
55
56
  description='Timestamp when the exit started',
56
57
  )
58
+
59
+ @field_serializer('start_at', when_used='always')
60
+ def serialize_start_at(self, start_at: datetime) -> float:
61
+ return start_at.timestamp()
62
+
57
63
  end_at: datetime | None = Field(
58
64
  default=None,
59
65
  description='Timestamp when the exit ended',
60
66
  )
61
67
 
68
+ @field_serializer('end_at', when_used='always')
69
+ def serialize_end_at(self, end_at: datetime | None) -> float | None:
70
+ return end_at.timestamp() if end_at else None
71
+
62
72
  # Derived / bookkeeping flags
63
73
  is_recalculated: bool = Field(
64
74
  default=False,