# 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/<steam_id>` route
  - [ ] Use PlayerService instead of direct DB queries
  - [ ] Pass features dict to template
  
- [ ] Add API endpoints
  - [ ] `/api/players/<steam_id>/features`
  - [ ] `/api/players/ranking`
  - [ ] `/api/players/<steam_id>/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，不做聚合
