Files
boc/quixzoom-capture-pipeline/persistence/postgres-schema.sql
T
Bernt bae705aa97 ARCHITECTURE: NFC roadmap, edge AI, audit logging
- Add NFC ePassport roadmap (ICAO 9303, eIDAS)
- Add TensorFlow.js edge face detection (BlazeFace)
- Add structured audit logger (GDPR-compliant)
- Risk scoring support

Part of KYC Apple Native UX v1.1.0
2026-06-29 16:24:48 +00:00

373 lines
11 KiB
PL/PgSQL

-- QUIXZOOM PostgreSQL + PostGIS Schema
--
-- Ansvarsområden:
-- - Observationer
-- - GPS
-- - Tidsserier
-- - Uppdrag
-- - Zoomers
-- - Videor
-- - Sensorloggar
-- - Spatiala frågor
-- Aktivera PostGIS
CREATE EXTENSION IF NOT EXISTS postgis;
-- ============================================================
-- ZOOMERS
-- ============================================================
CREATE TABLE zoomers (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name VARCHAR(255) NOT NULL,
email VARCHAR(255) UNIQUE,
phone VARCHAR(50),
status VARCHAR(50) DEFAULT 'active', -- active, inactive, suspended
capabilities JSONB DEFAULT '[]', -- ["video", "photo", "lidar"]
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- ============================================================
-- AREAS
-- ============================================================
CREATE TABLE areas (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name VARCHAR(255) NOT NULL,
bounds GEOMETRY(POLYGON, 4326), -- PostGIS
country VARCHAR(100),
city VARCHAR(100),
priority FLOAT DEFAULT 0.5,
status VARCHAR(50) DEFAULT 'active',
metadata JSONB DEFAULT '{}',
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- ============================================================
-- OBSERVATIONS
-- ============================================================
CREATE TABLE observations (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
observation_id VARCHAR(255) UNIQUE NOT NULL, -- Extern ID (obs_xxx)
-- Källa
source_type VARCHAR(50) NOT NULL, -- quixzoom, youtube_cc, inspector
source_id VARCHAR(255),
zoomer_id UUID REFERENCES zoomers(id),
-- Media
media_type VARCHAR(50), -- video, image
media_url TEXT,
frame_number INTEGER,
media_timestamp TIMESTAMP WITH TIME ZONE,
-- Plats
location GEOMETRY(POINT, 4326), -- PostGIS
gps_accuracy FLOAT,
altitude FLOAT,
-- Kvalitet
quality JSONB DEFAULT '{}', -- {resolution, sharpness, exposure}
-- AI-analys
ai_analysis JSONB DEFAULT '{}', -- {objectDetection, sceneClassification}
-- Metadata
metadata JSONB DEFAULT '{}',
-- Status
status VARCHAR(50) DEFAULT 'pending', -- pending, validated, rejected
validated_by VARCHAR(255),
validated_at TIMESTAMP WITH TIME ZONE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- Spatialt index
CREATE INDEX idx_observations_location ON observations USING GIST(location);
CREATE INDEX idx_observations_created_at ON observations(created_at);
CREATE INDEX idx_observations_source ON observations(source_type, source_id);
-- ============================================================
-- OBJECTS (Urban Objects)
-- ============================================================
CREATE TABLE objects (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
object_id VARCHAR(255) UNIQUE NOT NULL, -- Extern ID (street_lamp_xxx)
-- Typ
type VARCHAR(100) NOT NULL, -- street_lamp, pothole, crosswalk
subtype VARCHAR(100),
-- Plats
location GEOMETRY(POINT, 4326), -- PostGIS
location_accuracy FLOAT,
-- Attribut (kan ändras över tid)
attributes JSONB DEFAULT '{}',
-- Status
status VARCHAR(50) DEFAULT 'active', -- active, removed, modified
verification_status VARCHAR(50) DEFAULT 'unverified', -- unverified, verified, disputed
-- Konfidens
confidence FLOAT DEFAULT 0,
uncertainty FLOAT DEFAULT 1.0,
-- Metadata
first_seen_at TIMESTAMP WITH TIME ZONE,
last_seen_at TIMESTAMP WITH TIME ZONE,
observation_count INTEGER DEFAULT 0,
metadata JSONB DEFAULT '{}',
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- Spatialt index
CREATE INDEX idx_objects_location ON objects USING GIST(location);
CREATE INDEX idx_objects_type ON objects(type);
CREATE INDEX idx_objects_verification ON objects(verification_status);
-- ============================================================
-- EVIDENCE
-- ============================================================
CREATE TABLE evidence (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
evidence_id VARCHAR(255) UNIQUE NOT NULL, -- Extern ID (ev_xxx)
-- Referenser
object_id UUID REFERENCES objects(id),
observation_id UUID REFERENCES observations(id),
-- Bevis
proof_type VARCHAR(50), -- video, image, lidar, manual_inspection
proof_url TEXT,
frame_number INTEGER,
-- Källa
source_type VARCHAR(50),
source_id VARCHAR(255),
operator VARCHAR(255),
-- Kvalitet
quality JSONB DEFAULT '{}',
reliability FLOAT DEFAULT 0,
-- Metadata
timestamp TIMESTAMP WITH TIME ZONE,
metadata JSONB DEFAULT '{}',
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
CREATE INDEX idx_evidence_object ON evidence(object_id);
CREATE INDEX idx_evidence_observation ON evidence(observation_id);
-- ============================================================
-- TEMPORAL (Object History)
-- ============================================================
CREATE TABLE temporal_records (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
temporal_id VARCHAR(255) UNIQUE NOT NULL, -- Extern ID (tmp_xxx)
-- Referens
object_id UUID REFERENCES objects(id),
-- Tillstånd
state JSONB NOT NULL, -- {attributes, verificationStatus, confidence}
-- Ändringar
changes JSONB DEFAULT '[]', -- ["lean: 0 -> 2"]
-- Orsak
reason VARCHAR(255),
source VARCHAR(255),
-- Tid
recorded_at TIMESTAMP WITH TIME ZONE NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
CREATE INDEX idx_temporal_object ON temporal_records(object_id);
CREATE INDEX idx_temporal_recorded_at ON temporal_records(recorded_at);
-- ============================================================
-- MISSIONS
-- ============================================================
CREATE TABLE missions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
mission_id VARCHAR(255) UNIQUE NOT NULL, -- Extern ID (mission_xxx)
-- Typ
type VARCHAR(50) NOT NULL, -- verify, explore, document, diversify
-- Område
area_id UUID REFERENCES areas(id),
-- Mål
target_location GEOMETRY(POINT, 4326), -- PostGIS
target_object_id UUID REFERENCES objects(id),
-- Tilldelning
assigned_to UUID REFERENCES zoomers(id),
assigned_at TIMESTAMP WITH TIME ZONE,
-- Status
status VARCHAR(50) DEFAULT 'pending', -- pending, assigned, active, completed, failed
-- Resultat
result JSONB DEFAULT '{}',
completed_at TIMESTAMP WITH TIME ZONE,
failed_at TIMESTAMP WITH TIME ZONE,
failure_reason TEXT,
-- Prioritet
priority FLOAT DEFAULT 0.5,
-- Metadata
instructions JSONB DEFAULT '[]',
metadata JSONB DEFAULT '{}',
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
CREATE INDEX idx_missions_area ON missions(area_id);
CREATE INDEX idx_missions_status ON missions(status);
CREATE INDEX idx_missions_assigned ON missions(assigned_to);
-- ============================================================
-- EVENTS (Event Sourcing)
-- ============================================================
CREATE TABLE events (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
sequence_number BIGINT NOT NULL UNIQUE,
-- Event-typ
type VARCHAR(100) NOT NULL,
-- Stream
stream_id VARCHAR(255) NOT NULL,
aggregate_id VARCHAR(255) NOT NULL,
-- Payload
payload JSONB NOT NULL,
-- Metadata
metadata JSONB DEFAULT '{}',
-- Tid
timestamp TIMESTAMP WITH TIME ZONE NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
CREATE INDEX idx_events_stream ON events(stream_id);
CREATE INDEX idx_events_type ON events(type);
CREATE INDEX idx_events_timestamp ON events(timestamp);
CREATE INDEX idx_events_sequence ON events(sequence_number);
-- ============================================================
-- SENSOR LOGS
-- ============================================================
CREATE TABLE sensor_logs (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
-- Källa
zoomer_id UUID REFERENCES zoomers(id),
observation_id UUID REFERENCES observations(id),
-- Sensorer
gps_location GEOMETRY(POINT, 4326), -- PostGIS
gps_accuracy FLOAT,
altitude FLOAT,
imu_accelerometer JSONB, -- {x, y, z}
imu_gyroscope JSONB, -- {x, y, z}
imu_magnetometer JSONB, -- {x, y, z}
compass FLOAT,
-- Tid
recorded_at TIMESTAMP WITH TIME ZONE NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
CREATE INDEX idx_sensor_logs_zoomer ON sensor_logs(zoomer_id);
CREATE INDEX idx_sensor_logs_observation ON sensor_logs(observation_id);
CREATE INDEX idx_sensor_logs_recorded_at ON sensor_logs(recorded_at);
-- ============================================================
-- VIEWS (För snabb läsning)
-- ============================================================
CREATE VIEW object_summary AS
SELECT
o.id,
o.object_id,
o.type,
o.subtype,
ST_X(o.location::geometry) as longitude,
ST_Y(o.location::geometry) as latitude,
o.verification_status,
o.confidence,
o.observation_count,
o.first_seen_at,
o.last_seen_at,
COUNT(e.id) as evidence_count,
COUNT(t.id) as temporal_records_count
FROM objects o
LEFT JOIN evidence e ON e.object_id = o.id
LEFT JOIN temporal_records t ON t.object_id = o.id
GROUP BY o.id;
CREATE VIEW mission_summary AS
SELECT
m.id,
m.mission_id,
m.type,
m.status,
m.priority,
a.name as area_name,
z.name as assigned_to_name,
m.created_at,
m.completed_at
FROM missions m
LEFT JOIN areas a ON a.id = m.area_id
LEFT JOIN zoomers z ON z.id = m.assigned_to;
-- ============================================================
-- TRIGGERS (Uppdatera updated_at)
-- ============================================================
CREATE OR REPLACE FUNCTION update_updated_at_column()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = NOW();
RETURN NEW;
END;
$$ language 'plpgsql';
CREATE TRIGGER update_zoomers_updated_at BEFORE UPDATE ON zoomers
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
CREATE TRIGGER update_areas_updated_at BEFORE UPDATE ON areas
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
CREATE TRIGGER update_objects_updated_at BEFORE UPDATE ON objects
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
CREATE TRIGGER update_missions_updated_at BEFORE UPDATE ON missions
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();