database

package
v1.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 16, 2026 License: MIT Imports: 21 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ClickHouseDSNFromConfig

func ClickHouseDSNFromConfig(cfg Config) string

ClickHouseDSNFromConfig assembles a DSN understood by the lightweight HTTP driver. We parse host/port carefully so IPv6 literals keep their brackets intact.

func SetRealtimeConverter

func SetRealtimeConverter(fn func(float64, string) (float64, bool))

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

type AnalyticsSummaryItem struct {
	Label string
	Count int
}

AnalyticsSummaryItem holds a label/count pair for hourly rollups.

type Bounds

type Bounds struct {
	MinLat, MinLon float64
	MaxLat, MaxLon float64
}

Bounds описывает прямоугольник (minLat,minLon) – (maxLat,maxLon).

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

func NewDatabase(config Config) (*Database, error)

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

func (db *Database) AnalyticsVisitorSeed(ctx context.Context, dbType string) (int, error)

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

func (db *Database) CountImportHistory(ctx context.Context, source, dbType string) (int64, error)

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

func (db *Database) CountTrackIDsUpTo(ctx context.Context, trackID, dbType string) (int64, error)

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

func (db *Database) CountTracks(ctx context.Context) (int64, error)

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) DeleteTrackZoomAggregates

func (db *Database) DeleteTrackZoomAggregates(ctx context.Context, trackID, dbType string) error

DeleteTrackZoomAggregates removes derived map layers while preserving raw zoom=0 measurements for exports and future rebuilds.

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

func (db *Database) EnsureTrackPresence(ctx context.Context, trackID, dbType string) error

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) GetMaintenanceState

func (db *Database) GetMaintenanceState(ctx context.Context, dbType, task string) (string, bool, error)

GetMaintenanceState exposes lightweight task state to application-level maintenance jobs that own their rebuild logic outside the database package.

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 (db *Database) GetMarkersByTrackIDZoomBoundsSpeed(
	ctx context.Context,
	trackID string,
	zoom int,
	minLat, minLon, maxLat, maxLon float64,
	dateFrom, dateTo int64,
	speedRanges []SpeedRange,
	dbType string,
) ([]Marker, error)

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

func (db *Database) GetTrackDeviceName(ctx context.Context, trackID, dbType string) (string, error)

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

func (db *Database) InitSchema(cfg Config, logf func(string, ...any)) error

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 (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 (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

func (db *Database) PromoteStaleRealtime(cutoff int64, dbType string) error

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 (db *Database) ResolveShortLink(ctx context.Context, code string) (string, error)

ResolveShortLink expands a short code into the stored absolute URL.

func (*Database) ResolveTrackMarkerZoom

func (db *Database) ResolveTrackMarkerZoom(ctx context.Context, trackID string, requestedZoom int, dbType string) (int, error)

ResolveTrackMarkerZoom returns the densest precomputed zoom layer at or below the requested zoom, falling back to the nearest higher layer when older databases do not have a lower layer for that track.

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) SetMaintenanceState

func (db *Database) SetMaintenanceState(ctx context.Context, dbType, task, status, message string) error

SetMaintenanceState records task progress for application-level maintenance without forcing those jobs into package database.

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) StreamRawMarkersByTrackID

func (db *Database) StreamRawMarkersByTrackID(
	ctx context.Context,
	trackID string,
	dbType string,
) (<-chan Marker, <-chan error)

StreamRawMarkersByTrackID streams only zoom=0 markers for one track. Raw markers are the source of truth for rebuilding map zoom aggregates.

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

func (db *Database) TrackExists(ctx context.Context, trackID, dbType string) (bool, error)

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

func (db *Database) TrackHasDeviceName(ctx context.Context, trackID, dbType string) (bool, error)

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

func (db *Database) UpdateUserNameIfEmpty(ctx context.Context, userID, name, dbType string) error

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

type DeviceSummary struct {
	Detector   string
	DeviceName string
	Tube       string
	Transport  string
}

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
	AggregateKey string  `json:"aggregateKey,omitempty"` // Stable map-cell key used to replace aggregate winners while streaming.
	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

type MarkerBatchProgress struct {
	Total    int
	Done     int
	Batch    int
	Mode     string
	Duration time.Duration
}

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
)

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL