Spaces:
Sleeping
Sleeping
| import random | |
| from dataclasses import dataclass, field, asdict | |
| from typing import List, Dict, Optional, Any | |
| PRIORITIES = ["P0", "P1", "P2", "P3"] | |
| class Task: | |
| task_id: str | |
| title: str | |
| task_type: str | |
| priority: str | |
| created_t: int | |
| sla_due_t: int | |
| est_effort_min: int | |
| value_usd: float | |
| dependencies: List[str] = field(default_factory=list) | |
| status: str = "QUEUED" # QUEUED | IN_PROGRESS | BLOCKED | DONE | REWORK | |
| owner: Optional[str] = None | |
| started_t: Optional[int] = None | |
| completed_t: Optional[int] = None | |
| attempts: int = 0 | |
| notes: Dict[str, Any] = field(default_factory=dict) | |
| class TaskSystem: | |
| def __init__(self, seed: int): | |
| self.rng = random.Random(seed) | |
| self.tasks: Dict[str, Task] = {} | |
| self.counter = 0 | |
| def _next_id(self) -> str: | |
| self.counter += 1 | |
| return f"T{self.counter:05d}" | |
| def create_task( | |
| self, | |
| t_sim: int, | |
| title: str, | |
| task_type: str, | |
| priority: str, | |
| sla_ticks: int, | |
| est_effort_min: int, | |
| value_usd: float, | |
| dependencies: Optional[List[str]] = None, | |
| urgent: bool = False, | |
| ) -> Task: | |
| tid = self._next_id() | |
| sla_due = t_sim + max(1, int(sla_ticks)) | |
| if urgent: | |
| priority = "P0" | |
| task = Task( | |
| task_id=tid, | |
| title=title, | |
| task_type=task_type, | |
| priority=priority, | |
| created_t=t_sim, | |
| sla_due_t=sla_due, | |
| est_effort_min=est_effort_min, | |
| value_usd=value_usd, | |
| dependencies=dependencies or [], | |
| ) | |
| self.tasks[tid] = task | |
| return task | |
| def queued_tasks(self) -> List[Task]: | |
| return [t for t in self.tasks.values() if t.status in ("QUEUED", "REWORK")] | |
| def active_tasks(self) -> List[Task]: | |
| return [t for t in self.tasks.values() if t.status in ("IN_PROGRESS", "BLOCKED")] | |
| def done_tasks(self) -> List[Task]: | |
| return [t for t in self.tasks.values() if t.status == "DONE"] | |
| def overdue_tasks(self, t_sim: int) -> List[Task]: | |
| return [t for t in self.tasks.values() if t.status != "DONE" and t_sim > t.sla_due_t] | |
| def can_start(self, task: Task) -> bool: | |
| # Dependencies must be DONE | |
| for dep in task.dependencies: | |
| if dep in self.tasks and self.tasks[dep].status != "DONE": | |
| return False | |
| return True | |
| def pick_next_task(self, t_sim: int) -> Optional[Task]: | |
| # Priority then due date | |
| q = [t for t in self.queued_tasks() if self.can_start(t)] | |
| if not q: | |
| return None | |
| pr_order = {p: i for i, p in enumerate(PRIORITIES)} | |
| q.sort(key=lambda t: (pr_order.get(t.priority, 9), t.sla_due_t, t.created_t)) | |
| return q[0] | |
| def to_compact_table(self, t_sim: int, limit: int = 14) -> str: | |
| # Simple readable view for UI | |
| items = sorted(self.tasks.values(), key=lambda t: (t.status != "DONE", t.sla_due_t)) | |
| items = items[:limit] | |
| lines = ["id | pri | status | due(t) | owner | title"] | |
| lines.append("---|-----|--------|--------|-------|------") | |
| for t in items: | |
| due = t.sla_due_t | |
| lines.append(f"{t.task_id} | {t.priority} | {t.status} | {due} | {t.owner or '-'} | {t.title[:42]}") | |
| overdue = len(self.overdue_tasks(t_sim)) | |
| lines.append(f"\nOverdue: {overdue} | Total: {len(self.tasks)} | Done: {len(self.done_tasks())}") | |
| return "\n".join(lines) | |
| def as_dict(self) -> Dict[str, Any]: | |
| return {k: asdict(v) for k, v in self.tasks.items()} | |