bae705aa97
- 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
373 lines
11 KiB
PL/PgSQL
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();
|