Spaces:
Runtime error
Runtime error
Mathias Claude Opus 4.5 commited on
Commit ·
2510bf9
1
Parent(s): d95fbda
Add per-month start_week support for different week numbering
Browse files- January 2026 starts at week 2
- February 2026 starts at week 6
- Column positions remain the same, only week numbers change
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- app.py +29 -18
- months_config.json +4 -2
app.py
CHANGED
|
@@ -136,12 +136,13 @@ KNOWN_ACTIVITIES = {
|
|
| 136 |
CONFIG_FILE = "column_config.json"
|
| 137 |
|
| 138 |
# Default week configuration (fallback if config file not found)
|
| 139 |
-
# Format: (
|
|
|
|
| 140 |
DEFAULT_WEEK_CONFIGS = [
|
| 141 |
-
(
|
| 142 |
-
(
|
| 143 |
-
(
|
| 144 |
-
(
|
| 145 |
]
|
| 146 |
|
| 147 |
# Default monthly column configuration (fallback)
|
|
@@ -276,7 +277,7 @@ def group_rows_into_blocks(values):
|
|
| 276 |
return blocks
|
| 277 |
|
| 278 |
|
| 279 |
-
def process_activity_row(row, case_name, gs_name, case_data):
|
| 280 |
"""Process a single activity row and add data to case_data dict."""
|
| 281 |
activity = get_activity(row)
|
| 282 |
if not activity:
|
|
@@ -305,17 +306,21 @@ def process_activity_row(row, case_name, gs_name, case_data):
|
|
| 305 |
# Create key for this case+gs combination
|
| 306 |
key = f"{case_name}|{gs_name}"
|
| 307 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 308 |
if key not in case_data:
|
| 309 |
case_data[key] = {
|
| 310 |
"case": case_name,
|
| 311 |
"gs": gs_name,
|
| 312 |
-
"weeks": {
|
| 313 |
"monthlyTotal": {"sql": 0, "sqlTarget": 0, "activity": 0, "activityTarget": 0, "sqlPctList": [], "activityPctList": []}
|
| 314 |
}
|
| 315 |
|
| 316 |
# Extract weekly data: actual (sum of daily), target, and percentage from sheet
|
| 317 |
-
|
| 318 |
-
|
| 319 |
actual = sum_daily(row, daily_start, daily_end)
|
| 320 |
target = safe_int(row, target_col)
|
| 321 |
percentage = extract_percentage(row, pct_col)
|
|
@@ -356,7 +361,7 @@ def process_activity_row(row, case_name, gs_name, case_data):
|
|
| 356 |
case_data[key]["monthlyTotal"]["activityPctList"].append(monthly_pct)
|
| 357 |
|
| 358 |
|
| 359 |
-
def parse_sheet_data(values):
|
| 360 |
"""
|
| 361 |
Parse the DAILY sheet data into the format expected by the dashboard.
|
| 362 |
|
|
@@ -370,16 +375,21 @@ def parse_sheet_data(values):
|
|
| 370 |
- Column B (1): GS/SDR name (merged - may be empty)
|
| 371 |
- Column C (2): Activity type (Calls, Emails, LinkedIn, Prospects, SQL)
|
| 372 |
|
| 373 |
-
Each week block = 5 daily columns + 1 target column + 1 percentage column
|
| 374 |
-
|
| 375 |
-
-
|
| 376 |
-
-
|
| 377 |
-
-
|
|
|
|
| 378 |
|
| 379 |
Monthly totals:
|
| 380 |
- Column AG (32): Monthly TARGET
|
| 381 |
- Column AH (33): Monthly ACTUAL
|
| 382 |
- Column AI (34): Monthly PERCENTAGE
|
|
|
|
|
|
|
|
|
|
|
|
|
| 383 |
"""
|
| 384 |
if not values or len(values) < 5:
|
| 385 |
return []
|
|
@@ -395,7 +405,7 @@ def parse_sheet_data(values):
|
|
| 395 |
continue # Skip blocks without proper attribution
|
| 396 |
|
| 397 |
for row_idx, row in block:
|
| 398 |
-
process_activity_row(row, case_name, gs_name, case_data)
|
| 399 |
|
| 400 |
# Convert to list format
|
| 401 |
cases = []
|
|
@@ -547,8 +557,9 @@ async def get_data(month: str = None, source: str = None):
|
|
| 547 |
detail=f"Failed to load data. Errors: {'; '.join(error_messages)}"
|
| 548 |
)
|
| 549 |
|
| 550 |
-
# Parse the data
|
| 551 |
-
|
|
|
|
| 552 |
|
| 553 |
# Update cache
|
| 554 |
_cache[cache_key] = {"data": cases, "timestamp": now, "source": actual_source}
|
|
|
|
| 136 |
CONFIG_FILE = "column_config.json"
|
| 137 |
|
| 138 |
# Default week configuration (fallback if config file not found)
|
| 139 |
+
# Format: (slot_index, daily_start, daily_end, target_col, percentage_col)
|
| 140 |
+
# slot_index is 0-based; actual week = start_week + slot_index
|
| 141 |
DEFAULT_WEEK_CONFIGS = [
|
| 142 |
+
(0, 3, 7, 8, 9), # Slot 0: daily D-H (3-7), target I (8), pct J (9)
|
| 143 |
+
(1, 10, 14, 15, 16), # Slot 1: daily K-O (10-14), target P (15), pct Q (16)
|
| 144 |
+
(2, 17, 21, 22, 23), # Slot 2: daily R-V (17-21), target W (22), pct X (23)
|
| 145 |
+
(3, 24, 28, 30, 31), # Slot 3: daily Y-AC (24-28), target AE (30), pct AF (31) - extra empty col
|
| 146 |
]
|
| 147 |
|
| 148 |
# Default monthly column configuration (fallback)
|
|
|
|
| 277 |
return blocks
|
| 278 |
|
| 279 |
|
| 280 |
+
def process_activity_row(row, case_name, gs_name, case_data, start_week=2):
|
| 281 |
"""Process a single activity row and add data to case_data dict."""
|
| 282 |
activity = get_activity(row)
|
| 283 |
if not activity:
|
|
|
|
| 306 |
# Create key for this case+gs combination
|
| 307 |
key = f"{case_name}|{gs_name}"
|
| 308 |
|
| 309 |
+
# Calculate actual week numbers based on start_week
|
| 310 |
+
week_configs = get_week_configs()
|
| 311 |
+
week_numbers = [start_week + slot for slot, _, _, _, _ in week_configs]
|
| 312 |
+
|
| 313 |
if key not in case_data:
|
| 314 |
case_data[key] = {
|
| 315 |
"case": case_name,
|
| 316 |
"gs": gs_name,
|
| 317 |
+
"weeks": {w: {} for w in week_numbers},
|
| 318 |
"monthlyTotal": {"sql": 0, "sqlTarget": 0, "activity": 0, "activityTarget": 0, "sqlPctList": [], "activityPctList": []}
|
| 319 |
}
|
| 320 |
|
| 321 |
# Extract weekly data: actual (sum of daily), target, and percentage from sheet
|
| 322 |
+
for slot, daily_start, daily_end, target_col, pct_col in week_configs:
|
| 323 |
+
week_num = start_week + slot
|
| 324 |
actual = sum_daily(row, daily_start, daily_end)
|
| 325 |
target = safe_int(row, target_col)
|
| 326 |
percentage = extract_percentage(row, pct_col)
|
|
|
|
| 361 |
case_data[key]["monthlyTotal"]["activityPctList"].append(monthly_pct)
|
| 362 |
|
| 363 |
|
| 364 |
+
def parse_sheet_data(values, start_week=2):
|
| 365 |
"""
|
| 366 |
Parse the DAILY sheet data into the format expected by the dashboard.
|
| 367 |
|
|
|
|
| 375 |
- Column B (1): GS/SDR name (merged - may be empty)
|
| 376 |
- Column C (2): Activity type (Calls, Emails, LinkedIn, Prospects, SQL)
|
| 377 |
|
| 378 |
+
Each week block = 5 daily columns + 1 target column + 1 percentage column.
|
| 379 |
+
Column positions are the same each month, but week numbers vary:
|
| 380 |
+
- Slot 0: Cols 3-7 (daily), Col 8 (target), Col 9 (percentage)
|
| 381 |
+
- Slot 1: Cols 10-14 (daily), Col 15 (target), Col 16 (percentage)
|
| 382 |
+
- Slot 2: Cols 17-21 (daily), Col 22 (target), Col 23 (percentage)
|
| 383 |
+
- Slot 3: Cols 24-28 (daily), Col 30 (target), Col 31 (percentage)
|
| 384 |
|
| 385 |
Monthly totals:
|
| 386 |
- Column AG (32): Monthly TARGET
|
| 387 |
- Column AH (33): Monthly ACTUAL
|
| 388 |
- Column AI (34): Monthly PERCENTAGE
|
| 389 |
+
|
| 390 |
+
Args:
|
| 391 |
+
values: Raw sheet data
|
| 392 |
+
start_week: The first week number for this month (e.g., 2 for Jan, 6 for Feb)
|
| 393 |
"""
|
| 394 |
if not values or len(values) < 5:
|
| 395 |
return []
|
|
|
|
| 405 |
continue # Skip blocks without proper attribution
|
| 406 |
|
| 407 |
for row_idx, row in block:
|
| 408 |
+
process_activity_row(row, case_name, gs_name, case_data, start_week)
|
| 409 |
|
| 410 |
# Convert to list format
|
| 411 |
cases = []
|
|
|
|
| 557 |
detail=f"Failed to load data. Errors: {'; '.join(error_messages)}"
|
| 558 |
)
|
| 559 |
|
| 560 |
+
# Parse the data with month-specific start_week
|
| 561 |
+
start_week = month_config.get("start_week", 2)
|
| 562 |
+
cases = parse_sheet_data(values, start_week)
|
| 563 |
|
| 564 |
# Update cache
|
| 565 |
_cache[cache_key] = {"data": cases, "timestamp": now, "source": actual_source}
|
months_config.json
CHANGED
|
@@ -4,13 +4,15 @@
|
|
| 4 |
"id": "2026-01",
|
| 5 |
"label": "January 2026",
|
| 6 |
"sheet_id": "1af6-2KsRqeTQxdw5KVRp2WCrM6RT7HIcl70m-GgGZB4",
|
| 7 |
-
"tab_name": "DAILY - for SDR to add data🌟"
|
|
|
|
| 8 |
},
|
| 9 |
{
|
| 10 |
"id": "2026-02",
|
| 11 |
"label": "February 2026",
|
| 12 |
"sheet_id": "173_4EJ6pWjyP96d1zpZ8VasffsciCSiRLOGB_CNn7aI",
|
| 13 |
-
"tab_name": "DAILY - for SDR to add data🌟"
|
|
|
|
| 14 |
}
|
| 15 |
],
|
| 16 |
"default_month": "2026-02"
|
|
|
|
| 4 |
"id": "2026-01",
|
| 5 |
"label": "January 2026",
|
| 6 |
"sheet_id": "1af6-2KsRqeTQxdw5KVRp2WCrM6RT7HIcl70m-GgGZB4",
|
| 7 |
+
"tab_name": "DAILY - for SDR to add data🌟",
|
| 8 |
+
"start_week": 2
|
| 9 |
},
|
| 10 |
{
|
| 11 |
"id": "2026-02",
|
| 12 |
"label": "February 2026",
|
| 13 |
"sheet_id": "173_4EJ6pWjyP96d1zpZ8VasffsciCSiRLOGB_CNn7aI",
|
| 14 |
+
"tab_name": "DAILY - for SDR to add data🌟",
|
| 15 |
+
"start_week": 6
|
| 16 |
}
|
| 17 |
],
|
| 18 |
"default_month": "2026-02"
|