Spaces:
Sleeping
Sleeping
docs: Add comprehensive implementation documentation
Browse files- TECHNICAL_IMPLEMENTATION.md: Complete technical specs, configurations, algorithms
- SYSTEM_WORKFLOW.md: Sequential logic flow from data generation to outputs
- Covers all TOML configurations, decision trees, data transformations
- Detailed 8-checkpoint daily scheduling algorithm explanation
- Complete case lifecycle walkthrough with examples
- Production-ready documentation for hackathon submission
- SYSTEM_WORKFLOW.md +642 -0
- TECHNICAL_IMPLEMENTATION.md +658 -0
SYSTEM_WORKFLOW.md
ADDED
|
@@ -0,0 +1,642 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Court Scheduling System - Complete Workflow & Logic Flow
|
| 2 |
+
|
| 3 |
+
**Step-by-Step Guide: How the System Actually Works**
|
| 4 |
+
|
| 5 |
+
---
|
| 6 |
+
|
| 7 |
+
## Table of Contents
|
| 8 |
+
1. [System Workflow Overview](#system-workflow-overview)
|
| 9 |
+
2. [Phase 1: Data Preparation](#phase-1-data-preparation)
|
| 10 |
+
3. [Phase 2: Simulation Initialization](#phase-2-simulation-initialization)
|
| 11 |
+
4. [Phase 3: Daily Scheduling Loop](#phase-3-daily-scheduling-loop)
|
| 12 |
+
5. [Phase 4: Output Generation](#phase-4-output-generation)
|
| 13 |
+
6. [Phase 5: Analysis & Reporting](#phase-5-analysis--reporting)
|
| 14 |
+
7. [Complete Example Walkthrough](#complete-example-walkthrough)
|
| 15 |
+
8. [Data Flow Pipeline](#data-flow-pipeline)
|
| 16 |
+
|
| 17 |
+
---
|
| 18 |
+
|
| 19 |
+
## System Workflow Overview
|
| 20 |
+
|
| 21 |
+
The Court Scheduling System operates in **5 sequential phases** that transform historical court data into optimized daily cause lists:
|
| 22 |
+
|
| 23 |
+
```
|
| 24 |
+
Historical Data → Data Preparation → Simulation Setup → Daily Scheduling → Output Generation → Analysis
|
| 25 |
+
↓ ↓ ↓ ↓ ↓ ↓
|
| 26 |
+
739K hearings Parameters & Initialized Daily cause CSV files & Performance
|
| 27 |
+
134K cases Generated cases simulation lists for 384 Reports metrics
|
| 28 |
+
```
|
| 29 |
+
|
| 30 |
+
**Key Outputs:**
|
| 31 |
+
- **Daily Cause Lists**: CSV files for each courtroom/day
|
| 32 |
+
- **Simulation Report**: Overall performance summary
|
| 33 |
+
- **Metrics File**: Daily performance tracking
|
| 34 |
+
- **Individual Case Audit**: Complete hearing history
|
| 35 |
+
|
| 36 |
+
---
|
| 37 |
+
|
| 38 |
+
## Phase 1: Data Preparation
|
| 39 |
+
|
| 40 |
+
### Step 1.1: Historical Data Analysis (EDA Pipeline)
|
| 41 |
+
|
| 42 |
+
**Input**:
|
| 43 |
+
- `ISDMHack_Case.csv` (134,699 cases)
|
| 44 |
+
- `ISDMHack_Hear.csv` (739,670 hearings)
|
| 45 |
+
|
| 46 |
+
**Process**:
|
| 47 |
+
```python
|
| 48 |
+
# Load and merge historical data
|
| 49 |
+
cases_df = pd.read_csv("ISDMHack_Case.csv")
|
| 50 |
+
hearings_df = pd.read_csv("ISDMHack_Hear.csv")
|
| 51 |
+
merged_data = cases_df.merge(hearings_df, on="Case_ID")
|
| 52 |
+
|
| 53 |
+
# Extract key parameters
|
| 54 |
+
case_type_distribution = cases_df["Type"].value_counts(normalize=True)
|
| 55 |
+
stage_transitions = calculate_stage_progression_probabilities(merged_data)
|
| 56 |
+
adjournment_rates = calculate_adjournment_rates_by_stage(hearings_df)
|
| 57 |
+
daily_capacity = hearings_df.groupby("Hearing_Date").size().mean()
|
| 58 |
+
```
|
| 59 |
+
|
| 60 |
+
**Output**:
|
| 61 |
+
```python
|
| 62 |
+
# Extracted parameters stored in config.py
|
| 63 |
+
CASE_TYPE_DISTRIBUTION = {"CRP": 0.201, "CA": 0.200, ...}
|
| 64 |
+
STAGE_TRANSITIONS = {"ADMISSION->ARGUMENTS": 0.72, ...}
|
| 65 |
+
ADJOURNMENT_RATES = {"ADMISSION": 0.38, "ARGUMENTS": 0.31, ...}
|
| 66 |
+
DEFAULT_DAILY_CAPACITY = 151 # cases per courtroom per day
|
| 67 |
+
```
|
| 68 |
+
|
| 69 |
+
### Step 1.2: Synthetic Case Generation
|
| 70 |
+
|
| 71 |
+
**Input**:
|
| 72 |
+
- Configuration: `configs/generate.sample.toml`
|
| 73 |
+
- Extracted parameters from Step 1.1
|
| 74 |
+
|
| 75 |
+
**Process**:
|
| 76 |
+
```python
|
| 77 |
+
# Generate 10,000 synthetic cases
|
| 78 |
+
for i in range(10000):
|
| 79 |
+
case = Case(
|
| 80 |
+
case_id=f"C{i:06d}",
|
| 81 |
+
case_type=random_choice_weighted(CASE_TYPE_DISTRIBUTION),
|
| 82 |
+
filed_date=random_date_in_range("2022-01-01", "2023-12-31"),
|
| 83 |
+
current_stage=random_choice_weighted(STAGE_DISTRIBUTION),
|
| 84 |
+
is_urgent=random_boolean(0.05), # 5% urgent cases
|
| 85 |
+
)
|
| 86 |
+
|
| 87 |
+
# Add realistic hearing history
|
| 88 |
+
generate_hearing_history(case, historical_patterns)
|
| 89 |
+
cases.append(case)
|
| 90 |
+
```
|
| 91 |
+
|
| 92 |
+
**Output**:
|
| 93 |
+
- `data/generated/cases.csv` with 10,000 synthetic cases
|
| 94 |
+
- Each case has realistic attributes based on historical patterns
|
| 95 |
+
|
| 96 |
+
---
|
| 97 |
+
|
| 98 |
+
## Phase 2: Simulation Initialization
|
| 99 |
+
|
| 100 |
+
### Step 2.1: Load Configuration
|
| 101 |
+
|
| 102 |
+
**Input**: `configs/simulate.sample.toml`
|
| 103 |
+
```toml
|
| 104 |
+
cases = "data/generated/cases.csv"
|
| 105 |
+
days = 384 # 2-year simulation
|
| 106 |
+
policy = "readiness" # Scheduling policy
|
| 107 |
+
courtrooms = 5
|
| 108 |
+
daily_capacity = 151
|
| 109 |
+
```
|
| 110 |
+
|
| 111 |
+
### Step 2.2: Initialize System State
|
| 112 |
+
|
| 113 |
+
**Process**:
|
| 114 |
+
```python
|
| 115 |
+
# Load generated cases
|
| 116 |
+
cases = load_cases_from_csv("data/generated/cases.csv")
|
| 117 |
+
|
| 118 |
+
# Initialize courtrooms
|
| 119 |
+
courtrooms = [
|
| 120 |
+
Courtroom(id=1, daily_capacity=151),
|
| 121 |
+
Courtroom(id=2, daily_capacity=151),
|
| 122 |
+
# ... 5 courtrooms total
|
| 123 |
+
]
|
| 124 |
+
|
| 125 |
+
# Initialize scheduling policy
|
| 126 |
+
policy = ReadinessPolicy(
|
| 127 |
+
fairness_weight=0.4,
|
| 128 |
+
efficiency_weight=0.3,
|
| 129 |
+
urgency_weight=0.3
|
| 130 |
+
)
|
| 131 |
+
|
| 132 |
+
# Initialize simulation clock
|
| 133 |
+
current_date = datetime(2023, 12, 29) # Start date
|
| 134 |
+
end_date = current_date + timedelta(days=384)
|
| 135 |
+
```
|
| 136 |
+
|
| 137 |
+
**Output**:
|
| 138 |
+
- Simulation environment ready with 10,000 cases and 5 courtrooms
|
| 139 |
+
- Policy configured with optimization weights
|
| 140 |
+
|
| 141 |
+
---
|
| 142 |
+
|
| 143 |
+
## Phase 3: Daily Scheduling Loop
|
| 144 |
+
|
| 145 |
+
**This is the core algorithm that runs 384 times (once per working day)**
|
| 146 |
+
|
| 147 |
+
### Daily Loop Structure
|
| 148 |
+
```python
|
| 149 |
+
for day in range(384): # Each working day for 2 years
|
| 150 |
+
current_date += timedelta(days=1)
|
| 151 |
+
|
| 152 |
+
# Skip weekends and holidays
|
| 153 |
+
if not is_working_day(current_date):
|
| 154 |
+
continue
|
| 155 |
+
|
| 156 |
+
# Execute daily scheduling algorithm
|
| 157 |
+
daily_result = schedule_daily_hearings(cases, current_date)
|
| 158 |
+
|
| 159 |
+
# Update system state for next day
|
| 160 |
+
update_case_states(cases, daily_result)
|
| 161 |
+
|
| 162 |
+
# Generate daily outputs
|
| 163 |
+
generate_cause_lists(daily_result, current_date)
|
| 164 |
+
```
|
| 165 |
+
|
| 166 |
+
### Step 3.1: Daily Scheduling Algorithm (Core Logic)
|
| 167 |
+
|
| 168 |
+
**INPUT**:
|
| 169 |
+
- All active cases (initially 10,000)
|
| 170 |
+
- Current date
|
| 171 |
+
- Courtroom capacities
|
| 172 |
+
|
| 173 |
+
**CHECKPOINT 1: Case Status Filtering**
|
| 174 |
+
```python
|
| 175 |
+
# Filter out disposed cases
|
| 176 |
+
active_cases = [case for case in all_cases
|
| 177 |
+
if case.status in [PENDING, SCHEDULED]]
|
| 178 |
+
|
| 179 |
+
print(f"Day {day}: {len(active_cases)} active cases")
|
| 180 |
+
# Example: Day 1: 10,000 active cases → Day 200: 6,500 active cases
|
| 181 |
+
```
|
| 182 |
+
|
| 183 |
+
**CHECKPOINT 2: Case Attribute Updates**
|
| 184 |
+
```python
|
| 185 |
+
for case in active_cases:
|
| 186 |
+
# Update age (days since filing)
|
| 187 |
+
case.age_days = (current_date - case.filed_date).days
|
| 188 |
+
|
| 189 |
+
# Update readiness score based on stage and hearing history
|
| 190 |
+
case.readiness_score = calculate_readiness(case)
|
| 191 |
+
|
| 192 |
+
# Update days since last scheduled
|
| 193 |
+
if case.last_scheduled_date:
|
| 194 |
+
case.days_since_last_scheduled = (current_date - case.last_scheduled_date).days
|
| 195 |
+
```
|
| 196 |
+
|
| 197 |
+
**CHECKPOINT 3: Ripeness Classification (Critical Filter)**
|
| 198 |
+
```python
|
| 199 |
+
ripe_cases = []
|
| 200 |
+
ripeness_stats = {"RIPE": 0, "UNRIPE_SUMMONS": 0, "UNRIPE_DEPENDENT": 0, "UNRIPE_PARTY": 0}
|
| 201 |
+
|
| 202 |
+
for case in active_cases:
|
| 203 |
+
ripeness = RipenessClassifier.classify(case, current_date)
|
| 204 |
+
ripeness_stats[ripeness.status] += 1
|
| 205 |
+
|
| 206 |
+
if ripeness.is_ripe():
|
| 207 |
+
ripe_cases.append(case)
|
| 208 |
+
else:
|
| 209 |
+
case.bottleneck_reason = ripeness.reason
|
| 210 |
+
|
| 211 |
+
print(f"Ripeness Filter: {len(active_cases)} → {len(ripe_cases)} cases")
|
| 212 |
+
# Example: 6,500 active → 3,850 ripe cases (40.8% filtered out)
|
| 213 |
+
```
|
| 214 |
+
|
| 215 |
+
**Ripeness Classification Logic**:
|
| 216 |
+
```python
|
| 217 |
+
def classify(case, current_date):
|
| 218 |
+
# Step 1: Check explicit bottlenecks in last hearing purpose
|
| 219 |
+
if "SUMMONS" in case.last_hearing_purpose:
|
| 220 |
+
return RipenessStatus.UNRIPE_SUMMONS
|
| 221 |
+
if "STAY" in case.last_hearing_purpose:
|
| 222 |
+
return RipenessStatus.UNRIPE_DEPENDENT
|
| 223 |
+
|
| 224 |
+
# Step 2: Early admission cases likely waiting for service
|
| 225 |
+
if case.current_stage == "ADMISSION" and case.hearing_count < 3:
|
| 226 |
+
return RipenessStatus.UNRIPE_SUMMONS
|
| 227 |
+
|
| 228 |
+
# Step 3: Detect stuck cases (many hearings, no progress)
|
| 229 |
+
if case.hearing_count > 10 and case.avg_gap_days > 60:
|
| 230 |
+
return RipenessStatus.UNRIPE_PARTY
|
| 231 |
+
|
| 232 |
+
# Step 4: Advanced stages are usually ready
|
| 233 |
+
if case.current_stage in ["ARGUMENTS", "EVIDENCE", "ORDERS / JUDGMENT"]:
|
| 234 |
+
return RipenessStatus.RIPE
|
| 235 |
+
|
| 236 |
+
# Step 5: Conservative default
|
| 237 |
+
return RipenessStatus.RIPE
|
| 238 |
+
```
|
| 239 |
+
|
| 240 |
+
**CHECKPOINT 4: Eligibility Check (Timing Constraints)**
|
| 241 |
+
```python
|
| 242 |
+
eligible_cases = []
|
| 243 |
+
for case in ripe_cases:
|
| 244 |
+
# Check minimum 14-day gap between hearings
|
| 245 |
+
if case.last_hearing_date:
|
| 246 |
+
days_since_last = (current_date - case.last_hearing_date).days
|
| 247 |
+
if days_since_last < MIN_GAP_BETWEEN_HEARINGS:
|
| 248 |
+
continue
|
| 249 |
+
|
| 250 |
+
eligible_cases.append(case)
|
| 251 |
+
|
| 252 |
+
print(f"Eligibility Filter: {len(ripe_cases)} → {len(eligible_cases)} cases")
|
| 253 |
+
# Example: 3,850 ripe → 3,200 eligible cases
|
| 254 |
+
```
|
| 255 |
+
|
| 256 |
+
**CHECKPOINT 5: Priority Scoring (Policy Application)**
|
| 257 |
+
```python
|
| 258 |
+
for case in eligible_cases:
|
| 259 |
+
# Multi-factor priority calculation
|
| 260 |
+
age_component = min(case.age_days / 365, 1.0) * 0.35
|
| 261 |
+
readiness_component = case.readiness_score * 0.25
|
| 262 |
+
urgency_component = (1.0 if case.is_urgent else 0.5) * 0.25
|
| 263 |
+
boost_component = calculate_adjournment_boost(case) * 0.15
|
| 264 |
+
|
| 265 |
+
case.priority_score = age_component + readiness_component + urgency_component + boost_component
|
| 266 |
+
|
| 267 |
+
# Sort by priority (highest first)
|
| 268 |
+
prioritized_cases = sorted(eligible_cases, key=lambda c: c.priority_score, reverse=True)
|
| 269 |
+
```
|
| 270 |
+
|
| 271 |
+
**CHECKPOINT 6: Judge Overrides (Optional)**
|
| 272 |
+
```python
|
| 273 |
+
if daily_overrides:
|
| 274 |
+
# Apply ADD_CASE overrides (highest priority)
|
| 275 |
+
for override in add_case_overrides:
|
| 276 |
+
case_to_add = find_case_by_id(override.case_id)
|
| 277 |
+
prioritized_cases.insert(override.new_position, case_to_add)
|
| 278 |
+
|
| 279 |
+
# Apply REMOVE_CASE overrides
|
| 280 |
+
for override in remove_case_overrides:
|
| 281 |
+
prioritized_cases = [c for c in prioritized_cases if c.case_id != override.case_id]
|
| 282 |
+
|
| 283 |
+
# Apply PRIORITY overrides
|
| 284 |
+
for override in priority_overrides:
|
| 285 |
+
case = find_case_in_list(prioritized_cases, override.case_id)
|
| 286 |
+
case.priority_score = override.new_priority
|
| 287 |
+
|
| 288 |
+
# Re-sort after priority changes
|
| 289 |
+
prioritized_cases.sort(key=lambda c: c.priority_score, reverse=True)
|
| 290 |
+
```
|
| 291 |
+
|
| 292 |
+
**CHECKPOINT 7: Multi-Courtroom Allocation**
|
| 293 |
+
```python
|
| 294 |
+
# Load balancing algorithm
|
| 295 |
+
courtroom_loads = {1: 0, 2: 0, 3: 0, 4: 0, 5: 0}
|
| 296 |
+
daily_schedule = {1: [], 2: [], 3: [], 4: [], 5: []}
|
| 297 |
+
|
| 298 |
+
for case in prioritized_cases:
|
| 299 |
+
# Find least loaded courtroom
|
| 300 |
+
target_courtroom = min(courtroom_loads.items(), key=lambda x: x[1])[0]
|
| 301 |
+
|
| 302 |
+
# Check capacity constraint
|
| 303 |
+
if courtroom_loads[target_courtroom] >= DEFAULT_DAILY_CAPACITY:
|
| 304 |
+
# All courtrooms at capacity, remaining cases unscheduled
|
| 305 |
+
break
|
| 306 |
+
|
| 307 |
+
# Assign case to courtroom
|
| 308 |
+
daily_schedule[target_courtroom].append(case)
|
| 309 |
+
courtroom_loads[target_courtroom] += 1
|
| 310 |
+
case.last_scheduled_date = current_date
|
| 311 |
+
|
| 312 |
+
total_scheduled = sum(len(cases) for cases in daily_schedule.values())
|
| 313 |
+
print(f"Allocation: {total_scheduled} cases scheduled across 5 courtrooms")
|
| 314 |
+
# Example: 703 cases scheduled (5 × 140-141 per courtroom)
|
| 315 |
+
```
|
| 316 |
+
|
| 317 |
+
**CHECKPOINT 8: Generate Explanations**
|
| 318 |
+
```python
|
| 319 |
+
explanations = {}
|
| 320 |
+
for courtroom_id, cases in daily_schedule.items():
|
| 321 |
+
for i, case in enumerate(cases):
|
| 322 |
+
urgency_text = "HIGH URGENCY" if case.is_urgent else "standard urgency"
|
| 323 |
+
stage_text = f"{case.current_stage.lower()} stage"
|
| 324 |
+
assignment_text = f"assigned to Courtroom {courtroom_id}"
|
| 325 |
+
|
| 326 |
+
explanations[case.case_id] = f"{urgency_text} | {stage_text} | {assignment_text}"
|
| 327 |
+
```
|
| 328 |
+
|
| 329 |
+
### Step 3.2: Case State Updates (After Each Day)
|
| 330 |
+
|
| 331 |
+
```python
|
| 332 |
+
def update_case_states(cases, daily_result):
|
| 333 |
+
for case in cases:
|
| 334 |
+
if case.case_id in daily_result.scheduled_cases:
|
| 335 |
+
# Case was scheduled today
|
| 336 |
+
case.status = CaseStatus.SCHEDULED
|
| 337 |
+
case.hearing_count += 1
|
| 338 |
+
case.last_hearing_date = current_date
|
| 339 |
+
|
| 340 |
+
# Simulate hearing outcome
|
| 341 |
+
if random.random() < get_adjournment_rate(case.current_stage):
|
| 342 |
+
# Case adjourned - stays in same stage
|
| 343 |
+
case.history.append({
|
| 344 |
+
"date": current_date,
|
| 345 |
+
"outcome": "ADJOURNED",
|
| 346 |
+
"next_hearing": current_date + timedelta(days=21)
|
| 347 |
+
})
|
| 348 |
+
else:
|
| 349 |
+
# Case heard - may progress to next stage or dispose
|
| 350 |
+
if should_progress_stage(case):
|
| 351 |
+
case.current_stage = get_next_stage(case.current_stage)
|
| 352 |
+
|
| 353 |
+
if should_dispose(case):
|
| 354 |
+
case.status = CaseStatus.DISPOSED
|
| 355 |
+
case.disposal_date = current_date
|
| 356 |
+
else:
|
| 357 |
+
# Case not scheduled today
|
| 358 |
+
case.days_since_last_scheduled += 1
|
| 359 |
+
```
|
| 360 |
+
|
| 361 |
+
---
|
| 362 |
+
|
| 363 |
+
## Phase 4: Output Generation
|
| 364 |
+
|
| 365 |
+
### Step 4.1: Daily Cause List Generation
|
| 366 |
+
|
| 367 |
+
**For each courtroom and each day**:
|
| 368 |
+
```python
|
| 369 |
+
# Generate cause_list_courtroom_1_2024-01-15.csv
|
| 370 |
+
def generate_daily_cause_list(courtroom_id, date, scheduled_cases):
|
| 371 |
+
cause_list = []
|
| 372 |
+
for i, case in enumerate(scheduled_cases):
|
| 373 |
+
cause_list.append({
|
| 374 |
+
"Date": date.strftime("%Y-%m-%d"),
|
| 375 |
+
"Courtroom_ID": courtroom_id,
|
| 376 |
+
"Case_ID": case.case_id,
|
| 377 |
+
"Case_Type": case.case_type,
|
| 378 |
+
"Stage": case.current_stage,
|
| 379 |
+
"Purpose": "HEARING",
|
| 380 |
+
"Sequence_Number": i + 1,
|
| 381 |
+
"Explanation": explanations[case.case_id]
|
| 382 |
+
})
|
| 383 |
+
|
| 384 |
+
# Save to CSV
|
| 385 |
+
df = pd.DataFrame(cause_list)
|
| 386 |
+
df.to_csv(f"cause_list_courtroom_{courtroom_id}_{date.strftime('%Y-%m-%d')}.csv")
|
| 387 |
+
```
|
| 388 |
+
|
| 389 |
+
**Example Output**:
|
| 390 |
+
```csv
|
| 391 |
+
Date,Courtroom_ID,Case_ID,Case_Type,Stage,Purpose,Sequence_Number,Explanation
|
| 392 |
+
2024-01-15,1,C002847,CRP,ARGUMENTS,HEARING,1,"HIGH URGENCY | arguments stage | assigned to Courtroom 1"
|
| 393 |
+
2024-01-15,1,C005123,CA,ADMISSION,HEARING,2,"standard urgency | admission stage | assigned to Courtroom 1"
|
| 394 |
+
2024-01-15,1,C001456,RSA,EVIDENCE,HEARING,3,"standard urgency | evidence stage | assigned to Courtroom 1"
|
| 395 |
+
```
|
| 396 |
+
|
| 397 |
+
### Step 4.2: Daily Metrics Tracking
|
| 398 |
+
|
| 399 |
+
```python
|
| 400 |
+
def record_daily_metrics(date, daily_result):
|
| 401 |
+
metrics = {
|
| 402 |
+
"date": date,
|
| 403 |
+
"scheduled": daily_result.total_scheduled,
|
| 404 |
+
"heard": calculate_heard_cases(daily_result),
|
| 405 |
+
"adjourned": calculate_adjourned_cases(daily_result),
|
| 406 |
+
"disposed": count_disposed_today(daily_result),
|
| 407 |
+
"utilization": daily_result.total_scheduled / (COURTROOMS * DEFAULT_DAILY_CAPACITY),
|
| 408 |
+
"gini_coefficient": calculate_gini_coefficient(courtroom_loads),
|
| 409 |
+
"ripeness_filtered": daily_result.ripeness_filtered_count
|
| 410 |
+
}
|
| 411 |
+
|
| 412 |
+
# Append to metrics.csv
|
| 413 |
+
append_to_csv("metrics.csv", metrics)
|
| 414 |
+
```
|
| 415 |
+
|
| 416 |
+
**Example metrics.csv**:
|
| 417 |
+
```csv
|
| 418 |
+
date,scheduled,heard,adjourned,disposed,utilization,gini_coefficient,ripeness_filtered
|
| 419 |
+
2024-01-15,703,430,273,12,0.931,0.245,287
|
| 420 |
+
2024-01-16,698,445,253,15,0.924,0.248,301
|
| 421 |
+
2024-01-17,701,421,280,18,0.928,0.251,294
|
| 422 |
+
```
|
| 423 |
+
|
| 424 |
+
---
|
| 425 |
+
|
| 426 |
+
## Phase 5: Analysis & Reporting
|
| 427 |
+
|
| 428 |
+
### Step 5.1: Simulation Summary Report
|
| 429 |
+
|
| 430 |
+
**After all 384 days complete**:
|
| 431 |
+
```python
|
| 432 |
+
def generate_simulation_report():
|
| 433 |
+
total_hearings = sum(daily_metrics["scheduled"])
|
| 434 |
+
total_heard = sum(daily_metrics["heard"])
|
| 435 |
+
total_adjourned = sum(daily_metrics["adjourned"])
|
| 436 |
+
total_disposed = count_disposed_cases()
|
| 437 |
+
|
| 438 |
+
report = f"""
|
| 439 |
+
SIMULATION SUMMARY
|
| 440 |
+
Horizon: {start_date} → {end_date} ({simulation_days} days)
|
| 441 |
+
|
| 442 |
+
Case Metrics:
|
| 443 |
+
Initial cases: {initial_case_count:,}
|
| 444 |
+
Cases disposed: {total_disposed:,} ({total_disposed/initial_case_count:.1%})
|
| 445 |
+
Cases remaining: {initial_case_count - total_disposed:,}
|
| 446 |
+
|
| 447 |
+
Hearing Metrics:
|
| 448 |
+
Total hearings: {total_hearings:,}
|
| 449 |
+
Heard: {total_heard:,} ({total_heard/total_hearings:.1%})
|
| 450 |
+
Adjourned: {total_adjourned:,} ({total_adjourned/total_hearings:.1%})
|
| 451 |
+
|
| 452 |
+
Efficiency Metrics:
|
| 453 |
+
Disposal rate: {total_disposed/initial_case_count:.1%}
|
| 454 |
+
Utilization: {avg_utilization:.1%}
|
| 455 |
+
Gini coefficient: {avg_gini:.3f}
|
| 456 |
+
Ripeness filtering: {avg_ripeness_filtered/avg_eligible:.1%}
|
| 457 |
+
"""
|
| 458 |
+
|
| 459 |
+
with open("simulation_report.txt", "w") as f:
|
| 460 |
+
f.write(report)
|
| 461 |
+
```
|
| 462 |
+
|
| 463 |
+
### Step 5.2: Performance Analysis
|
| 464 |
+
|
| 465 |
+
```python
|
| 466 |
+
# Calculate key performance indicators
|
| 467 |
+
disposal_rate = total_disposed / initial_cases # Target: >70%
|
| 468 |
+
load_balance = calculate_gini_coefficient(courtroom_loads) # Target: <0.4
|
| 469 |
+
case_coverage = scheduled_cases / eligible_cases # Target: >95%
|
| 470 |
+
bottleneck_efficiency = ripeness_filtered / total_cases # Higher = better filtering
|
| 471 |
+
|
| 472 |
+
print(f"PERFORMANCE RESULTS:")
|
| 473 |
+
print(f"Disposal Rate: {disposal_rate:.1%} ({'✓' if disposal_rate > 0.70 else '✗'})")
|
| 474 |
+
print(f"Load Balance: {load_balance:.3f} ({'✓' if load_balance < 0.40 else '✗'})")
|
| 475 |
+
print(f"Case Coverage: {case_coverage:.1%} ({'✓' if case_coverage > 0.95 else '✗'})")
|
| 476 |
+
```
|
| 477 |
+
|
| 478 |
+
---
|
| 479 |
+
|
| 480 |
+
## Complete Example Walkthrough
|
| 481 |
+
|
| 482 |
+
Let's trace a single case through the entire system:
|
| 483 |
+
|
| 484 |
+
### Case: C002847 (Civil Revision Petition)
|
| 485 |
+
|
| 486 |
+
**Day 0: Case Generation**
|
| 487 |
+
```python
|
| 488 |
+
case = Case(
|
| 489 |
+
case_id="C002847",
|
| 490 |
+
case_type="CRP",
|
| 491 |
+
filed_date=date(2022, 03, 15),
|
| 492 |
+
current_stage="ADMISSION",
|
| 493 |
+
is_urgent=True, # Medical emergency
|
| 494 |
+
hearing_count=0,
|
| 495 |
+
last_hearing_date=None
|
| 496 |
+
)
|
| 497 |
+
```
|
| 498 |
+
|
| 499 |
+
**Day 1: First Scheduling Attempt (2023-12-29)**
|
| 500 |
+
```python
|
| 501 |
+
# Checkpoint 1: Active? YES (status = PENDING)
|
| 502 |
+
# Checkpoint 2: Updates
|
| 503 |
+
case.age_days = 654 # Almost 2 years old
|
| 504 |
+
case.readiness_score = 0.3 # Low (admission stage)
|
| 505 |
+
|
| 506 |
+
# Checkpoint 3: Ripeness
|
| 507 |
+
ripeness = classify(case, current_date) # UNRIPE_SUMMONS (admission stage, 0 hearings)
|
| 508 |
+
|
| 509 |
+
# Result: FILTERED OUT (not scheduled)
|
| 510 |
+
```
|
| 511 |
+
|
| 512 |
+
**Day 45: Second Attempt (2024-02-26)**
|
| 513 |
+
```python
|
| 514 |
+
# Case now has 3 hearings, still in admission but making progress
|
| 515 |
+
case.hearing_count = 3
|
| 516 |
+
case.current_stage = "ADMISSION"
|
| 517 |
+
|
| 518 |
+
# Checkpoint 3: Ripeness
|
| 519 |
+
ripeness = classify(case, current_date) # RIPE (>3 hearings in admission)
|
| 520 |
+
|
| 521 |
+
# Checkpoint 5: Priority Scoring
|
| 522 |
+
age_component = min(689 / 365, 1.0) * 0.35 = 0.35
|
| 523 |
+
readiness_component = 0.4 * 0.25 = 0.10
|
| 524 |
+
urgency_component = 1.0 * 0.25 = 0.25 # HIGH URGENCY
|
| 525 |
+
boost_component = 0.0 * 0.15 = 0.0
|
| 526 |
+
case.priority_score = 0.70 # High priority
|
| 527 |
+
|
| 528 |
+
# Checkpoint 7: Allocation
|
| 529 |
+
# Assigned to Courtroom 1 (least loaded), Position 3
|
| 530 |
+
|
| 531 |
+
# Result: SCHEDULED
|
| 532 |
+
```
|
| 533 |
+
|
| 534 |
+
**Daily Cause List Entry**:
|
| 535 |
+
```csv
|
| 536 |
+
2024-02-26,1,C002847,CRP,ADMISSION,HEARING,3,"HIGH URGENCY | admission stage | assigned to Courtroom 1"
|
| 537 |
+
```
|
| 538 |
+
|
| 539 |
+
**Hearing Outcome**:
|
| 540 |
+
```python
|
| 541 |
+
# Simulated outcome: Case heard successfully, progresses to ARGUMENTS
|
| 542 |
+
case.current_stage = "ARGUMENTS"
|
| 543 |
+
case.hearing_count = 4
|
| 544 |
+
case.last_hearing_date = date(2024, 2, 26)
|
| 545 |
+
case.history.append({
|
| 546 |
+
"date": date(2024, 2, 26),
|
| 547 |
+
"outcome": "HEARD",
|
| 548 |
+
"stage_progression": "ADMISSION → ARGUMENTS"
|
| 549 |
+
})
|
| 550 |
+
```
|
| 551 |
+
|
| 552 |
+
**Day 125: Arguments Stage (2024-06-15)**
|
| 553 |
+
```python
|
| 554 |
+
# Case now in arguments, higher readiness
|
| 555 |
+
case.current_stage = "ARGUMENTS"
|
| 556 |
+
case.readiness_score = 0.8 # High (arguments stage)
|
| 557 |
+
|
| 558 |
+
# Priority calculation
|
| 559 |
+
age_component = 0.35 # Still max age
|
| 560 |
+
readiness_component = 0.8 * 0.25 = 0.20 # Higher
|
| 561 |
+
urgency_component = 0.25 # Still urgent
|
| 562 |
+
boost_component = 0.0
|
| 563 |
+
case.priority_score = 0.80 # Very high priority
|
| 564 |
+
|
| 565 |
+
# Result: Scheduled in Position 1 (highest priority)
|
| 566 |
+
```
|
| 567 |
+
|
| 568 |
+
**Final Disposal (Day 200: 2024-09-15)**
|
| 569 |
+
```python
|
| 570 |
+
# After multiple hearings in arguments stage
|
| 571 |
+
case.current_stage = "ORDERS / JUDGMENT"
|
| 572 |
+
case.hearing_count = 12
|
| 573 |
+
|
| 574 |
+
# Hearing outcome: Case disposed
|
| 575 |
+
case.status = CaseStatus.DISPOSED
|
| 576 |
+
case.disposal_date = date(2024, 9, 15)
|
| 577 |
+
case.total_lifecycle_days = (disposal_date - filed_date).days # 549 days
|
| 578 |
+
```
|
| 579 |
+
|
| 580 |
+
---
|
| 581 |
+
|
| 582 |
+
## Data Flow Pipeline
|
| 583 |
+
|
| 584 |
+
### Complete Data Transformation Chain
|
| 585 |
+
|
| 586 |
+
```
|
| 587 |
+
1. Historical CSV Files (Raw Data)
|
| 588 |
+
├── ISDMHack_Case.csv (134,699 rows × 24 columns)
|
| 589 |
+
└── ISDMHack_Hear.csv (739,670 rows × 31 columns)
|
| 590 |
+
|
| 591 |
+
2. Parameter Extraction (EDA Analysis)
|
| 592 |
+
├── case_type_distribution.json
|
| 593 |
+
├── stage_transition_probabilities.json
|
| 594 |
+
├── adjournment_rates_by_stage.json
|
| 595 |
+
└── daily_capacity_statistics.json
|
| 596 |
+
|
| 597 |
+
3. Synthetic Case Generation
|
| 598 |
+
└── cases.csv (10,000 rows × 15 columns)
|
| 599 |
+
├── Case_ID, Case_Type, Filed_Date
|
| 600 |
+
├── Current_Stage, Is_Urgent, Hearing_Count
|
| 601 |
+
└── Last_Hearing_Date, Last_Purpose
|
| 602 |
+
|
| 603 |
+
4. Daily Scheduling Loop (384 iterations)
|
| 604 |
+
├── Day 1: cases.csv → ripeness_filter → 6,850 → eligible_filter → 5,200 → priority_sort → allocate → 703 scheduled
|
| 605 |
+
├── Day 2: updated_cases → ripeness_filter → 6,820 → eligible_filter → 5,180 → priority_sort → allocate → 698 scheduled
|
| 606 |
+
└── Day 384: updated_cases → ripeness_filter → 2,100 → eligible_filter → 1,950 → priority_sort → allocate → 421 scheduled
|
| 607 |
+
|
| 608 |
+
5. Daily Output Generation (per day × 5 courtrooms)
|
| 609 |
+
├── cause_list_courtroom_1_2024-01-15.csv (140 rows)
|
| 610 |
+
├── cause_list_courtroom_2_2024-01-15.csv (141 rows)
|
| 611 |
+
├── cause_list_courtroom_3_2024-01-15.csv (140 rows)
|
| 612 |
+
├── cause_list_courtroom_4_2024-01-15.csv (141 rows)
|
| 613 |
+
└── cause_list_courtroom_5_2024-01-15.csv (141 rows)
|
| 614 |
+
|
| 615 |
+
6. Aggregated Metrics
|
| 616 |
+
├── metrics.csv (384 rows × 8 columns)
|
| 617 |
+
├── simulation_report.txt (summary statistics)
|
| 618 |
+
└── case_audit_trail.csv (complete hearing history)
|
| 619 |
+
```
|
| 620 |
+
|
| 621 |
+
### Data Volume at Each Stage
|
| 622 |
+
- **Input**: 874K+ historical records
|
| 623 |
+
- **Generated**: 10K synthetic cases
|
| 624 |
+
- **Daily Processing**: ~6K cases evaluated daily
|
| 625 |
+
- **Daily Output**: ~700 scheduled cases/day
|
| 626 |
+
- **Total Output**: ~42K total cause list entries
|
| 627 |
+
- **Final Reports**: 384 daily metrics + summary reports
|
| 628 |
+
|
| 629 |
+
---
|
| 630 |
+
|
| 631 |
+
**Key Takeaways:**
|
| 632 |
+
1. **Ripeness filtering** removes 40.8% of cases daily (most critical efficiency gain)
|
| 633 |
+
2. **Priority scoring** ensures fairness while handling urgent cases
|
| 634 |
+
3. **Load balancing** achieves near-perfect distribution (Gini 0.002)
|
| 635 |
+
4. **Daily loop** processes 6,000+ cases in seconds with multi-objective optimization
|
| 636 |
+
5. **Complete audit trail** tracks every case decision for transparency
|
| 637 |
+
|
| 638 |
+
---
|
| 639 |
+
|
| 640 |
+
**Last Updated**: 2025-11-25
|
| 641 |
+
**Version**: 1.0
|
| 642 |
+
**Status**: Production Ready
|
TECHNICAL_IMPLEMENTATION.md
ADDED
|
@@ -0,0 +1,658 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Court Scheduling System - Technical Implementation Documentation
|
| 2 |
+
|
| 3 |
+
**Complete Implementation Guide for Code4Change Hackathon Submission**
|
| 4 |
+
|
| 5 |
+
---
|
| 6 |
+
|
| 7 |
+
## Table of Contents
|
| 8 |
+
1. [System Overview](#system-overview)
|
| 9 |
+
2. [Architecture & Design](#architecture--design)
|
| 10 |
+
3. [Configuration Management](#configuration-management)
|
| 11 |
+
4. [Core Algorithms](#core-algorithms)
|
| 12 |
+
5. [Data Models](#data-models)
|
| 13 |
+
6. [Decision Logic](#decision-logic)
|
| 14 |
+
7. [Input/Output Specifications](#inputoutput-specifications)
|
| 15 |
+
8. [Deployment & Usage](#deployment--usage)
|
| 16 |
+
9. [Assumptions & Constraints](#assumptions--constraints)
|
| 17 |
+
|
| 18 |
+
---
|
| 19 |
+
|
| 20 |
+
## System Overview
|
| 21 |
+
|
| 22 |
+
### Purpose
|
| 23 |
+
Production-ready court scheduling system for Karnataka High Court that optimizes daily cause lists across multiple courtrooms while ensuring fairness, efficiency, and judicial control.
|
| 24 |
+
|
| 25 |
+
### Key Achievements
|
| 26 |
+
- **81.4% Disposal Rate** - Exceeds baseline expectations
|
| 27 |
+
- **Perfect Load Balance** - Gini coefficient 0.002 across courtrooms
|
| 28 |
+
- **97.7% Case Coverage** - Near-zero case abandonment
|
| 29 |
+
- **Smart Bottleneck Detection** - 40.8% unripe cases filtered
|
| 30 |
+
- **Complete Judge Control** - Override system with audit trails
|
| 31 |
+
|
| 32 |
+
### Technology Stack
|
| 33 |
+
```toml
|
| 34 |
+
# Core Dependencies (from pyproject.toml)
|
| 35 |
+
dependencies = [
|
| 36 |
+
"pandas>=2.2", # Data manipulation
|
| 37 |
+
"polars>=1.30", # High-performance data processing
|
| 38 |
+
"plotly>=6.0", # Visualization
|
| 39 |
+
"numpy>=2.0", # Numerical computing
|
| 40 |
+
"simpy>=4.1", # Discrete event simulation
|
| 41 |
+
"typer>=0.12", # CLI interface
|
| 42 |
+
"pydantic>=2.0", # Data validation
|
| 43 |
+
"scipy>=1.14", # Statistical algorithms
|
| 44 |
+
"streamlit>=1.28", # Dashboard (future)
|
| 45 |
+
]
|
| 46 |
+
```
|
| 47 |
+
|
| 48 |
+
---
|
| 49 |
+
|
| 50 |
+
## Architecture & Design
|
| 51 |
+
|
| 52 |
+
### System Architecture
|
| 53 |
+
```
|
| 54 |
+
Court Scheduling System
|
| 55 |
+
├── Core Domain Layer (scheduler/core/)
|
| 56 |
+
│ ├── case.py # Case entity with lifecycle management
|
| 57 |
+
│ ├── courtroom.py # Courtroom resource management
|
| 58 |
+
│ ├── ripeness.py # Bottleneck detection classifier
|
| 59 |
+
│ ├── policy.py # Scheduling policy interface
|
| 60 |
+
│ └── algorithm.py # Main scheduling algorithm
|
| 61 |
+
├── Simulation Engine (scheduler/simulation/)
|
| 62 |
+
│ ├── engine.py # Discrete event simulation
|
| 63 |
+
│ ├── allocator.py # Multi-courtroom load balancer
|
| 64 |
+
│ └── policies/ # FIFO, Age, Readiness policies
|
| 65 |
+
├── Data Management (scheduler/data/)
|
| 66 |
+
│ ├── param_loader.py # Historical parameter loading
|
| 67 |
+
│ ├── case_generator.py # Synthetic case generation
|
| 68 |
+
│ └── config.py # System configuration
|
| 69 |
+
├── Control Systems (scheduler/control/)
|
| 70 |
+
│ └── overrides.py # Judge override & audit system
|
| 71 |
+
├── Output Generation (scheduler/output/)
|
| 72 |
+
│ └── cause_list.py # Daily cause list CSV generation
|
| 73 |
+
└── Analysis Tools (src/, scripts/)
|
| 74 |
+
├── EDA pipeline # Historical data analysis
|
| 75 |
+
└── Validation tools # Performance verification
|
| 76 |
+
```
|
| 77 |
+
|
| 78 |
+
### Design Principles
|
| 79 |
+
1. **Clean Architecture** - Domain-driven design with clear layer separation
|
| 80 |
+
2. **Production Ready** - Type hints, error handling, comprehensive logging
|
| 81 |
+
3. **Data-Driven** - All parameters extracted from 739K+ historical hearings
|
| 82 |
+
4. **Judge Autonomy** - Complete override system with audit trails
|
| 83 |
+
5. **Scalable** - Supports multiple courtrooms, thousands of cases
|
| 84 |
+
|
| 85 |
+
---
|
| 86 |
+
|
| 87 |
+
## Configuration Management
|
| 88 |
+
|
| 89 |
+
### Primary Configuration (scheduler/data/config.py)
|
| 90 |
+
```python
|
| 91 |
+
# Court Operational Constants
|
| 92 |
+
WORKING_DAYS_PER_YEAR = 192 # Karnataka HC calendar
|
| 93 |
+
COURTROOMS = 5 # Number of courtrooms
|
| 94 |
+
SIMULATION_DAYS = 384 # 2-year simulation period
|
| 95 |
+
|
| 96 |
+
# Scheduling Constraints
|
| 97 |
+
MIN_GAP_BETWEEN_HEARINGS = 14 # Days between hearings
|
| 98 |
+
MAX_GAP_WITHOUT_ALERT = 90 # Alert threshold
|
| 99 |
+
DEFAULT_DAILY_CAPACITY = 151 # Cases per courtroom per day
|
| 100 |
+
|
| 101 |
+
# Case Type Distribution (from EDA)
|
| 102 |
+
CASE_TYPE_DISTRIBUTION = {
|
| 103 |
+
"CRP": 0.201, # Civil Revision Petition (most common)
|
| 104 |
+
"CA": 0.200, # Civil Appeal
|
| 105 |
+
"RSA": 0.196, # Regular Second Appeal
|
| 106 |
+
"RFA": 0.167, # Regular First Appeal
|
| 107 |
+
"CCC": 0.111, # Civil Contempt Petition
|
| 108 |
+
"CP": 0.096, # Civil Petition
|
| 109 |
+
"CMP": 0.028, # Civil Miscellaneous Petition
|
| 110 |
+
}
|
| 111 |
+
|
| 112 |
+
# Multi-objective Optimization Weights
|
| 113 |
+
FAIRNESS_WEIGHT = 0.4 # Age-based fairness priority
|
| 114 |
+
EFFICIENCY_WEIGHT = 0.3 # Readiness-based efficiency
|
| 115 |
+
URGENCY_WEIGHT = 0.3 # High-priority case handling
|
| 116 |
+
```
|
| 117 |
+
|
| 118 |
+
### TOML Configuration Files
|
| 119 |
+
|
| 120 |
+
#### Case Generation (configs/generate.sample.toml)
|
| 121 |
+
```toml
|
| 122 |
+
n_cases = 10000
|
| 123 |
+
start = "2022-01-01"
|
| 124 |
+
end = "2023-12-31"
|
| 125 |
+
output = "data/generated/cases.csv"
|
| 126 |
+
seed = 42
|
| 127 |
+
```
|
| 128 |
+
|
| 129 |
+
#### Simulation (configs/simulate.sample.toml)
|
| 130 |
+
```toml
|
| 131 |
+
cases = "data/generated/cases.csv"
|
| 132 |
+
days = 384
|
| 133 |
+
policy = "readiness" # readiness|fifo|age
|
| 134 |
+
seed = 42
|
| 135 |
+
courtrooms = 5
|
| 136 |
+
daily_capacity = 151
|
| 137 |
+
```
|
| 138 |
+
|
| 139 |
+
#### Parameter Sweep (configs/parameter_sweep.toml)
|
| 140 |
+
```toml
|
| 141 |
+
[sweep]
|
| 142 |
+
simulation_days = 500
|
| 143 |
+
policies = ["fifo", "age", "readiness"]
|
| 144 |
+
|
| 145 |
+
# Dataset variations for comprehensive testing
|
| 146 |
+
[[datasets]]
|
| 147 |
+
name = "baseline"
|
| 148 |
+
cases = 10000
|
| 149 |
+
stage_mix_auto = true
|
| 150 |
+
urgent_percentage = 0.10
|
| 151 |
+
|
| 152 |
+
[[datasets]]
|
| 153 |
+
name = "admission_heavy"
|
| 154 |
+
cases = 10000
|
| 155 |
+
stage_mix = { "ADMISSION" = 0.70, "ARGUMENTS" = 0.15 }
|
| 156 |
+
urgent_percentage = 0.10
|
| 157 |
+
```
|
| 158 |
+
|
| 159 |
+
---
|
| 160 |
+
|
| 161 |
+
## Core Algorithms
|
| 162 |
+
|
| 163 |
+
### 1. Ripeness Classification System
|
| 164 |
+
|
| 165 |
+
#### Purpose
|
| 166 |
+
Identifies cases with substantive bottlenecks to prevent wasteful scheduling of unready cases.
|
| 167 |
+
|
| 168 |
+
#### Algorithm (scheduler/core/ripeness.py)
|
| 169 |
+
```python
|
| 170 |
+
def classify(case: Case, current_date: date) -> RipenessStatus:
|
| 171 |
+
"""5-step hierarchical classifier"""
|
| 172 |
+
|
| 173 |
+
# Step 1: Check hearing purpose for explicit bottlenecks
|
| 174 |
+
if "SUMMONS" in last_hearing_purpose or "NOTICE" in last_hearing_purpose:
|
| 175 |
+
return UNRIPE_SUMMONS
|
| 176 |
+
if "STAY" in last_hearing_purpose or "PENDING" in last_hearing_purpose:
|
| 177 |
+
return UNRIPE_DEPENDENT
|
| 178 |
+
|
| 179 |
+
# Step 2: Stage analysis - Early admission cases likely unripe
|
| 180 |
+
if current_stage == "ADMISSION" and hearing_count < 3:
|
| 181 |
+
return UNRIPE_SUMMONS
|
| 182 |
+
|
| 183 |
+
# Step 3: Detect "stuck" cases (many hearings, no progress)
|
| 184 |
+
if hearing_count > 10 and avg_gap_days > 60:
|
| 185 |
+
return UNRIPE_PARTY
|
| 186 |
+
|
| 187 |
+
# Step 4: Stage-based classification
|
| 188 |
+
if current_stage in ["ARGUMENTS", "EVIDENCE", "ORDERS / JUDGMENT"]:
|
| 189 |
+
return RIPE
|
| 190 |
+
|
| 191 |
+
# Step 5: Conservative default
|
| 192 |
+
return RIPE
|
| 193 |
+
```
|
| 194 |
+
|
| 195 |
+
#### Ripeness Statuses
|
| 196 |
+
| Status | Meaning | Impact |
|
| 197 |
+
|--------|---------|---------|
|
| 198 |
+
| `RIPE` | Ready for hearing | Eligible for scheduling |
|
| 199 |
+
| `UNRIPE_SUMMONS` | Awaiting summons service | Blocked until served |
|
| 200 |
+
| `UNRIPE_DEPENDENT` | Waiting for dependent case | Blocked until resolved |
|
| 201 |
+
| `UNRIPE_PARTY` | Party/lawyer unavailable | Blocked until responsive |
|
| 202 |
+
|
| 203 |
+
### 2. Multi-Courtroom Load Balancing
|
| 204 |
+
|
| 205 |
+
#### Algorithm (scheduler/simulation/allocator.py)
|
| 206 |
+
```python
|
| 207 |
+
def allocate(cases: List[Case], current_date: date) -> Dict[str, int]:
|
| 208 |
+
"""Dynamic load-balanced allocation"""
|
| 209 |
+
|
| 210 |
+
allocation = {}
|
| 211 |
+
courtroom_loads = {room.id: room.get_current_load() for room in courtrooms}
|
| 212 |
+
|
| 213 |
+
for case in cases:
|
| 214 |
+
# Find least-loaded courtroom
|
| 215 |
+
target_room = min(courtroom_loads.items(), key=lambda x: x[1])
|
| 216 |
+
|
| 217 |
+
# Assign case and update load
|
| 218 |
+
allocation[case.case_id] = target_room[0]
|
| 219 |
+
courtroom_loads[target_room[0]] += 1
|
| 220 |
+
|
| 221 |
+
# Respect capacity constraints
|
| 222 |
+
if courtroom_loads[target_room[0]] >= room.daily_capacity:
|
| 223 |
+
break
|
| 224 |
+
|
| 225 |
+
return allocation
|
| 226 |
+
```
|
| 227 |
+
|
| 228 |
+
#### Load Balancing Results
|
| 229 |
+
- **Perfect Distribution**: Gini coefficient 0.002
|
| 230 |
+
- **Courtroom Loads**: 67.6-68.3 cases/day (±0.5% variance)
|
| 231 |
+
- **Zero Capacity Violations**: All constraints respected
|
| 232 |
+
|
| 233 |
+
### 3. Intelligent Priority Scheduling
|
| 234 |
+
|
| 235 |
+
#### Readiness-Based Policy (scheduler/simulation/policies/readiness.py)
|
| 236 |
+
```python
|
| 237 |
+
def prioritize(cases: List[Case], current_date: date) -> List[Case]:
|
| 238 |
+
"""Multi-factor priority calculation"""
|
| 239 |
+
|
| 240 |
+
for case in cases:
|
| 241 |
+
# Age component (35%) - Fairness
|
| 242 |
+
age_score = min(case.age_days / 365, 1.0) * 0.35
|
| 243 |
+
|
| 244 |
+
# Readiness component (25%) - Efficiency
|
| 245 |
+
readiness_score = case.compute_readiness_score() * 0.25
|
| 246 |
+
|
| 247 |
+
# Urgency component (25%) - Critical cases
|
| 248 |
+
urgency_score = (1.0 if case.is_urgent else 0.5) * 0.25
|
| 249 |
+
|
| 250 |
+
# Adjournment boost (15%) - Prevent indefinite postponement
|
| 251 |
+
boost_score = case.get_adjournment_boost() * 0.15
|
| 252 |
+
|
| 253 |
+
case.priority_score = age_score + readiness_score + urgency_score + boost_score
|
| 254 |
+
|
| 255 |
+
return sorted(cases, key=lambda c: c.priority_score, reverse=True)
|
| 256 |
+
```
|
| 257 |
+
|
| 258 |
+
#### Adjournment Boost Calculation
|
| 259 |
+
```python
|
| 260 |
+
def get_adjournment_boost(self) -> float:
|
| 261 |
+
"""Exponential decay boost for recently adjourned cases"""
|
| 262 |
+
if not self.last_hearing_date:
|
| 263 |
+
return 0.0
|
| 264 |
+
|
| 265 |
+
days_since = (current_date - self.last_hearing_date).days
|
| 266 |
+
return math.exp(-days_since / 21) # 21-day half-life
|
| 267 |
+
```
|
| 268 |
+
|
| 269 |
+
### 4. Judge Override System
|
| 270 |
+
|
| 271 |
+
#### Override Types (scheduler/control/overrides.py)
|
| 272 |
+
```python
|
| 273 |
+
class OverrideType(Enum):
|
| 274 |
+
RIPENESS = "ripeness" # Override ripeness classification
|
| 275 |
+
PRIORITY = "priority" # Adjust case priority
|
| 276 |
+
ADD_CASE = "add_case" # Manually add case to list
|
| 277 |
+
REMOVE_CASE = "remove_case" # Remove case from list
|
| 278 |
+
REORDER = "reorder" # Change hearing sequence
|
| 279 |
+
CAPACITY = "capacity" # Adjust daily capacity
|
| 280 |
+
```
|
| 281 |
+
|
| 282 |
+
#### Validation Logic
|
| 283 |
+
```python
|
| 284 |
+
def validate(self, override: Override) -> bool:
|
| 285 |
+
"""Comprehensive override validation"""
|
| 286 |
+
|
| 287 |
+
if override.override_type == OverrideType.RIPENESS:
|
| 288 |
+
return self.validate_ripeness_override(override)
|
| 289 |
+
elif override.override_type == OverrideType.CAPACITY:
|
| 290 |
+
return self.validate_capacity_override(override)
|
| 291 |
+
elif override.override_type == OverrideType.PRIORITY:
|
| 292 |
+
return 0 <= override.new_priority <= 1.0
|
| 293 |
+
|
| 294 |
+
return True
|
| 295 |
+
```
|
| 296 |
+
|
| 297 |
+
---
|
| 298 |
+
|
| 299 |
+
## Data Models
|
| 300 |
+
|
| 301 |
+
### Core Case Entity (scheduler/core/case.py)
|
| 302 |
+
```python
|
| 303 |
+
@dataclass
|
| 304 |
+
class Case:
|
| 305 |
+
# Core Identification
|
| 306 |
+
case_id: str
|
| 307 |
+
case_type: str # CRP, CA, RSA, etc.
|
| 308 |
+
filed_date: date
|
| 309 |
+
|
| 310 |
+
# Lifecycle Tracking
|
| 311 |
+
current_stage: str = "ADMISSION"
|
| 312 |
+
status: CaseStatus = CaseStatus.PENDING
|
| 313 |
+
hearing_count: int = 0
|
| 314 |
+
last_hearing_date: Optional[date] = None
|
| 315 |
+
|
| 316 |
+
# Scheduling Attributes
|
| 317 |
+
priority_score: float = 0.0
|
| 318 |
+
readiness_score: float = 0.0
|
| 319 |
+
is_urgent: bool = False
|
| 320 |
+
|
| 321 |
+
# Ripeness Classification
|
| 322 |
+
ripeness_status: str = "UNKNOWN"
|
| 323 |
+
bottleneck_reason: Optional[str] = None
|
| 324 |
+
ripeness_updated_at: Optional[datetime] = None
|
| 325 |
+
|
| 326 |
+
# No-Case-Left-Behind Tracking
|
| 327 |
+
last_scheduled_date: Optional[date] = None
|
| 328 |
+
days_since_last_scheduled: int = 0
|
| 329 |
+
|
| 330 |
+
# Audit Trail
|
| 331 |
+
history: List[dict] = field(default_factory=list)
|
| 332 |
+
```
|
| 333 |
+
|
| 334 |
+
### Override Entity
|
| 335 |
+
```python
|
| 336 |
+
@dataclass
|
| 337 |
+
class Override:
|
| 338 |
+
# Core Fields
|
| 339 |
+
override_id: str
|
| 340 |
+
override_type: OverrideType
|
| 341 |
+
case_id: str
|
| 342 |
+
judge_id: str
|
| 343 |
+
timestamp: datetime
|
| 344 |
+
reason: str = ""
|
| 345 |
+
|
| 346 |
+
# Type-Specific Fields
|
| 347 |
+
make_ripe: Optional[bool] = None # For RIPENESS
|
| 348 |
+
new_position: Optional[int] = None # For REORDER/ADD_CASE
|
| 349 |
+
new_priority: Optional[float] = None # For PRIORITY
|
| 350 |
+
new_capacity: Optional[int] = None # For CAPACITY
|
| 351 |
+
```
|
| 352 |
+
|
| 353 |
+
### Scheduling Result
|
| 354 |
+
```python
|
| 355 |
+
@dataclass
|
| 356 |
+
class SchedulingResult:
|
| 357 |
+
# Core Output
|
| 358 |
+
scheduled_cases: Dict[int, List[Case]] # courtroom_id -> cases
|
| 359 |
+
|
| 360 |
+
# Transparency
|
| 361 |
+
explanations: Dict[str, SchedulingExplanation]
|
| 362 |
+
applied_overrides: List[Override]
|
| 363 |
+
|
| 364 |
+
# Diagnostics
|
| 365 |
+
unscheduled_cases: List[Tuple[Case, str]]
|
| 366 |
+
ripeness_filtered: int
|
| 367 |
+
capacity_limited: int
|
| 368 |
+
|
| 369 |
+
# Metadata
|
| 370 |
+
scheduling_date: date
|
| 371 |
+
policy_used: str
|
| 372 |
+
total_scheduled: int
|
| 373 |
+
```
|
| 374 |
+
|
| 375 |
+
---
|
| 376 |
+
|
| 377 |
+
## Decision Logic
|
| 378 |
+
|
| 379 |
+
### Daily Scheduling Sequence
|
| 380 |
+
```python
|
| 381 |
+
def schedule_day(cases, courtrooms, current_date, overrides=None):
|
| 382 |
+
"""Complete daily scheduling algorithm"""
|
| 383 |
+
|
| 384 |
+
# CHECKPOINT 1: Filter disposed cases
|
| 385 |
+
active_cases = [c for c in cases if c.status != DISPOSED]
|
| 386 |
+
|
| 387 |
+
# CHECKPOINT 2: Update case attributes
|
| 388 |
+
for case in active_cases:
|
| 389 |
+
case.update_age(current_date)
|
| 390 |
+
case.compute_readiness_score()
|
| 391 |
+
|
| 392 |
+
# CHECKPOINT 3: Ripeness filtering (CRITICAL)
|
| 393 |
+
ripe_cases = []
|
| 394 |
+
for case in active_cases:
|
| 395 |
+
ripeness = RipenessClassifier.classify(case, current_date)
|
| 396 |
+
if ripeness.is_ripe():
|
| 397 |
+
ripe_cases.append(case)
|
| 398 |
+
else:
|
| 399 |
+
# Track filtered cases for metrics
|
| 400 |
+
unripe_filtered_count += 1
|
| 401 |
+
|
| 402 |
+
# CHECKPOINT 4: Eligibility check (MIN_GAP_BETWEEN_HEARINGS)
|
| 403 |
+
eligible_cases = [c for c in ripe_cases
|
| 404 |
+
if c.is_ready_for_scheduling(MIN_GAP_DAYS)]
|
| 405 |
+
|
| 406 |
+
# CHECKPOINT 5: Apply scheduling policy
|
| 407 |
+
prioritized_cases = policy.prioritize(eligible_cases, current_date)
|
| 408 |
+
|
| 409 |
+
# CHECKPOINT 6: Apply judge overrides
|
| 410 |
+
if overrides:
|
| 411 |
+
prioritized_cases = apply_overrides(prioritized_cases, overrides)
|
| 412 |
+
|
| 413 |
+
# CHECKPOINT 7: Allocate to courtrooms
|
| 414 |
+
allocation = allocator.allocate(prioritized_cases, current_date)
|
| 415 |
+
|
| 416 |
+
# CHECKPOINT 8: Generate explanations
|
| 417 |
+
explanations = generate_explanations(allocation, unscheduled_cases)
|
| 418 |
+
|
| 419 |
+
return SchedulingResult(...)
|
| 420 |
+
```
|
| 421 |
+
|
| 422 |
+
### Override Application Logic
|
| 423 |
+
```python
|
| 424 |
+
def apply_overrides(cases: List[Case], overrides: List[Override]) -> List[Case]:
|
| 425 |
+
"""Apply judge overrides in priority order"""
|
| 426 |
+
|
| 427 |
+
result = cases.copy()
|
| 428 |
+
|
| 429 |
+
# 1. Apply ADD_CASE overrides (highest priority)
|
| 430 |
+
for override in [o for o in overrides if o.override_type == ADD_CASE]:
|
| 431 |
+
case_to_add = find_case_by_id(override.case_id)
|
| 432 |
+
if case_to_add and case_to_add not in result:
|
| 433 |
+
insert_position = override.new_position or 0
|
| 434 |
+
result.insert(insert_position, case_to_add)
|
| 435 |
+
|
| 436 |
+
# 2. Apply REMOVE_CASE overrides
|
| 437 |
+
for override in [o for o in overrides if o.override_type == REMOVE_CASE]:
|
| 438 |
+
result = [c for c in result if c.case_id != override.case_id]
|
| 439 |
+
|
| 440 |
+
# 3. Apply PRIORITY overrides
|
| 441 |
+
for override in [o for o in overrides if o.override_type == PRIORITY]:
|
| 442 |
+
case = find_case_in_list(result, override.case_id)
|
| 443 |
+
if case and override.new_priority is not None:
|
| 444 |
+
case.priority_score = override.new_priority
|
| 445 |
+
|
| 446 |
+
# 4. Re-sort by updated priorities
|
| 447 |
+
result.sort(key=lambda c: c.priority_score, reverse=True)
|
| 448 |
+
|
| 449 |
+
# 5. Apply REORDER overrides (final positioning)
|
| 450 |
+
for override in [o for o in overrides if o.override_type == REORDER]:
|
| 451 |
+
case = find_case_in_list(result, override.case_id)
|
| 452 |
+
if case and override.new_position is not None:
|
| 453 |
+
result.remove(case)
|
| 454 |
+
result.insert(override.new_position, case)
|
| 455 |
+
|
| 456 |
+
return result
|
| 457 |
+
```
|
| 458 |
+
|
| 459 |
+
---
|
| 460 |
+
|
| 461 |
+
## Input/Output Specifications
|
| 462 |
+
|
| 463 |
+
### Input Data Requirements
|
| 464 |
+
|
| 465 |
+
#### Historical Data (for parameter extraction)
|
| 466 |
+
- **ISDMHack_Case.csv**: 134,699 cases with 24 attributes
|
| 467 |
+
- **ISDMHack_Hear.csv**: 739,670 hearings with 31 attributes
|
| 468 |
+
- Required fields: Case_ID, Type, Filed_Date, Current_Stage, Hearing_Date, Purpose_Of_Hearing
|
| 469 |
+
|
| 470 |
+
#### Generated Case Data (for simulation)
|
| 471 |
+
```python
|
| 472 |
+
# Case generation schema
|
| 473 |
+
Case(
|
| 474 |
+
case_id="C{:06d}", # C000001, C000002, etc.
|
| 475 |
+
case_type=random_choice(types), # CRP, CA, RSA, etc.
|
| 476 |
+
filed_date=random_date(range), # Within specified period
|
| 477 |
+
current_stage=stage_from_mix, # Based on distribution
|
| 478 |
+
is_urgent=random_bool(0.05), # 5% urgent cases
|
| 479 |
+
last_hearing_purpose=purpose, # For ripeness classification
|
| 480 |
+
)
|
| 481 |
+
```
|
| 482 |
+
|
| 483 |
+
### Output Specifications
|
| 484 |
+
|
| 485 |
+
#### Daily Cause Lists (CSV)
|
| 486 |
+
```csv
|
| 487 |
+
Date,Courtroom_ID,Case_ID,Case_Type,Stage,Purpose,Sequence_Number,Explanation
|
| 488 |
+
2024-01-15,1,C000123,CRP,ARGUMENTS,HEARING,1,"HIGH URGENCY | ready for orders/judgment | assigned to Courtroom 1"
|
| 489 |
+
2024-01-15,1,C000456,CA,ADMISSION,HEARING,2,"standard urgency | admission stage | assigned to Courtroom 1"
|
| 490 |
+
```
|
| 491 |
+
|
| 492 |
+
#### Simulation Report (report.txt)
|
| 493 |
+
```
|
| 494 |
+
SIMULATION SUMMARY
|
| 495 |
+
Horizon: 2023-12-29 → 2024-03-21 (60 days)
|
| 496 |
+
|
| 497 |
+
Hearing Metrics:
|
| 498 |
+
Total: 42,193
|
| 499 |
+
Heard: 26,245 (62.2%)
|
| 500 |
+
Adjourned: 15,948 (37.8%)
|
| 501 |
+
|
| 502 |
+
Disposal Metrics:
|
| 503 |
+
Cases disposed: 4,401 (44.0%)
|
| 504 |
+
Gini coefficient: 0.255
|
| 505 |
+
|
| 506 |
+
Efficiency:
|
| 507 |
+
Utilization: 93.1%
|
| 508 |
+
Avg hearings/day: 703.2
|
| 509 |
+
```
|
| 510 |
+
|
| 511 |
+
#### Metrics CSV (metrics.csv)
|
| 512 |
+
```csv
|
| 513 |
+
date,scheduled,heard,adjourned,disposed,utilization,gini_coefficient,ripeness_filtered
|
| 514 |
+
2024-01-15,703,430,273,12,0.931,0.245,287
|
| 515 |
+
2024-01-16,698,445,253,15,0.924,0.248,301
|
| 516 |
+
```
|
| 517 |
+
|
| 518 |
+
---
|
| 519 |
+
|
| 520 |
+
## Deployment & Usage
|
| 521 |
+
|
| 522 |
+
### Installation
|
| 523 |
+
```bash
|
| 524 |
+
# Clone repository
|
| 525 |
+
git clone git@github.com:RoyAalekh/hackathon_code4change.git
|
| 526 |
+
cd hackathon_code4change
|
| 527 |
+
|
| 528 |
+
# Setup environment
|
| 529 |
+
uv sync
|
| 530 |
+
|
| 531 |
+
# Verify installation
|
| 532 |
+
uv run court-scheduler --help
|
| 533 |
+
```
|
| 534 |
+
|
| 535 |
+
### CLI Commands
|
| 536 |
+
|
| 537 |
+
#### Quick Start
|
| 538 |
+
```bash
|
| 539 |
+
# Generate test cases
|
| 540 |
+
uv run court-scheduler generate --cases 10000 --output data/cases.csv
|
| 541 |
+
|
| 542 |
+
# Run simulation
|
| 543 |
+
uv run court-scheduler simulate --cases data/cases.csv --days 384
|
| 544 |
+
|
| 545 |
+
# Full pipeline
|
| 546 |
+
uv run court-scheduler workflow --cases 10000 --days 384
|
| 547 |
+
```
|
| 548 |
+
|
| 549 |
+
#### Advanced Usage
|
| 550 |
+
```bash
|
| 551 |
+
# Custom policy simulation
|
| 552 |
+
uv run court-scheduler simulate \
|
| 553 |
+
--cases data/cases.csv \
|
| 554 |
+
--days 384 \
|
| 555 |
+
--policy readiness \
|
| 556 |
+
--seed 42 \
|
| 557 |
+
--log-dir data/sim_runs/custom
|
| 558 |
+
|
| 559 |
+
# Parameter sweep comparison
|
| 560 |
+
uv run python scripts/compare_policies.py
|
| 561 |
+
|
| 562 |
+
# Generate cause lists
|
| 563 |
+
uv run python scripts/generate_all_cause_lists.py
|
| 564 |
+
```
|
| 565 |
+
|
| 566 |
+
### Configuration Override
|
| 567 |
+
```bash
|
| 568 |
+
# Use custom config file
|
| 569 |
+
uv run court-scheduler simulate --config configs/custom.toml
|
| 570 |
+
|
| 571 |
+
# Override specific parameters
|
| 572 |
+
uv run court-scheduler simulate \
|
| 573 |
+
--cases data/cases.csv \
|
| 574 |
+
--days 60 \
|
| 575 |
+
--courtrooms 3 \
|
| 576 |
+
--daily-capacity 100
|
| 577 |
+
```
|
| 578 |
+
|
| 579 |
+
---
|
| 580 |
+
|
| 581 |
+
## Assumptions & Constraints
|
| 582 |
+
|
| 583 |
+
### Operational Assumptions
|
| 584 |
+
|
| 585 |
+
#### Court Operations
|
| 586 |
+
1. **Working Days**: 192 days/year (Karnataka HC calendar)
|
| 587 |
+
2. **Courtroom Availability**: 5 courtrooms, single-judge benches
|
| 588 |
+
3. **Daily Capacity**: 151 hearings/courtroom/day (from historical data)
|
| 589 |
+
4. **Hearing Duration**: Not modeled explicitly (capacity is count-based)
|
| 590 |
+
|
| 591 |
+
#### Case Dynamics
|
| 592 |
+
1. **Filing Rate**: Steady-state assumption (disposal ≈ filing)
|
| 593 |
+
2. **Stage Progression**: Markovian (history-independent transitions)
|
| 594 |
+
3. **Adjournment Rate**: 31-38% depending on stage and case type
|
| 595 |
+
4. **Case Independence**: No inter-case dependencies modeled
|
| 596 |
+
|
| 597 |
+
#### Scheduling Constraints
|
| 598 |
+
1. **Minimum Gap**: 14 days between hearings (same case)
|
| 599 |
+
2. **Maximum Gap**: 90 days triggers alert
|
| 600 |
+
3. **Ripeness Re-evaluation**: Every 7 days
|
| 601 |
+
4. **Judge Availability**: Assumed 100% (no vacation modeling)
|
| 602 |
+
|
| 603 |
+
### Technical Constraints
|
| 604 |
+
|
| 605 |
+
#### Performance Limits
|
| 606 |
+
- **Case Volume**: Tested up to 15,000 cases
|
| 607 |
+
- **Simulation Period**: Up to 500 working days
|
| 608 |
+
- **Memory Usage**: <500MB for typical workload
|
| 609 |
+
- **Execution Time**: ~30 seconds for 10K cases, 384 days
|
| 610 |
+
|
| 611 |
+
#### Data Limitations
|
| 612 |
+
- **No Real-time Integration**: Batch processing only
|
| 613 |
+
- **Synthetic Ripeness Data**: Real purpose-of-hearing analysis needed
|
| 614 |
+
- **Fixed Parameters**: No dynamic learning from outcomes
|
| 615 |
+
- **Single Court Model**: No multi-court coordination
|
| 616 |
+
|
| 617 |
+
### Validation Boundaries
|
| 618 |
+
|
| 619 |
+
#### Tested Scenarios
|
| 620 |
+
- **Baseline**: 10,000 cases, balanced distribution
|
| 621 |
+
- **Admission Heavy**: 70% early-stage cases (backlog scenario)
|
| 622 |
+
- **Advanced Heavy**: 70% late-stage cases (efficient court)
|
| 623 |
+
- **High Urgency**: 20% urgent cases (medical/custodial heavy)
|
| 624 |
+
- **Large Backlog**: 15,000 cases (capacity stress test)
|
| 625 |
+
|
| 626 |
+
#### Success Criteria Met
|
| 627 |
+
- **Disposal Rate**: 81.4% achieved (target: >70%)
|
| 628 |
+
- **Load Balance**: Gini 0.002 (target: <0.4)
|
| 629 |
+
- **Case Coverage**: 97.7% (target: >95%)
|
| 630 |
+
- **Utilization**: 45% (realistic given constraints)
|
| 631 |
+
|
| 632 |
+
---
|
| 633 |
+
|
| 634 |
+
## Performance Benchmarks
|
| 635 |
+
|
| 636 |
+
### Execution Performance
|
| 637 |
+
- **EDA Pipeline**: ~2 minutes for 739K hearings
|
| 638 |
+
- **Case Generation**: ~5 seconds for 10K cases
|
| 639 |
+
- **2-Year Simulation**: ~30 seconds for 10K cases
|
| 640 |
+
- **Cause List Generation**: ~10 seconds for 42K hearings
|
| 641 |
+
|
| 642 |
+
### Algorithm Efficiency
|
| 643 |
+
- **Ripeness Classification**: O(n) per case, O(n²) total with re-evaluation
|
| 644 |
+
- **Load Balancing**: O(n log k) where n=cases, k=courtrooms
|
| 645 |
+
- **Priority Calculation**: O(n log n) sorting overhead
|
| 646 |
+
- **Override Processing**: O(m·n) where m=overrides, n=cases
|
| 647 |
+
|
| 648 |
+
### Memory Usage
|
| 649 |
+
- **Case Objects**: ~1KB per case (10K cases = 10MB)
|
| 650 |
+
- **Simulation State**: ~50MB working memory
|
| 651 |
+
- **Output Generation**: ~100MB for full reports
|
| 652 |
+
- **Total Peak**: <500MB for largest tested scenarios
|
| 653 |
+
|
| 654 |
+
---
|
| 655 |
+
|
| 656 |
+
**Last Updated**: 2025-11-25
|
| 657 |
+
**Version**: 1.0
|
| 658 |
+
**Status**: Production Ready
|