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,153 @@
|
|
|
1
|
+
# src/fleetpull/models/samsara/driver_fuel_energy_report.py
|
|
2
|
+
"""Samsara DriverFuelEnergyReport response model
|
|
3
|
+
(``GET /fleet/reports/drivers/fuel-energy``, post-decoder
|
|
4
|
+
window-stamped grain).
|
|
5
|
+
|
|
6
|
+
Written from captured live responses (2026-07-20/21 probe session: a
|
|
7
|
+
47/47 total census on the 1-day walk), never from docs. The vehicle
|
|
8
|
+
arm's shape with the entity swapped: the same metric core plus
|
|
9
|
+
``estFuelEnergyCost``, attributed to a ``driver {id, name}`` ref
|
|
10
|
+
instead of the vehicle block -- and NO ``externalIds`` anywhere on this
|
|
11
|
+
arm (never observed, so unmodeled as unobserved, never excluded).
|
|
12
|
+
|
|
13
|
+
The model mirrors the record ``SamsaraWindowReportPageDecoder`` emits:
|
|
14
|
+
``window_start`` / ``window_end`` are DECODER-SYNTHESIZED
|
|
15
|
+
(``windowStartDate``/``windowEndDate``) -- report rows carry NO
|
|
16
|
+
event-time key of any kind, so the decoder stamps each row with the
|
|
17
|
+
window the SENT spec asked for, copied verbatim from its
|
|
18
|
+
``startDate``/``endDate`` params (the request-sourced contrast with the
|
|
19
|
+
stats triple's record-sourced identity keys); everything else is
|
|
20
|
+
wire-verbatim.
|
|
21
|
+
|
|
22
|
+
**NON-ADDITIVITY -- the rollup grain is the request window
|
|
23
|
+
(2026-07-21).** Proven on this surface family twice: widening a 1-day
|
|
24
|
+
window to 2 days GREW per-entity metrics, and summing two adjacent day
|
|
25
|
+
rollups mismatched the two-day rollup on 89 of 267 vehicle reports.
|
|
26
|
+
Each row is the provider's answer for exactly its window, nothing else:
|
|
27
|
+
day rows MUST NOT be summed to reproduce a wider window's rollup --
|
|
28
|
+
which is why the binding declares ``fixed_unit_days=1``.
|
|
29
|
+
|
|
30
|
+
Requiredness posture: the window stamps, the ``driver`` ref, and its
|
|
31
|
+
``id`` are required STRUCTURALLY (a rollup row without its window or
|
|
32
|
+
its entity is meaningless); the metric core is required on the
|
|
33
|
+
WHOLE-WALK posture (47/47 total census -- a per-window rollup surface
|
|
34
|
+
has no absence mechanism to be conservative about, so a missing metric
|
|
35
|
+
is a contract change worth a loud failure). The ref's ``name`` stays
|
|
36
|
+
optional per the conservative posture.
|
|
37
|
+
|
|
38
|
+
``efficiencyMpge``, ``estCarbonEmissionsKg``, and the cost ``amount``
|
|
39
|
+
are MIXED int|float on the wire -- modeled ``float``; ``currencyCode``
|
|
40
|
+
(observed only ``'USD'`` on a 100-report sample) is census-open, a
|
|
41
|
+
plain ``str``.
|
|
42
|
+
|
|
43
|
+
Wire keys are camelCase; fields are snake_case via the ``to_camel``
|
|
44
|
+
alias generator, except the decoder-synthesized window stamps, which
|
|
45
|
+
take explicit aliases.
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
from datetime import datetime
|
|
49
|
+
|
|
50
|
+
from pydantic import ConfigDict, Field
|
|
51
|
+
from pydantic.alias_generators import to_camel
|
|
52
|
+
|
|
53
|
+
from fleetpull.model_contract import ResponseModel
|
|
54
|
+
|
|
55
|
+
__all__: list[str] = [
|
|
56
|
+
'DriverFuelEnergyCost',
|
|
57
|
+
'DriverFuelEnergyDriverRef',
|
|
58
|
+
'DriverFuelEnergyReport',
|
|
59
|
+
]
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class DriverFuelEnergyCost(ResponseModel):
|
|
63
|
+
"""The ``estFuelEnergyCost`` block: the window's estimated cost.
|
|
64
|
+
|
|
65
|
+
Both keys were 47/47 in census and required per the whole-walk
|
|
66
|
+
posture (module docstring).
|
|
67
|
+
|
|
68
|
+
Attributes:
|
|
69
|
+
amount: The monetary amount -- MIXED int|float on the wire,
|
|
70
|
+
modeled float.
|
|
71
|
+
currency_code: The currency code -- observed only ``'USD'`` on
|
|
72
|
+
a 100-report sample, census-open, so a plain ``str``.
|
|
73
|
+
"""
|
|
74
|
+
|
|
75
|
+
model_config = ConfigDict(alias_generator=to_camel)
|
|
76
|
+
|
|
77
|
+
amount: float
|
|
78
|
+
currency_code: str
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class DriverFuelEnergyDriverRef(ResponseModel):
|
|
82
|
+
"""The ``driver`` block: the rollup's driver entity.
|
|
83
|
+
|
|
84
|
+
Both keys were 47/47 in census; only ``id`` is required by
|
|
85
|
+
structural judgment (a ref without an id references nothing), while
|
|
86
|
+
``name`` stays optional per the conservative posture. No
|
|
87
|
+
``externalIds`` was ever observed on this arm -- unmodeled as
|
|
88
|
+
unobserved.
|
|
89
|
+
|
|
90
|
+
Attributes:
|
|
91
|
+
id: Samsara's driver id -- a string, mirrored as string.
|
|
92
|
+
name: The driver's display name.
|
|
93
|
+
"""
|
|
94
|
+
|
|
95
|
+
id: str
|
|
96
|
+
name: str | None = None
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class DriverFuelEnergyReport(ResponseModel):
|
|
100
|
+
"""One driver's fuel-energy rollup over exactly one request window.
|
|
101
|
+
|
|
102
|
+
A pure mirror of the window-stamped post-decoder record (module
|
|
103
|
+
docstring: the window stamps are decoder-synthesized from the sent
|
|
104
|
+
spec; everything else is wire-verbatim). Field semantics and units
|
|
105
|
+
are Samsara's; no value is derived or interpreted here. Day rows
|
|
106
|
+
MUST NOT be summed to reproduce a wider window's rollup (module
|
|
107
|
+
docstring).
|
|
108
|
+
|
|
109
|
+
Attributes:
|
|
110
|
+
window_start: The request window's start (decoder-synthesized
|
|
111
|
+
``windowStartDate``, verbatim from the sent ``startDate``)
|
|
112
|
+
-- the event-time column: the row's time identity is the
|
|
113
|
+
window that produced it.
|
|
114
|
+
window_end: The request window's end (decoder-synthesized
|
|
115
|
+
``windowEndDate``, verbatim from the sent ``endDate``).
|
|
116
|
+
driver: The rollup's driver entity.
|
|
117
|
+
distance_traveled_meters: Distance traveled in the window, in
|
|
118
|
+
meters, a bare int.
|
|
119
|
+
efficiency_mpge: Efficiency in MPGe -- MIXED int|float on the
|
|
120
|
+
wire, modeled float.
|
|
121
|
+
energy_used_kwh: Energy used in the window, in kWh, a bare int.
|
|
122
|
+
engine_idle_time_duration_ms: Engine idle time in the window,
|
|
123
|
+
in milliseconds, a bare int.
|
|
124
|
+
engine_run_time_duration_ms: Engine run time in the window, in
|
|
125
|
+
milliseconds, a bare int.
|
|
126
|
+
est_carbon_emissions_kg: Estimated carbon emissions in the
|
|
127
|
+
window, in kilograms -- MIXED int|float on the wire,
|
|
128
|
+
modeled float.
|
|
129
|
+
fuel_consumed_ml: Fuel consumed in the window, in milliliters,
|
|
130
|
+
a bare int.
|
|
131
|
+
est_fuel_energy_cost: The window's estimated fuel/energy cost
|
|
132
|
+
block.
|
|
133
|
+
"""
|
|
134
|
+
|
|
135
|
+
model_config = ConfigDict(alias_generator=to_camel)
|
|
136
|
+
|
|
137
|
+
# Decoder-synthesized window identity (the sent spec's own window).
|
|
138
|
+
window_start: datetime = Field(alias='windowStartDate')
|
|
139
|
+
window_end: datetime = Field(alias='windowEndDate')
|
|
140
|
+
|
|
141
|
+
# The rollup's entity.
|
|
142
|
+
driver: DriverFuelEnergyDriverRef
|
|
143
|
+
|
|
144
|
+
# The wire-verbatim metric core (whole-walk required; provider
|
|
145
|
+
# units mirrored verbatim).
|
|
146
|
+
distance_traveled_meters: int
|
|
147
|
+
efficiency_mpge: float
|
|
148
|
+
energy_used_kwh: int
|
|
149
|
+
engine_idle_time_duration_ms: int
|
|
150
|
+
engine_run_time_duration_ms: int
|
|
151
|
+
est_carbon_emissions_kg: float
|
|
152
|
+
fuel_consumed_ml: int
|
|
153
|
+
est_fuel_energy_cost: DriverFuelEnergyCost
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# src/fleetpull/models/samsara/driver_vehicle_assignment.py
|
|
2
|
+
"""Samsara DriverVehicleAssignment response model
|
|
3
|
+
(``GET /fleet/driver-vehicle-assignments``).
|
|
4
|
+
|
|
5
|
+
Written from captured live responses (2026-07-20 probe session: a full
|
|
6
|
+
24-hour walk of the fleet under BOTH ``filterBy`` values -- 216 records
|
|
7
|
+
each, proven identical as tuple sets, so the two sweeps are one
|
|
8
|
+
dataset), never from docs.
|
|
9
|
+
|
|
10
|
+
The record census is total: EVERY key was present on 216/216 records --
|
|
11
|
+
``startTime``/``endTime`` (RFC3339 strs, recovered tz-aware UTC; no
|
|
12
|
+
empty or missing ``endTime`` was observed -- assignments were only
|
|
13
|
+
ever observed complete), ``assignedAtTime`` (present on every record
|
|
14
|
+
but the EMPTY STRING on all of them -- the 2026-07-21 live proof
|
|
15
|
+
failed datetime parsing on record 0, and a 6,921-row week-wide value
|
|
16
|
+
census found '' on every single row: the Samsara empty-string posture,
|
|
17
|
+
mirrored verbatim as ``str``; a populated value's wire format is
|
|
18
|
+
UNOBSERVED, so no datetime recovery is presumed -- revisit on a
|
|
19
|
+
capture that shows one), ``assignmentType``
|
|
20
|
+
(str), ``isPassenger`` (bool), ``driver {id: str, name: str}``, and
|
|
21
|
+
``vehicle {id: str, name: str, externalIds}``.
|
|
22
|
+
|
|
23
|
+
``assignmentType``'s 24h census observed ``{'static': 158, 'HOS': 58}``
|
|
24
|
+
and the 2026-07-21 week-wide live proof added ``driverApp`` (25 of
|
|
25
|
+
8,042 rows) -- an OPEN vocabulary, not API-enforced on output (the
|
|
26
|
+
eldExemptReason lesson; contrast ``filterBy``'s INPUT vocabulary, which
|
|
27
|
+
IS 400-enforced), so the field stays a plain ``str`` with the observed
|
|
28
|
+
values documented here, never an enum -- exactly the posture that let
|
|
29
|
+
the third value land without a failure.
|
|
30
|
+
|
|
31
|
+
``vehicle.externalIds`` carries the LITERAL DOTTED wire keys
|
|
32
|
+
``samsara.serial`` and ``samsara.vin`` (both str, 216/216) on a NESTED
|
|
33
|
+
object, mirrored via explicit ``Field`` aliases -- the Samsara
|
|
34
|
+
``VehicleExternalIds`` precedent. Note the contrast with the stats
|
|
35
|
+
triple's ``vehicleSerial``/``vehicleVin``: those are flat keys the
|
|
36
|
+
series-unnesting DECODER synthesizes; these are the wire's own dotted
|
|
37
|
+
keys on the record, mirrored verbatim.
|
|
38
|
+
|
|
39
|
+
Requiredness posture: 216/216 across one day's two-sweep walk is NOT a
|
|
40
|
+
whole-population-over-time oath (the drivers conservative posture would
|
|
41
|
+
leave everything optional), but the structural core is required anyway
|
|
42
|
+
by structural judgment -- an assignment without its parties (``driver``,
|
|
43
|
+
``vehicle``, and their ``id``\\s: a party ref without an id references
|
|
44
|
+
nothing) or its bounds (``startTime``/``endTime``) is structurally
|
|
45
|
+
meaningless, so a future record omitting them should fail loudly, never
|
|
46
|
+
land an all-null row. Everything else (``assignedAtTime``,
|
|
47
|
+
``assignmentType``, ``isPassenger``, the ref ``name``\\s, and
|
|
48
|
+
``externalIds`` with its keys) stays optional per the conservative
|
|
49
|
+
posture. This is the asset_locations judgment, recorded here and in
|
|
50
|
+
DESIGN section 8.
|
|
51
|
+
|
|
52
|
+
Wire keys are camelCase; fields are snake_case via the ``to_camel``
|
|
53
|
+
alias generator, except the dotted external-id keys, which take
|
|
54
|
+
explicit aliases.
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
from datetime import datetime
|
|
58
|
+
|
|
59
|
+
from pydantic import ConfigDict, Field
|
|
60
|
+
from pydantic.alias_generators import to_camel
|
|
61
|
+
|
|
62
|
+
from fleetpull.model_contract import ResponseModel
|
|
63
|
+
|
|
64
|
+
__all__: list[str] = [
|
|
65
|
+
'AssignmentDriverRef',
|
|
66
|
+
'AssignmentVehicleExternalIds',
|
|
67
|
+
'AssignmentVehicleRef',
|
|
68
|
+
'DriverVehicleAssignment',
|
|
69
|
+
]
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class AssignmentDriverRef(ResponseModel):
|
|
73
|
+
"""The ``driver`` block: the assignment's driver party.
|
|
74
|
+
|
|
75
|
+
Both keys were 216/216 in census; only ``id`` is required by
|
|
76
|
+
structural judgment (module docstring) -- a party ref without an id
|
|
77
|
+
references nothing, while ``name`` stays optional per the
|
|
78
|
+
conservative posture.
|
|
79
|
+
|
|
80
|
+
Attributes:
|
|
81
|
+
id: Samsara's driver id -- a string, mirrored as string.
|
|
82
|
+
name: The driver's display name.
|
|
83
|
+
"""
|
|
84
|
+
|
|
85
|
+
id: str
|
|
86
|
+
name: str | None = None
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class AssignmentVehicleExternalIds(ResponseModel):
|
|
90
|
+
"""The vehicle ref's ``externalIds`` block: namespaced external ids.
|
|
91
|
+
|
|
92
|
+
The wire keys are the LITERAL DOTTED ``samsara.serial`` and
|
|
93
|
+
``samsara.vin`` (both str, 216/216 in census), mirrored via explicit
|
|
94
|
+
aliases on this NESTED object -- the ``VehicleExternalIds``
|
|
95
|
+
precedent, and the contrast with the stats triple's
|
|
96
|
+
decoder-synthesized flat keys (module docstring). Each key is
|
|
97
|
+
independently optional (the conservative posture; the vehicles
|
|
98
|
+
surface proves ``externalIds`` variance exists in this fleet).
|
|
99
|
+
|
|
100
|
+
Attributes:
|
|
101
|
+
samsara_serial: The gateway serial (wire key ``samsara.serial``).
|
|
102
|
+
samsara_vin: The VIN (wire key ``samsara.vin``).
|
|
103
|
+
"""
|
|
104
|
+
|
|
105
|
+
samsara_serial: str | None = Field(default=None, alias='samsara.serial')
|
|
106
|
+
samsara_vin: str | None = Field(default=None, alias='samsara.vin')
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
class AssignmentVehicleRef(ResponseModel):
|
|
110
|
+
"""The ``vehicle`` block: the assignment's vehicle party.
|
|
111
|
+
|
|
112
|
+
All three keys were 216/216 in census; only ``id`` is required by
|
|
113
|
+
structural judgment (module docstring), while ``name`` and
|
|
114
|
+
``external_ids`` stay optional per the conservative posture.
|
|
115
|
+
|
|
116
|
+
Attributes:
|
|
117
|
+
id: Samsara's vehicle id -- a string, mirrored as string.
|
|
118
|
+
name: The vehicle's display name.
|
|
119
|
+
external_ids: The dotted-key external-id block (wire key
|
|
120
|
+
``externalIds``).
|
|
121
|
+
"""
|
|
122
|
+
|
|
123
|
+
model_config = ConfigDict(alias_generator=to_camel)
|
|
124
|
+
|
|
125
|
+
id: str
|
|
126
|
+
name: str | None = None
|
|
127
|
+
external_ids: AssignmentVehicleExternalIds | None = None
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
class DriverVehicleAssignment(ResponseModel):
|
|
131
|
+
"""One driver-vehicle assignment interval.
|
|
132
|
+
|
|
133
|
+
A pure mirror of the captured record. Field semantics and units are
|
|
134
|
+
Samsara's; no value is derived or interpreted here. The structural
|
|
135
|
+
core (``driver``, ``vehicle``, ``start_time``, ``end_time``) is
|
|
136
|
+
required by structural judgment; the rest is optional per the
|
|
137
|
+
conservative posture (module docstring).
|
|
138
|
+
|
|
139
|
+
Attributes:
|
|
140
|
+
driver: The driver party of the assignment.
|
|
141
|
+
vehicle: The vehicle party of the assignment.
|
|
142
|
+
start_time: The assignment interval's start (RFC3339, recovered
|
|
143
|
+
tz-aware UTC) -- the event-time column: retrieval is
|
|
144
|
+
overlap-anchored, and ownership anchors here via the
|
|
145
|
+
runner's post-fetch window filter.
|
|
146
|
+
end_time: The assignment interval's end (RFC3339, recovered
|
|
147
|
+
tz-aware UTC); never observed empty or missing --
|
|
148
|
+
assignments were only ever observed complete.
|
|
149
|
+
assigned_at_time: The instant the assignment was made --
|
|
150
|
+
observed as the EMPTY STRING on every one of 6,921
|
|
151
|
+
week-censused rows (live-proven 2026-07-21), mirrored
|
|
152
|
+
verbatim; a populated value has never been observed.
|
|
153
|
+
assignment_type: How the assignment arose. Observed values
|
|
154
|
+
``static``, ``HOS``, and ``driverApp`` (the third surfaced
|
|
155
|
+
only at week scale) -- an open vocabulary, so a plain
|
|
156
|
+
``str`` (module docstring).
|
|
157
|
+
is_passenger: Whether the driver rode as a passenger.
|
|
158
|
+
"""
|
|
159
|
+
|
|
160
|
+
model_config = ConfigDict(alias_generator=to_camel)
|
|
161
|
+
|
|
162
|
+
driver: AssignmentDriverRef
|
|
163
|
+
vehicle: AssignmentVehicleRef
|
|
164
|
+
start_time: datetime
|
|
165
|
+
end_time: datetime
|
|
166
|
+
assigned_at_time: str | None = None
|
|
167
|
+
assignment_type: str | None = None
|
|
168
|
+
is_passenger: bool | None = None
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# src/fleetpull/models/samsara/engine_state.py
|
|
2
|
+
"""Samsara EngineState response model
|
|
3
|
+
(``GET /fleet/vehicles/stats/history``, ``types=engineStates``,
|
|
4
|
+
post-decoder reading grain).
|
|
5
|
+
|
|
6
|
+
Written from captured live responses (2026-07-20 probe session: a
|
|
7
|
+
1,045-reading census over a 24-hour window, 138 vehicles -- every
|
|
8
|
+
vehicle returned per the requested type carried data; no empty-array
|
|
9
|
+
padding was observed), never from docs. The model mirrors the FLAT
|
|
10
|
+
record ``SamsaraVehicleSeriesPageDecoder`` emits, one row per reading
|
|
11
|
+
-- the grain the records pipeline represents (scalars, not
|
|
12
|
+
list-of-objects; DESIGN section 9) -- and its two field families have
|
|
13
|
+
different provenance:
|
|
14
|
+
|
|
15
|
+
- ``vehicle_id`` / ``vehicle_name`` / ``vehicle_serial`` /
|
|
16
|
+
``vehicle_vin`` are DECODER-SYNTHESIZED: the unnesting lifts them
|
|
17
|
+
from the per-vehicle ``id``/``name`` keys and the ``externalIds``
|
|
18
|
+
object's literal dotted ``samsara.serial``/``samsara.vin`` wire keys
|
|
19
|
+
onto every reading. ``vehicle_id`` and ``vehicle_name`` were 74/74
|
|
20
|
+
on the censused mixed-type page and are required; ``vehicle_serial``
|
|
21
|
+
and ``vehicle_vin`` were also 74/74 there, but one page is not a
|
|
22
|
+
whole-population oath (an unplugged or serial-less unit could omit
|
|
23
|
+
its ``externalIds`` block -- the vehicles surface shows exactly that
|
|
24
|
+
variance), so both stay OPTIONAL -- the drivers conservative posture.
|
|
25
|
+
- ``time`` / ``value`` are WIRE-VERBATIM reading keys: the series
|
|
26
|
+
census observed exactly ``{time, value}`` on every one of the 1,045
|
|
27
|
+
readings, so both are required.
|
|
28
|
+
|
|
29
|
+
``value`` is a plain ``str``, NOT an enum: the observed vocabulary is
|
|
30
|
+
exactly ``{'On': 475, 'Off': 301, 'Idle': 269}``, but that closure is
|
|
31
|
+
census-only -- the API does not 400-enforce output values (unlike
|
|
32
|
+
``driverActivationStatus``'s proven closure; the ``eldExemptReason``
|
|
33
|
+
lesson), so an unobserved state lands as data, not a crash.
|
|
34
|
+
|
|
35
|
+
``time`` is an RFC3339 string recovered as a tz-aware UTC datetime by
|
|
36
|
+
Pydantic's standard parse. Readings fall strictly inside the requested
|
|
37
|
+
``[startTime, endTime)`` window (probe-proven), so ``time`` is the
|
|
38
|
+
endpoint's event-time column with retrieval and routing coinciding
|
|
39
|
+
natively.
|
|
40
|
+
|
|
41
|
+
Wire keys are camelCase; fields are snake_case via the ``to_camel``
|
|
42
|
+
alias generator.
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
from datetime import datetime
|
|
46
|
+
|
|
47
|
+
from pydantic import ConfigDict
|
|
48
|
+
from pydantic.alias_generators import to_camel
|
|
49
|
+
|
|
50
|
+
from fleetpull.model_contract import ResponseModel
|
|
51
|
+
|
|
52
|
+
__all__: list[str] = ['EngineState']
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class EngineState(ResponseModel):
|
|
56
|
+
"""One engine-state reading of one vehicle, at the reading grain.
|
|
57
|
+
|
|
58
|
+
A pure mirror of the flat post-decoder record (module docstring:
|
|
59
|
+
the identity fields are decoder-synthesized, the reading fields
|
|
60
|
+
wire-verbatim). Field semantics are Samsara's; no value is derived
|
|
61
|
+
or interpreted here.
|
|
62
|
+
|
|
63
|
+
Attributes:
|
|
64
|
+
vehicle_id: The vehicle's Samsara id -- a numeric string,
|
|
65
|
+
decoder-synthesized from the vehicle record's ``id``.
|
|
66
|
+
vehicle_name: The vehicle's display name, decoder-synthesized
|
|
67
|
+
from the vehicle record's ``name``.
|
|
68
|
+
vehicle_serial: The gateway serial, decoder-synthesized from
|
|
69
|
+
``externalIds['samsara.serial']`` (74/74 on the censused
|
|
70
|
+
page; optional -- module docstring).
|
|
71
|
+
vehicle_vin: The VIN, decoder-synthesized from
|
|
72
|
+
``externalIds['samsara.vin']`` (74/74 on the censused page;
|
|
73
|
+
optional -- module docstring).
|
|
74
|
+
time: The reading instant (RFC3339, recovered tz-aware UTC) --
|
|
75
|
+
the event-time column; readings fall strictly inside the
|
|
76
|
+
requested window.
|
|
77
|
+
value: The engine state -- a plain string; observed vocabulary
|
|
78
|
+
exactly ``On``/``Off``/``Idle``, census-closed only, so
|
|
79
|
+
membership is not enforced (module docstring).
|
|
80
|
+
"""
|
|
81
|
+
|
|
82
|
+
model_config = ConfigDict(alias_generator=to_camel)
|
|
83
|
+
|
|
84
|
+
# Decoder-synthesized vehicle identity.
|
|
85
|
+
vehicle_id: str
|
|
86
|
+
vehicle_name: str
|
|
87
|
+
vehicle_serial: str | None = None
|
|
88
|
+
vehicle_vin: str | None = None
|
|
89
|
+
|
|
90
|
+
# Wire-verbatim reading payload.
|
|
91
|
+
time: datetime
|
|
92
|
+
value: str
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# src/fleetpull/models/samsara/gps_reading.py
|
|
2
|
+
"""Samsara GpsReading response model
|
|
3
|
+
(``GET /fleet/vehicles/stats/history``, ``types=gps``, post-decoder
|
|
4
|
+
reading grain).
|
|
5
|
+
|
|
6
|
+
Written from captured live responses (2026-07-20 probe session: a
|
|
7
|
+
2,512-reading sample over 8 cursor pages of a 24-hour window whose
|
|
8
|
+
full walk spanned 569 vehicles -- every vehicle returned per the
|
|
9
|
+
requested type carried data; no empty-array padding was observed),
|
|
10
|
+
never from docs. The model mirrors the FLAT record
|
|
11
|
+
``SamsaraVehicleSeriesPageDecoder`` emits, one row per reading -- the
|
|
12
|
+
grain the records pipeline represents (scalars, not list-of-objects;
|
|
13
|
+
DESIGN section 9) -- and its two field families have different
|
|
14
|
+
provenance:
|
|
15
|
+
|
|
16
|
+
- ``vehicle_id`` / ``vehicle_name`` / ``vehicle_serial`` /
|
|
17
|
+
``vehicle_vin`` are DECODER-SYNTHESIZED: the unnesting lifts them
|
|
18
|
+
from the per-vehicle ``id``/``name`` keys and the ``externalIds``
|
|
19
|
+
object's literal dotted ``samsara.serial``/``samsara.vin`` wire keys
|
|
20
|
+
onto every reading. ``vehicle_id`` and ``vehicle_name`` were 74/74
|
|
21
|
+
on the censused mixed-type page and are required; ``vehicle_serial``
|
|
22
|
+
and ``vehicle_vin`` were also 74/74 there, but one page is not a
|
|
23
|
+
whole-population oath (an unplugged or serial-less unit could omit
|
|
24
|
+
its ``externalIds`` block -- the vehicles surface shows exactly that
|
|
25
|
+
variance), so both stay OPTIONAL -- the drivers conservative posture.
|
|
26
|
+
- The reading keys are WIRE-VERBATIM. Sampled presence out of 2,512:
|
|
27
|
+
``time``, ``latitude``, ``longitude``, ``headingDegrees``,
|
|
28
|
+
``speedMilesPerHour``, ``isEcuSpeed``, and ``reverseGeo``
|
|
29
|
+
(``{formattedLocation}``, the key always present in every carrying
|
|
30
|
+
block) rode every reading and are required; ``address``
|
|
31
|
+
(``{id, name}``, the defined-address-book reference) rode 401/2,512
|
|
32
|
+
and is optional.
|
|
33
|
+
|
|
34
|
+
``speedMilesPerHour`` is MIXED int|float on the wire -- modeled
|
|
35
|
+
``float``, lax coercion lifting the int shape (the idling_events
|
|
36
|
+
``fuelConsumedMilliliters`` precedent). ``headingDegrees`` is a bare
|
|
37
|
+
int on every sampled reading. ``time`` is an RFC3339 string recovered
|
|
38
|
+
as a tz-aware UTC datetime by Pydantic's standard parse. Readings fall
|
|
39
|
+
strictly inside the requested ``[startTime, endTime)`` window
|
|
40
|
+
(probe-proven), so ``time`` is the endpoint's event-time column with
|
|
41
|
+
retrieval and routing coinciding natively.
|
|
42
|
+
|
|
43
|
+
The reverse-geocoded ``formattedLocation`` string is PII-adjacent --
|
|
44
|
+
capture fixtures are fully synthetic (the trips precedent).
|
|
45
|
+
|
|
46
|
+
Wire keys are camelCase; fields are snake_case via the ``to_camel``
|
|
47
|
+
alias generator.
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
from datetime import datetime
|
|
51
|
+
|
|
52
|
+
from pydantic import ConfigDict
|
|
53
|
+
from pydantic.alias_generators import to_camel
|
|
54
|
+
|
|
55
|
+
from fleetpull.model_contract import ResponseModel
|
|
56
|
+
|
|
57
|
+
__all__: list[str] = [
|
|
58
|
+
'GpsReading',
|
|
59
|
+
'GpsReadingAddressRef',
|
|
60
|
+
'GpsReadingReverseGeo',
|
|
61
|
+
]
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class GpsReadingReverseGeo(ResponseModel):
|
|
65
|
+
"""The ``reverseGeo`` block: the reading's reverse-geocoded location.
|
|
66
|
+
|
|
67
|
+
Present on every sampled reading (2,512/2,512), with
|
|
68
|
+
``formattedLocation`` present in every carrying block.
|
|
69
|
+
|
|
70
|
+
Attributes:
|
|
71
|
+
formatted_location: The reverse-geocoded location string,
|
|
72
|
+
mirrored verbatim (PII-adjacent; fixtures are synthetic).
|
|
73
|
+
"""
|
|
74
|
+
|
|
75
|
+
model_config = ConfigDict(alias_generator=to_camel)
|
|
76
|
+
|
|
77
|
+
formatted_location: str
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class GpsReadingAddressRef(ResponseModel):
|
|
81
|
+
"""The ``address`` block: a matched address-book reference (401/2,512).
|
|
82
|
+
|
|
83
|
+
Present when the reading fell inside a defined address/geofence --
|
|
84
|
+
the addresses surface is the book it references.
|
|
85
|
+
|
|
86
|
+
Attributes:
|
|
87
|
+
id: The defined address's id -- a string on the wire.
|
|
88
|
+
name: The defined address's display name.
|
|
89
|
+
"""
|
|
90
|
+
|
|
91
|
+
id: str
|
|
92
|
+
name: str
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class GpsReading(ResponseModel):
|
|
96
|
+
"""One GPS reading of one vehicle, at the reading grain.
|
|
97
|
+
|
|
98
|
+
A pure mirror of the flat post-decoder record (module docstring:
|
|
99
|
+
the identity fields are decoder-synthesized, the reading fields
|
|
100
|
+
wire-verbatim). Field semantics and units are Samsara's; no value
|
|
101
|
+
is derived or interpreted here.
|
|
102
|
+
|
|
103
|
+
Attributes:
|
|
104
|
+
vehicle_id: The vehicle's Samsara id -- a numeric string,
|
|
105
|
+
decoder-synthesized from the vehicle record's ``id``.
|
|
106
|
+
vehicle_name: The vehicle's display name, decoder-synthesized
|
|
107
|
+
from the vehicle record's ``name``.
|
|
108
|
+
vehicle_serial: The gateway serial, decoder-synthesized from
|
|
109
|
+
``externalIds['samsara.serial']`` (74/74 on the censused
|
|
110
|
+
page; optional -- module docstring).
|
|
111
|
+
vehicle_vin: The VIN, decoder-synthesized from
|
|
112
|
+
``externalIds['samsara.vin']`` (74/74 on the censused page;
|
|
113
|
+
optional -- module docstring).
|
|
114
|
+
time: The reading instant (RFC3339, recovered tz-aware UTC) --
|
|
115
|
+
the event-time column; readings fall strictly inside the
|
|
116
|
+
requested window.
|
|
117
|
+
latitude: Reading latitude, decimal degrees.
|
|
118
|
+
longitude: Reading longitude, decimal degrees.
|
|
119
|
+
heading_degrees: Heading in degrees, a bare int on every
|
|
120
|
+
sampled reading.
|
|
121
|
+
speed_miles_per_hour: Speed in miles per hour -- MIXED
|
|
122
|
+
int|float on the wire, modeled float (module docstring).
|
|
123
|
+
is_ecu_speed: Whether the speed came from the ECU.
|
|
124
|
+
reverse_geo: The reverse-geocoded location block (2,512/2,512).
|
|
125
|
+
address: The matched address-book reference (401/2,512).
|
|
126
|
+
"""
|
|
127
|
+
|
|
128
|
+
model_config = ConfigDict(alias_generator=to_camel)
|
|
129
|
+
|
|
130
|
+
# Decoder-synthesized vehicle identity.
|
|
131
|
+
vehicle_id: str
|
|
132
|
+
vehicle_name: str
|
|
133
|
+
vehicle_serial: str | None = None
|
|
134
|
+
vehicle_vin: str | None = None
|
|
135
|
+
|
|
136
|
+
# Wire-verbatim reading payload.
|
|
137
|
+
time: datetime
|
|
138
|
+
latitude: float
|
|
139
|
+
longitude: float
|
|
140
|
+
heading_degrees: int
|
|
141
|
+
speed_miles_per_hour: float
|
|
142
|
+
is_ecu_speed: bool
|
|
143
|
+
reverse_geo: GpsReadingReverseGeo
|
|
144
|
+
|
|
145
|
+
# The partial block (absence-shaped).
|
|
146
|
+
address: GpsReadingAddressRef | None = None
|