Documentation
¶
Index ¶
- func ClickHouseDSNFromConfig(cfg Config) string
- func SetRealtimeConverter(fn func(float64, string) (float64, bool))
- type AnalyticsEvent
- type AnalyticsSession
- type AnalyticsSummary
- type AnalyticsSummaryItem
- type Bounds
- type Config
- type Data
- type Database
- func (db *Database) AnalyticsSessionByFingerprint(ctx context.Context, fingerprint string, dbType string) (AnalyticsSession, bool, error)
- func (db *Database) AnalyticsVisitorSeed(ctx context.Context, dbType string) (int, error)
- func (db *Database) AnnotateAreaRadiationWindow(ctx context.Context, fromUnix, toUnix int64, ...) error
- func (db *Database) AnnotateTrackRadiationWindow(ctx context.Context, trackID string, fromUnix, toUnix int64, ...) error
- func (db *Database) CountImportHistory(ctx context.Context, source, dbType string) (int64, error)
- func (db *Database) CountTrackIDsUpTo(ctx context.Context, trackID, dbType string) (int64, error)
- func (db *Database) CountTracks(ctx context.Context) (int64, error)
- func (db *Database) CountTracksInRange(ctx context.Context, from, to int64, dbType string) (int64, error)
- func (db *Database) DetectExistingTrackID(markers []Marker, threshold int, dbType string) (string, error)
- func (db *Database) EnsureImportHistory(ctx context.Context, source, sourceID, trackID, status, message, dbType string) error
- func (db *Database) EnsureIndexesAsync(ctx context.Context, cfg Config, logf func(string, ...any)) <-chan struct{}
- func (db *Database) EnsureTrackPresence(ctx context.Context, trackID, dbType string) error
- func (db *Database) EnsureTrackUser(ctx context.Context, trackID, userID, source, dbType string) error
- func (db *Database) EnsureUserBySource(ctx context.Context, source, sourceUserID, name, dbType string) (string, error)
- func (db *Database) FillMissingTrackDeviceName(ctx context.Context, trackID, deviceName, dbType string) error
- func (db *Database) FindImportHistory(ctx context.Context, source, sourceID, dbType string) (ImportHistory, bool, error)
- func (db *Database) GetLatestRealtimeByBounds(ctx context.Context, minLat, minLon, maxLat, maxLon float64, dbType string) ([]Marker, error)
- func (db *Database) GetMarkersByTrackID(ctx context.Context, trackID string, dbType string) ([]Marker, error)
- func (db *Database) GetMarkersByTrackIDAndBounds(ctx context.Context, trackID string, minLat, minLon, maxLat, maxLon float64, ...) ([]Marker, error)
- func (db *Database) GetMarkersByTrackIDZoomAndBounds(ctx context.Context, trackID string, zoom int, ...) ([]Marker, error)
- func (db *Database) GetMarkersByTrackIDZoomBoundsSpeed(ctx context.Context, trackID string, zoom int, ...) ([]Marker, error)
- func (db *Database) GetMarkersByZoomAndBounds(ctx context.Context, zoom int, minLat, minLon, maxLat, maxLon float64, ...) ([]Marker, error)
- func (db *Database) GetMarkersByZoomBoundsSpeed(ctx context.Context, zoom int, minLat, minLon, maxLat, maxLon float64, ...) ([]Marker, error)
- func (db *Database) GetRealtimeHistory(deviceID string, since int64, dbType string) ([]RealtimeMeasurement, error)
- func (db *Database) GetTrackDeviceName(ctx context.Context, trackID, dbType string) (string, error)
- func (db *Database) GetTrackDeviceSummary(ctx context.Context, trackID, dbType string) (DeviceSummary, error)
- func (db *Database) GetTrackIDByIndex(ctx context.Context, index int64, dbType string) (string, error)
- func (db *Database) GetTrackSummary(ctx context.Context, trackID, dbType string) (TrackSummary, error)
- func (db *Database) ImportHistoryStats(ctx context.Context, source, dbType string) (int64, time.Time, error)
- func (db *Database) InitSchema(cfg Config, logf func(string, ...any)) error
- func (db *Database) InsertAnalyticsEvent(ctx context.Context, event AnalyticsEvent, dbType string) error
- func (db *Database) InsertMarkersBulk(ctx context.Context, tx *sql.Tx, markers []Marker, dbType string, batch int, ...) (err error)
- func (db *Database) InsertRealtimeMeasurement(m RealtimeMeasurement, dbType string) error
- func (db *Database) LatestImportHistory(ctx context.Context, source, dbType string) (string, time.Time, error)
- func (db *Database) PersistShortLink(ctx context.Context, target, code string, now time.Time, length int) (string, error)
- func (db *Database) PreviewShortLink(ctx context.Context, target string, length int) (code string, stored bool, err error)
- func (db *Database) PromoteStaleRealtime(cutoff int64, dbType string) error
- func (db *Database) QueryAnalyticsSummary(ctx context.Context, start, end int64, limit int, dbType string) (AnalyticsSummary, error)
- func (db *Database) ResolveShortLink(ctx context.Context, code string) (string, error)
- func (db *Database) ResolveUserBySource(ctx context.Context, source, sourceUserID, dbType string) (string, string, error)
- func (db *Database) SaveMarkerAtomic(ctx context.Context, exec sqlExecutor, m Marker, dbType string) error
- func (db *Database) ScheduleDuckDBMaintenance(ctx context.Context, logf func(string, ...any)) <-chan error
- func (db *Database) StreamLatestMarkersNear(ctx context.Context, lat float64, lon float64, radiusMeters float64, limit int, ...) (<-chan Marker, <-chan error)
- func (db *Database) StreamMarkersByTrackIDZoomAndBounds(ctx context.Context, trackID string, zoom int, ...) (<-chan Marker, <-chan error)
- func (db *Database) StreamMarkersByTrackIDZoomBoundsSpeed(ctx context.Context, trackID string, zoom int, ...) (<-chan Marker, <-chan error)
- func (db *Database) StreamMarkersByTrackRange(ctx context.Context, trackID string, fromID int64, toID int64, limit int, ...) (<-chan Marker, <-chan error)
- func (db *Database) StreamMarkersByZoomAndBounds(ctx context.Context, zoom int, minLat, minLon, maxLat, maxLon float64, ...) (<-chan Marker, <-chan error)
- func (db *Database) StreamMarkersByZoomBoundsSpeed(ctx context.Context, zoom int, minLat, minLon, maxLat, maxLon float64, ...) (<-chan Marker, <-chan error)
- func (db *Database) StreamMarkersByZoomBoundsSpeedOrderedByTrackDate(ctx context.Context, zoom int, minLat, minLon, maxLat, maxLon float64, ...) (<-chan Marker, <-chan error)
- func (db *Database) StreamTrackSummaries(ctx context.Context, startAfter string, limit int, dbType string) (<-chan TrackSummary, <-chan error)
- func (db *Database) StreamTrackSummariesByDateRange(ctx context.Context, startAfter string, limit int, from int64, to int64, ...) (<-chan TrackSummary, <-chan error)
- func (db *Database) TrackExists(ctx context.Context, trackID, dbType string) (bool, error)
- func (db *Database) TrackHasDeviceName(ctx context.Context, trackID, dbType string) (bool, error)
- func (db *Database) UpdateTrackDeviceName(ctx context.Context, trackID, deviceName, dbType string) error
- func (db *Database) UpdateUserNameIfEmpty(ctx context.Context, userID, name, dbType string) error
- func (db *Database) UpsertAnalyticsSession(ctx context.Context, session AnalyticsSession, dbType string) error
- type DeviceSummary
- type ImportHistory
- type Marker
- type MarkerBatchProgress
- type RealtimeMeasurement
- type SpeedRange
- type TrackSummary
- type WorkloadKind
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func ClickHouseDSNFromConfig ¶
ClickHouseDSNFromConfig assembles a DSN understood by the lightweight HTTP driver. We parse host/port carefully so IPv6 literals keep their brackets intact.
func SetRealtimeConverter ¶
SetRealtimeConverter stores the helper used to convert Safecast realtime units. We call it from main when the realtime feature is enabled so other database code can stay agnostic of specific detector logic.
Types ¶
type AnalyticsEvent ¶
type AnalyticsEvent struct {
SessionID string
DisplayName string
OccurredAt int64
Kind string
Path string
IP string
Referer string
UserAgent string
Region string
Theme string
Layer string
Speed string
MapZoom int
CenterLat float64
CenterLon float64
DoseClass string
TrackKind string
TrackID string
Detector string
Detail string
}
AnalyticsEvent captures a single user action or request with lightweight metadata for later summarization.
type AnalyticsSession ¶
type AnalyticsSession struct {
SessionID string
DisplayName string
VisitorNumber int
Fingerprint string
IP string
UserAgent string
Referer string
CreatedAt int64
LastSeenAt int64
VisitCount int
}
AnalyticsSession stores durable, human-readable visitor identity metadata so logs can tell real people apart across requests.
type AnalyticsSummary ¶
type AnalyticsSummary struct {
TotalEvents int
UniqueSessions int
TopUsers []AnalyticsSummaryItem
TopKinds []AnalyticsSummaryItem
TopRegions []AnalyticsSummaryItem
TopDoseClasses []AnalyticsSummaryItem
TopTrackKinds []AnalyticsSummaryItem
TopReferrers []AnalyticsSummaryItem
}
AnalyticsSummary aggregates the headline hourly metrics into a single struct so the logger can emit concise, human-readable lines.
type AnalyticsSummaryItem ¶
AnalyticsSummaryItem holds a label/count pair for hourly rollups.
type Config ¶
type Config struct {
DBType string // The type of the database driver (e.g., "sqlite", "chai", or "pgx" (PostgreSQL))
DBPath string // The file path to the database file (for file-based databases)
DBConn string // Raw DSN for network drivers (pgx or clickhouse)
DBHost string // The host for PostgreSQL
DBPort int // The port for PostgreSQL
DBUser string // The user for PostgreSQL
DBPass string // The password for PostgreSQL
DBName string // The name of the PostgreSQL database
PGSSLMode string // The SSL mode for PostgreSQL
ClickSecure bool // Enable TLS when connecting to ClickHouse over HTTP transport
Port int // The port number (used in database file naming if needed)
}
Config holds the configuration details for initializing the database.
type Data ¶
type Data struct {
ID string `json:"id"`
Markers []Marker `json:"markers"`
Title string `json:"title"`
Devices []string `json:"devices"` // Devices keeps upstream identifiers so importers can derive the model name.
// NEW — принимаем оба варианта имён
IsSievert bool `json:"sv"` // новое поле Radiacode-Android
IsSievertLegacy bool `json:"isSievert"` // старые iOS-дампы
}
type Database ¶
type Database struct {
DB *sql.DB // The underlying SQL database connection
Driver string // Normalized driver name so SQL builders can stay declarative
// contains filtered or unexported fields
}
Database represents the interface for interacting with the database.
func NewDatabase ¶
NewDatabase opens DB and configures connection pooling. For SQLite/Chai we force single-connection mode (no concurrent DB access).
func (*Database) AnalyticsSessionByFingerprint ¶
func (db *Database) AnalyticsSessionByFingerprint(ctx context.Context, fingerprint string, dbType string) (AnalyticsSession, bool, error)
AnalyticsSessionByFingerprint resolves a session using the stable fingerprint so repeat visitors can be recognized even when their IP changes.
func (*Database) AnalyticsVisitorSeed ¶
AnalyticsVisitorSeed returns the highest known visitor index so new sessions can continue the sequential counter without reusing numbers after restarts.
func (*Database) AnnotateAreaRadiationWindow ¶
func (db *Database) AnnotateAreaRadiationWindow(ctx context.Context, fromUnix, toUnix int64, minLat, minLon, maxLat, maxLon float64, radiationText, dbType string) error
AnnotateAreaRadiationWindow writes qualitative isotope composition into markers inside a bounding box and time window.
func (*Database) AnnotateTrackRadiationWindow ¶
func (db *Database) AnnotateTrackRadiationWindow(ctx context.Context, trackID string, fromUnix, toUnix int64, radiationText, dbType string) error
AnnotateTrackRadiationWindow writes qualitative isotope composition into the radiation field for markers captured in a time window.
func (*Database) CountImportHistory ¶
CountImportHistory reports how many records exist for a source so loaders can decide whether to run a full backfill or a delta refresh.
func (*Database) CountTrackIDsUpTo ¶
CountTrackIDsUpTo returns how many distinct track IDs are lexicographically less than or equal to the provided ID. We use it to translate string track IDs into stable numeric indices for the API, skipping realtime-only entries so indices align with paged track summaries.
func (*Database) CountTracks ¶
CountTracks returns the total number of distinct track IDs. The API layer uses this to hint clients about the upper bound of the pagination sequence so they can plan how many requests to issue. We count all markers regardless of zoom so archive exports never miss tracks whose data arrived with differing zoom levels, while filtering out realtime and blank IDs so the total matches the paged track summaries.
func (*Database) CountTracksInRange ¶
func (db *Database) CountTracksInRange(ctx context.Context, from, to int64, dbType string) (int64, error)
CountTracksInRange reports how many distinct tracks contain markers inside the provided date window. Handlers expose the number so API users know how many pages exist for the requested period, excluding realtime-only IDs so the totals match the paged summary streams.
func (*Database) DetectExistingTrackID ¶
func (db *Database) DetectExistingTrackID( markers []Marker, threshold int, dbType string, ) (string, error)
DetectExistingTrackID scans the first incoming markers and tries to recognise an already-stored track. If ≥ threshold identical markers (lat,lon,date,doseRate) point to the same TrackID, that TrackID is returned; otherwise an empty string is returned.
– Works with any SQL driver: only simple equality, no vendor features. – No mutexes: each call owns its slice and DB handle is concurrency-safe. – Follows the “return early” advice: exits as soon as threshold is reached.
func (*Database) EnsureImportHistory ¶
func (db *Database) EnsureImportHistory(ctx context.Context, source, sourceID, trackID, status, message, dbType string) error
EnsureImportHistory records a completed import so future runs can skip already-processed sources. The caller controls the status value.
func (*Database) EnsureIndexesAsync ¶
func (db *Database) EnsureIndexesAsync(ctx context.Context, cfg Config, logf func(string, ...any)) <-chan struct{}
EnsureIndexesAsync builds non-critical indexes in background, politely. - No pinned connections (important for sqlite/chai with MaxOpenConns(1)). - No pre-checks: just CREATE INDEX IF NOT EXISTS. - Retries with exponential backoff on "database is locked"/"SQLITE_BUSY".
func (*Database) EnsureTrackPresence ¶
EnsureTrackPresence keeps the lightweight tracks registry in sync with incoming marker inserts so pagination can avoid repeated DISTINCT scans. We deliberately use INSERT…WHERE NOT EXISTS instead of ON CONFLICT to stay portable across the supported engines while still preventing duplicate rows.
func (*Database) EnsureTrackUser ¶
func (db *Database) EnsureTrackUser(ctx context.Context, trackID, userID, source, dbType string) error
EnsureTrackUser records that a track belongs to a user so future account pages can filter by ownership without rescanning markers.
func (*Database) EnsureUserBySource ¶
func (db *Database) EnsureUserBySource(ctx context.Context, source, sourceUserID, name, dbType string) (string, error)
EnsureUserBySource returns a stable internal user ID and stores the display name when it is available, keeping the mapping ready for future account links.
func (*Database) FillMissingTrackDeviceName ¶
func (db *Database) FillMissingTrackDeviceName(ctx context.Context, trackID, deviceName, dbType string) error
FillMissingTrackDeviceName updates only empty device_name values so existing labels remain unchanged.
func (*Database) FindImportHistory ¶
func (db *Database) FindImportHistory(ctx context.Context, source, sourceID, dbType string) (ImportHistory, bool, error)
FindImportHistory returns an existing import history record for the given source ID so importers can skip already-processed payloads.
func (*Database) GetLatestRealtimeByBounds ¶
func (db *Database) GetLatestRealtimeByBounds(ctx context.Context, minLat, minLon, maxLat, maxLon float64, dbType string) ([]Marker, error)
GetLatestRealtimeByBounds returns the newest reading per device within bounds. We keep SQL portable and filter duplicates in Go, following "Clear is better than clever".
func (*Database) GetMarkersByTrackID ¶
func (db *Database) GetMarkersByTrackID(ctx context.Context, trackID string, dbType string) ([]Marker, error)
GetMarkersByTrackID retrieves markers filtered by trackID.
func (*Database) GetMarkersByTrackIDAndBounds ¶
func (db *Database) GetMarkersByTrackIDAndBounds(ctx context.Context, trackID string, minLat, minLon, maxLat, maxLon float64, dbType string) ([]Marker, error)
GetMarkersByTrackIDAndBounds retrieves markers filtered by trackID and geographical bounds.
func (*Database) GetMarkersByTrackIDZoomAndBounds ¶
func (db *Database) GetMarkersByTrackIDZoomAndBounds( ctx context.Context, trackID string, zoom int, minLat, minLon, maxLat, maxLon float64, dbType string, ) ([]Marker, error)
GetMarkersByTrackIDZoomAndBounds исправленный вариант
func (*Database) GetMarkersByTrackIDZoomBoundsSpeed ¶
func (*Database) GetMarkersByZoomAndBounds ¶
func (db *Database) GetMarkersByZoomAndBounds(ctx context.Context, zoom int, minLat, minLon, maxLat, maxLon float64, dbType string) ([]Marker, error)
GetMarkersByZoomAndBounds retrieves markers filtered by zoom level and geographical bounds. The caller supplies a context so web requests can cancel ongoing scans and free the serialized DuckDB lane for imports while still bounding the maximum wait via WithTimeout.
func (*Database) GetMarkersByZoomBoundsSpeed ¶
func (db *Database) GetMarkersByZoomBoundsSpeed( ctx context.Context, zoom int, minLat, minLon, maxLat, maxLon float64, dateFrom, dateTo int64, speedRanges []SpeedRange, dbType string, ) ([]Marker, error)
GetMarkersByZoomBoundsSpeed — z/bounds/date/speed фильтр. Мини-оптимизация: если несколько speedRanges образуют один непрерывный интервал, склеиваем их в ОДИН "speed BETWEEN lo AND hi", чтобы планировщик использовал составной индекс (zoom,lat,lon,speed).
func (*Database) GetRealtimeHistory ¶
func (db *Database) GetRealtimeHistory(deviceID string, since int64, dbType string) ([]RealtimeMeasurement, error)
GetRealtimeHistory returns all realtime measurements for a device since the requested timestamp. Callers can reuse the raw transport/name metadata to describe the sensor while charting the values.
func (*Database) GetTrackDeviceName ¶
GetTrackDeviceName returns the first non-empty device name for a track so exports can attach a track-level instrument label.
func (*Database) GetTrackDeviceSummary ¶
func (db *Database) GetTrackDeviceSummary(ctx context.Context, trackID, dbType string) (DeviceSummary, error)
GetTrackDeviceSummary fetches a single marker for the track so uploads can be tagged with detector metadata without scanning the full dataset.
func (*Database) GetTrackIDByIndex ¶
func (db *Database) GetTrackIDByIndex(ctx context.Context, index int64, dbType string) (string, error)
GetTrackIDByIndex resolves a 1-based numeric index to the actual track ID. Returning an empty string keeps HTTP handlers free to decide how to map it to status codes, while filtering realtime-only IDs to keep indexes aligned.
func (*Database) GetTrackSummary ¶
func (db *Database) GetTrackSummary(ctx context.Context, trackID, dbType string) (TrackSummary, error)
GetTrackSummary returns metadata for a single track. Keeping this function tiny lets the HTTP handler reuse the information for range validation without duplicating SQL statements.
func (*Database) ImportHistoryStats ¶
func (db *Database) ImportHistoryStats(ctx context.Context, source, dbType string) (int64, time.Time, error)
ImportHistoryStats returns the total count and latest import timestamp for a source so callers can log health information without scanning full history.
func (*Database) InitSchema ¶
InitSchema creates minimal required schema synchronously so that the app can accept traffic immediately. Heavy indexes are built later by EnsureIndexesAsync in background.
func (*Database) InsertAnalyticsEvent ¶
func (db *Database) InsertAnalyticsEvent(ctx context.Context, event AnalyticsEvent, dbType string) error
InsertAnalyticsEvent records an activity event without blocking the caller.
func (*Database) InsertMarkersBulk ¶
func (db *Database) InsertMarkersBulk(ctx context.Context, tx *sql.Tx, markers []Marker, dbType string, batch int, progress chan<- MarkerBatchProgress, lane WorkloadKind) (err error)
InsertMarkersBulk inserts markers in batches using multi-row VALUES. - Portable: only standard SQL and database/sql, no vendor extensions. - Fast: far fewer statements, WAL and B-Tree updates coalesce better. - Safe: still respects the unique key via ON CONFLICT DO NOTHING.
Go-proverbs applied:
- "A little copying is better than a little dependency" — we build SQL by hand.
- "Don't communicate by sharing memory; share memory by communicating" — idGenerator via channel.
- "Make the zero value useful" — batch<=0 falls back to 500.
- "The bigger the interface, the weaker the abstraction" — DuckDB wraps in one transaction instead of leaking SAVEPOINT assumptions per chunk.
Context is threaded through so stalled archive entries can abandon long-running database calls instead of blocking later work. We check cancellation between batches and rely on ExecContext to let drivers break out promptly. When the serialized pipeline is enabled we funnel each batch through the provided workload lane so long TGZ imports cannot monopolize the single DuckDB writer and block web reads or one-off uploads.
func (*Database) InsertRealtimeMeasurement ¶
func (db *Database) InsertRealtimeMeasurement(m RealtimeMeasurement, dbType string) error
InsertRealtimeMeasurement stores live device data and skips duplicates. A little copying is better than a little dependency, so we build SQL by hand.
func (*Database) LatestImportHistory ¶
func (db *Database) LatestImportHistory(ctx context.Context, source, dbType string) (string, time.Time, error)
LatestImportHistory returns the newest source ID and timestamp for a source so callers can include the freshest identifier in logs.
func (*Database) PersistShortLink ¶
func (db *Database) PersistShortLink(ctx context.Context, target, code string, now time.Time, length int) (string, error)
PersistShortLink inserts the provided mapping if it does not exist yet. We accept an optional pre-selected code so the UI can reserve a string for the user before they confirm copying, matching the "share memory by communicating" proverb — the browser communicates the reservation back instead of reaching for shared globals.
func (*Database) PreviewShortLink ¶
func (db *Database) PreviewShortLink(ctx context.Context, target string, length int) (code string, stored bool, err error)
PreviewShortLink fetches an existing mapping or proposes a fresh random code. We prefer explicit control flow over clever caching so operators can reason about the behaviour — echoing the proverb "Clear is better than clever." The helper never writes to the database; it simply avoids duplicates by probing the current table and respects context cancellation so callers can bail out early if the user navigates away.
func (*Database) PromoteStaleRealtime ¶
PromoteStaleRealtime moves device histories from the realtime table into the regular markers table when a device has been offline for more than a day and changed its position. This keeps long tracks for mobile devices while leaving stationary sensors in place. The cutoff value is a Unix timestamp (seconds) – any device whose newest fetched_at is older is eligible.
func (*Database) QueryAnalyticsSummary ¶
func (db *Database) QueryAnalyticsSummary(ctx context.Context, start, end int64, limit int, dbType string) (AnalyticsSummary, error)
QueryAnalyticsSummary returns an hourly rollup used for log output.
func (*Database) ResolveShortLink ¶
ResolveShortLink expands a short code into the stored absolute URL.
func (*Database) ResolveUserBySource ¶
func (db *Database) ResolveUserBySource(ctx context.Context, source, sourceUserID, dbType string) (string, string, error)
ResolveUserBySource returns the internal user ID associated with an external provider identifier so imports can connect tracks to future accounts.
func (*Database) SaveMarkerAtomic ¶
func (db *Database) SaveMarkerAtomic( ctx context.Context, exec sqlExecutor, m Marker, dbType string, ) error
SaveMarkerAtomic inserts a marker and silently ignores duplicates.
- PostgreSQL (pgx) – опираемся на BIGSERIAL, id не передаём;
- SQLite и Chai – если id == 0, берём следующий из idGenerator. Это устраняет ошибку, когда все агрегатные маркеры имели id-0 и вторая вставка ломалась на UNIQUE PRIMARY KEY.
func (*Database) ScheduleDuckDBMaintenance ¶
func (db *Database) ScheduleDuckDBMaintenance(ctx context.Context, logf func(string, ...any)) <-chan error
scheduleDuckDBMaintenance exposes a channel-driven hook for callers that want to run a checkpoint/optimize/vacuum cycle after heavy imports. We only wire it for the DuckDB driver because other engines handle maintenance differently.
func (*Database) StreamLatestMarkersNear ¶
func (db *Database) StreamLatestMarkersNear( ctx context.Context, lat float64, lon float64, radiusMeters float64, limit int, dbType string, ) (<-chan Marker, <-chan error)
StreamLatestMarkersNear streams the newest markers around a coordinate. We favour a streaming design so callers can start encoding results without waiting for the full slice, mirroring the Go proverb "Don't communicate by sharing memory; share memory by communicating".
func (*Database) StreamMarkersByTrackIDZoomAndBounds ¶
func (db *Database) StreamMarkersByTrackIDZoomAndBounds(ctx context.Context, trackID string, zoom int, minLat, minLon, maxLat, maxLon float64, dbType string) (<-chan Marker, <-chan error)
StreamMarkersByTrackIDZoomAndBounds streams markers of one track within bounds. This keeps memory usage low while focusing on a single track only.
func (*Database) StreamMarkersByTrackIDZoomBoundsSpeed ¶
func (db *Database) StreamMarkersByTrackIDZoomBoundsSpeed(ctx context.Context, trackID string, zoom int, minLat, minLon, maxLat, maxLon float64, speedRanges []SpeedRange, dbType string) (<-chan Marker, <-chan error)
StreamMarkersByTrackIDZoomBoundsSpeed streams markers for a single track with speed filters. Keeping the speed filter in SQL avoids client-side work on large tracks.
func (*Database) StreamMarkersByTrackRange ¶
func (db *Database) StreamMarkersByTrackRange( ctx context.Context, trackID string, fromID int64, toID int64, limit int, dbType string, ) (<-chan Marker, <-chan error)
StreamMarkersByTrackRange streams markers by track ID and ID range. An optional LIMIT keeps the dataset bounded when callers request a window; otherwise we stream the entire track.
func (*Database) StreamMarkersByZoomAndBounds ¶
func (db *Database) StreamMarkersByZoomAndBounds(ctx context.Context, zoom int, minLat, minLon, maxLat, maxLon float64, dbType string) (<-chan Marker, <-chan error)
StreamMarkersByZoomAndBounds streams markers row by row through a channel. It avoids loading large result sets into memory and stops when the context is done.
func (*Database) StreamMarkersByZoomBoundsSpeed ¶
func (db *Database) StreamMarkersByZoomBoundsSpeed(ctx context.Context, zoom int, minLat, minLon, maxLat, maxLon float64, speedRanges []SpeedRange, dbType string) (<-chan Marker, <-chan error)
StreamMarkersByZoomBoundsSpeed streams markers for the current viewport and speed filters. It mirrors GetMarkersByZoomBoundsSpeed but keeps memory usage low for large tiles.
func (*Database) StreamMarkersByZoomBoundsSpeedOrderedByTrackDate ¶
func (db *Database) StreamMarkersByZoomBoundsSpeedOrderedByTrackDate( ctx context.Context, zoom int, minLat, minLon, maxLat, maxLon float64, dateFrom, dateTo int64, speedRanges []SpeedRange, dbType string, ) (<-chan Marker, <-chan error)
------------------------------------------------------------------ StreamMarkersByZoomBoundsSpeedOrderedByTrackDate ------------------------------------------------------------------ Streams markers in track/date order so playback can render each track as soon as its buffer arrives, without waiting for the full dataset in memory. We keep the SQL portable by building the WHERE clause with placeholders and only add the ORDER BY clause for deterministic ordering.
func (*Database) StreamTrackSummaries ¶
func (db *Database) StreamTrackSummaries( ctx context.Context, startAfter string, limit int, dbType string, ) (<-chan TrackSummary, <-chan error)
StreamTrackSummaries streams metadata about tracks ordered by their ID. We delegate to the shared streamTrackSummaries helper so future filters (year/month) reuse the same channel-based plumbing.
func (*Database) StreamTrackSummariesByDateRange ¶
func (db *Database) StreamTrackSummariesByDateRange( ctx context.Context, startAfter string, limit int, from int64, to int64, dbType string, ) (<-chan TrackSummary, <-chan error)
StreamTrackSummariesByDateRange restricts tracks to a time window. We expose it for the year/month API variants so they can reuse the streaming pattern without duplicating SQL logic.
func (*Database) TrackExists ¶
TrackExists checks whether a track identifier already exists in the registry table. We keep it lightweight so loaders can avoid duplicate network fetches.
func (*Database) TrackHasDeviceName ¶
TrackHasDeviceName checks whether any marker in the track already carries a device label. We use it to avoid downloading full track payloads when only the device name is missing.
func (*Database) UpdateTrackDeviceName ¶
func (db *Database) UpdateTrackDeviceName(ctx context.Context, trackID, deviceName, dbType string) error
UpdateTrackDeviceName stamps a device label onto all markers in a track so the UI can show instrument names without extra joins.
func (*Database) UpdateUserNameIfEmpty ¶
UpdateUserNameIfEmpty populates missing display names so later UI work can show a friendly label without rewriting non-empty data.
func (*Database) UpsertAnalyticsSession ¶
func (db *Database) UpsertAnalyticsSession(ctx context.Context, session AnalyticsSession, dbType string) error
UpsertAnalyticsSession records the latest activity for a session while keeping the visit count aligned to unique days.
type DeviceSummary ¶
DeviceSummary holds track-level device hints for activity logging.
type ImportHistory ¶
type ImportHistory struct {
Source string
SourceID string
TrackID string
Status string
Imported int64
Message string
}
ImportHistory describes a single import attempt stored for de-duplication. The table is intentionally non-authoritative so operators can wipe it and force a full re-import without touching the real track data.
type Marker ¶
type Marker struct {
ID int64 `json:"id"` // Unique identifier for the marker (added for database purposes)
DoseRate float64 `json:"doseRate"` // The radiation dose rate in µSv/h (microsieverts per hour)
Date int64 `json:"date"` // Timestamp of the measurement (in UNIX time format)
Lon float64 `json:"lon"` // Longitude of the location where the measurement was taken
Lat float64 `json:"lat"` // Latitude of the location where the measurement was taken
CountRate float64 `json:"countRate"` // Count rate of the measurement (CPS - counts per second)
Zoom int `json:"zoom"` // Zoom level
Speed float64 `json:"speed"` // Speed of the measurement point
TrackID string `json:"trackID"` // Identifier of the track
Altitude float64 `json:"altitude,omitempty"` // Elevation in metres above sea level when provided
Detector string `json:"detector,omitempty"` // Detector model or type recorded for the point
Radiation string `json:"radiation,omitempty"` // Radiation channels captured (alpha, beta, gamma)
Temperature float64 `json:"temperature,omitempty"` // Ambient temperature in Celsius when present
Humidity float64 `json:"humidity,omitempty"` // Relative humidity percentage when available
// Live metadata is kept optional so historical markers remain lightweight.
DeviceID string `json:"deviceID,omitempty"` // Safecast device identifier for realtime markers
DeviceName string `json:"deviceName,omitempty"` // Human readable device title when provided
Transport string `json:"transport,omitempty"` // Transport hint such as walk, car or bike
Tube string `json:"tube,omitempty"` // Detector tube description advertised by the feed
Country string `json:"country,omitempty"` // Coarse country hint derived from Safecast payload
LiveExtra map[string]float64 `json:"liveExtra,omitempty"` // Additional numeric metrics (temperature, humidity, ...)
AltitudeValid bool `json:"-"` // Tracks whether altitude was explicitly supplied so exporters can omit empty fields.
TemperatureValid bool `json:"-"` // Marks that temperature was present in the source payload instead of default zero values.
HumidityValid bool `json:"-"` // Signals that humidity existed; keeps downstream encoders from inventing placeholders.
}
Marker represents dosimeter data for a specific location or point.
type MarkerBatchProgress ¶
MarkerBatchProgress reports how many markers a bulk insert has flushed so operators can track forward momentum. We keep a mode flag to distinguish fast-path multi-row execution from fallback duplicate handling, making stall investigations simpler when archives contain unexpected overlap.
type RealtimeMeasurement ¶
type RealtimeMeasurement struct {
ID int64 `json:"id"` // Primary key for database storage
DeviceID string `json:"deviceID"` // Remote device identifier
Transport string `json:"transport"` // How the device moves (car, walk), kept for future use
Value float64 `json:"value"` // Reported radiation value
Unit string `json:"unit"` // Measurement unit from the device
Lat float64 `json:"lat"` // Device latitude
Lon float64 `json:"lon"` // Device longitude
MeasuredAt int64 `json:"measuredAt"` // Timestamp supplied by the device
FetchedAt int64 `json:"fetchedAt"` // When we pulled it, aids freshness checks
DeviceName string `json:"deviceName"` // Human friendly name, stored to describe the sensor in popups
Tube string `json:"tube"` // Detector tube advertised by the device feed
Country string `json:"country"` // Country hint reported or inferred from coordinates
Extra string `json:"extra"` // JSON encoded bag with optional metrics (temperature, humidity)
}
RealtimeMeasurement keeps the latest network readings. We store raw numbers so the history can be rendered later.
type SpeedRange ¶
type SpeedRange struct{ Min, Max float64 }
SpeedRange задаёт замкнутый диапазон [Min, Max] скорости.
type TrackSummary ¶
type TrackSummary struct {
TrackID string `json:"trackID"`
FirstID int64 `json:"firstID"`
LastID int64 `json:"lastID"`
MarkerCount int64 `json:"markerCount"`
Index int64 `json:"index,omitempty"` // 1-based order so clients can fetch by number.
APIURL string `json:"apiURL,omitempty"` // Direct API link helps developers discover the track endpoint.
}
TrackSummary provides lightweight metadata for iterating over tracks. We expose index boundaries so clients can page through markers without issuing unbounded queries, mirroring Go's advice to "keep the interface small" and only return what API callers actually need.
type WorkloadKind ¶
type WorkloadKind int
WorkloadKind enumerates the separate queues we run through select/case so realtime feeds, archive imports, user uploads, and web readers time-share the single connection without letting one long backlog starve the others. Using an explicit type keeps the routing logic readable and mirrors the Go Proverb "Make the zero value useful" by defaulting to the general queue.
const ( WorkloadGeneral WorkloadKind = iota // catch-all when callers do not care WorkloadWebRead // API/UI fetches for map tiles and history WorkloadUserUpload // single track uploads from the web form WorkloadArchive // large TGZ imports and bulk archive loaders WorkloadRealtime // live Safecast device updates )
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package drivers groups database/sql driver registrations so heavy dependencies stay out of lightweight go test/go vet runs unless a binary explicitly imports this package.
|
Package drivers groups database/sql driver registrations so heavy dependencies stay out of lightweight go test/go vet runs unless a binary explicitly imports this package. |