# L3 Implementation Roadmap & Checklist > **Based on**: L3_ARCHITECTURE_PLAN.md v2.0 > **Start Date**: 2026-01-28 > **Estimated Duration**: 8-10 days --- ## Quick Start Checklist ### ✅ Pre-requisites - [x] L1 database完整 (208 matches) - [x] L2 database完整 (100% coverage, 51,860 rows) - [x] L2 schema documented - [x] Profile requirements analyzed - [x] L3 architecture designed ### 🎯 Implementation Phases --- ## Phase 1: Schema & Infrastructure (Day 1-2) ### 1.1 Create L3 Database Schema - [ ] Create `database/L3/schema.sql` - [ ] dm_player_features (207 columns) - [ ] dm_player_match_history - [ ] dm_player_map_stats - [ ] dm_player_weapon_stats - [ ] All indexes ### 1.2 Initialize L3 Database - [ ] Update `database/L3/L3_Builder.py` init_db() - [ ] Run schema creation - [ ] Verify tables created ### 1.3 Processor Base Classes - [ ] Create `database/L3/processors/__init__.py` - [ ] Create `database/L3/processors/base_processor.py` - [ ] BaseFeatureProcessor interface - [ ] SafeAggregator utility class - [ ] Z-score normalization functions **验收标准**: ```bash sqlite3 database/L3/L3.db ".tables" # 应输出: dm_player_features, dm_player_match_history, dm_player_map_stats, dm_player_weapon_stats ``` --- ## Phase 2: Tier 1 - Core Processors (Day 3-4) ### 2.1 BasicProcessor Implementation - [ ] Create `database/L3/processors/basic_processor.py` **Sub-tasks**: - [ ] `calculate_basic_stats()` - 15 columns - [ ] AVG(rating, rating2, kd, adr, kast, rws) from fact_match_players - [ ] AVG(headshot_count), hs_rate = SUM(hs)/SUM(kills) - [ ] total_kills, total_deaths, total_assists - [ ] kpr, dpr, survival_rate - [ ] `calculate_match_stats()` - 8 columns - [ ] win_rate, wins, losses - [ ] avg_match_duration from fact_matches - [ ] avg_mvps, mvp_rate - [ ] avg_elo_change, total_elo_gained from fact_match_teams - [ ] `calculate_weapon_stats()` - 12 columns - [ ] avg_awp_kills, awp_usage_rate - [ ] avg_knife_kills, avg_zeus_kills, zeus_buy_rate - [ ] top_weapon (GROUP BY weapon in fact_round_events) - [ ] weapon_diversity (Shannon entropy) - [ ] rifle/pistol/smg hs_rates - [ ] `calculate_objective_stats()` - 6 columns - [ ] avg_plants, avg_defuses, avg_flash_assists - [ ] plant_success_rate, defuse_success_rate - [ ] objective_impact (weighted score) **测试用例**: ```python features = BasicProcessor.calculate('76561198012345678', conn_l2) assert 'core_avg_rating' in features assert features['core_total_kills'] > 0 assert 0 <= features['core_hs_rate'] <= 1 ``` --- ## Phase 3: Tier 2 - Tactical Processors (Day 4-5) ### 3.1 TacticalProcessor Implementation - [ ] Create `database/L3/processors/tactical_processor.py` **Sub-tasks**: - [ ] `calculate_opening_impact()` - 8 columns - [ ] avg_fk, avg_fd from fact_match_players - [ ] fk_rate, fd_rate - [ ] fk_success_rate (team win when FK) - [ ] entry_kill_rate, entry_death_rate - [ ] opening_duel_winrate - [ ] `calculate_multikill()` - 6 columns - [ ] avg_2k, avg_3k, avg_4k, avg_5k - [ ] multikill_rate - [ ] ace_count (5k count) - [ ] `calculate_clutch()` - 10 columns - [ ] clutch_1v1/1v2_attempts/wins/rate - [ ] clutch_1v3_plus aggregated - [ ] clutch_impact_score (weighted) - [ ] `calculate_utility()` - 12 columns - [ ] util_X_per_round for flash/smoke/molotov/he - [ ] util_usage_rate - [ ] nade_dmg metrics - [ ] flash_efficiency, smoke_timing_score - [ ] util_impact_score - [ ] `calculate_economy()` - 8 columns - [ ] dmg_per_1k from fact_round_player_economy - [ ] kpr/kd for eco/force/full rounds - [ ] save_discipline, force_success_rate - [ ] eco_efficiency_score **测试**: ```python features = TacticalProcessor.calculate('76561198012345678', conn_l2) assert 'tac_fk_rate' in features assert features['tac_multikill_rate'] >= 0 ``` --- ## Phase 4: Tier 3 - Intelligence Processors (Day 5-7) ### 4.1 IntelligenceProcessor Implementation - [ ] Create `database/L3/processors/intelligence_processor.py` **Sub-tasks**: - [ ] `calculate_high_iq_kills()` - 8 columns - [ ] wallbang/smoke/blind/noscope kills from fact_round_events flags - [ ] Rates: X_kills / total_kills - [ ] high_iq_score (weighted formula) - [ ] `calculate_timing_analysis()` - 12 columns - [ ] early/mid/late kills by event_time bins (0-30s, 30-60s, 60s+) - [ ] timing shares - [ ] avg_kill_time, avg_death_time - [ ] aggression_index, patience_score - [ ] first_contact_time (MIN(event_time) per round) - [ ] `calculate_pressure_performance()` - 10 columns - [ ] comeback_kd/rating (when down 4+ rounds) - [ ] losing_streak_kd (3+ round loss streak) - [ ] matchpoint_kpr/rating (at 15-X or 12-X) - [ ] clutch_composure, entry_in_loss - [ ] pressure_performance_index, big_moment_score - [ ] tilt_resistance - [ ] `calculate_position_mastery()` - 15 columns ⚠️ Complex - [ ] site_a/b/mid_control_rate from xyz clustering - [ ] favorite_position (most common cluster) - [ ] position_diversity (entropy) - [ ] rotation_speed (distance between kills) - [ ] map_coverage, defensive/aggressive positioning - [ ] lurk_tendency, site_anchor_score - [ ] spatial_iq_score - [ ] `calculate_trade_network()` - 8 columns - [ ] trade_kill_count (kills within 5s of teammate death) - [ ] trade_kill_rate - [ ] trade_response_time (AVG seconds) - [ ] trade_given (deaths traded by teammate) - [ ] trade_balance, trade_efficiency - [ ] teamwork_score **Position Mastery特别注意**: ```python # 需要使用sklearn DBSCAN聚类 from sklearn.cluster import DBSCAN def cluster_player_positions(steam_id, conn_l2): """从fact_round_events提取xyz坐标并聚类""" cursor = conn_l2.cursor() cursor.execute(""" SELECT attacker_pos_x, attacker_pos_y, attacker_pos_z FROM fact_round_events WHERE attacker_steam_id = ? AND attacker_pos_x IS NOT NULL """, (steam_id,)) coords = cursor.fetchall() # DBSCAN clustering... ``` **测试**: ```python features = IntelligenceProcessor.calculate('76561198012345678', conn_l2) assert 'int_high_iq_score' in features assert features['int_timing_early_kill_share'] + features['int_timing_mid_kill_share'] + features['int_timing_late_kill_share'] <= 1.1 # Allow rounding ``` --- ## Phase 5: Tier 4 - Meta Processors (Day 7-8) ### 5.1 MetaProcessor Implementation - [ ] Create `database/L3/processors/meta_processor.py` **Sub-tasks**: - [ ] `calculate_stability()` - 8 columns - [ ] rating_volatility (STDDEV of last 20 matches) - [ ] recent_form_rating (AVG last 10) - [ ] win/loss_rating - [ ] rating_consistency (100 - volatility_norm) - [ ] time_rating_correlation (CORR(duration, rating)) - [ ] map_stability, elo_tier_stability - [ ] `calculate_side_preference()` - 14 columns - [ ] side_ct/t_rating from fact_match_players_ct/t - [ ] side_ct/t_kd, win_rate, fk_rate, kast - [ ] side_rating_diff, side_kd_diff - [ ] side_preference ('CT'/'T'/'Balanced') - [ ] side_balance_score - [ ] `calculate_opponent_adaptation()` - 12 columns - [ ] vs_lower/similar/higher_elo_rating/kd - [ ] Based on fact_match_teams.group_origin_elo差值 - [ ] elo_adaptation, stomping_score, upset_score - [ ] consistency_across_elos, rank_resistance - [ ] smurf_detection - [ ] `calculate_map_specialization()` - 10 columns - [ ] best/worst_map, best/worst_rating - [ ] map_diversity (entropy) - [ ] map_pool_size (maps with 5+ matches) - [ ] map_specialist_score, map_versatility - [ ] comfort_zone_rate, map_adaptation - [ ] `calculate_session_pattern()` - 8 columns - [ ] avg_matches_per_day - [ ] longest_streak (consecutive days) - [ ] weekend/weekday_rating - [ ] morning/afternoon/evening/night_rating (based on timestamp) **测试**: ```python features = MetaProcessor.calculate('76561198012345678', conn_l2) assert 'meta_rating_volatility' in features assert features['meta_side_preference'] in ['CT', 'T', 'Balanced'] ``` --- ## Phase 6: Tier 5 - Composite Processors (Day 8) ### 6.1 CompositeProcessor Implementation - [ ] Create `database/L3/processors/composite_processor.py` **Sub-tasks**: - [ ] `normalize_and_standardize()` helper - [ ] Z-score normalization function - [ ] Global mean/std calculation from all players - [ ] Map Z-score to 0-100 range - [ ] `calculate_radar_scores()` - 8 scores - [ ] score_aim: 25% Rating + 20% KD + 15% ADR + 10% DuelWin + 10% HighEloKD + 20% MultiKill - [ ] score_clutch: 25% 1v3+ + 20% MatchPtWin + 20% ComebackKD + 15% PressureEntry + 20% Rating - [ ] score_pistol: 30% PistolKills + 30% PistolWin + 20% PistolKD + 20% PistolHS% - [ ] score_defense: 35% CT_Rating + 35% T_Rating + 15% CT_FK + 15% T_FK - [ ] score_utility: 35% UsageRate + 25% NadeDmg + 20% FlashEff + 20% FlashEnemy - [ ] score_stability: 30% (100-Volatility) + 30% LossRating + 20% WinRating + 20% Consistency - [ ] score_economy: 50% Dmg/$1k + 30% EcoKPR + 20% SaveRoundKD - [ ] score_pace: 40% EntryTiming + 30% TradeSpeed + 30% AggressionIndex - [ ] `calculate_overall_score()` - AVG of 8 scores - [ ] `classify_tier()` - Performance tier - [ ] Elite: overall > 75 - [ ] Advanced: 60-75 - [ ] Intermediate: 40-60 - [ ] Beginner: < 40 - [ ] `calculate_percentile()` - Rank among all players **依赖**: ```python def calculate(steam_id: str, conn_l2: sqlite3.Connection, pre_features: dict) -> dict: """ 需要前面4个Tier的特征作为输入 Args: pre_features: 包含Tier 1-4的所有特征 """ pass ``` **测试**: ```python # 需要先计算所有前置特征 features = {} features.update(BasicProcessor.calculate(steam_id, conn_l2)) features.update(TacticalProcessor.calculate(steam_id, conn_l2)) features.update(IntelligenceProcessor.calculate(steam_id, conn_l2)) features.update(MetaProcessor.calculate(steam_id, conn_l2)) composite = CompositeProcessor.calculate(steam_id, conn_l2, features) assert 0 <= composite['score_aim'] <= 100 assert composite['tier_classification'] in ['Elite', 'Advanced', 'Intermediate', 'Beginner'] ``` --- ## Phase 7: L3_Builder Integration (Day 8-9) ### 7.1 Main Builder Logic - [ ] Update `database/L3/L3_Builder.py` - [ ] Import all processors - [ ] Main loop: iterate all players from dim_players - [ ] Call processors in order - [ ] _upsert_features() helper - [ ] Batch commit every 100 players - [ ] Progress logging ```python def main(): logger.info("Starting L3 Builder...") # 1. Init DB init_db() # 2. Connect conn_l2 = sqlite3.connect(L2_DB_PATH) conn_l3 = sqlite3.connect(L3_DB_PATH) # 3. Get all players cursor = conn_l2.cursor() cursor.execute("SELECT DISTINCT steam_id_64 FROM dim_players") players = cursor.fetchall() logger.info(f"Processing {len(players)} players...") for idx, (steam_id,) in enumerate(players, 1): try: # 4. Calculate features tier by tier features = {} features.update(BasicProcessor.calculate(steam_id, conn_l2)) features.update(TacticalProcessor.calculate(steam_id, conn_l2)) features.update(IntelligenceProcessor.calculate(steam_id, conn_l2)) features.update(MetaProcessor.calculate(steam_id, conn_l2)) features.update(CompositeProcessor.calculate(steam_id, conn_l2, features)) # 5. Upsert to L3 _upsert_features(conn_l3, steam_id, features) # 6. Commit batch if idx % 100 == 0: conn_l3.commit() logger.info(f"Processed {idx}/{len(players)} players") except Exception as e: logger.error(f"Error processing {steam_id}: {e}") conn_l3.commit() logger.info("Done!") ``` ### 7.2 Auxiliary Tables Population - [ ] Populate `dm_player_match_history` - [ ] FROM fact_match_players JOIN fact_matches - [ ] ORDER BY match date - [ ] Calculate match_sequence, rolling averages - [ ] Populate `dm_player_map_stats` - [ ] GROUP BY steam_id, map_name - [ ] FROM fact_match_players - [ ] Populate `dm_player_weapon_stats` - [ ] GROUP BY steam_id, weapon_name - [ ] FROM fact_round_events - [ ] TOP 10 weapons per player ### 7.3 Full Build Test - [ ] Run: `python database/L3/L3_Builder.py` - [ ] Verify: All players processed - [ ] Check: Row counts in all L3 tables - [ ] Validate: Sample features make sense **验收标准**: ```sql SELECT COUNT(*) FROM dm_player_features; -- 应该 = dim_players count SELECT AVG(core_avg_rating) FROM dm_player_features; -- 应该接近1.0 SELECT COUNT(*) FROM dm_player_features WHERE score_aim > 0; -- 大部分玩家有评分 ``` --- ## Phase 8: Web Services Refactoring (Day 9-10) ### 8.1 Create PlayerService - [ ] Create `web/services/player_service.py` ```python class PlayerService: @staticmethod def get_player_features(steam_id: str) -> dict: """获取完整特征(dm_player_features)""" pass @staticmethod def get_player_radar_data(steam_id: str) -> dict: """获取雷达图8维数据""" pass @staticmethod def get_player_core_stats(steam_id: str) -> dict: """获取核心Dashboard数据""" pass @staticmethod def get_player_history(steam_id: str, limit: int = 20) -> list: """获取历史趋势数据""" pass @staticmethod def get_player_map_stats(steam_id: str) -> list: """获取各地图统计""" pass @staticmethod def get_player_weapon_stats(steam_id: str, top_n: int = 10) -> list: """获取Top N武器""" pass @staticmethod def get_players_ranking(order_by: str = 'core_avg_rating', limit: int = 100, offset: int = 0) -> list: """获取排行榜""" pass ``` - [ ] Implement all methods - [ ] Add error handling - [ ] Add caching (optional) ### 8.2 Refactor Routes - [ ] Update `web/routes/players.py` - [ ] `/profile/` route - [ ] Use PlayerService instead of direct DB queries - [ ] Pass features dict to template - [ ] Add API endpoints - [ ] `/api/players//features` - [ ] `/api/players/ranking` - [ ] `/api/players//history` ### 8.3 Update feature_service.py - [ ] Mark old rebuild methods as DEPRECATED - [ ] Redirect to L3_Builder.py - [ ] Keep query methods for backward compatibility --- ## Phase 9: Frontend Integration (Day 10-11) ### 9.1 Update profile.html Template - [ ] Dashboard cards: use `features.core_*` - [ ] Radar chart: use `features.score_*` - [ ] Trend chart: use `history` data - [ ] Core Performance section - [ ] Gunfight section - [ ] Opening Impact section - [ ] Clutch section - [ ] High IQ Kills section - [ ] Map stats table - [ ] Weapon stats table ### 9.2 JavaScript Integration - [ ] Radar chart rendering (Chart.js) - [ ] Trend chart rendering - [ ] Dynamic data loading ### 9.3 UI Polish - [ ] Responsive design - [ ] Loading states - [ ] Error handling - [ ] Tooltips for complex metrics --- ## Phase 10: Testing & Validation (Day 11-12) ### 10.1 Unit Tests - [ ] Test each processor independently - [ ] Mock L2 data - [ ] Verify calculation correctness ### 10.2 Integration Tests - [ ] Full L3_Builder run - [ ] Verify all tables populated - [ ] Check data consistency ### 10.3 Performance Tests - [ ] Benchmark L3_Builder runtime - [ ] Profile slow queries - [ ] Optimize if needed ### 10.4 Data Quality Checks - [ ] Verify no NULL values where expected - [ ] Check value ranges (e.g., 0 <= rate <= 1) - [ ] Validate composite scores (0-100) - [ ] Cross-check with L2 source data --- ## Success Criteria ### ✅ L3 Database - [ ] All 4 tables created with correct schemas - [ ] dm_player_features has 207 columns - [ ] All players from L2 have corresponding L3 rows - [ ] No critical NULL values ### ✅ Feature Calculation - [ ] All 5 processors implemented and tested - [ ] 207 features calculated correctly - [ ] Composite scores in 0-100 range - [ ] Tier classification working ### ✅ Services & Routes - [ ] PlayerService provides all query methods - [ ] Routes use services correctly - [ ] API endpoints return valid JSON - [ ] No direct DB queries in routes ### ✅ Frontend - [ ] Profile page renders correctly - [ ] Radar chart displays 8 dimensions - [ ] Trend chart shows history - [ ] All sections populated with data ### ✅ Performance - [ ] L3_Builder completes in < 20 min for 1000 players - [ ] Profile page loads in < 200ms - [ ] No N+1 query problems --- ## Risk Mitigation ### 🔴 High Risk Items 1. **Position Mastery (xyz clustering)** - Mitigation: Start with simple grid-based approach, defer ML clustering 2. **Composite Score Standardization** - Mitigation: Use simple percentile-based normalization as fallback 3. **Performance at Scale** - Mitigation: Implement incremental updates, add indexes ### 🟡 Medium Risk Items 1. **Time Window Calculations (trades)** - Mitigation: Use efficient self-JOIN with time bounds 2. **Missing Data Handling** - Mitigation: Comprehensive NULL handling, default values ### 🟢 Low Risk Items 1. Basic aggregations (AVG, SUM, COUNT) 2. Service layer refactoring 3. Template updates --- ## Next Actions **Immediate (Today)**: 1. Create schema.sql 2. Initialize L3.db 3. Create processor base classes **Tomorrow**: 1. Implement BasicProcessor 2. Test with sample player 3. Start TacticalProcessor **This Week**: 1. Complete all 5 processors 2. Full L3_Builder run 3. Service refactoring **Next Week**: 1. Frontend integration 2. Testing & validation 3. Documentation --- ## Notes - 保持每个processor独立,便于单元测试 - 使用动态SQL避免column count错误 - 所有rate/percentage使用0-1范围存储,UI展示时乘100 - 时间戳统一使用Unix timestamp (INTEGER) - 遵循"查询不计算"原则:web层只SELECT,不做聚合