fix: prediction e2e — 누락 테이블 12개 + 컬럼 매핑 + NUMERIC precision 통합 수정
- V014: fleet_vessels, fleet_tracking_snapshot, gear_identity_log, gear_correlation_scores/raw_metrics, correlation_param_models, group_polygon_snapshots, gear_group_episodes/episode_snapshots, gear_group_parent_candidate_snapshots, gear_parent_label_tracking_cycles, system_config 테이블 추가 - V015: 점수/비율 NUMERIC precision 일괄 확대 (score→7,4 / pct→12,2) + vessel_analysis_results UNIQUE(mmsi, analyzed_at) 인덱스 추가 - prediction kcgdb.py: timestamp→analyzed_at, zone→zone_code, is_leader→fleet_is_leader, is_transship_suspect→transship_suspect 매핑 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
부모
4db7874082
커밋
e12d1c33e2
@ -0,0 +1,283 @@
|
|||||||
|
-- V014: prediction에서 참조하는 누락 테이블 추가
|
||||||
|
-- fleet_vessels, fleet_tracking_snapshot, gear_identity_log,
|
||||||
|
-- gear_correlation_scores, gear_correlation_raw_metrics, correlation_param_models,
|
||||||
|
-- group_polygon_snapshots, gear_group_episodes, gear_group_episode_snapshots,
|
||||||
|
-- gear_group_parent_candidate_snapshots, gear_parent_label_tracking_cycles, system_config
|
||||||
|
|
||||||
|
-- ===== 1. fleet_vessels =====
|
||||||
|
CREATE TABLE IF NOT EXISTS kcg.fleet_vessels (
|
||||||
|
id BIGSERIAL PRIMARY KEY,
|
||||||
|
company_id BIGINT NOT NULL REFERENCES kcg.fleet_companies(id),
|
||||||
|
permit_no VARCHAR(50),
|
||||||
|
name_cn VARCHAR(100),
|
||||||
|
name_en VARCHAR(100),
|
||||||
|
tonnage NUMERIC(10,2),
|
||||||
|
gear_code VARCHAR(20),
|
||||||
|
fleet_role VARCHAR(20) DEFAULT 'CREW', -- MAIN, CREW, TRANSPORT, NOISE
|
||||||
|
pair_vessel_id BIGINT,
|
||||||
|
mmsi VARCHAR(20),
|
||||||
|
match_confidence NUMERIC(4,3),
|
||||||
|
match_method VARCHAR(30), -- NAME_EXACT, NAME_PARENT
|
||||||
|
last_seen_at TIMESTAMPTZ,
|
||||||
|
created_at TIMESTAMPTZ DEFAULT now(),
|
||||||
|
updated_at TIMESTAMPTZ DEFAULT now()
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX idx_fleet_vessels_company ON kcg.fleet_vessels(company_id);
|
||||||
|
CREATE INDEX idx_fleet_vessels_mmsi ON kcg.fleet_vessels(mmsi);
|
||||||
|
CREATE INDEX idx_fleet_vessels_name_cn ON kcg.fleet_vessels(name_cn);
|
||||||
|
|
||||||
|
-- ===== 2. fleet_tracking_snapshot =====
|
||||||
|
CREATE TABLE IF NOT EXISTS kcg.fleet_tracking_snapshot (
|
||||||
|
id BIGSERIAL PRIMARY KEY,
|
||||||
|
company_id BIGINT NOT NULL REFERENCES kcg.fleet_companies(id),
|
||||||
|
snapshot_time TIMESTAMPTZ NOT NULL,
|
||||||
|
total_vessels INT NOT NULL DEFAULT 0,
|
||||||
|
active_vessels INT NOT NULL DEFAULT 0,
|
||||||
|
center_lat NUMERIC(9,6),
|
||||||
|
center_lon NUMERIC(10,6)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX idx_fleet_snapshot_company ON kcg.fleet_tracking_snapshot(company_id, snapshot_time DESC);
|
||||||
|
|
||||||
|
-- ===== 3. gear_identity_log =====
|
||||||
|
CREATE TABLE IF NOT EXISTS kcg.gear_identity_log (
|
||||||
|
id BIGSERIAL PRIMARY KEY,
|
||||||
|
mmsi VARCHAR(20) NOT NULL,
|
||||||
|
name VARCHAR(200) NOT NULL,
|
||||||
|
parent_name VARCHAR(100),
|
||||||
|
parent_mmsi VARCHAR(20),
|
||||||
|
parent_vessel_id BIGINT,
|
||||||
|
gear_index_1 INT,
|
||||||
|
gear_index_2 INT,
|
||||||
|
lat NUMERIC(9,6),
|
||||||
|
lon NUMERIC(10,6),
|
||||||
|
match_method VARCHAR(30),
|
||||||
|
match_confidence NUMERIC(4,3),
|
||||||
|
first_seen_at TIMESTAMPTZ NOT NULL,
|
||||||
|
last_seen_at TIMESTAMPTZ NOT NULL,
|
||||||
|
is_active BOOLEAN NOT NULL DEFAULT TRUE
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX idx_gear_identity_mmsi ON kcg.gear_identity_log(mmsi, is_active);
|
||||||
|
CREATE INDEX idx_gear_identity_name ON kcg.gear_identity_log(name, is_active);
|
||||||
|
|
||||||
|
-- ===== 4. correlation_param_models =====
|
||||||
|
CREATE TABLE IF NOT EXISTS kcg.correlation_param_models (
|
||||||
|
id BIGSERIAL PRIMARY KEY,
|
||||||
|
name VARCHAR(100) NOT NULL,
|
||||||
|
params JSONB NOT NULL DEFAULT '{}',
|
||||||
|
is_active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||||
|
is_default BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
|
created_at TIMESTAMPTZ DEFAULT now()
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 기본 모델 시드
|
||||||
|
INSERT INTO kcg.correlation_param_models (name, params, is_active, is_default) VALUES
|
||||||
|
('default', '{"ema_alpha": 0.3, "proximity_weight": 0.25, "visit_weight": 0.2, "activity_sync_weight": 0.15, "heading_weight": 0.15, "dtw_weight": 0.1, "speed_weight": 0.1, "drift_weight": 0.05}', TRUE, TRUE)
|
||||||
|
ON CONFLICT DO NOTHING;
|
||||||
|
|
||||||
|
-- ===== 5. gear_correlation_scores =====
|
||||||
|
CREATE TABLE IF NOT EXISTS kcg.gear_correlation_scores (
|
||||||
|
model_id BIGINT NOT NULL REFERENCES kcg.correlation_param_models(id),
|
||||||
|
group_key VARCHAR(255) NOT NULL,
|
||||||
|
sub_cluster_id INT NOT NULL,
|
||||||
|
target_mmsi VARCHAR(20) NOT NULL,
|
||||||
|
target_type VARCHAR(20), -- VESSEL, GEAR_BUOY
|
||||||
|
target_name VARCHAR(200),
|
||||||
|
current_score NUMERIC(6,4) DEFAULT 0,
|
||||||
|
proximity_ratio NUMERIC(6,4),
|
||||||
|
visit_score NUMERIC(6,4),
|
||||||
|
heading_coherence NUMERIC(6,4),
|
||||||
|
streak_count INT DEFAULT 0,
|
||||||
|
freeze_state VARCHAR(20) DEFAULT 'ACTIVE', -- ACTIVE, FREEZE, SIGNAL_LOSS
|
||||||
|
observation_count INT DEFAULT 0,
|
||||||
|
first_observed_at TIMESTAMPTZ,
|
||||||
|
last_observed_at TIMESTAMPTZ,
|
||||||
|
updated_at TIMESTAMPTZ DEFAULT now(),
|
||||||
|
PRIMARY KEY (model_id, group_key, sub_cluster_id, target_mmsi)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX idx_corr_scores_group ON kcg.gear_correlation_scores(group_key, sub_cluster_id);
|
||||||
|
CREATE INDEX idx_corr_scores_target ON kcg.gear_correlation_scores(target_mmsi);
|
||||||
|
|
||||||
|
-- ===== 6. gear_correlation_raw_metrics =====
|
||||||
|
CREATE TABLE IF NOT EXISTS kcg.gear_correlation_raw_metrics (
|
||||||
|
id BIGSERIAL PRIMARY KEY,
|
||||||
|
observed_at TIMESTAMPTZ NOT NULL,
|
||||||
|
group_key VARCHAR(255) NOT NULL,
|
||||||
|
sub_cluster_id INT NOT NULL,
|
||||||
|
target_mmsi VARCHAR(20) NOT NULL,
|
||||||
|
target_type VARCHAR(20),
|
||||||
|
target_name VARCHAR(200),
|
||||||
|
proximity_ratio NUMERIC(6,4),
|
||||||
|
visit_score NUMERIC(6,4),
|
||||||
|
activity_sync NUMERIC(6,4),
|
||||||
|
dtw_similarity NUMERIC(6,4),
|
||||||
|
speed_correlation NUMERIC(6,4),
|
||||||
|
heading_coherence NUMERIC(6,4),
|
||||||
|
drift_similarity NUMERIC(6,4),
|
||||||
|
shadow_stay BOOLEAN DEFAULT FALSE,
|
||||||
|
shadow_return BOOLEAN DEFAULT FALSE,
|
||||||
|
gear_group_active_ratio NUMERIC(6,4)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX idx_raw_metrics_observed ON kcg.gear_correlation_raw_metrics(observed_at);
|
||||||
|
CREATE INDEX idx_raw_metrics_group ON kcg.gear_correlation_raw_metrics(group_key, sub_cluster_id, observed_at);
|
||||||
|
|
||||||
|
-- ===== 7. group_polygon_snapshots =====
|
||||||
|
CREATE TABLE IF NOT EXISTS kcg.group_polygon_snapshots (
|
||||||
|
id BIGSERIAL PRIMARY KEY,
|
||||||
|
group_type VARCHAR(30) NOT NULL, -- FLEET, GEAR_IN_ZONE, GEAR_OUT_ZONE
|
||||||
|
group_key VARCHAR(255) NOT NULL,
|
||||||
|
group_label VARCHAR(255),
|
||||||
|
sub_cluster_id INT NOT NULL DEFAULT 0,
|
||||||
|
resolution VARCHAR(10) DEFAULT '6h', -- 6h, 1h
|
||||||
|
snapshot_time TIMESTAMPTZ NOT NULL,
|
||||||
|
polygon GEOMETRY(Polygon, 4326),
|
||||||
|
center_point GEOMETRY(Point, 4326),
|
||||||
|
area_sq_nm NUMERIC(12,4),
|
||||||
|
member_count INT DEFAULT 0,
|
||||||
|
zone_id VARCHAR(50),
|
||||||
|
zone_name VARCHAR(100),
|
||||||
|
members JSONB,
|
||||||
|
color VARCHAR(20)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX idx_polygon_snap_group ON kcg.group_polygon_snapshots(group_key, sub_cluster_id, snapshot_time DESC);
|
||||||
|
CREATE INDEX idx_polygon_snap_time ON kcg.group_polygon_snapshots(snapshot_time);
|
||||||
|
CREATE INDEX idx_polygon_snap_geom ON kcg.group_polygon_snapshots USING GIST (polygon);
|
||||||
|
|
||||||
|
-- ===== 8. gear_group_episodes =====
|
||||||
|
CREATE TABLE IF NOT EXISTS kcg.gear_group_episodes (
|
||||||
|
episode_id VARCHAR(50) PRIMARY KEY, -- 'ep-{12hex}'
|
||||||
|
lineage_key VARCHAR(255) NOT NULL,
|
||||||
|
group_key VARCHAR(255) NOT NULL,
|
||||||
|
normalized_parent_name VARCHAR(100),
|
||||||
|
current_sub_cluster_id INT,
|
||||||
|
status VARCHAR(20) NOT NULL DEFAULT 'ACTIVE', -- ACTIVE, EXPIRED, MERGED
|
||||||
|
continuity_source VARCHAR(30), -- NEW, CONTINUED, SPLIT_NEW, SPLIT_CONTINUE, MERGE_NEW
|
||||||
|
continuity_score NUMERIC(6,4),
|
||||||
|
first_seen_at TIMESTAMPTZ,
|
||||||
|
last_seen_at TIMESTAMPTZ,
|
||||||
|
last_snapshot_time TIMESTAMPTZ,
|
||||||
|
current_member_count INT DEFAULT 0,
|
||||||
|
current_member_mmsis JSONB,
|
||||||
|
current_center_point GEOMETRY(Point, 4326),
|
||||||
|
split_from_episode_id VARCHAR(50),
|
||||||
|
merged_from_episode_ids JSONB,
|
||||||
|
merged_into_episode_id VARCHAR(50),
|
||||||
|
metadata JSONB,
|
||||||
|
updated_at TIMESTAMPTZ DEFAULT now()
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX idx_episodes_lineage ON kcg.gear_group_episodes(lineage_key, status);
|
||||||
|
CREATE INDEX idx_episodes_group ON kcg.gear_group_episodes(group_key, status);
|
||||||
|
CREATE INDEX idx_episodes_snapshot ON kcg.gear_group_episodes(last_snapshot_time);
|
||||||
|
|
||||||
|
-- ===== 9. gear_group_episode_snapshots =====
|
||||||
|
CREATE TABLE IF NOT EXISTS kcg.gear_group_episode_snapshots (
|
||||||
|
episode_id VARCHAR(50) NOT NULL,
|
||||||
|
lineage_key VARCHAR(255),
|
||||||
|
group_key VARCHAR(255) NOT NULL,
|
||||||
|
normalized_parent_name VARCHAR(100),
|
||||||
|
sub_cluster_id INT NOT NULL,
|
||||||
|
observed_at TIMESTAMPTZ NOT NULL,
|
||||||
|
member_count INT DEFAULT 0,
|
||||||
|
member_mmsis JSONB,
|
||||||
|
center_point GEOMETRY(Point, 4326),
|
||||||
|
continuity_source VARCHAR(30),
|
||||||
|
continuity_score NUMERIC(6,4),
|
||||||
|
parent_episode_ids JSONB,
|
||||||
|
top_candidate_mmsi VARCHAR(20),
|
||||||
|
top_candidate_score NUMERIC(6,4),
|
||||||
|
resolution_status VARCHAR(30),
|
||||||
|
metadata JSONB,
|
||||||
|
PRIMARY KEY (episode_id, observed_at)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX idx_ep_snap_group ON kcg.gear_group_episode_snapshots(group_key, sub_cluster_id, observed_at DESC);
|
||||||
|
|
||||||
|
-- ===== 10. gear_group_parent_candidate_snapshots =====
|
||||||
|
CREATE TABLE IF NOT EXISTS kcg.gear_group_parent_candidate_snapshots (
|
||||||
|
id BIGSERIAL PRIMARY KEY,
|
||||||
|
observed_at TIMESTAMPTZ NOT NULL,
|
||||||
|
group_key VARCHAR(255) NOT NULL,
|
||||||
|
sub_cluster_id INT NOT NULL,
|
||||||
|
parent_name VARCHAR(100),
|
||||||
|
normalized_parent_name VARCHAR(100),
|
||||||
|
episode_id VARCHAR(50),
|
||||||
|
candidate_mmsi VARCHAR(20) NOT NULL,
|
||||||
|
candidate_name VARCHAR(200),
|
||||||
|
candidate_vessel_id BIGINT,
|
||||||
|
rank INT,
|
||||||
|
candidate_source VARCHAR(30),
|
||||||
|
model_id BIGINT,
|
||||||
|
model_name VARCHAR(100),
|
||||||
|
base_corr_score NUMERIC(6,4),
|
||||||
|
name_match_score NUMERIC(6,4),
|
||||||
|
track_similarity_score NUMERIC(6,4),
|
||||||
|
visit_score_6h NUMERIC(6,4),
|
||||||
|
proximity_score_6h NUMERIC(6,4),
|
||||||
|
activity_sync_score_6h NUMERIC(6,4),
|
||||||
|
stability_score NUMERIC(6,4),
|
||||||
|
registry_bonus NUMERIC(6,4),
|
||||||
|
episode_prior_bonus NUMERIC(6,4),
|
||||||
|
lineage_prior_bonus NUMERIC(6,4),
|
||||||
|
label_prior_bonus NUMERIC(6,4),
|
||||||
|
final_score NUMERIC(6,4),
|
||||||
|
margin_from_top NUMERIC(6,4),
|
||||||
|
evidence JSONB
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX idx_candidate_snap_group ON kcg.gear_group_parent_candidate_snapshots(group_key, sub_cluster_id, observed_at DESC);
|
||||||
|
CREATE INDEX idx_candidate_snap_episode ON kcg.gear_group_parent_candidate_snapshots(episode_id, observed_at DESC);
|
||||||
|
CREATE INDEX idx_candidate_snap_norm ON kcg.gear_group_parent_candidate_snapshots(normalized_parent_name);
|
||||||
|
|
||||||
|
-- ===== 11. gear_parent_label_tracking_cycles =====
|
||||||
|
CREATE TABLE IF NOT EXISTS kcg.gear_parent_label_tracking_cycles (
|
||||||
|
label_session_id BIGINT NOT NULL REFERENCES kcg.gear_parent_label_sessions(id),
|
||||||
|
observed_at TIMESTAMPTZ NOT NULL,
|
||||||
|
candidate_snapshot_observed_at TIMESTAMPTZ,
|
||||||
|
auto_status VARCHAR(30),
|
||||||
|
top_candidate_mmsi VARCHAR(20),
|
||||||
|
top_candidate_name VARCHAR(200),
|
||||||
|
top_candidate_score NUMERIC(6,4),
|
||||||
|
top_candidate_margin NUMERIC(6,4),
|
||||||
|
candidate_count INT,
|
||||||
|
labeled_candidate_present BOOLEAN,
|
||||||
|
labeled_candidate_rank INT,
|
||||||
|
labeled_candidate_score NUMERIC(6,4),
|
||||||
|
labeled_candidate_pre_bonus_score NUMERIC(6,4),
|
||||||
|
labeled_candidate_margin_from_top NUMERIC(6,4),
|
||||||
|
matched_top1 BOOLEAN,
|
||||||
|
matched_top3 BOOLEAN,
|
||||||
|
evidence_summary JSONB,
|
||||||
|
PRIMARY KEY (label_session_id, observed_at)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- ===== 12. system_config =====
|
||||||
|
CREATE TABLE IF NOT EXISTS kcg.system_config (
|
||||||
|
key VARCHAR(100) PRIMARY KEY,
|
||||||
|
value JSONB,
|
||||||
|
description TEXT,
|
||||||
|
updated_at TIMESTAMPTZ DEFAULT now()
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 파티션 기본값 시드
|
||||||
|
INSERT INTO kcg.system_config (key, value, description) VALUES
|
||||||
|
('partition.raw_metrics.retention_days', '7', '원시 메트릭 보관 일수'),
|
||||||
|
('partition.raw_metrics.create_ahead_days', '3', '파티션 사전 생성 일수'),
|
||||||
|
('partition.scores.cleanup_days', '30', '점수 정리 일수')
|
||||||
|
ON CONFLICT (key) DO NOTHING;
|
||||||
|
|
||||||
|
-- ===== fleet_vessels 시드 데이터 (fleet_companies 기반) =====
|
||||||
|
-- 중국 원양어업 회사 소속 선박 (데모용)
|
||||||
|
INSERT INTO kcg.fleet_vessels (company_id, permit_no, name_cn, name_en, tonnage, gear_code, fleet_role) VALUES
|
||||||
|
(1, 'ZY-2024-001', '鲁荣渔2682', 'LU RONG YU 2682', 450.0, 'TRAWL', 'MAIN'),
|
||||||
|
(1, 'ZY-2024-002', '鲁荣渔2683', 'LU RONG YU 2683', 380.0, 'TRAWL', 'CREW'),
|
||||||
|
(1, 'ZY-2024-003', '鲁荣渔2680', 'LU RONG YU 2680', 520.0, 'PURSE', 'MAIN'),
|
||||||
|
(2, 'ZY-2024-010', '浙岱渔11032', 'ZHE DAI YU 11032', 600.0, 'PURSE', 'MAIN'),
|
||||||
|
(2, 'ZY-2024-011', '浙岱渔11033', 'ZHE DAI YU 11033', 550.0, 'PURSE', 'CREW'),
|
||||||
|
(2, 'ZY-2024-012', '浙岱渔运108', 'ZHE DAI YU YUN 108', 800.0, 'TRANSPORT', 'TRANSPORT')
|
||||||
|
ON CONFLICT DO NOTHING;
|
||||||
@ -0,0 +1,80 @@
|
|||||||
|
-- V015: 점수/비율 NUMERIC 컬럼 precision 일괄 확대 + vessel_analysis_results UNIQUE 제약
|
||||||
|
|
||||||
|
-- === 1. vessel_analysis_results 점수 컬럼 ===
|
||||||
|
ALTER TABLE kcg.vessel_analysis_results ALTER COLUMN confidence TYPE NUMERIC(7,4);
|
||||||
|
ALTER TABLE kcg.vessel_analysis_results ALTER COLUMN fishing_pct TYPE NUMERIC(7,4);
|
||||||
|
ALTER TABLE kcg.vessel_analysis_results ALTER COLUMN ucaf_score TYPE NUMERIC(7,4);
|
||||||
|
ALTER TABLE kcg.vessel_analysis_results ALTER COLUMN ucft_score TYPE NUMERIC(7,4);
|
||||||
|
ALTER TABLE kcg.vessel_analysis_results ALTER COLUMN spoofing_score TYPE NUMERIC(7,4);
|
||||||
|
|
||||||
|
-- ON CONFLICT용 UNIQUE 제약 (파티션 테이블은 PK가 있지만 ON CONFLICT 절에서 인식 안 됨)
|
||||||
|
-- 파티션 PK (id, analyzed_at)와 별도로 mmsi+analyzed_at UNIQUE 추가
|
||||||
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_var_mmsi_analyzed ON kcg.vessel_analysis_results (mmsi, analyzed_at);
|
||||||
|
|
||||||
|
-- === 2. prediction_events/alerts ai_confidence ===
|
||||||
|
ALTER TABLE kcg.prediction_events ALTER COLUMN ai_confidence TYPE NUMERIC(7,4);
|
||||||
|
ALTER TABLE kcg.prediction_alerts ALTER COLUMN ai_confidence TYPE NUMERIC(7,4);
|
||||||
|
ALTER TABLE kcg.enforcement_records ALTER COLUMN ai_confidence TYPE NUMERIC(7,4);
|
||||||
|
ALTER TABLE kcg.prediction_label_input ALTER COLUMN confidence TYPE NUMERIC(7,4);
|
||||||
|
|
||||||
|
-- === 3. AI 모델 메트릭 ===
|
||||||
|
ALTER TABLE kcg.ai_model_versions ALTER COLUMN accuracy_pct TYPE NUMERIC(7,2);
|
||||||
|
ALTER TABLE kcg.ai_model_versions ALTER COLUMN precision_pct TYPE NUMERIC(7,2);
|
||||||
|
ALTER TABLE kcg.ai_model_versions ALTER COLUMN recall_pct TYPE NUMERIC(7,2);
|
||||||
|
ALTER TABLE kcg.ai_model_versions ALTER COLUMN f1_score TYPE NUMERIC(7,4);
|
||||||
|
|
||||||
|
-- === 4. gear 관련 score 컬럼 NUMERIC(6,4) → NUMERIC(7,4) ===
|
||||||
|
-- gear_correlation_scores
|
||||||
|
ALTER TABLE kcg.gear_correlation_scores ALTER COLUMN current_score TYPE NUMERIC(7,4);
|
||||||
|
ALTER TABLE kcg.gear_correlation_scores ALTER COLUMN proximity_ratio TYPE NUMERIC(7,4);
|
||||||
|
ALTER TABLE kcg.gear_correlation_scores ALTER COLUMN visit_score TYPE NUMERIC(7,4);
|
||||||
|
ALTER TABLE kcg.gear_correlation_scores ALTER COLUMN heading_coherence TYPE NUMERIC(7,4);
|
||||||
|
|
||||||
|
-- gear_correlation_raw_metrics
|
||||||
|
ALTER TABLE kcg.gear_correlation_raw_metrics ALTER COLUMN proximity_ratio TYPE NUMERIC(7,4);
|
||||||
|
ALTER TABLE kcg.gear_correlation_raw_metrics ALTER COLUMN visit_score TYPE NUMERIC(7,4);
|
||||||
|
ALTER TABLE kcg.gear_correlation_raw_metrics ALTER COLUMN activity_sync TYPE NUMERIC(7,4);
|
||||||
|
ALTER TABLE kcg.gear_correlation_raw_metrics ALTER COLUMN dtw_similarity TYPE NUMERIC(7,4);
|
||||||
|
ALTER TABLE kcg.gear_correlation_raw_metrics ALTER COLUMN speed_correlation TYPE NUMERIC(7,4);
|
||||||
|
ALTER TABLE kcg.gear_correlation_raw_metrics ALTER COLUMN heading_coherence TYPE NUMERIC(7,4);
|
||||||
|
ALTER TABLE kcg.gear_correlation_raw_metrics ALTER COLUMN drift_similarity TYPE NUMERIC(7,4);
|
||||||
|
ALTER TABLE kcg.gear_correlation_raw_metrics ALTER COLUMN gear_group_active_ratio TYPE NUMERIC(7,4);
|
||||||
|
|
||||||
|
-- gear_group_episodes
|
||||||
|
ALTER TABLE kcg.gear_group_episodes ALTER COLUMN continuity_score TYPE NUMERIC(7,4);
|
||||||
|
|
||||||
|
-- gear_group_episode_snapshots
|
||||||
|
ALTER TABLE kcg.gear_group_episode_snapshots ALTER COLUMN continuity_score TYPE NUMERIC(7,4);
|
||||||
|
ALTER TABLE kcg.gear_group_episode_snapshots ALTER COLUMN top_candidate_score TYPE NUMERIC(7,4);
|
||||||
|
|
||||||
|
-- gear_group_parent_candidate_snapshots (13개 score 컬럼)
|
||||||
|
ALTER TABLE kcg.gear_group_parent_candidate_snapshots ALTER COLUMN base_corr_score TYPE NUMERIC(7,4);
|
||||||
|
ALTER TABLE kcg.gear_group_parent_candidate_snapshots ALTER COLUMN name_match_score TYPE NUMERIC(7,4);
|
||||||
|
ALTER TABLE kcg.gear_group_parent_candidate_snapshots ALTER COLUMN track_similarity_score TYPE NUMERIC(7,4);
|
||||||
|
ALTER TABLE kcg.gear_group_parent_candidate_snapshots ALTER COLUMN visit_score_6h TYPE NUMERIC(7,4);
|
||||||
|
ALTER TABLE kcg.gear_group_parent_candidate_snapshots ALTER COLUMN proximity_score_6h TYPE NUMERIC(7,4);
|
||||||
|
ALTER TABLE kcg.gear_group_parent_candidate_snapshots ALTER COLUMN activity_sync_score_6h TYPE NUMERIC(7,4);
|
||||||
|
ALTER TABLE kcg.gear_group_parent_candidate_snapshots ALTER COLUMN stability_score TYPE NUMERIC(7,4);
|
||||||
|
ALTER TABLE kcg.gear_group_parent_candidate_snapshots ALTER COLUMN registry_bonus TYPE NUMERIC(7,4);
|
||||||
|
ALTER TABLE kcg.gear_group_parent_candidate_snapshots ALTER COLUMN episode_prior_bonus TYPE NUMERIC(7,4);
|
||||||
|
ALTER TABLE kcg.gear_group_parent_candidate_snapshots ALTER COLUMN lineage_prior_bonus TYPE NUMERIC(7,4);
|
||||||
|
ALTER TABLE kcg.gear_group_parent_candidate_snapshots ALTER COLUMN label_prior_bonus TYPE NUMERIC(7,4);
|
||||||
|
ALTER TABLE kcg.gear_group_parent_candidate_snapshots ALTER COLUMN final_score TYPE NUMERIC(7,4);
|
||||||
|
ALTER TABLE kcg.gear_group_parent_candidate_snapshots ALTER COLUMN margin_from_top TYPE NUMERIC(7,4);
|
||||||
|
|
||||||
|
-- gear_parent_label_tracking_cycles
|
||||||
|
ALTER TABLE kcg.gear_parent_label_tracking_cycles ALTER COLUMN top_candidate_score TYPE NUMERIC(7,4);
|
||||||
|
ALTER TABLE kcg.gear_parent_label_tracking_cycles ALTER COLUMN top_candidate_margin TYPE NUMERIC(7,4);
|
||||||
|
ALTER TABLE kcg.gear_parent_label_tracking_cycles ALTER COLUMN labeled_candidate_score TYPE NUMERIC(7,4);
|
||||||
|
ALTER TABLE kcg.gear_parent_label_tracking_cycles ALTER COLUMN labeled_candidate_pre_bonus_score TYPE NUMERIC(7,4);
|
||||||
|
ALTER TABLE kcg.gear_parent_label_tracking_cycles ALTER COLUMN labeled_candidate_margin_from_top TYPE NUMERIC(7,4);
|
||||||
|
|
||||||
|
-- === 5. match_confidence NUMERIC(4,3) → NUMERIC(7,4) ===
|
||||||
|
ALTER TABLE kcg.fleet_vessels ALTER COLUMN match_confidence TYPE NUMERIC(7,4);
|
||||||
|
ALTER TABLE kcg.gear_identity_log ALTER COLUMN match_confidence TYPE NUMERIC(7,4);
|
||||||
|
|
||||||
|
-- === 6. stats/kpi 테이블 ===
|
||||||
|
ALTER TABLE kcg.prediction_stats_daily ALTER COLUMN ai_accuracy_pct TYPE NUMERIC(12,2);
|
||||||
|
ALTER TABLE kcg.prediction_stats_monthly ALTER COLUMN ai_accuracy_pct TYPE NUMERIC(12,2);
|
||||||
|
ALTER TABLE kcg.prediction_kpi_realtime ALTER COLUMN delta_pct TYPE NUMERIC(12,2);
|
||||||
|
ALTER TABLE kcg.prediction_risk_grid ALTER COLUMN avg_risk TYPE NUMERIC(12,2);
|
||||||
@ -71,22 +71,22 @@ def upsert_results(results: list['AnalysisResult']) -> int:
|
|||||||
|
|
||||||
insert_sql = """
|
insert_sql = """
|
||||||
INSERT INTO vessel_analysis_results (
|
INSERT INTO vessel_analysis_results (
|
||||||
mmsi, timestamp, vessel_type, confidence, fishing_pct,
|
mmsi, analyzed_at, vessel_type, confidence, fishing_pct,
|
||||||
cluster_id, season, zone, dist_to_baseline_nm, activity_state,
|
cluster_id, season, zone_code, dist_to_baseline_nm, activity_state,
|
||||||
ucaf_score, ucft_score, is_dark, gap_duration_min,
|
ucaf_score, ucft_score, is_dark, gap_duration_min,
|
||||||
spoofing_score, bd09_offset_m, speed_jump_count,
|
spoofing_score, bd09_offset_m, speed_jump_count,
|
||||||
cluster_size, is_leader, fleet_role,
|
fleet_cluster_id, fleet_is_leader, fleet_role,
|
||||||
risk_score, risk_level,
|
risk_score, risk_level,
|
||||||
is_transship_suspect, transship_pair_mmsi, transship_duration_min,
|
transship_suspect, transship_pair_mmsi, transship_duration_min,
|
||||||
features, analyzed_at
|
features
|
||||||
) VALUES %s
|
) VALUES %s
|
||||||
ON CONFLICT (mmsi, timestamp) DO UPDATE SET
|
ON CONFLICT (mmsi, analyzed_at) DO UPDATE SET
|
||||||
vessel_type = EXCLUDED.vessel_type,
|
vessel_type = EXCLUDED.vessel_type,
|
||||||
confidence = EXCLUDED.confidence,
|
confidence = EXCLUDED.confidence,
|
||||||
fishing_pct = EXCLUDED.fishing_pct,
|
fishing_pct = EXCLUDED.fishing_pct,
|
||||||
cluster_id = EXCLUDED.cluster_id,
|
cluster_id = EXCLUDED.cluster_id,
|
||||||
season = EXCLUDED.season,
|
season = EXCLUDED.season,
|
||||||
zone = EXCLUDED.zone,
|
zone_code = EXCLUDED.zone_code,
|
||||||
dist_to_baseline_nm = EXCLUDED.dist_to_baseline_nm,
|
dist_to_baseline_nm = EXCLUDED.dist_to_baseline_nm,
|
||||||
activity_state = EXCLUDED.activity_state,
|
activity_state = EXCLUDED.activity_state,
|
||||||
ucaf_score = EXCLUDED.ucaf_score,
|
ucaf_score = EXCLUDED.ucaf_score,
|
||||||
@ -96,16 +96,15 @@ def upsert_results(results: list['AnalysisResult']) -> int:
|
|||||||
spoofing_score = EXCLUDED.spoofing_score,
|
spoofing_score = EXCLUDED.spoofing_score,
|
||||||
bd09_offset_m = EXCLUDED.bd09_offset_m,
|
bd09_offset_m = EXCLUDED.bd09_offset_m,
|
||||||
speed_jump_count = EXCLUDED.speed_jump_count,
|
speed_jump_count = EXCLUDED.speed_jump_count,
|
||||||
cluster_size = EXCLUDED.cluster_size,
|
fleet_cluster_id = EXCLUDED.fleet_cluster_id,
|
||||||
is_leader = EXCLUDED.is_leader,
|
fleet_is_leader = EXCLUDED.fleet_is_leader,
|
||||||
fleet_role = EXCLUDED.fleet_role,
|
fleet_role = EXCLUDED.fleet_role,
|
||||||
risk_score = EXCLUDED.risk_score,
|
risk_score = EXCLUDED.risk_score,
|
||||||
risk_level = EXCLUDED.risk_level,
|
risk_level = EXCLUDED.risk_level,
|
||||||
is_transship_suspect = EXCLUDED.is_transship_suspect,
|
transship_suspect = EXCLUDED.transship_suspect,
|
||||||
transship_pair_mmsi = EXCLUDED.transship_pair_mmsi,
|
transship_pair_mmsi = EXCLUDED.transship_pair_mmsi,
|
||||||
transship_duration_min = EXCLUDED.transship_duration_min,
|
transship_duration_min = EXCLUDED.transship_duration_min,
|
||||||
features = EXCLUDED.features,
|
features = EXCLUDED.features
|
||||||
analyzed_at = EXCLUDED.analyzed_at
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|||||||
@ -75,13 +75,13 @@ class AnalysisResult:
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
str(self.mmsi),
|
str(self.mmsi),
|
||||||
self.timestamp,
|
self.analyzed_at, # analyzed_at (PK 파티션키)
|
||||||
str(self.vessel_type),
|
str(self.vessel_type),
|
||||||
_f(self.confidence),
|
_f(self.confidence),
|
||||||
_f(self.fishing_pct),
|
_f(self.fishing_pct),
|
||||||
_i(self.cluster_id),
|
_i(self.cluster_id),
|
||||||
str(self.season),
|
str(self.season),
|
||||||
str(self.zone),
|
str(self.zone), # → zone_code
|
||||||
_f(self.dist_to_baseline_nm),
|
_f(self.dist_to_baseline_nm),
|
||||||
str(self.activity_state),
|
str(self.activity_state),
|
||||||
_f(self.ucaf_score),
|
_f(self.ucaf_score),
|
||||||
@ -91,14 +91,13 @@ class AnalysisResult:
|
|||||||
_f(self.spoofing_score),
|
_f(self.spoofing_score),
|
||||||
_f(self.bd09_offset_m),
|
_f(self.bd09_offset_m),
|
||||||
_i(self.speed_jump_count),
|
_i(self.speed_jump_count),
|
||||||
_i(self.cluster_size),
|
_i(self.cluster_id), # → fleet_cluster_id
|
||||||
bool(self.is_leader),
|
bool(self.is_leader), # → fleet_is_leader
|
||||||
str(self.fleet_role),
|
str(self.fleet_role),
|
||||||
_i(self.risk_score),
|
_i(self.risk_score),
|
||||||
str(self.risk_level),
|
str(self.risk_level),
|
||||||
bool(self.is_transship_suspect),
|
bool(self.is_transship_suspect), # → transship_suspect
|
||||||
str(self.transship_pair_mmsi),
|
str(self.transship_pair_mmsi),
|
||||||
_i(self.transship_duration_min),
|
_i(self.transship_duration_min),
|
||||||
json.dumps(safe_features),
|
json.dumps(safe_features),
|
||||||
self.analyzed_at,
|
|
||||||
)
|
)
|
||||||
|
|||||||
불러오는 중...
Reference in New Issue
Block a user