2026-01-24 02:48:56 +08:00
|
|
|
|
|
|
|
|
import logging
|
|
|
|
|
import os
|
2026-01-26 21:10:42 +08:00
|
|
|
import sys
|
|
|
|
|
|
|
|
|
|
# Add parent directory to path to allow importing web module
|
|
|
|
|
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
|
|
|
|
|
|
|
|
from web.services.feature_service import FeatureService
|
|
|
|
|
from web.config import Config
|
2026-01-27 00:57:35 +08:00
|
|
|
from web.app import create_app
|
2026-01-26 21:10:42 +08:00
|
|
|
import sqlite3
|
2026-01-24 02:48:56 +08:00
|
|
|
|
|
|
|
|
# Setup logging
|
|
|
|
|
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
2026-01-26 21:10:42 +08:00
|
|
|
L3_DB_PATH = Config.DB_L3_PATH
|
|
|
|
|
SCHEMA_PATH = os.path.join(Config.BASE_DIR, 'database', 'L3', 'schema.sql')
|
2026-01-24 02:48:56 +08:00
|
|
|
|
2026-01-28 01:20:26 +08:00
|
|
|
def _get_existing_columns(conn, table_name):
|
|
|
|
|
cur = conn.execute(f"PRAGMA table_info({table_name})")
|
|
|
|
|
return {row[1] for row in cur.fetchall()}
|
|
|
|
|
|
|
|
|
|
def _ensure_columns(conn, table_name, columns):
|
|
|
|
|
existing = _get_existing_columns(conn, table_name)
|
|
|
|
|
for col, col_type in columns.items():
|
|
|
|
|
if col in existing:
|
|
|
|
|
continue
|
|
|
|
|
conn.execute(f"ALTER TABLE {table_name} ADD COLUMN {col} {col_type}")
|
|
|
|
|
|
2026-01-24 02:48:56 +08:00
|
|
|
def init_db():
|
2026-01-26 21:10:42 +08:00
|
|
|
l3_dir = os.path.dirname(L3_DB_PATH)
|
|
|
|
|
if not os.path.exists(l3_dir):
|
|
|
|
|
os.makedirs(l3_dir)
|
2026-01-24 02:48:56 +08:00
|
|
|
|
|
|
|
|
conn = sqlite3.connect(L3_DB_PATH)
|
|
|
|
|
with open(SCHEMA_PATH, 'r', encoding='utf-8') as f:
|
|
|
|
|
conn.executescript(f.read())
|
2026-01-28 01:20:26 +08:00
|
|
|
|
|
|
|
|
_ensure_columns(
|
|
|
|
|
conn,
|
|
|
|
|
"dm_player_features",
|
|
|
|
|
{
|
|
|
|
|
"rd_phase_kill_early_share": "REAL",
|
|
|
|
|
"rd_phase_kill_mid_share": "REAL",
|
|
|
|
|
"rd_phase_kill_late_share": "REAL",
|
|
|
|
|
"rd_phase_death_early_share": "REAL",
|
|
|
|
|
"rd_phase_death_mid_share": "REAL",
|
|
|
|
|
"rd_phase_death_late_share": "REAL",
|
2026-01-28 01:38:45 +08:00
|
|
|
"rd_phase_kill_early_share_t": "REAL",
|
|
|
|
|
"rd_phase_kill_mid_share_t": "REAL",
|
|
|
|
|
"rd_phase_kill_late_share_t": "REAL",
|
|
|
|
|
"rd_phase_kill_early_share_ct": "REAL",
|
|
|
|
|
"rd_phase_kill_mid_share_ct": "REAL",
|
|
|
|
|
"rd_phase_kill_late_share_ct": "REAL",
|
|
|
|
|
"rd_phase_death_early_share_t": "REAL",
|
|
|
|
|
"rd_phase_death_mid_share_t": "REAL",
|
|
|
|
|
"rd_phase_death_late_share_t": "REAL",
|
|
|
|
|
"rd_phase_death_early_share_ct": "REAL",
|
|
|
|
|
"rd_phase_death_mid_share_ct": "REAL",
|
|
|
|
|
"rd_phase_death_late_share_ct": "REAL",
|
2026-01-28 01:20:26 +08:00
|
|
|
"rd_firstdeath_team_first_death_rounds": "INTEGER",
|
|
|
|
|
"rd_firstdeath_team_first_death_win_rate": "REAL",
|
|
|
|
|
"rd_invalid_death_rounds": "INTEGER",
|
|
|
|
|
"rd_invalid_death_rate": "REAL",
|
|
|
|
|
"rd_pressure_kpr_ratio": "REAL",
|
|
|
|
|
"rd_pressure_perf_ratio": "REAL",
|
|
|
|
|
"rd_pressure_rounds_down3": "INTEGER",
|
|
|
|
|
"rd_pressure_rounds_normal": "INTEGER",
|
|
|
|
|
"rd_matchpoint_kpr_ratio": "REAL",
|
|
|
|
|
"rd_matchpoint_perf_ratio": "REAL",
|
|
|
|
|
"rd_matchpoint_rounds": "INTEGER",
|
|
|
|
|
"rd_comeback_kill_share": "REAL",
|
|
|
|
|
"rd_comeback_rounds": "INTEGER",
|
|
|
|
|
"rd_trade_response_10s_rate": "REAL",
|
|
|
|
|
"rd_weapon_top_json": "TEXT",
|
|
|
|
|
"rd_roundtype_split_json": "TEXT",
|
|
|
|
|
"map_stability_coef": "REAL",
|
|
|
|
|
"basic_avg_knife_kill": "REAL",
|
|
|
|
|
"basic_avg_zeus_kill": "REAL",
|
|
|
|
|
"basic_zeus_pick_rate": "REAL",
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
|
2026-01-24 02:48:56 +08:00
|
|
|
conn.commit()
|
|
|
|
|
conn.close()
|
2026-01-26 21:10:42 +08:00
|
|
|
logger.info("L3 DB Initialized/Updated with Schema.")
|
2026-01-24 02:48:56 +08:00
|
|
|
|
2026-01-26 21:10:42 +08:00
|
|
|
def main():
|
|
|
|
|
logger.info("Starting L3 Builder (Delegating to FeatureService)...")
|
2026-01-24 02:48:56 +08:00
|
|
|
|
2026-01-26 21:10:42 +08:00
|
|
|
# 1. Ensure Schema is up to date
|
|
|
|
|
init_db()
|
2026-01-24 02:48:56 +08:00
|
|
|
|
2026-01-26 21:10:42 +08:00
|
|
|
# 2. Rebuild Features using the centralized logic
|
2026-01-24 02:48:56 +08:00
|
|
|
try:
|
2026-01-27 00:57:35 +08:00
|
|
|
app = create_app()
|
|
|
|
|
with app.app_context():
|
|
|
|
|
count = FeatureService.rebuild_all_features()
|
|
|
|
|
logger.info(f"Successfully rebuilt features for {count} players.")
|
2026-01-24 02:48:56 +08:00
|
|
|
except Exception as e:
|
2026-01-26 21:10:42 +08:00
|
|
|
logger.error(f"Error rebuilding features: {e}")
|
|
|
|
|
import traceback
|
|
|
|
|
traceback.print_exc()
|
2026-01-24 02:48:56 +08:00
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
2026-01-26 21:10:42 +08:00
|
|
|
main()
|