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,217 @@
|
|
|
1
|
+
# src/fleetpull/models/motive/user.py
|
|
2
|
+
"""The Motive user response model (``GET /v1/users``, captured 2026-07-21).
|
|
3
|
+
|
|
4
|
+
One record per account — driver or otherwise. Written from a
|
|
5
|
+
whole-population walk (2,665 records, 27 pages at ``per_page`` 100) whose
|
|
6
|
+
shape is perfectly role-partitioned: ``role='driver'`` records (2,359)
|
|
7
|
+
carry a driver-only key block on top of the shared keys, while ``admin``
|
|
8
|
+
(32) and ``fleet_user`` (274) records carry exactly the shared keys —
|
|
9
|
+
zero partial-presence keys within any role. This is the Samsara drivers
|
|
10
|
+
decision-1 posture with the split inverted: one population and ONE
|
|
11
|
+
dataset, but a role-dependent shape rather than a provider filter quirk —
|
|
12
|
+
the ``role`` column carries the split, and the driver-only block is
|
|
13
|
+
ABSENT, not null, on non-driver records.
|
|
14
|
+
|
|
15
|
+
Requiredness mirrors the census: the shared keys, present on every one of
|
|
16
|
+
the 2,665 records, are required (nullable exactly where the census
|
|
17
|
+
observed null); the driver-only keys are optional on the model — absent
|
|
18
|
+
for non-drivers — with nullability per the census inside the driver role.
|
|
19
|
+
``role`` (``driver``/``admin``/``fleet_user`` observed) and ``status``
|
|
20
|
+
(``active``: 1,020 / ``deactivated``: 1,645) are census-closed
|
|
21
|
+
vocabularies only, not API-enforced on output, so they mirror as plain
|
|
22
|
+
strings; likewise ``duty_status`` (observed ``on_duty``/``off_duty``/
|
|
23
|
+
``driving``), ``eld_mode`` (``logs``/``none``/``exempt``),
|
|
24
|
+
``violation_alerts`` (``never``/``1_hour``/``45_minutes``/
|
|
25
|
+
``30_minutes``/``15_minutes``), and the HOS cycle pair (``cycle2``
|
|
26
|
+
observed values like ``70_8_2020``/``60_7_o_2020`` on 37 drivers).
|
|
27
|
+
``joined_at`` is a DATE-ONLY wire value (``YYYY-MM-DD``, 34 of 2,359
|
|
28
|
+
drivers populated -- the whole-population value census), recovered as a
|
|
29
|
+
``date``; null on the rest.
|
|
30
|
+
|
|
31
|
+
The always-present key partition, exactly: 22 keys ride every record
|
|
32
|
+
(20 modeled; ``external_ids`` and ``phone_ext`` never populated
|
|
33
|
+
anywhere); ``admin``/``fleet_user`` records add 3 keys of their own
|
|
34
|
+
(``expires_at``, ``phone2``, ``phone_country_code2`` -- never populated
|
|
35
|
+
among them); driver records add 39 (38 modeled;
|
|
36
|
+
``associated_dispatcher_id`` never populated).
|
|
37
|
+
|
|
38
|
+
Excluded fields, per capture discipline: the six never-populated keys
|
|
39
|
+
named above were present but always null/empty across all 2,665
|
|
40
|
+
records -- the value types are unobservable (the value-unobservable
|
|
41
|
+
exclusion rule); they join the model when a capture types them
|
|
42
|
+
(``extra='ignore'`` makes exclusion exactly "don't model it"). Contrast
|
|
43
|
+
``joined_at``: value-OBSERVED (34 dates), hence modeled.
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
from datetime import date, datetime
|
|
47
|
+
|
|
48
|
+
from pydantic import Field
|
|
49
|
+
|
|
50
|
+
from fleetpull.model_contract import ResponseModel
|
|
51
|
+
|
|
52
|
+
__all__: list[str] = ['User']
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class User(ResponseModel):
|
|
56
|
+
"""One Motive user account: a driver, admin, or fleet_user.
|
|
57
|
+
|
|
58
|
+
A pure mirror of the role-partitioned census. Groups below follow
|
|
59
|
+
the partition: the shared block first (required on every record),
|
|
60
|
+
then the driver-only block (absent, not null, on non-driver
|
|
61
|
+
records — every field there defaults to ``None``). Field semantics
|
|
62
|
+
are Motive's; no value is derived or interpreted here.
|
|
63
|
+
|
|
64
|
+
Attributes:
|
|
65
|
+
user_id: Motive's internal user identifier (wire key ``id``).
|
|
66
|
+
first_name: User's first name.
|
|
67
|
+
last_name: User's last name.
|
|
68
|
+
email: User's email address; null when unset.
|
|
69
|
+
phone: Contact phone number; null when unset.
|
|
70
|
+
phone_country_code: Phone country code; null when unset.
|
|
71
|
+
company_reference_id: Company-assigned reference identifier;
|
|
72
|
+
null when unset.
|
|
73
|
+
role: Free-form user-role string; ``driver`` / ``admin`` /
|
|
74
|
+
``fleet_user`` observed — the column that carries the
|
|
75
|
+
shape partition.
|
|
76
|
+
status: Free-form account-status string; ``active`` /
|
|
77
|
+
``deactivated`` observed.
|
|
78
|
+
group_ids: Identifiers of the groups the user belongs to;
|
|
79
|
+
often empty.
|
|
80
|
+
metric_units: Whether the account displays metric units.
|
|
81
|
+
time_zone: Rails-style time-zone display name; null when unset.
|
|
82
|
+
created_at: When the account was created.
|
|
83
|
+
updated_at: Last modification timestamp.
|
|
84
|
+
mobile_current_sign_in_at: Current mobile-session sign-in;
|
|
85
|
+
null when none.
|
|
86
|
+
mobile_last_active_at: Last mobile activity; null when none.
|
|
87
|
+
mobile_last_sign_in_at: Previous mobile sign-in; null when none.
|
|
88
|
+
web_current_sign_in_at: Current web-session sign-in; null when
|
|
89
|
+
none.
|
|
90
|
+
web_last_active_at: Last web activity; null when none.
|
|
91
|
+
web_last_sign_in_at: Previous web sign-in; null when none.
|
|
92
|
+
username: Login username; driver-only; null when unset.
|
|
93
|
+
driver_company_id: Company-assigned driver identifier;
|
|
94
|
+
driver-only; null when unset.
|
|
95
|
+
drivers_license_number: Driver's license number; driver-only;
|
|
96
|
+
null when unset.
|
|
97
|
+
drivers_license_state: Driver's license state/province;
|
|
98
|
+
driver-only; null when unset.
|
|
99
|
+
joined_at: Driver-only; the driver's join date -- a DATE-ONLY
|
|
100
|
+
wire value (``YYYY-MM-DD``; 34 of 2,359 populated), null on
|
|
101
|
+
the rest.
|
|
102
|
+
conservative nullable-str posture (see module docstring).
|
|
103
|
+
duty_status: Free-form HOS duty-status string; driver-only.
|
|
104
|
+
eld_mode: Free-form ELD-mode string; driver-only.
|
|
105
|
+
cycle: Free-form HOS cycle string; driver-only; null when
|
|
106
|
+
unset.
|
|
107
|
+
cycle2: Second-jurisdiction HOS cycle; driver-only; null when
|
|
108
|
+
unset.
|
|
109
|
+
violation_alerts: Free-form violation-alert cadence string;
|
|
110
|
+
driver-only.
|
|
111
|
+
carrier_name: Carrier name; driver-only.
|
|
112
|
+
carrier_street: Carrier street address; driver-only.
|
|
113
|
+
carrier_city: Carrier city; driver-only.
|
|
114
|
+
carrier_state: Carrier state; driver-only.
|
|
115
|
+
carrier_zip: Carrier postal code; driver-only.
|
|
116
|
+
terminal_street: Home-terminal street; driver-only; null when
|
|
117
|
+
unset.
|
|
118
|
+
terminal_city: Home-terminal city; driver-only; null when
|
|
119
|
+
unset.
|
|
120
|
+
terminal_state: Home-terminal state; driver-only; null when
|
|
121
|
+
unset.
|
|
122
|
+
terminal_zip: Home-terminal postal code; driver-only; null
|
|
123
|
+
when unset.
|
|
124
|
+
exception_24_hour_restart: HOS exception flag; driver-only.
|
|
125
|
+
exception_8_hour_break: HOS exception flag; driver-only.
|
|
126
|
+
exception_adverse_driving: HOS exception flag; driver-only.
|
|
127
|
+
exception_ca_farm_school_bus: HOS exception flag; driver-only.
|
|
128
|
+
exception_short_haul: HOS exception flag; driver-only.
|
|
129
|
+
exception_wait_time: HOS exception flag; driver-only.
|
|
130
|
+
exception_24_hour_restart2: Second-jurisdiction HOS exception
|
|
131
|
+
flag; driver-only (likewise the other ``*2`` flags).
|
|
132
|
+
exception_8_hour_break2: See above; driver-only.
|
|
133
|
+
exception_adverse_driving2: See above; driver-only.
|
|
134
|
+
exception_ca_farm_school_bus2: See above; driver-only.
|
|
135
|
+
exception_short_haul2: See above; driver-only.
|
|
136
|
+
exception_wait_time2: See above; driver-only.
|
|
137
|
+
export_combined: Log-export setting; driver-only.
|
|
138
|
+
export_odometers: Log-export setting; driver-only.
|
|
139
|
+
export_recap: Log-export setting; driver-only.
|
|
140
|
+
manual_driving_enabled: Driving-mode setting; driver-only.
|
|
141
|
+
minute_logs: Minute-grain logging setting; driver-only.
|
|
142
|
+
personal_conveyance_enabled: Driving-mode setting; driver-only.
|
|
143
|
+
yard_moves_enabled: Driving-mode setting; driver-only.
|
|
144
|
+
"""
|
|
145
|
+
|
|
146
|
+
# The shared block: present on every record of every role.
|
|
147
|
+
user_id: int = Field(alias='id')
|
|
148
|
+
first_name: str
|
|
149
|
+
last_name: str
|
|
150
|
+
email: str | None
|
|
151
|
+
phone: str | None
|
|
152
|
+
phone_country_code: str | None
|
|
153
|
+
company_reference_id: str | None
|
|
154
|
+
role: str
|
|
155
|
+
status: str
|
|
156
|
+
group_ids: list[int]
|
|
157
|
+
metric_units: bool
|
|
158
|
+
time_zone: str | None
|
|
159
|
+
created_at: datetime
|
|
160
|
+
updated_at: datetime
|
|
161
|
+
mobile_current_sign_in_at: datetime | None
|
|
162
|
+
mobile_last_active_at: datetime | None
|
|
163
|
+
mobile_last_sign_in_at: datetime | None
|
|
164
|
+
web_current_sign_in_at: datetime | None
|
|
165
|
+
web_last_active_at: datetime | None
|
|
166
|
+
web_last_sign_in_at: datetime | None
|
|
167
|
+
|
|
168
|
+
# The driver-only block: absent, not null, on non-driver records.
|
|
169
|
+
# Identity and licensing.
|
|
170
|
+
username: str | None = None
|
|
171
|
+
driver_company_id: str | None = None
|
|
172
|
+
drivers_license_number: str | None = None
|
|
173
|
+
drivers_license_state: str | None = None
|
|
174
|
+
joined_at: date | None = None
|
|
175
|
+
|
|
176
|
+
# HOS configuration.
|
|
177
|
+
duty_status: str | None = None
|
|
178
|
+
eld_mode: str | None = None
|
|
179
|
+
cycle: str | None = None
|
|
180
|
+
cycle2: str | None = None
|
|
181
|
+
violation_alerts: str | None = None
|
|
182
|
+
|
|
183
|
+
# Carrier and home-terminal identity.
|
|
184
|
+
carrier_name: str | None = None
|
|
185
|
+
carrier_street: str | None = None
|
|
186
|
+
carrier_city: str | None = None
|
|
187
|
+
carrier_state: str | None = None
|
|
188
|
+
carrier_zip: str | None = None
|
|
189
|
+
terminal_street: str | None = None
|
|
190
|
+
terminal_city: str | None = None
|
|
191
|
+
terminal_state: str | None = None
|
|
192
|
+
terminal_zip: str | None = None
|
|
193
|
+
|
|
194
|
+
# HOS exception flags (the *2 variants are the second jurisdiction).
|
|
195
|
+
exception_24_hour_restart: bool | None = None
|
|
196
|
+
exception_8_hour_break: bool | None = None
|
|
197
|
+
exception_adverse_driving: bool | None = None
|
|
198
|
+
exception_ca_farm_school_bus: bool | None = None
|
|
199
|
+
exception_short_haul: bool | None = None
|
|
200
|
+
exception_wait_time: bool | None = None
|
|
201
|
+
exception_24_hour_restart2: bool | None = None
|
|
202
|
+
exception_8_hour_break2: bool | None = None
|
|
203
|
+
exception_adverse_driving2: bool | None = None
|
|
204
|
+
exception_ca_farm_school_bus2: bool | None = None
|
|
205
|
+
exception_short_haul2: bool | None = None
|
|
206
|
+
exception_wait_time2: bool | None = None
|
|
207
|
+
|
|
208
|
+
# Log-export settings.
|
|
209
|
+
export_combined: bool | None = None
|
|
210
|
+
export_odometers: bool | None = None
|
|
211
|
+
export_recap: bool | None = None
|
|
212
|
+
|
|
213
|
+
# Driving-mode settings.
|
|
214
|
+
manual_driving_enabled: bool | None = None
|
|
215
|
+
minute_logs: bool | None = None
|
|
216
|
+
personal_conveyance_enabled: bool | None = None
|
|
217
|
+
yard_moves_enabled: bool | None = None
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
# src/fleetpull/models/motive/vehicle.py
|
|
2
|
+
"""Motive vehicles-endpoint response model (``/v1/vehicles``).
|
|
3
|
+
|
|
4
|
+
Holds the ``Vehicle`` record and the shapes used only by it —
|
|
5
|
+
``AvailabilityDetails`` and the ``VehicleStatus`` / ``AvailabilityStatus``
|
|
6
|
+
enums. Cross-endpoint embedded shapes (``UserSummary``, ``EldDeviceInfo``)
|
|
7
|
+
are imported from ``fleetpull.models.motive.shared``.
|
|
8
|
+
|
|
9
|
+
Pure API mirrors — typed fields and nothing else. No use-case logic, no
|
|
10
|
+
derived properties, no normalizing validators: flattening and schema
|
|
11
|
+
derivation are the records layer's generic concern (DESIGN §9), and
|
|
12
|
+
fleetpull assumes no end use, so a field is mirrored, never interpreted.
|
|
13
|
+
The response *wrapper* (the ``{"vehicles": [{"vehicle": {...}}]}`` envelope)
|
|
14
|
+
is not modeled here — the endpoints layer's extractor and paginator own it,
|
|
15
|
+
so this module mirrors only the inner per-vehicle object.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from datetime import datetime
|
|
19
|
+
from enum import StrEnum
|
|
20
|
+
|
|
21
|
+
from pydantic import Field
|
|
22
|
+
|
|
23
|
+
from fleetpull.model_contract import ResponseModel
|
|
24
|
+
from fleetpull.models.motive.shared import EldDeviceInfo, UserSummary
|
|
25
|
+
|
|
26
|
+
__all__: list[str] = [
|
|
27
|
+
'AvailabilityDetails',
|
|
28
|
+
'AvailabilityStatus',
|
|
29
|
+
'Vehicle',
|
|
30
|
+
'VehicleStatus',
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class VehicleStatus(StrEnum):
|
|
35
|
+
"""Operational status for a Motive vehicle.
|
|
36
|
+
|
|
37
|
+
A closed mirror of Motive's documented status vocabulary. Kept as an
|
|
38
|
+
enum (not downgraded to ``str``) because the wire values match the
|
|
39
|
+
member values exactly — no normalizing logic is needed to land them,
|
|
40
|
+
so the enum stays a faithful mirror while adding documentation and
|
|
41
|
+
membership validation. Live responses show ``active`` and
|
|
42
|
+
``deactivated``; ``inactive`` is documented.
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
ACTIVE = 'active'
|
|
46
|
+
INACTIVE = 'inactive'
|
|
47
|
+
DEACTIVATED = 'deactivated'
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class AvailabilityStatus(StrEnum):
|
|
51
|
+
"""In-service / out-of-service availability for a Motive vehicle.
|
|
52
|
+
|
|
53
|
+
Closed mirror of Motive's documented availability vocabulary; an exact
|
|
54
|
+
wire-to-member match, so it stays an enum for the same reason as
|
|
55
|
+
``VehicleStatus``.
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
IN_SERVICE = 'in_service'
|
|
59
|
+
OUT_OF_SERVICE = 'out_of_service'
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class AvailabilityDetails(ResponseModel):
|
|
63
|
+
"""Availability status block embedded in the vehicle record.
|
|
64
|
+
|
|
65
|
+
Tracks whether a vehicle is in or out of service and when the status
|
|
66
|
+
last changed.
|
|
67
|
+
|
|
68
|
+
Attributes:
|
|
69
|
+
availability_status: Current in-service / out-of-service value.
|
|
70
|
+
updated_at: Timestamp of the last status change.
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
availability_status: AvailabilityStatus
|
|
74
|
+
updated_at: datetime
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class Vehicle(ResponseModel):
|
|
78
|
+
"""Complete vehicle record from Motive's vehicles endpoint.
|
|
79
|
+
|
|
80
|
+
A single fleet vehicle with its metadata, current and permanent driver
|
|
81
|
+
references, ELD device, and availability block. A pure mirror: every
|
|
82
|
+
field maps a Motive response field, with no derived or interpreted
|
|
83
|
+
values.
|
|
84
|
+
|
|
85
|
+
Attributes:
|
|
86
|
+
vehicle_id: Motive's internal vehicle identifier (wire key ``id``).
|
|
87
|
+
company_id: Parent company identifier.
|
|
88
|
+
number: User-assigned fleet/unit number.
|
|
89
|
+
status: Vehicle operational status.
|
|
90
|
+
ifta: Whether the vehicle is IFTA-reportable.
|
|
91
|
+
vin: Vehicle Identification Number; null when unset.
|
|
92
|
+
make: Manufacturer; null when unset.
|
|
93
|
+
model: Model name; null when unset.
|
|
94
|
+
year: Model year, mirrored as a string (Motive's convention); null
|
|
95
|
+
when unset.
|
|
96
|
+
license_plate_state: Registration state/province; null when unset.
|
|
97
|
+
license_plate_number: License plate number; null when unset.
|
|
98
|
+
license_plate_country_code: Registration country code; null when
|
|
99
|
+
unset. Present in live responses but absent from the predecessor
|
|
100
|
+
model — added here so the mirror is faithful to the wire.
|
|
101
|
+
metric_units: Whether the vehicle displays metric units.
|
|
102
|
+
fuel_type: Primary fuel type, mirrored as a free-form ``str``
|
|
103
|
+
rather than a constrained enum — Motive's casing varies and
|
|
104
|
+
fleetpull does not interpret the value, so a string mirror
|
|
105
|
+
avoids carrying a normalizing validator on an otherwise pure
|
|
106
|
+
model; null when unset.
|
|
107
|
+
prevent_auto_odometer_entry: Whether automatic odometer capture is
|
|
108
|
+
disabled.
|
|
109
|
+
notes: Free-form notes; null when unset.
|
|
110
|
+
incab_alert_live_stream_enable: Motive camera-config field; ``-1``
|
|
111
|
+
is Motive's "not configured" sentinel and the default.
|
|
112
|
+
driver_facing_camera: Camera-config field; ``-1`` sentinel default.
|
|
113
|
+
incab_audio_recording: Camera-config field; ``-1`` sentinel default.
|
|
114
|
+
group_ids: Identifiers of the groups the vehicle belongs to.
|
|
115
|
+
created_at: When the vehicle was added to Motive.
|
|
116
|
+
updated_at: Last modification timestamp.
|
|
117
|
+
permanent_driver: Permanently assigned driver; null when none.
|
|
118
|
+
availability_details: Current availability block; null when absent.
|
|
119
|
+
eld_device: Installed ELD hardware; null when none.
|
|
120
|
+
current_driver: Currently logged-in driver; null when none.
|
|
121
|
+
carb_ctc_test_enabled: CARB compliance flag; null when absent.
|
|
122
|
+
carb_ctc_emission_status: CARB emission status string; null when
|
|
123
|
+
absent.
|
|
124
|
+
registration_expiry_date: Registration expiry, mirrored as a string
|
|
125
|
+
(Motive's convention); null when absent.
|
|
126
|
+
"""
|
|
127
|
+
|
|
128
|
+
vehicle_id: int = Field(alias='id')
|
|
129
|
+
company_id: int
|
|
130
|
+
number: str
|
|
131
|
+
status: VehicleStatus
|
|
132
|
+
ifta: bool
|
|
133
|
+
vin: str | None = None
|
|
134
|
+
make: str | None = None
|
|
135
|
+
model: str | None = None
|
|
136
|
+
year: str | None = None
|
|
137
|
+
license_plate_state: str | None = None
|
|
138
|
+
license_plate_number: str | None = None
|
|
139
|
+
license_plate_country_code: str | None = None
|
|
140
|
+
metric_units: bool = False
|
|
141
|
+
fuel_type: str | None = None
|
|
142
|
+
prevent_auto_odometer_entry: bool = False
|
|
143
|
+
notes: str | None = None
|
|
144
|
+
|
|
145
|
+
# Motive uses -1 as a "not configured" sentinel for these camera ints.
|
|
146
|
+
incab_alert_live_stream_enable: int = -1
|
|
147
|
+
driver_facing_camera: int = -1
|
|
148
|
+
incab_audio_recording: int = -1
|
|
149
|
+
|
|
150
|
+
group_ids: list[int] = Field(default_factory=list)
|
|
151
|
+
|
|
152
|
+
created_at: datetime
|
|
153
|
+
updated_at: datetime
|
|
154
|
+
|
|
155
|
+
permanent_driver: UserSummary | None = None
|
|
156
|
+
availability_details: AvailabilityDetails | None = None
|
|
157
|
+
eld_device: EldDeviceInfo | None = None
|
|
158
|
+
current_driver: UserSummary | None = None
|
|
159
|
+
|
|
160
|
+
carb_ctc_test_enabled: bool | None = None
|
|
161
|
+
carb_ctc_emission_status: str | None = None
|
|
162
|
+
registration_expiry_date: str | None = None
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# src/fleetpull/models/motive/vehicle_location.py
|
|
2
|
+
"""Motive vehicle-locations-endpoint response model (``/v3/vehicle_locations``).
|
|
3
|
+
|
|
4
|
+
Holds the ``VehicleLocation`` breadcrumb record and the ``VehicleLocationType``
|
|
5
|
+
enum used only by it. Cross-endpoint embedded shapes (``UserSummary``,
|
|
6
|
+
``EldDeviceInfo``) are imported from ``fleetpull.models.motive.shared``.
|
|
7
|
+
|
|
8
|
+
Pure API mirrors — typed fields and nothing else. No use-case logic, no derived
|
|
9
|
+
properties, no normalizing validators: flattening and schema derivation are the
|
|
10
|
+
records layer's generic concern (DESIGN §9), and fleetpull assumes no end use, so a
|
|
11
|
+
field is mirrored, never interpreted. The response *wrapper* (the
|
|
12
|
+
``{"vehicle_locations": [{"vehicle_location": {...}}]}`` envelope) is not modeled
|
|
13
|
+
here — the endpoints layer's decoder owns it, so this module mirrors only the inner
|
|
14
|
+
per-location object.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from datetime import datetime
|
|
18
|
+
from enum import StrEnum
|
|
19
|
+
|
|
20
|
+
from pydantic import Field
|
|
21
|
+
|
|
22
|
+
from fleetpull.model_contract import ResponseModel
|
|
23
|
+
from fleetpull.models.motive.shared import EldDeviceInfo, UserSummary
|
|
24
|
+
|
|
25
|
+
__all__: list[str] = [
|
|
26
|
+
'VehicleLocation',
|
|
27
|
+
'VehicleLocationType',
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class VehicleLocationType(StrEnum):
|
|
32
|
+
"""Type of a Motive vehicle-location (breadcrumb) record.
|
|
33
|
+
|
|
34
|
+
A closed mirror of Motive's documented location-type vocabulary. Kept as an
|
|
35
|
+
enum (not downgraded to ``str``) for the same reason as ``VehicleStatus``: the
|
|
36
|
+
wire values match the member values exactly, so the enum is a faithful mirror
|
|
37
|
+
that adds documentation and membership validation with no normalizing logic.
|
|
38
|
+
The blast radius differs from ``VehicleStatus`` — this endpoint is high-volume
|
|
39
|
+
per-vehicle breadcrumbs, so a type Motive begins emitting that is not a member
|
|
40
|
+
fails validation loudly across every affected row rather than a handful of
|
|
41
|
+
vehicles. That is fail-fast-and-loud as intended (extend the member set,
|
|
42
|
+
re-fetch); it is also the field most likely to want ``str`` instead if a silent
|
|
43
|
+
land-as-data is ever preferred on this surface.
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
BREADCRUMB = 'breadcrumb'
|
|
47
|
+
VEHICLE_STOPPED = 'vehicle_stopped'
|
|
48
|
+
VEHICLE_MOVING = 'vehicle_moving'
|
|
49
|
+
IGNITION_ON = 'ignition_on'
|
|
50
|
+
IGNITION_OFF = 'ignition_off'
|
|
51
|
+
ENGINE_START = 'engine_start'
|
|
52
|
+
ENGINE_STOP = 'engine_stop'
|
|
53
|
+
GPS_MOVING = 'gps_moving'
|
|
54
|
+
GPS_STOPPED = 'gps_stopped'
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class VehicleLocation(ResponseModel):
|
|
58
|
+
"""A single vehicle-location (breadcrumb) record from Motive.
|
|
59
|
+
|
|
60
|
+
One point-in-time fix of a vehicle's position and telemetry from the
|
|
61
|
+
``/v3/vehicle_locations/{vehicle_id}`` endpoint, which returns a vehicle's
|
|
62
|
+
location history over a date range. A pure mirror: every field maps a Motive
|
|
63
|
+
response field, with no derived or interpreted values.
|
|
64
|
+
|
|
65
|
+
Attributes:
|
|
66
|
+
location_id: Motive's identifier for this location record, a UUID string
|
|
67
|
+
(wire key ``id``).
|
|
68
|
+
located_at: Timestamp the location was recorded -- the endpoint's event
|
|
69
|
+
time.
|
|
70
|
+
latitude: GPS latitude in decimal degrees (wire key ``lat``).
|
|
71
|
+
longitude: GPS longitude in decimal degrees (wire key ``lon``).
|
|
72
|
+
location_type: The kind of location event (wire key ``type``).
|
|
73
|
+
description: Human-readable place description (e.g. city, state); null
|
|
74
|
+
when absent.
|
|
75
|
+
speed: Vehicle speed in the vehicle's own units; null when absent.
|
|
76
|
+
bearing: Compass heading in degrees; null when absent.
|
|
77
|
+
battery_voltage: Vehicle battery voltage; null when absent.
|
|
78
|
+
odometer: Calculated odometer reading; null when absent.
|
|
79
|
+
true_odometer: ECM-reported odometer; null when absent.
|
|
80
|
+
engine_hours: Calculated engine-hours reading; null when absent.
|
|
81
|
+
true_engine_hours: ECM-reported engine hours; null when absent.
|
|
82
|
+
fuel: Cumulative fuel consumption; null when absent.
|
|
83
|
+
fuel_primary_remaining_percentage: Primary tank level, 0-100; null when
|
|
84
|
+
absent.
|
|
85
|
+
fuel_secondary_remaining_percentage: Secondary tank level, 0-100; null
|
|
86
|
+
when absent.
|
|
87
|
+
veh_range: Estimated remaining range (EV); null for non-EV.
|
|
88
|
+
hvb_state_of_charge: High-voltage-battery charge state (EV); null for
|
|
89
|
+
non-EV.
|
|
90
|
+
hvb_charge_status: High-voltage-battery charge status string (EV); null
|
|
91
|
+
for non-EV.
|
|
92
|
+
hvb_charge_source: High-voltage-battery charge source string (EV); null
|
|
93
|
+
for non-EV.
|
|
94
|
+
hvb_lifetime_energy_output: High-voltage-battery lifetime energy output
|
|
95
|
+
(EV); null for non-EV.
|
|
96
|
+
driver: Driver logged in at capture time; null when none.
|
|
97
|
+
eld_device: ELD device that captured the location; null when none.
|
|
98
|
+
"""
|
|
99
|
+
|
|
100
|
+
location_id: str = Field(alias='id')
|
|
101
|
+
located_at: datetime
|
|
102
|
+
latitude: float = Field(alias='lat')
|
|
103
|
+
longitude: float = Field(alias='lon')
|
|
104
|
+
location_type: VehicleLocationType = Field(alias='type')
|
|
105
|
+
description: str | None = None
|
|
106
|
+
|
|
107
|
+
speed: float | None = None
|
|
108
|
+
bearing: float | None = None
|
|
109
|
+
|
|
110
|
+
battery_voltage: float | None = None
|
|
111
|
+
odometer: float | None = None
|
|
112
|
+
true_odometer: float | None = None
|
|
113
|
+
engine_hours: float | None = None
|
|
114
|
+
true_engine_hours: float | None = None
|
|
115
|
+
|
|
116
|
+
fuel: float | None = None
|
|
117
|
+
fuel_primary_remaining_percentage: float | None = None
|
|
118
|
+
fuel_secondary_remaining_percentage: float | None = None
|
|
119
|
+
|
|
120
|
+
veh_range: float | None = None
|
|
121
|
+
hvb_state_of_charge: float | None = None
|
|
122
|
+
hvb_charge_status: str | None = None
|
|
123
|
+
hvb_charge_source: str | None = None
|
|
124
|
+
hvb_lifetime_energy_output: float | None = None
|
|
125
|
+
|
|
126
|
+
driver: UserSummary | None = None
|
|
127
|
+
eld_device: EldDeviceInfo | None = None
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# src/fleetpull/models/motive/vehicle_utilization.py
|
|
2
|
+
"""Motive VehicleUtilization response model
|
|
3
|
+
(``GET /v2/vehicle_utilization``, post-decoder window-stamped grain).
|
|
4
|
+
|
|
5
|
+
Written from captured live responses (2026-07-21 probe session: 120
|
|
6
|
+
records sampled across the 1,466-vehicle listing, structurally uniform
|
|
7
|
+
-- every key on every sampled record), never from docs. The model
|
|
8
|
+
mirrors the record ``MotiveWindowReportPageDecoder`` emits, and its two
|
|
9
|
+
field families have different provenance:
|
|
10
|
+
|
|
11
|
+
- ``window_start`` / ``window_end`` are DECODER-SYNTHESIZED
|
|
12
|
+
(``windowStartDate``/``windowEndDate``): rollup rows carry NO date or
|
|
13
|
+
time identity of any kind, so the decoder stamps each row with the
|
|
14
|
+
window the SENT spec asked for, copied verbatim from its
|
|
15
|
+
``start_date``/``end_date`` date labels (the fuel-energy pair's
|
|
16
|
+
request-sourced stamp, on Motive wire). ``MotiveWindowStamp`` lifts
|
|
17
|
+
each label to its UTC-midnight instant -- a representation for
|
|
18
|
+
partition routing, never a timezone conversion (``shared.py``).
|
|
19
|
+
- Everything else is WIRE-VERBATIM: the metric core, the ``vehicle``
|
|
20
|
+
ref (the shared ``VehicleSummary``, its third carrying surface), and
|
|
21
|
+
the ``message`` status string, snake_case keys mirrored directly.
|
|
22
|
+
|
|
23
|
+
**THE COMPANY-LOCAL CAVEAT (the documented obligation on this
|
|
24
|
+
mirror).** The ``start_date``/``end_date`` labels are interpreted in
|
|
25
|
+
COMPANY-LOCAL days -- the account's ``/v1/companies`` capture carries a
|
|
26
|
+
company-local zone at a UTC-5 offset (DESIGN section 8) -- so each row
|
|
27
|
+
is the provider's rollup over its company-local day(s), mirrored
|
|
28
|
+
verbatim. The window stamps carry the day LABELS, not UTC day
|
|
29
|
+
boundaries; nothing here converts anything.
|
|
30
|
+
|
|
31
|
+
**THE ROLLUP GRAIN IS THE REQUEST WINDOW -- proven on this surface;
|
|
32
|
+
do not sum day rows (precedent-based).** A 1-day and a 6-day request
|
|
33
|
+
each returned exactly one rollup row per vehicle over the SAME
|
|
34
|
+
1,466-vehicle population -- the grain is the window, which is why the
|
|
35
|
+
binding declares ``fixed_unit_days=1``. Additivity was NOT probed here;
|
|
36
|
+
on the provider family's only probed rollup surfaces (the Samsara
|
|
37
|
+
fuel-energy pair) day rollups were NON-ADDITIVE into wider windows
|
|
38
|
+
(89/267 mismatched), so the same posture applies as precedent: day rows
|
|
39
|
+
MUST NOT be summed to reproduce a wider window's rollup.
|
|
40
|
+
|
|
41
|
+
The population is the WHOLE vehicle fleet regardless of window (the
|
|
42
|
+
1-day and 6-day totals were both 1,466): inactive vehicles ride with
|
|
43
|
+
zeroed metrics and a populated ``message`` status string -- there is no
|
|
44
|
+
absence arm on this surface, so the metric core is REQUIRED (the
|
|
45
|
+
fuel-energy whole-walk reasoning: a rollup surface computes its metrics
|
|
46
|
+
per window and has no absence mechanism to be conservative about). The
|
|
47
|
+
window stamps and the ``vehicle`` ref are required STRUCTURALLY -- a
|
|
48
|
+
rollup row without its window or its entity is meaningless.
|
|
49
|
+
|
|
50
|
+
``last_located_at`` is the one nullable key (str-or-None in census) and
|
|
51
|
+
mirrors VERBATIM as a string: its value format and zone semantics are
|
|
52
|
+
unprobed, and Motive's rollup timestamps are documented company-local,
|
|
53
|
+
so parsing it would presume what no capture has shown. ``message`` is
|
|
54
|
+
free text -- a plain ``str``, no vocabulary claim.
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
from pydantic import Field
|
|
58
|
+
|
|
59
|
+
from fleetpull.model_contract import ResponseModel
|
|
60
|
+
from fleetpull.models.motive.shared import MotiveWindowStamp, VehicleSummary
|
|
61
|
+
|
|
62
|
+
__all__: list[str] = ['VehicleUtilization']
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class VehicleUtilization(ResponseModel):
|
|
66
|
+
"""One vehicle's utilization rollup over exactly one request window.
|
|
67
|
+
|
|
68
|
+
A pure mirror of the window-stamped post-decoder record (module
|
|
69
|
+
docstring: the window stamps are decoder-synthesized from the sent
|
|
70
|
+
spec's date labels; everything else is wire-verbatim). Field
|
|
71
|
+
semantics and units are Motive's; no value is derived or
|
|
72
|
+
interpreted here. Day rows MUST NOT be summed to reproduce a wider
|
|
73
|
+
window's rollup (module docstring -- the provider-family
|
|
74
|
+
precedent).
|
|
75
|
+
|
|
76
|
+
Attributes:
|
|
77
|
+
window_start: The request window's start-date label
|
|
78
|
+
(decoder-synthesized ``windowStartDate``, verbatim from the
|
|
79
|
+
sent ``start_date``) at UTC midnight -- the event-time
|
|
80
|
+
column: the row's time identity is the window that produced
|
|
81
|
+
it. A COMPANY-LOCAL day label (module docstring).
|
|
82
|
+
window_end: The request window's end-date label
|
|
83
|
+
(decoder-synthesized ``windowEndDate``, verbatim from the
|
|
84
|
+
sent ``end_date``) at UTC midnight. INCLUSIVE: at the fixed
|
|
85
|
+
1-day unit it equals ``window_start``.
|
|
86
|
+
vehicle: The rollup's vehicle entity (the shared
|
|
87
|
+
``VehicleSummary``; ``vin`` null on some rows of this
|
|
88
|
+
surface, every other key populated in census).
|
|
89
|
+
driving_fuel: Fuel used while driving in the window, provider
|
|
90
|
+
units mirrored verbatim.
|
|
91
|
+
driving_time: Time spent driving in the window, a float
|
|
92
|
+
(contrast the driver arm's bare-int durations -- each arm
|
|
93
|
+
mirrors its own wire).
|
|
94
|
+
idle_fuel: Fuel used while idling in the window.
|
|
95
|
+
idle_time: Time spent idling in the window, a float.
|
|
96
|
+
total_distance: Distance covered in the window.
|
|
97
|
+
total_fuel: Total fuel used in the window.
|
|
98
|
+
utilization_percentage: The provider's utilization figure for
|
|
99
|
+
the window, mirrored uninterpreted.
|
|
100
|
+
last_located_at: The vehicle's last-location timestamp, mirrored
|
|
101
|
+
VERBATIM as a string (format and zone semantics unprobed;
|
|
102
|
+
the company-local documentation obligation -- module
|
|
103
|
+
docstring); null when the provider has none.
|
|
104
|
+
message: Free-text status string -- populated on inactive
|
|
105
|
+
zero-metric rows (e.g. a no-data notice), empty otherwise;
|
|
106
|
+
a plain ``str``, no vocabulary claim.
|
|
107
|
+
"""
|
|
108
|
+
|
|
109
|
+
# Decoder-synthesized window identity (the sent spec's own labels).
|
|
110
|
+
window_start: MotiveWindowStamp = Field(alias='windowStartDate')
|
|
111
|
+
window_end: MotiveWindowStamp = Field(alias='windowEndDate')
|
|
112
|
+
|
|
113
|
+
# The rollup's entity.
|
|
114
|
+
vehicle: VehicleSummary
|
|
115
|
+
|
|
116
|
+
# The wire-verbatim metric core (required -- no absence arm exists
|
|
117
|
+
# on this surface; provider units mirrored verbatim).
|
|
118
|
+
driving_fuel: float
|
|
119
|
+
driving_time: float
|
|
120
|
+
idle_fuel: float
|
|
121
|
+
idle_time: float
|
|
122
|
+
total_distance: float
|
|
123
|
+
total_fuel: float
|
|
124
|
+
utilization_percentage: float
|
|
125
|
+
|
|
126
|
+
# The wire-verbatim status pair.
|
|
127
|
+
last_located_at: str | None = None
|
|
128
|
+
message: str
|