fleetpull 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fleetpull/__init__.py +36 -0
- fleetpull/api/__init__.py +32 -0
- fleetpull/api/auth_ingress.py +216 -0
- fleetpull/api/catalog.py +139 -0
- fleetpull/api/fetch.py +229 -0
- fleetpull/api/identity.py +90 -0
- fleetpull/api/sync.py +735 -0
- fleetpull/cli.py +146 -0
- fleetpull/config/__init__.py +47 -0
- fleetpull/config/base.py +18 -0
- fleetpull/config/example.py +90 -0
- fleetpull/config/geotab.py +78 -0
- fleetpull/config/http.py +32 -0
- fleetpull/config/loading.py +195 -0
- fleetpull/config/logger.py +139 -0
- fleetpull/config/providers.py +520 -0
- fleetpull/config/rate_limit.py +50 -0
- fleetpull/config/resolution.py +156 -0
- fleetpull/config/retry.py +69 -0
- fleetpull/config/root.py +157 -0
- fleetpull/config/sections.py +146 -0
- fleetpull/endpoints/__init__.py +24 -0
- fleetpull/endpoints/geotab/__init__.py +12 -0
- fleetpull/endpoints/geotab/_requests.py +427 -0
- fleetpull/endpoints/geotab/annotation_logs.py +73 -0
- fleetpull/endpoints/geotab/audits.py +70 -0
- fleetpull/endpoints/geotab/devices.py +79 -0
- fleetpull/endpoints/geotab/driver_changes.py +73 -0
- fleetpull/endpoints/geotab/duty_status_logs.py +75 -0
- fleetpull/endpoints/geotab/dvir_logs.py +76 -0
- fleetpull/endpoints/geotab/exception_events.py +119 -0
- fleetpull/endpoints/geotab/fault_data.py +72 -0
- fleetpull/endpoints/geotab/fill_ups.py +79 -0
- fleetpull/endpoints/geotab/fuel_and_energy_used.py +74 -0
- fleetpull/endpoints/geotab/fuel_tax_details.py +75 -0
- fleetpull/endpoints/geotab/log_records.py +74 -0
- fleetpull/endpoints/geotab/media_files.py +74 -0
- fleetpull/endpoints/geotab/shipment_logs.py +71 -0
- fleetpull/endpoints/geotab/status_data.py +71 -0
- fleetpull/endpoints/geotab/text_messages.py +77 -0
- fleetpull/endpoints/geotab/trips.py +102 -0
- fleetpull/endpoints/geotab/users.py +81 -0
- fleetpull/endpoints/motive/__init__.py +12 -0
- fleetpull/endpoints/motive/_spec_builders.py +94 -0
- fleetpull/endpoints/motive/driver_idle_rollups.py +113 -0
- fleetpull/endpoints/motive/driving_periods.py +85 -0
- fleetpull/endpoints/motive/groups.py +61 -0
- fleetpull/endpoints/motive/idle_events.py +94 -0
- fleetpull/endpoints/motive/users.py +64 -0
- fleetpull/endpoints/motive/vehicle_locations.py +170 -0
- fleetpull/endpoints/motive/vehicle_utilizations.py +122 -0
- fleetpull/endpoints/motive/vehicles.py +106 -0
- fleetpull/endpoints/registry.py +280 -0
- fleetpull/endpoints/samsara/__init__.py +12 -0
- fleetpull/endpoints/samsara/_spec_builders.py +202 -0
- fleetpull/endpoints/samsara/addresses.py +77 -0
- fleetpull/endpoints/samsara/asset_locations.py +217 -0
- fleetpull/endpoints/samsara/driver_fuel_energy_reports.py +124 -0
- fleetpull/endpoints/samsara/driver_vehicle_assignments.py +211 -0
- fleetpull/endpoints/samsara/drivers.py +169 -0
- fleetpull/endpoints/samsara/engine_states.py +114 -0
- fleetpull/endpoints/samsara/gps_readings.py +113 -0
- fleetpull/endpoints/samsara/idling_events.py +195 -0
- fleetpull/endpoints/samsara/odometer_readings.py +116 -0
- fleetpull/endpoints/samsara/trips.py +217 -0
- fleetpull/endpoints/samsara/vehicle_fuel_energy_reports.py +139 -0
- fleetpull/endpoints/samsara/vehicles.py +121 -0
- fleetpull/endpoints/shared/__init__.py +57 -0
- fleetpull/endpoints/shared/base.py +529 -0
- fleetpull/endpoints/shared/request_shape.py +227 -0
- fleetpull/endpoints/shared/resume.py +74 -0
- fleetpull/endpoints/shared/spec_builders.py +59 -0
- fleetpull/endpoints/shared/sync_mode.py +159 -0
- fleetpull/endpoints/shared/url_paths.py +149 -0
- fleetpull/exceptions.py +320 -0
- fleetpull/incremental/__init__.py +27 -0
- fleetpull/incremental/cursor.py +66 -0
- fleetpull/incremental/resolution.py +152 -0
- fleetpull/incremental/seed.py +53 -0
- fleetpull/incremental/window.py +94 -0
- fleetpull/logger/__init__.py +5 -0
- fleetpull/logger/setup.py +145 -0
- fleetpull/model_contract/__init__.py +6 -0
- fleetpull/model_contract/coercions.py +33 -0
- fleetpull/model_contract/response.py +45 -0
- fleetpull/models/__init__.py +7 -0
- fleetpull/models/geotab/__init__.py +166 -0
- fleetpull/models/geotab/annotation_log.py +109 -0
- fleetpull/models/geotab/audit.py +56 -0
- fleetpull/models/geotab/device.py +217 -0
- fleetpull/models/geotab/driver_change.py +102 -0
- fleetpull/models/geotab/duty_status_log.py +200 -0
- fleetpull/models/geotab/dvir_log.py +184 -0
- fleetpull/models/geotab/exception_event.py +135 -0
- fleetpull/models/geotab/fault_data.py +168 -0
- fleetpull/models/geotab/fill_up.py +189 -0
- fleetpull/models/geotab/fuel_and_energy_used.py +81 -0
- fleetpull/models/geotab/fuel_tax_detail.py +150 -0
- fleetpull/models/geotab/log_record.py +68 -0
- fleetpull/models/geotab/media_file.py +133 -0
- fleetpull/models/geotab/shared.py +221 -0
- fleetpull/models/geotab/shipment_log.py +105 -0
- fleetpull/models/geotab/status_data.py +108 -0
- fleetpull/models/geotab/text_message.py +125 -0
- fleetpull/models/geotab/trip.py +162 -0
- fleetpull/models/geotab/user.py +187 -0
- fleetpull/models/motive/__init__.py +43 -0
- fleetpull/models/motive/driver_idle_rollup.py +109 -0
- fleetpull/models/motive/driving_period.py +82 -0
- fleetpull/models/motive/group.py +49 -0
- fleetpull/models/motive/idle_event.py +77 -0
- fleetpull/models/motive/shared.py +192 -0
- fleetpull/models/motive/user.py +217 -0
- fleetpull/models/motive/vehicle.py +162 -0
- fleetpull/models/motive/vehicle_location.py +127 -0
- fleetpull/models/motive/vehicle_utilization.py +128 -0
- fleetpull/models/samsara/__init__.py +108 -0
- fleetpull/models/samsara/address.py +149 -0
- fleetpull/models/samsara/asset_location.py +131 -0
- fleetpull/models/samsara/driver.py +232 -0
- fleetpull/models/samsara/driver_fuel_energy_report.py +153 -0
- fleetpull/models/samsara/driver_vehicle_assignment.py +168 -0
- fleetpull/models/samsara/engine_state.py +92 -0
- fleetpull/models/samsara/gps_reading.py +146 -0
- fleetpull/models/samsara/idling_event.py +174 -0
- fleetpull/models/samsara/odometer_reading.py +90 -0
- fleetpull/models/samsara/trip.py +199 -0
- fleetpull/models/samsara/vehicle.py +167 -0
- fleetpull/models/samsara/vehicle_fuel_energy_report.py +191 -0
- fleetpull/network/__init__.py +9 -0
- fleetpull/network/auth/__init__.py +15 -0
- fleetpull/network/auth/authenticate.py +271 -0
- fleetpull/network/auth/manager.py +223 -0
- fleetpull/network/auth/models.py +57 -0
- fleetpull/network/auth/strategies.py +169 -0
- fleetpull/network/classifiers/__init__.py +11 -0
- fleetpull/network/classifiers/geotab.py +145 -0
- fleetpull/network/classifiers/motive.py +79 -0
- fleetpull/network/classifiers/samsara.py +80 -0
- fleetpull/network/client/__init__.py +25 -0
- fleetpull/network/client/page.py +32 -0
- fleetpull/network/client/profile.py +27 -0
- fleetpull/network/client/registry.py +103 -0
- fleetpull/network/client/registry_base.py +150 -0
- fleetpull/network/client/runtime.py +44 -0
- fleetpull/network/client/transport.py +381 -0
- fleetpull/network/contract/__init__.py +50 -0
- fleetpull/network/contract/auth.py +48 -0
- fleetpull/network/contract/classifier.py +189 -0
- fleetpull/network/contract/envelope_fetcher.py +33 -0
- fleetpull/network/contract/envelopes.py +189 -0
- fleetpull/network/contract/outcome.py +43 -0
- fleetpull/network/contract/page_decoder.py +101 -0
- fleetpull/network/contract/request.py +97 -0
- fleetpull/network/decoders/__init__.py +40 -0
- fleetpull/network/decoders/_window_stamp.py +78 -0
- fleetpull/network/decoders/geotab.py +309 -0
- fleetpull/network/decoders/motive.py +204 -0
- fleetpull/network/decoders/motive_reports.py +114 -0
- fleetpull/network/decoders/samsara.py +343 -0
- fleetpull/network/decoders/samsara_reports.py +119 -0
- fleetpull/network/decoders/single_page.py +55 -0
- fleetpull/network/limits/__init__.py +13 -0
- fleetpull/network/limits/bucket_math.py +58 -0
- fleetpull/network/limits/limiter.py +153 -0
- fleetpull/network/limits/registry.py +96 -0
- fleetpull/network/posture/__init__.py +6 -0
- fleetpull/network/posture/client_options.py +96 -0
- fleetpull/network/retry/__init__.py +13 -0
- fleetpull/network/retry/decision.py +155 -0
- fleetpull/network/tls/__init__.py +5 -0
- fleetpull/network/tls/truststore_context.py +35 -0
- fleetpull/orchestrator/__init__.py +30 -0
- fleetpull/orchestrator/backfill.py +127 -0
- fleetpull/orchestrator/batch.py +155 -0
- fleetpull/orchestrator/bisection.py +271 -0
- fleetpull/orchestrator/drivers.py +350 -0
- fleetpull/orchestrator/entry.py +347 -0
- fleetpull/orchestrator/executors.py +128 -0
- fleetpull/orchestrator/fanout.py +163 -0
- fleetpull/orchestrator/feed_drive.py +266 -0
- fleetpull/orchestrator/metadata_projection.py +195 -0
- fleetpull/orchestrator/outcome.py +53 -0
- fleetpull/orchestrator/recording.py +85 -0
- fleetpull/orchestrator/resume.py +143 -0
- fleetpull/orchestrator/roster_harvest.py +108 -0
- fleetpull/orchestrator/roster_refresh.py +363 -0
- fleetpull/orchestrator/runner.py +262 -0
- fleetpull/orchestrator/shape_resolution.py +221 -0
- fleetpull/orchestrator/spine.py +166 -0
- fleetpull/orchestrator/streaming.py +153 -0
- fleetpull/orchestrator/unit_loop.py +273 -0
- fleetpull/orchestrator/watermark_drive.py +455 -0
- fleetpull/paths/__init__.py +13 -0
- fleetpull/paths/datasets.py +38 -0
- fleetpull/paths/partitions.py +41 -0
- fleetpull/paths/resolution.py +98 -0
- fleetpull/polars_typing/__init__.py +20 -0
- fleetpull/py.typed +0 -0
- fleetpull/records/__init__.py +19 -0
- fleetpull/records/convert.py +53 -0
- fleetpull/records/dataframe.py +63 -0
- fleetpull/records/event_time.py +70 -0
- fleetpull/records/fields.py +190 -0
- fleetpull/records/flatten.py +54 -0
- fleetpull/records/roster_members.py +74 -0
- fleetpull/records/schema.py +73 -0
- fleetpull/records/validation.py +62 -0
- fleetpull/resources/__init__.py +10 -0
- fleetpull/resources/config.example.yaml +222 -0
- fleetpull/roster/__init__.py +16 -0
- fleetpull/roster/definition.py +46 -0
- fleetpull/roster/key.py +40 -0
- fleetpull/roster/registry.py +93 -0
- fleetpull/state/__init__.py +36 -0
- fleetpull/state/cursors.py +417 -0
- fleetpull/state/database.py +463 -0
- fleetpull/state/migrations.py +430 -0
- fleetpull/state/reconcile.py +127 -0
- fleetpull/state/rosters.py +159 -0
- fleetpull/state/run_ledger.py +572 -0
- fleetpull/state/work_units.py +648 -0
- fleetpull/storage/__init__.py +30 -0
- fleetpull/storage/append.py +187 -0
- fleetpull/storage/atomic.py +161 -0
- fleetpull/storage/files.py +176 -0
- fleetpull/storage/frames.py +98 -0
- fleetpull/storage/metadata.py +161 -0
- fleetpull/storage/partitioned.py +205 -0
- fleetpull/storage/pruning.py +169 -0
- fleetpull/storage/read.py +35 -0
- fleetpull/storage/result.py +34 -0
- fleetpull/storage/single_file.py +130 -0
- fleetpull/storage/splitting.py +88 -0
- fleetpull/storage/staging.py +178 -0
- fleetpull/storage/writers.py +145 -0
- fleetpull/timing/__init__.py +21 -0
- fleetpull/timing/canon.py +92 -0
- fleetpull/timing/clock.py +192 -0
- fleetpull/timing/codec.py +122 -0
- fleetpull/timing/sleeper.py +65 -0
- fleetpull/vocabulary/__init__.py +16 -0
- fleetpull/vocabulary/json_types.py +20 -0
- fleetpull/vocabulary/provider.py +29 -0
- fleetpull/vocabulary/quota_scope.py +47 -0
- fleetpull/vocabulary/response_category.py +32 -0
- fleetpull-0.1.0.dist-info/METADATA +248 -0
- fleetpull-0.1.0.dist-info/RECORD +252 -0
- fleetpull-0.1.0.dist-info/WHEEL +5 -0
- fleetpull-0.1.0.dist-info/entry_points.txt +2 -0
- fleetpull-0.1.0.dist-info/licenses/LICENSE +202 -0
- fleetpull-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# src/fleetpull/models/samsara/__init__.py
|
|
2
|
+
"""Samsara response models; the face re-exports each endpoint module's models."""
|
|
3
|
+
|
|
4
|
+
from fleetpull.models.samsara.address import (
|
|
5
|
+
Address,
|
|
6
|
+
AddressGeofence,
|
|
7
|
+
AddressGeofenceCircle,
|
|
8
|
+
AddressGeofenceSettings,
|
|
9
|
+
)
|
|
10
|
+
from fleetpull.models.samsara.asset_location import (
|
|
11
|
+
AssetLocation,
|
|
12
|
+
AssetLocationAssetRef,
|
|
13
|
+
AssetLocationFix,
|
|
14
|
+
)
|
|
15
|
+
from fleetpull.models.samsara.driver import (
|
|
16
|
+
Driver,
|
|
17
|
+
DriverActivationStatus,
|
|
18
|
+
DriverCarrierSettings,
|
|
19
|
+
DriverHosSetting,
|
|
20
|
+
DriverStaticAssignedVehicleRef,
|
|
21
|
+
DriverTagRef,
|
|
22
|
+
)
|
|
23
|
+
from fleetpull.models.samsara.driver_fuel_energy_report import (
|
|
24
|
+
DriverFuelEnergyCost,
|
|
25
|
+
DriverFuelEnergyDriverRef,
|
|
26
|
+
DriverFuelEnergyReport,
|
|
27
|
+
)
|
|
28
|
+
from fleetpull.models.samsara.driver_vehicle_assignment import (
|
|
29
|
+
AssignmentDriverRef,
|
|
30
|
+
AssignmentVehicleExternalIds,
|
|
31
|
+
AssignmentVehicleRef,
|
|
32
|
+
DriverVehicleAssignment,
|
|
33
|
+
)
|
|
34
|
+
from fleetpull.models.samsara.engine_state import EngineState
|
|
35
|
+
from fleetpull.models.samsara.gps_reading import (
|
|
36
|
+
GpsReading,
|
|
37
|
+
GpsReadingAddressRef,
|
|
38
|
+
GpsReadingReverseGeo,
|
|
39
|
+
)
|
|
40
|
+
from fleetpull.models.samsara.idling_event import (
|
|
41
|
+
AssetRef,
|
|
42
|
+
FuelCost,
|
|
43
|
+
IdlingAddress,
|
|
44
|
+
IdlingEvent,
|
|
45
|
+
OperatorRef,
|
|
46
|
+
)
|
|
47
|
+
from fleetpull.models.samsara.odometer_reading import OdometerReading
|
|
48
|
+
from fleetpull.models.samsara.trip import (
|
|
49
|
+
Trip,
|
|
50
|
+
TripAddress,
|
|
51
|
+
TripCoordinates,
|
|
52
|
+
)
|
|
53
|
+
from fleetpull.models.samsara.vehicle import (
|
|
54
|
+
Vehicle,
|
|
55
|
+
VehicleExternalIds,
|
|
56
|
+
VehicleGatewayRef,
|
|
57
|
+
VehicleStaticAssignedDriverRef,
|
|
58
|
+
)
|
|
59
|
+
from fleetpull.models.samsara.vehicle_fuel_energy_report import (
|
|
60
|
+
VehicleFuelEnergyCost,
|
|
61
|
+
VehicleFuelEnergyExternalIds,
|
|
62
|
+
VehicleFuelEnergyReport,
|
|
63
|
+
VehicleFuelEnergyVehicleRef,
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
__all__: list[str] = [
|
|
67
|
+
'Address',
|
|
68
|
+
'AddressGeofence',
|
|
69
|
+
'AddressGeofenceCircle',
|
|
70
|
+
'AddressGeofenceSettings',
|
|
71
|
+
'AssetLocation',
|
|
72
|
+
'AssetLocationAssetRef',
|
|
73
|
+
'AssetLocationFix',
|
|
74
|
+
'AssetRef',
|
|
75
|
+
'AssignmentDriverRef',
|
|
76
|
+
'AssignmentVehicleExternalIds',
|
|
77
|
+
'AssignmentVehicleRef',
|
|
78
|
+
'Driver',
|
|
79
|
+
'DriverActivationStatus',
|
|
80
|
+
'DriverCarrierSettings',
|
|
81
|
+
'DriverFuelEnergyCost',
|
|
82
|
+
'DriverFuelEnergyDriverRef',
|
|
83
|
+
'DriverFuelEnergyReport',
|
|
84
|
+
'DriverHosSetting',
|
|
85
|
+
'DriverStaticAssignedVehicleRef',
|
|
86
|
+
'DriverTagRef',
|
|
87
|
+
'DriverVehicleAssignment',
|
|
88
|
+
'EngineState',
|
|
89
|
+
'FuelCost',
|
|
90
|
+
'GpsReading',
|
|
91
|
+
'GpsReadingAddressRef',
|
|
92
|
+
'GpsReadingReverseGeo',
|
|
93
|
+
'IdlingAddress',
|
|
94
|
+
'IdlingEvent',
|
|
95
|
+
'OdometerReading',
|
|
96
|
+
'OperatorRef',
|
|
97
|
+
'Trip',
|
|
98
|
+
'TripAddress',
|
|
99
|
+
'TripCoordinates',
|
|
100
|
+
'Vehicle',
|
|
101
|
+
'VehicleExternalIds',
|
|
102
|
+
'VehicleFuelEnergyCost',
|
|
103
|
+
'VehicleFuelEnergyExternalIds',
|
|
104
|
+
'VehicleFuelEnergyReport',
|
|
105
|
+
'VehicleFuelEnergyVehicleRef',
|
|
106
|
+
'VehicleGatewayRef',
|
|
107
|
+
'VehicleStaticAssignedDriverRef',
|
|
108
|
+
]
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
# src/fleetpull/models/samsara/address.py
|
|
2
|
+
"""Samsara Address response model (``GET /addresses``).
|
|
3
|
+
|
|
4
|
+
Written from captured live responses (2026-07-20 probe session: a
|
|
5
|
+
full-population walk -- one page, all 25 records), never from docs. The
|
|
6
|
+
walk observed no null value anywhere -- Samsara omits absent keys
|
|
7
|
+
rather than nulling them (the vehicles posture). Because the walk was
|
|
8
|
+
the whole population, the vehicles optionality posture applies: the
|
|
9
|
+
seven 25/25 keys (``id``, ``name``, ``createdAtTime``,
|
|
10
|
+
``formattedAddress``, ``latitude``, ``longitude``, ``geofence``) are
|
|
11
|
+
required; ``addressTypes`` (20/25) is optional. ``createdAtTime`` is
|
|
12
|
+
UTC ISO-8601 with milliseconds, recovered as a tz-aware datetime by
|
|
13
|
+
Pydantic's standard parse.
|
|
14
|
+
|
|
15
|
+
Within the required ``geofence`` block (presence out of 25 blocks):
|
|
16
|
+
``circle`` 1/25 (``{latitude, longitude, radiusMeters}``, all three in
|
|
17
|
+
the one carrying block), ``polygon`` 24/25, ``settings`` 13/25
|
|
18
|
+
(``{showAddresses}``, present in every carrying block). ``circle`` and
|
|
19
|
+
``polygon`` were mutually exclusive in capture (1 vs 24, never both) --
|
|
20
|
+
both are mirrored as independent optionals with NO XOR enforcement
|
|
21
|
+
(mirror, never interpret).
|
|
22
|
+
|
|
23
|
+
Excluded fields (``extra='ignore'`` makes exclusion exactly "don't
|
|
24
|
+
model it"):
|
|
25
|
+
|
|
26
|
+
- ``tags`` -- a list of tag objects (9/25); the records layer's schema
|
|
27
|
+
derivation supports scalars, enums, ``list[scalar]``, and nested
|
|
28
|
+
models only (the GeoTab Device/User exclusion precedent, same as the
|
|
29
|
+
vehicles/drivers models) -- modeled when the list-of-structs
|
|
30
|
+
derivation vertical lands.
|
|
31
|
+
- ``geofence.polygon`` -- excluded WHOLESALE (24/25): its ONLY key is
|
|
32
|
+
``vertices``, a list of ``{latitude, longitude}`` objects, so the
|
|
33
|
+
same exclusion precedent applies one level down and an empty polygon
|
|
34
|
+
model would mirror nothing. The top-level ``latitude``/``longitude``
|
|
35
|
+
still carry the address's center point on every record, so a
|
|
36
|
+
polygon-fenced address keeps its location while the boundary awaits
|
|
37
|
+
the list-of-structs vertical.
|
|
38
|
+
|
|
39
|
+
Wire keys are camelCase; fields are snake_case via the ``to_camel``
|
|
40
|
+
alias generator.
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
from datetime import datetime
|
|
44
|
+
|
|
45
|
+
from pydantic import ConfigDict
|
|
46
|
+
from pydantic.alias_generators import to_camel
|
|
47
|
+
|
|
48
|
+
from fleetpull.model_contract import ResponseModel
|
|
49
|
+
|
|
50
|
+
__all__: list[str] = [
|
|
51
|
+
'Address',
|
|
52
|
+
'AddressGeofence',
|
|
53
|
+
'AddressGeofenceCircle',
|
|
54
|
+
'AddressGeofenceSettings',
|
|
55
|
+
]
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class AddressGeofenceCircle(ResponseModel):
|
|
59
|
+
"""The ``geofence.circle`` block: a circular boundary (1/25).
|
|
60
|
+
|
|
61
|
+
All three keys were present in the one carrying block. Mutually
|
|
62
|
+
exclusive with ``polygon`` in capture, mirrored without enforcement
|
|
63
|
+
(module docstring).
|
|
64
|
+
|
|
65
|
+
Attributes:
|
|
66
|
+
latitude: The circle center's latitude, decimal degrees.
|
|
67
|
+
longitude: The circle center's longitude, decimal degrees.
|
|
68
|
+
radius_meters: The circle's radius in meters, a bare int.
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
model_config = ConfigDict(alias_generator=to_camel)
|
|
72
|
+
|
|
73
|
+
latitude: float
|
|
74
|
+
longitude: float
|
|
75
|
+
radius_meters: int
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class AddressGeofenceSettings(ResponseModel):
|
|
79
|
+
"""The ``geofence.settings`` block: geofence display settings (13/25).
|
|
80
|
+
|
|
81
|
+
Attributes:
|
|
82
|
+
show_addresses: Whether the geofence displays addresses --
|
|
83
|
+
present in every carrying block.
|
|
84
|
+
"""
|
|
85
|
+
|
|
86
|
+
model_config = ConfigDict(alias_generator=to_camel)
|
|
87
|
+
|
|
88
|
+
show_addresses: bool
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class AddressGeofence(ResponseModel):
|
|
92
|
+
"""The ``geofence`` block: boundary and settings (present on all 25).
|
|
93
|
+
|
|
94
|
+
Models ``circle`` and ``settings`` only -- ``polygon`` is excluded
|
|
95
|
+
wholesale because its only content is a list-of-objects vertex list
|
|
96
|
+
(module docstring), so a polygon-fenced address validates to a
|
|
97
|
+
geofence with both modeled fields ``None`` while its center point
|
|
98
|
+
survives on the parent's ``latitude``/``longitude``.
|
|
99
|
+
|
|
100
|
+
Attributes:
|
|
101
|
+
circle: The circular boundary (1/25; mutually exclusive with
|
|
102
|
+
the unmodeled ``polygon`` in capture, not enforced).
|
|
103
|
+
settings: The display-settings block (13/25).
|
|
104
|
+
"""
|
|
105
|
+
|
|
106
|
+
circle: AddressGeofenceCircle | None = None
|
|
107
|
+
settings: AddressGeofenceSettings | None = None
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class Address(ResponseModel):
|
|
111
|
+
"""One Samsara defined address (a named location with a geofence).
|
|
112
|
+
|
|
113
|
+
A pure mirror of the captured fields (``tags`` and
|
|
114
|
+
``geofence.polygon`` excluded, module docstring). Field semantics
|
|
115
|
+
are Samsara's; no value is derived or interpreted here. The walk
|
|
116
|
+
was the whole 25-record population, so the always-present keys are
|
|
117
|
+
required (the vehicles posture).
|
|
118
|
+
|
|
119
|
+
Attributes:
|
|
120
|
+
id: Samsara's address id -- a string, mirrored as string.
|
|
121
|
+
name: The address's display name.
|
|
122
|
+
created_at_time: Record creation (UTC, millisecond ISO-8601).
|
|
123
|
+
formatted_address: The full street address, one formatted
|
|
124
|
+
string.
|
|
125
|
+
latitude: The address's center-point latitude, decimal degrees
|
|
126
|
+
-- carried on every record, polygon-fenced ones included.
|
|
127
|
+
longitude: The address's center-point longitude, decimal
|
|
128
|
+
degrees.
|
|
129
|
+
geofence: The geofence block (circle/settings modeled; polygon
|
|
130
|
+
excluded).
|
|
131
|
+
address_types: The address's type tags, a list of strings
|
|
132
|
+
(20/25).
|
|
133
|
+
"""
|
|
134
|
+
|
|
135
|
+
model_config = ConfigDict(alias_generator=to_camel)
|
|
136
|
+
|
|
137
|
+
# Identity and lifecycle.
|
|
138
|
+
id: str
|
|
139
|
+
name: str
|
|
140
|
+
created_at_time: datetime
|
|
141
|
+
|
|
142
|
+
# Location.
|
|
143
|
+
formatted_address: str
|
|
144
|
+
latitude: float
|
|
145
|
+
longitude: float
|
|
146
|
+
|
|
147
|
+
# Boundary and classification.
|
|
148
|
+
geofence: AddressGeofence
|
|
149
|
+
address_types: list[str] | None = None
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# src/fleetpull/models/samsara/asset_location.py
|
|
2
|
+
"""Samsara AssetLocation response model
|
|
3
|
+
(``GET /assets/location-and-speed/stream``).
|
|
4
|
+
|
|
5
|
+
Written from captured live responses (2026-07-20 probe session: a
|
|
6
|
+
454-record page census with every nested block censused over 300
|
|
7
|
+
records; a 50-id one-hour walk of 2 pages / 701 records), never from
|
|
8
|
+
docs. The legacy hub called this surface ``location_stream``; the
|
|
9
|
+
catalog name is ``asset_locations`` per the name=plural-of-entity
|
|
10
|
+
invariant -- the stored entity is the asset-location reading, one row
|
|
11
|
+
per fix.
|
|
12
|
+
|
|
13
|
+
The record census (454/454 unless noted): ``happenedAtTime`` (RFC3339
|
|
14
|
+
str, recovered tz-aware UTC -- the event-time column; readings fall
|
|
15
|
+
strictly inside the requested half-open ``[startTime, endTime)``
|
|
16
|
+
window, probe-proven on a 12:00-13:00Z window returning min 12:00:03Z /
|
|
17
|
+
max 12:59:56Z), ``asset`` (an object whose ONLY observed key is ``id``,
|
|
18
|
+
a STRING on the wire, 300/300 -- note the contrast with idling_events'
|
|
19
|
+
bare-int ``asset.id``: per-endpoint wire truth, mirrored per endpoint),
|
|
20
|
+
and ``location`` (300/300 census within the block: ``accuracyMeters``
|
|
21
|
+
int on every censused record but FLOAT on the live walk -- the
|
|
22
|
+
2026-07-20 full-day live proof failed validation on a float value at
|
|
23
|
+
record 351, so the field is float, the census sample proven narrower
|
|
24
|
+
than the wire; ``headingDegrees`` int, ``latitude`` float,
|
|
25
|
+
``longitude`` float).
|
|
26
|
+
|
|
27
|
+
Requiredness posture: 300/300 on a 454-record page is NOT a
|
|
28
|
+
whole-population oath (the drivers conservative posture would leave
|
|
29
|
+
everything optional), but the location core is required anyway by
|
|
30
|
+
structural judgment -- a location record without coordinates mirrors
|
|
31
|
+
nothing and is structurally meaningless, so a future record omitting
|
|
32
|
+
them should fail loudly rather than land an all-null coordinate row.
|
|
33
|
+
The same judgment covers ``asset``/``asset.id`` (an unattributed
|
|
34
|
+
reading cannot be a reading of anything) and ``happenedAtTime`` (the
|
|
35
|
+
event-time column the watermark routes on). This is the one deliberate
|
|
36
|
+
departure from the pure conservative posture, recorded here and in
|
|
37
|
+
DESIGN section 8.
|
|
38
|
+
|
|
39
|
+
Not modeled, with different provenance:
|
|
40
|
+
|
|
41
|
+
- ``location.geofence`` -- OBSERVED-EMPTY, not excluded: present
|
|
42
|
+
300/300 but an empty object with ZERO keys on every censused record,
|
|
43
|
+
so there is nothing to mirror (``extra='ignore'`` drops it). Revisit
|
|
44
|
+
on a capture showing content.
|
|
45
|
+
- Any speed key -- UNOBSERVED despite the surface's name
|
|
46
|
+
(``location-and-speed``): no speed key appeared anywhere in the
|
|
47
|
+
census. Unmodeled as unobserved (never excluded); revisit on a
|
|
48
|
+
capture that shows one.
|
|
49
|
+
|
|
50
|
+
Wire keys are camelCase; fields are snake_case via the ``to_camel``
|
|
51
|
+
alias generator.
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
from datetime import datetime
|
|
55
|
+
|
|
56
|
+
from pydantic import ConfigDict
|
|
57
|
+
from pydantic.alias_generators import to_camel
|
|
58
|
+
|
|
59
|
+
from fleetpull.model_contract import ResponseModel
|
|
60
|
+
|
|
61
|
+
__all__: list[str] = [
|
|
62
|
+
'AssetLocation',
|
|
63
|
+
'AssetLocationAssetRef',
|
|
64
|
+
'AssetLocationFix',
|
|
65
|
+
]
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class AssetLocationAssetRef(ResponseModel):
|
|
69
|
+
"""The ``asset`` block: the reading's asset reference.
|
|
70
|
+
|
|
71
|
+
Its ONLY observed key is ``id`` (300/300) -- a STRING on the wire,
|
|
72
|
+
unlike idling_events' bare-int ``asset.id`` (per-endpoint wire
|
|
73
|
+
truth, mirrored per endpoint).
|
|
74
|
+
|
|
75
|
+
Attributes:
|
|
76
|
+
id: Samsara's asset id -- a string, mirrored as string.
|
|
77
|
+
"""
|
|
78
|
+
|
|
79
|
+
id: str
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class AssetLocationFix(ResponseModel):
|
|
83
|
+
"""The ``location`` block: one position fix.
|
|
84
|
+
|
|
85
|
+
All four modeled keys were 300/300 in the block census and are
|
|
86
|
+
required by structural judgment (module docstring): a fix without
|
|
87
|
+
coordinates mirrors nothing. The block's ``geofence`` key is
|
|
88
|
+
observed-empty (an empty object on every censused record) and is
|
|
89
|
+
not modeled -- there is nothing to mirror.
|
|
90
|
+
|
|
91
|
+
Attributes:
|
|
92
|
+
accuracy_meters: The fix's reported accuracy in meters --
|
|
93
|
+
float: the census saw only bare ints (300/300), but the
|
|
94
|
+
live full-day walk carried floats (proven 2026-07-20, the
|
|
95
|
+
validation failure that widened this field).
|
|
96
|
+
heading_degrees: Heading in degrees, a bare int.
|
|
97
|
+
latitude: Fix latitude, decimal degrees.
|
|
98
|
+
longitude: Fix longitude, decimal degrees.
|
|
99
|
+
"""
|
|
100
|
+
|
|
101
|
+
model_config = ConfigDict(alias_generator=to_camel)
|
|
102
|
+
|
|
103
|
+
accuracy_meters: float
|
|
104
|
+
heading_degrees: int
|
|
105
|
+
latitude: float
|
|
106
|
+
longitude: float
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
class AssetLocation(ResponseModel):
|
|
110
|
+
"""One location reading of one asset, at the reading grain.
|
|
111
|
+
|
|
112
|
+
A pure mirror of the captured record (``location.geofence``
|
|
113
|
+
observed-empty and any speed key unobserved -- module docstring).
|
|
114
|
+
Field semantics and units are Samsara's; no value is derived or
|
|
115
|
+
interpreted here.
|
|
116
|
+
|
|
117
|
+
Attributes:
|
|
118
|
+
happened_at_time: The reading instant (RFC3339, recovered
|
|
119
|
+
tz-aware UTC) -- the event-time column; readings fall
|
|
120
|
+
strictly inside the requested window.
|
|
121
|
+
asset: The asset reference (string id) -- the per-record
|
|
122
|
+
attribution that makes the batched fan-out pure transport
|
|
123
|
+
packing.
|
|
124
|
+
location: The position fix block.
|
|
125
|
+
"""
|
|
126
|
+
|
|
127
|
+
model_config = ConfigDict(alias_generator=to_camel)
|
|
128
|
+
|
|
129
|
+
happened_at_time: datetime
|
|
130
|
+
asset: AssetLocationAssetRef
|
|
131
|
+
location: AssetLocationFix
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
# src/fleetpull/models/samsara/driver.py
|
|
2
|
+
"""Samsara Driver response model (``GET /fleet/drivers``).
|
|
3
|
+
|
|
4
|
+
Written from captured live responses (2026-07-20 probe session: the
|
|
5
|
+
full two-sweep census over all 832 records -- 460 active plus 372
|
|
6
|
+
deactivated, fully disjoint -- plus captured record variants), never
|
|
7
|
+
from docs. The census observed no null value anywhere -- Samsara omits
|
|
8
|
+
absent keys rather than nulling them (the vehicles posture), while
|
|
9
|
+
also using empty strings (``homeTerminalName`` / ``homeTerminalAddress``
|
|
10
|
+
are ``""`` on 204 and 268 of the 460 active records), which mirror
|
|
11
|
+
verbatim and normalize to null at the DataFrame boundary.
|
|
12
|
+
|
|
13
|
+
Optionality is conservative: only ``id`` is required. The per-key
|
|
14
|
+
presence counts were fully enumerated on the active sweep only (the
|
|
15
|
+
attribute docs cite them, out of 460); the deactivated sweep matched
|
|
16
|
+
structurally but was not per-key sworn, so unlike the vehicles model
|
|
17
|
+
(whose one sweep was the whole population) the always-present-in-capture
|
|
18
|
+
keys stay optional here rather than required.
|
|
19
|
+
|
|
20
|
+
``driverActivationStatus`` is a strict CLOSED enum, proven by the API
|
|
21
|
+
itself: any other value -- case variants, comma-joins, repeated keys,
|
|
22
|
+
bogus strings -- returns HTTP 400 naming the two admissible values
|
|
23
|
+
(captured 2026-07-20), so the two-member ``DriverActivationStatus``
|
|
24
|
+
mirror is closed by evidence, not assumption. ``dotNumber`` is a BARE
|
|
25
|
+
integer on the wire (not quoted). ``eldExemptReason`` is a free-text
|
|
26
|
+
reason string on the wire (282/460 presence, captured 2026-07-20).
|
|
27
|
+
|
|
28
|
+
Excluded fields (``extra='ignore'`` makes exclusion exactly "don't
|
|
29
|
+
model it"):
|
|
30
|
+
|
|
31
|
+
- ``tags`` -- a list of ``{id, name, parentTagId}`` objects (441/460);
|
|
32
|
+
the records layer's schema derivation supports scalars, enums,
|
|
33
|
+
``list[scalar]``, and nested models only (the GeoTab Device/User
|
|
34
|
+
exclusion precedent) -- modeled when the list-of-structs derivation
|
|
35
|
+
vertical lands.
|
|
36
|
+
- ``eldSettings`` -- ``{rulesets: [{break, cycle, restart, shift}]}``,
|
|
37
|
+
a list-of-objects block (190/460); same exclusion precedent.
|
|
38
|
+
|
|
39
|
+
``externalIds`` was NEVER observed on any of the 832 swept records --
|
|
40
|
+
deliberately not modeled (unobserved, not excluded); revisit only on a
|
|
41
|
+
capture that shows one, per the union-of-observed discipline.
|
|
42
|
+
|
|
43
|
+
Wire keys are camelCase; fields are snake_case via the ``to_camel``
|
|
44
|
+
alias generator.
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
from datetime import datetime
|
|
48
|
+
from enum import StrEnum
|
|
49
|
+
|
|
50
|
+
from pydantic import ConfigDict
|
|
51
|
+
from pydantic.alias_generators import to_camel
|
|
52
|
+
|
|
53
|
+
from fleetpull.model_contract import ResponseModel
|
|
54
|
+
|
|
55
|
+
__all__: list[str] = [
|
|
56
|
+
'Driver',
|
|
57
|
+
'DriverActivationStatus',
|
|
58
|
+
'DriverCarrierSettings',
|
|
59
|
+
'DriverHosSetting',
|
|
60
|
+
'DriverStaticAssignedVehicleRef',
|
|
61
|
+
'DriverTagRef',
|
|
62
|
+
]
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class DriverActivationStatus(StrEnum):
|
|
66
|
+
"""Activation status for a Samsara driver.
|
|
67
|
+
|
|
68
|
+
A closed mirror whose closure is API-enforced: every probed variant
|
|
69
|
+
outside these two values returned HTTP 400 naming exactly this
|
|
70
|
+
vocabulary (captured 2026-07-20), so the enum is evidence-closed.
|
|
71
|
+
Kept as an enum (not downgraded to ``str``) because the wire values
|
|
72
|
+
match the member values exactly -- membership validation for free on
|
|
73
|
+
a faithful mirror (the Motive ``VehicleStatus`` precedent).
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
ACTIVE = 'active'
|
|
77
|
+
DEACTIVATED = 'deactivated'
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class DriverCarrierSettings(ResponseModel):
|
|
81
|
+
"""The ``carrierSettings`` block: carrier identity and home terminal.
|
|
82
|
+
|
|
83
|
+
Present on every captured record, with all five keys present in
|
|
84
|
+
every carrying block; the home-terminal pair frequently carries
|
|
85
|
+
empty strings (204/460 and 268/460 active), mirrored verbatim.
|
|
86
|
+
|
|
87
|
+
Attributes:
|
|
88
|
+
carrier_name: The carrier's display name.
|
|
89
|
+
dot_number: The carrier's USDOT number -- a BARE integer on the
|
|
90
|
+
wire (captured six-digit), never quoted.
|
|
91
|
+
main_office_address: The carrier's main office address.
|
|
92
|
+
home_terminal_name: The driver's home terminal name; ``""`` on
|
|
93
|
+
204/460 active records.
|
|
94
|
+
home_terminal_address: The home terminal address; ``""`` on
|
|
95
|
+
268/460 active records.
|
|
96
|
+
"""
|
|
97
|
+
|
|
98
|
+
model_config = ConfigDict(alias_generator=to_camel)
|
|
99
|
+
|
|
100
|
+
carrier_name: str
|
|
101
|
+
dot_number: int
|
|
102
|
+
main_office_address: str
|
|
103
|
+
home_terminal_name: str
|
|
104
|
+
home_terminal_address: str
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class DriverHosSetting(ResponseModel):
|
|
108
|
+
"""The ``hosSetting`` block: hours-of-service configuration flags.
|
|
109
|
+
|
|
110
|
+
Attributes:
|
|
111
|
+
heavy_haul_exemption_toggle_enabled: Whether the heavy-haul
|
|
112
|
+
exemption toggle is enabled for the driver.
|
|
113
|
+
"""
|
|
114
|
+
|
|
115
|
+
model_config = ConfigDict(alias_generator=to_camel)
|
|
116
|
+
|
|
117
|
+
heavy_haul_exemption_toggle_enabled: bool
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class DriverStaticAssignedVehicleRef(ResponseModel):
|
|
121
|
+
"""The statically assigned vehicle reference: id and display name."""
|
|
122
|
+
|
|
123
|
+
id: str
|
|
124
|
+
name: str
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
class DriverTagRef(ResponseModel):
|
|
128
|
+
"""A tag reference: the shared ``{id, name, parentTagId}`` shape.
|
|
129
|
+
|
|
130
|
+
The one shape both singular tag references (``peerGroupTag``,
|
|
131
|
+
``vehicleGroupTag``) carry -- all three keys present in every
|
|
132
|
+
captured block. Deliberately NOT reused for the ``tags`` list, which
|
|
133
|
+
stays excluded wholesale (module docstring).
|
|
134
|
+
|
|
135
|
+
Attributes:
|
|
136
|
+
id: The tag's id.
|
|
137
|
+
name: The tag's display name.
|
|
138
|
+
parent_tag_id: The parent tag's id.
|
|
139
|
+
"""
|
|
140
|
+
|
|
141
|
+
model_config = ConfigDict(alias_generator=to_camel)
|
|
142
|
+
|
|
143
|
+
id: str
|
|
144
|
+
name: str
|
|
145
|
+
parent_tag_id: str
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
class Driver(ResponseModel):
|
|
149
|
+
"""One Samsara fleet driver, active or deactivated.
|
|
150
|
+
|
|
151
|
+
A pure mirror of the captured fields (``tags`` and ``eldSettings``
|
|
152
|
+
excluded, ``externalIds`` unobserved -- module docstring). Field
|
|
153
|
+
semantics are Samsara's; no value is derived or interpreted here.
|
|
154
|
+
The one complete driver dataset is the union of the two activation
|
|
155
|
+
sweeps; ``driver_activation_status`` carries the split.
|
|
156
|
+
|
|
157
|
+
Presence counts below are out of the 460 active records; keys with
|
|
158
|
+
no count were present on every one (the deactivated sweep matched
|
|
159
|
+
structurally). Only ``id`` is required (module docstring).
|
|
160
|
+
|
|
161
|
+
Attributes:
|
|
162
|
+
id: Samsara's driver id -- a numeric string, mirrored as string.
|
|
163
|
+
name: The driver's display name.
|
|
164
|
+
username: The driver-app login name.
|
|
165
|
+
driver_activation_status: ``active`` / ``deactivated`` -- the
|
|
166
|
+
API-enforced closed enum, and the two-sweep split column.
|
|
167
|
+
timezone: The driver's IANA timezone (e.g.
|
|
168
|
+
``America/Chicago``).
|
|
169
|
+
created_at_time: Record creation (UTC, millisecond ISO-8601).
|
|
170
|
+
updated_at_time: Last record update (UTC).
|
|
171
|
+
has_vehicle_unpinning_enabled: Vehicle-unpinning flag.
|
|
172
|
+
carrier_settings: Carrier identity and home terminal block.
|
|
173
|
+
hos_setting: Hours-of-service configuration block.
|
|
174
|
+
static_assigned_vehicle: Statically assigned vehicle reference
|
|
175
|
+
(102/460).
|
|
176
|
+
peer_group_tag: Peer-group tag reference (4/460).
|
|
177
|
+
vehicle_group_tag: Vehicle-group tag reference (8/460).
|
|
178
|
+
license_number: Driving license number (172/460).
|
|
179
|
+
license_state: License issuing state (269/460).
|
|
180
|
+
phone: Contact phone number (7/460).
|
|
181
|
+
locale: Display locale (1/460).
|
|
182
|
+
notes: Free-form notes (1/460).
|
|
183
|
+
profile_image_url: Profile image URL (1/460).
|
|
184
|
+
eld_exempt: ELD exemption flag (270/460).
|
|
185
|
+
eld_exempt_reason: Free-text exemption reason (282/460).
|
|
186
|
+
eld_adverse_weather_exemption_enabled: ELD adverse-weather
|
|
187
|
+
exemption flag (191/460).
|
|
188
|
+
eld_big_day_exemption_enabled: ELD big-day exemption flag
|
|
189
|
+
(186/460).
|
|
190
|
+
eld_pc_enabled: ELD personal-conveyance flag (77/460).
|
|
191
|
+
eld_ym_enabled: ELD yard-move flag (100/460).
|
|
192
|
+
waiting_time_duty_status_enabled: Waiting-time duty-status flag
|
|
193
|
+
(8/460).
|
|
194
|
+
"""
|
|
195
|
+
|
|
196
|
+
model_config = ConfigDict(alias_generator=to_camel)
|
|
197
|
+
|
|
198
|
+
# Identity and lifecycle.
|
|
199
|
+
id: str
|
|
200
|
+
name: str | None = None
|
|
201
|
+
username: str | None = None
|
|
202
|
+
driver_activation_status: DriverActivationStatus | None = None
|
|
203
|
+
timezone: str | None = None
|
|
204
|
+
created_at_time: datetime | None = None
|
|
205
|
+
updated_at_time: datetime | None = None
|
|
206
|
+
has_vehicle_unpinning_enabled: bool | None = None
|
|
207
|
+
|
|
208
|
+
# Carrier and HOS configuration blocks.
|
|
209
|
+
carrier_settings: DriverCarrierSettings | None = None
|
|
210
|
+
hos_setting: DriverHosSetting | None = None
|
|
211
|
+
|
|
212
|
+
# Assignment and grouping references.
|
|
213
|
+
static_assigned_vehicle: DriverStaticAssignedVehicleRef | None = None
|
|
214
|
+
peer_group_tag: DriverTagRef | None = None
|
|
215
|
+
vehicle_group_tag: DriverTagRef | None = None
|
|
216
|
+
|
|
217
|
+
# Person and contact.
|
|
218
|
+
license_number: str | None = None
|
|
219
|
+
license_state: str | None = None
|
|
220
|
+
phone: str | None = None
|
|
221
|
+
locale: str | None = None
|
|
222
|
+
notes: str | None = None
|
|
223
|
+
profile_image_url: str | None = None
|
|
224
|
+
|
|
225
|
+
# ELD and duty-status flags.
|
|
226
|
+
eld_exempt: bool | None = None
|
|
227
|
+
eld_exempt_reason: str | None = None
|
|
228
|
+
eld_adverse_weather_exemption_enabled: bool | None = None
|
|
229
|
+
eld_big_day_exemption_enabled: bool | None = None
|
|
230
|
+
eld_pc_enabled: bool | None = None
|
|
231
|
+
eld_ym_enabled: bool | None = None
|
|
232
|
+
waiting_time_duty_status_enabled: bool | None = None
|