Repository navigation
feat/measurements-uploader - #23
Merged
Merged
Conversation
Record the legacy last-known-location fallback for mobile measurements, that the app uploads mobile measurements only (AB3 SD sync excepted), and the drain-before-finish rule that removes the finished_at open decision. CLAUDE.md: take undocumented business logic from the legacy app. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…nary encoder Add binary payload encoder and test suite for mobile measurement session uploads. - Introduce `MobileFrame` data model and `MobileMeasurementPayload.encode` for encoding frames into the expected binary format (magic header `0xABBA`, 16-bit count, 25 bytes per frame, and trailing XOR checksum). - Enforce validation rules: batch size capped at 3000 frames, non-empty list required, and `sensorTypeId` bounded to uint8. - Add `MobileMeasurementPayloadTest` to verify byte-by-byte encoding accuracy, timestamp truncation to whole seconds, frame limits, and checksum generation.
BE agreed to accept the client's finished_at on mobile /finish. Every frame is stamped by the same phone clock at or before end_time, so upload and /finish may run in any order and the uploader never reads finished_at back. Replaces the drain-before-finish rule. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
v3 cannot add a stream to an existing session, and frames need the sensor_type_id from the create response. Streams without one are skipped by the uploader and logged. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…or mobile sessions
Phone clocks are network-synced and the legacy app never pre-checked timestamps. An invalid_epoch batch throws, stays queued, and is surfaced to the user and crash reporting. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…dy, 409 recovery Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ssion endpoint support
finishSynced -> isFinishedOnServer, contribute -> isContributingToCrowdMap (domain, DB columns, DTOs; wire key stays "contribute" via @SerialName). No DB migration: schema is unreleased. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
AppPreferences: is{CrowdMap,LocationTracking,PushNotifications}Enabled,
isDarkMode, isWifiOnlySync. AuthFormState: isPasswordVisible, isSubmitting.
CustomServerForm: hasHostError/hasPortError; Testing.isTakingTooLong.
AirBeamDevice.canReportOwnState, MicrophonePermission.isGranted,
Deletion.AwaitingCode.isRejected. Storage keys unchanged.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…cument upload triggers Upload is no longer tied to backend sync: v3 has no sync_with_versioning, and mobile measurements now go up live, driven by the outbox. Docs record the legacy triggers and the new loop/backstop design. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds the client side of uploading mobile sessions to
/api/v3: creating the session, sending measurements in batches as binary frames, and finishing it. The code is three layered classes indata/network/upload/. Each one can be called again after a failure and picks up where it stopped.The class that decides when uploads run (the outbox-observing loop, plus WorkManager /
beginBackgroundTaskas a backstop) is not built here. Its design is agreed and written down indocs/API_V3.md→ When the uploader runs.What's in it
MobileMeasurementPayload: binary frame encoder0xAB 0xBA+uint16frame count, then one frame per measurement (int32epoch seconds,uint8sensor_type_id,float32value,float64lat,float64lng), then a trailing XOR checksum.MaxFrames = 3000frames, and any sensor_type_id outside1..255.MobileMeasurementUploader: sends one session's outbox/mobile_sessions/{uuid}/measurements.sensor_type_id, or the row has no location) is skipped and logged as an error. It stays in the outbox and is never dropped silently.MobileSessionUpload: brings one session up to dateRuns create (once) → upload outbox →
/finish(once, after stop). Each step is skipped if it was already done:sensor_type_id. The ids from the response are written back to the local streams.409on create means an earlier create reached the server but its response was lost. In that case the ids are read back withGET /mobile_sessions/{uuid}.end_timeasfinished_at, then sets the newis_finished_on_serverflag.API / DTO changes
CreateMobileSessionRequestDtonow has a flat body (thesessionwrapper is gone) and addsdevice { mac_address, model },tag_listand a requiredstreams.createSessionnow returnsCreateMobileSessionResponseDto(streams[] { sensor_name, sensor_type_id }).finishSession(token, uuid, finishedAt)and thev3MobileSessionFinishendpoint.GETsession/list DTOs now includestreams, keyed bysensor_name. This is what the 409 read-back uses.Persistence
sessions.is_finished_on_servercolumn,markFinishedOnServerquery, and a matching method onSessionRepository.contribute→is_contributing_to_crowd_map. The JSON name stayscontributevia@SerialName.Boolean naming refactor (mechanical)
Boolean fields across the app now read as questions:
granted→isGranted,darkMode→isDarkMode,contribute→isContributingToCrowdMap, and the same in auth, settings, mic and custom-server. No behavior change.Decisions recorded in docs
docs/API_V3.mdsensor_type_id.finished_at, so upload and/finishcan run in either order.docs/DOMAIN_KNOWLEDGE.mdV2MobileMeasurementsInserter).CLAUDE.md: when a business rule isn't covered by/docs/, take it from the legacy app's code and record it in/docs/.is_finished_on_serverwas added directly inSession.sq, with no.sqmfile. That's fine before release, but any existing local install needs a clean reinstall or app-data wipe.MobileSessionUploadyet. That comes with the trigger loop in a follow-up.Tests
MobileMeasurementPayloadTest: byte layout, checksum, input limits.MobileMeasurementUploaderTest: batching at 3000, acknowledge only on success, skipped unsendable rows, no token → nothing sent.MobileSessionUploadTest: create once and write the ids back, 409 read-back, skip create when ids exist, finish only after stop and only once, resume after a partial failure.UploadFakes.kt; existing tests updated for the boolean renames.