import os import json base = r"d:/edunlab/milicontent2/uploads/DAT10" os.makedirs(base, exist_ok=True) for sub in ["data/schema", "data/samples", "docs", "src/preprocess", "src/models", "src/verification", "src/ui", "src/hitl", "tests", "configs"]: os.makedirs(os.path.join(base, sub), exist_ok=True) # 1. requirements.txt req_content = """pandas>=2.0.0 numpy>=1.24.0 scikit-learn>=1.3.0 streamlit>=1.28.0 pyyaml>=6.0 pytest>=7.4.0 plotly>=5.17.0 cryptography>=41.0.0 """ with open(os.path.join(base, "requirements.txt"), "w", encoding="utf-8") as f: f.write(req_content) # 2. data/schema/dat10_input_schema.json schema_content = { "$schema": "http://json-schema.org/draft-07/schema#", "title": "DAT10_Tactical_SupplyChain_Inventory_Schema", "description": "전술 통신 두절 환경 야전 군수 재고 및 방산 공급망 트랜잭션 표준 입력 스키마", "type": "object", "properties": { "transaction_id": { "type": "string", "pattern": "^TX-[A-Z0-9]{8,16}$", "description": "고유 트랜잭션 식별자 (UUID 또는 해시 기반)" }, "terminal_id": { "type": "string", "pattern": "^TERM-[A-Z0-9]{4,8}$", "description": "작전 단말기 ID" }, "unit_id": { "type": "string", "pattern": "^(DIV|BDE|BN|CO)-[0-9]{2,4}$", "description": "부대 제대 식별자" }, "nsn_code": { "type": "string", "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{3}-[0-9]{4}$", "description": "국가재고번호 (NATO Stock Number)" }, "item_name": { "type": "string", "minLength": 2, "maxLength": 100, "description": "품목명" }, "supply_class": { "type": "string", "enum": ["Class_I", "Class_II", "Class_III", "Class_IV", "Class_V", "Class_IX"], "description": "군수 보급 분류 (I:식량, II:피복장구, III:유류, IV:건설자재, V:탄약, IX:수리부속)" }, "tx_type": { "type": "string", "enum": ["RECEIPT", "ISSUE", "TRANSFER", "DISPOSAL", "ADJUSTMENT", "FIELD_CONSUME"], "description": "트랜잭션 유형 (입고, 불출, 이관, 폐기, 재고조정, 야전소모)" }, "quantity": { "type": "integer", "minimum": 1, "maximum": 1000000, "description": "변동 수량" }, "current_stock": { "type": "integer", "minimum": 0, "description": "트랜잭션 발생 직후 로컬 잔여 재고" }, "unit_cost": { "type": "number", "minimum": 0.0, "description": "단위당 조달 단가 (USD / KRW)" }, "comms_status": { "type": "string", "enum": ["ONLINE", "OFFLINE_LOCAL", "OFFLINE_P2P", "DEGRADED_EW"], "description": "트랜잭션 기록 당시 통신 상태" }, "grid_coords": { "type": "string", "pattern": "^[0-9]{2}[A-Z]{3}[0-9]{8}$", "description": "MGRS 군사격자 좌표 (예: 52SDJ12345678)" }, "weather_condition": { "type": "string", "enum": ["CLEAR", "HEAVY_RAIN", "SNOW_STORM", "DESERT_HEAT", "FOG"], "description": "야전 기상 환경" }, "lamport_timestamp": { "type": "integer", "minimum": 0, "description": "분산 인과율 추적을 위한 램포트 논리 시계 (Lamport Logical Clock)" }, "timestamp_utc": { "type": "string", "format": "date-time", "description": "ISO 8601 UTC 생성 일시" }, "prev_hash": { "type": "string", "pattern": "^[a-f0-9]{64}$", "description": "이전 로컬 트랜잭션 레코드의 SHA-256 해시" }, "record_hash": { "type": "string", "pattern": "^[a-f0-9]{64}$", "description": "무결성 보증을 위한 본 트랜잭션의 SHA-256 해시" } }, "required": [ "transaction_id", "terminal_id", "unit_id", "nsn_code", "item_name", "supply_class", "tx_type", "quantity", "current_stock", "comms_status", "lamport_timestamp", "timestamp_utc", "prev_hash", "record_hash" ], "additionalProperties": False } with open(os.path.join(base, "data/schema/dat10_input_schema.json"), "w", encoding="utf-8") as f: json.dump(schema_content, f, ensure_ascii=False, indent=2) # 3. data/schema/sqlite_schema.sql sql_content = """-- DAT10 전술 오프라인 단말 로컬 SQLite DB 스키마 PRAGMA foreign_keys = ON; -- 1. 마스터 재고 품목 테이블 CREATE TABLE IF NOT EXISTS inventory_items ( nsn_code TEXT PRIMARY KEY, item_name TEXT NOT NULL, supply_class TEXT NOT NULL CHECK(supply_class IN ('Class_I', 'Class_II', 'Class_III', 'Class_IV', 'Class_V', 'Class_IX')), criticality_level TEXT DEFAULT 'NORMAL' CHECK(criticality_level IN ('CRITICAL', 'HIGH', 'NORMAL', 'LOW')), unit_cost REAL NOT NULL DEFAULT 0.0, safety_stock_threshold INTEGER NOT NULL DEFAULT 10, total_local_stock INTEGER NOT NULL DEFAULT 0, crdt_clock INTEGER NOT NULL DEFAULT 0, updated_at_utc TEXT NOT NULL ); -- 2. 오프라인 트랜잭션 및 해시체인 로그 테이블 CREATE TABLE IF NOT EXISTS offline_transactions ( transaction_id TEXT PRIMARY KEY, terminal_id TEXT NOT NULL, unit_id TEXT NOT NULL, nsn_code TEXT NOT NULL, tx_type TEXT NOT NULL CHECK(tx_type IN ('RECEIPT', 'ISSUE', 'TRANSFER', 'DISPOSAL', 'ADJUSTMENT', 'FIELD_CONSUME')), quantity INTEGER NOT NULL CHECK(quantity > 0), stock_after INTEGER NOT NULL CHECK(stock_after >= 0), comms_status TEXT NOT NULL, grid_coords TEXT, weather_condition TEXT, lamport_timestamp INTEGER NOT NULL, timestamp_utc TEXT NOT NULL, prev_hash TEXT NOT NULL, record_hash TEXT NOT NULL, sync_status TEXT DEFAULT 'PENDING' CHECK(sync_status IN ('PENDING', 'SYNCED_P2P', 'SYNCED_CLOUD', 'CONFLICT_LOCKED')), FOREIGN KEY(nsn_code) REFERENCES inventory_items(nsn_code) ); -- 3. CRDT 양방향 동기화 및 충돌 큐 테이블 CREATE TABLE IF NOT EXISTS sync_conflict_queue ( conflict_id TEXT PRIMARY KEY, transaction_id TEXT NOT NULL, nsn_code TEXT NOT NULL, local_stock INTEGER NOT NULL, incoming_stock INTEGER NOT NULL, local_clock INTEGER NOT NULL, incoming_clock INTEGER NOT NULL, conflict_reason TEXT NOT NULL, hitl_status TEXT DEFAULT 'PENDING_REVIEW' CHECK(hitl_status IN ('PENDING_REVIEW', 'APPROVED_LOCAL', 'APPROVED_REMOTE', 'MANUAL_MERGED', 'REJECTED')), resolved_by TEXT, resolved_stock INTEGER, resolution_memo TEXT, resolved_at_utc TEXT, FOREIGN KEY(transaction_id) REFERENCES offline_transactions(transaction_id) ); -- 4. P2P 피어 단말 정보 테이블 CREATE TABLE IF NOT EXISTS p2p_peers ( peer_terminal_id TEXT PRIMARY KEY, unit_id TEXT NOT NULL, last_known_ip TEXT, last_sync_timestamp TEXT, last_sync_lamport INTEGER DEFAULT 0, trust_score REAL DEFAULT 1.0 ); -- 5. 공급망 재난 및 이상 징후 알림 테이블 CREATE TABLE IF NOT EXISTS supply_disaster_alerts ( alert_id TEXT PRIMARY KEY, nsn_code TEXT NOT NULL, alert_type TEXT NOT NULL, severity TEXT NOT NULL CHECK(severity IN ('CRITICAL', 'WARNING', 'INFO')), confidence_score REAL NOT NULL, description TEXT NOT NULL, detected_at_utc TEXT NOT NULL, is_acknowledged INTEGER DEFAULT 0 ); -- 인덱스 생성 CREATE INDEX IF NOT EXISTS idx_tx_timestamp ON offline_transactions(timestamp_utc); CREATE INDEX IF NOT EXISTS idx_tx_nsn ON offline_transactions(nsn_code); CREATE INDEX IF NOT EXISTS idx_tx_sync_status ON offline_transactions(sync_status); """ with open(os.path.join(base, "data/schema/sqlite_schema.sql"), "w", encoding="utf-8") as f: f.write(sql_content) print("Schema files created.")