Receber por email
← Voltar para o catálogo

critical-path-analyzer

Analyze construction schedule critical path. Identify critical activities, calculate float, simulate schedule impacts, and optimize project duration.

190estrelas
Atualizado há 8 meses

Ver no GitHub ↗ Licença: MIT

Como adicionar

/plugin marketplace add datadrivenconstruction/DDC_Skills_for_AI_Agents_in_Construction

O comando exato pode variar conforme o repositório. Confira o README no GitHub.

Para o autor da skill

Cole no README do seu repo

Mostra que sua skill está catalogada na Skillteca, gera backlink e tráfego rastreável.

[![Skillteca](https://www.skillteca.com.br/api/badge/critical-path-analyzer-mpt2lwjz/svg)](https://www.skillteca.com.br/skills/critical-path-analyzer-mpt2lwjz)
#ai

Skills relacionadas

Ver todas de Documentos →

Alerta por categoria

Receba novas skills de Documentos toda segunda

1 email curto, só com as skills novas de Documentos. 4 minutos de leitura, sem spam, cancela com 1 clique.

Você confirma o email no primeiro envio. Sem spam. Cancela com 1 clique.

Ler descrição completa

Critical Path Analyzer

Overview

Analyze construction schedules using Critical Path Method (CPM). Identify critical activities, calculate total and free float, simulate what-if scenarios, and find opportunities to compress schedule.

"Understanding the critical path is essential for effective schedule management" — DDC Community

CPM Concepts

┌─────────────────────────────────────────────────────────────────┐
│                    CRITICAL PATH METHOD                          │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  Activity A (5d)  ──→  Activity C (10d)  ──→  Activity E (3d)   │
│       ↓                                            ↑             │
│  Activity B (8d)  ──────────→  Activity D (6d)  ───┘            │
│                                                                  │
│  Critical Path: A → C → E (18 days)                             │
│  Float on B→D: 4 days                                           │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

Technical Implementation

from dataclasses import dataclass, field
from typing import List, Dict, Optional, Set, Tuple
from datetime import datetime, timedelta
from collections import defaultdict
import heapq

@dataclass
class Activity:
    id: str
    name: str
    duration: int  # days
    predecessors: List[str] = field(default_factory=list)
    successors: List[str] = field(default_factory=list)

    # Calculated values
    early_start: int = 0
    early_finish: int = 0
    late_start: int = 0
    late_finish: int = 0
    total_float: int = 0
    free_float: int = 0
    is_critical: bool = False

    # Optional attributes
    resources: List[str] = field(default_factory=list)
    cost: float = 0.0
    actual_start: Optional[datetime] = None
    actual_finish: Optional[datetime] = None
    percent_complete: float = 0.0

@dataclass
class ScheduleAnalysis:
    project_duration: int
    critical_path: List[str]
    critical_activities: List[Activity]
    near_critical_activities: List[Activity]  # Float <= threshold
    total_activities: int
    float_distribution: Dict[int, int]

class CriticalPathAnalyzer:
    """Analyze construction schedule critical path."""

    def __init__(self):
        self.activities: Dict[str, Activity] = {}
        self.analysis: Optional[ScheduleAnalysis] = None

    def add_activity(self, id: str, name: str, duration: int,
                    predecessors: List[str] = None,
                    resources: List[str] = None,
                    cost: float = 0.0) -> Activity:
        """Add activity to schedule."""
        activity = Activity(
            id=id,
            name=name,
            duration=duration,
            predecessors=predecessors or [],
            resources=resources or [],
            cost=cost
        )
        self.activities[id] = activity

        # Update successor relationships
        for pred_id in activity.predecessors:
            if pred_id in self.activities:
                self.activities[pred_id].successors.append(id)

        return activity

    def import_schedule(self, activities: List[Dict]) -> int:
        """Import activities from list of dicts."""
        count = 0
        for act in activities:
            self.add_activity(
                id=act['id'],
                name=act['name'],
                duration=act['duration'],
                predecessors=act.get('predecessors', []),
                resources=act.get('resources', []),
                cost=act.get('cost', 0)
            )
            count += 1
        return count

    def calculate_critical_path(self, near_critical_threshold: int = 5) -> ScheduleAnalysis:
        """Calculate critical path using forward and backward pass."""
        if not self.activities:
            raise ValueError("No activities to analyze")

        # Reset calculations
        for act in self.activities.values():
            act.early_start = 0
            act.early_finish = 0
            act.late_start = 0
            act.late_finish = 0
            act.total_float = 0
            act.free_float = 0
            act.is_critical = False

        # Forward pass - calculate early start/finish
        self._forward_pass()

        # Determine project duration
        project_duration = max(act.early_finish for act in self.activities.values())

        # Backward pass - calculate late start/finish
        self._backward_pass(project_duration)

        # Calculate float and identify critical activities
        critical_path = []
        critical_activities = []
        near_critical = []

        for act in self.activities.values():
            act.total_float = act.late_start - act.early_start

            # Calculate free float
            if act.successors:
                min_succ_es = min(self.activities[s].early_start for s in act.successors)
                act.free_float = min_succ_es - act.early_finish
            else:
                act.free_float = project_duration - act.early_finish

            # Identify critical (zero float)
            if act.total_float == 0:
                act.is_critical = True
                critical_activities.append(act)
            elif act.total_float <= near_critical_threshold:
                near_critical.append(act)

        # Build critical path sequence
        critical_path = self._build_critical_path_sequence(critical_activities)

        # Float distribution
        float_dist = defaultdict(int)
        for act in self.activities.values():
            float_dist[act.total_float] += 1

        self.analysis = ScheduleAnalysis(
            project_duration=project_duration,
            critical_path=critical_path,
            critical_activities=critical_activities,
            near_critical_activities=near_critical,
            total_activities=len(self.activities),
            float_distribution=dict(float_dist)
        )

        return self.analysis

    def _forward_pass(self):
        """Forward pass to calculate early start/finish."""
        # Topological sort
        in_degree = {act_id: len(act.predecessors) for act_id, act in self.activities.items()}
        queue = [act_id for act_id, degree in in_degree.items() if degree == 0]

        while queue:
            act_id = queue.pop(0)
            act = self.activities[act_id]

            # Early start is max of predecessor early finishes
            if act.predecessors:
                act.early_start = max(
                    self.activities[pred].early_finish for pred in act.predecessors
                )
            else:
                act.early_start = 0

            act.early_finish = act.early_start + act.duration

            # Process successors
            for succ_id in act.successors:
                in_degree[succ_id] -= 1
                if in_degree[succ_id] == 0:
                    queue.append(succ_id)

    def _backward_pass(self, project_duration: int):
        """Backward pass to calculate late start/finish."""
        # Start from end activities
        end_activities = [act for act in self.activities.values() if not act.successors]

        for act in end_activities:
            act.late_finish = project_duration
            act.late_start = act.late_finish - act.duration

        # Process in reverse topological order
        processed = set(act.id for act in end_activities)
        queue = list(end_activities)

        while queue:
            act = queue.pop(0)

            for pred_id in act.predecessors:
                pred = self.activities[pred_id]

                # Late finish is min of successor late starts
                new_late_finish = act.late_start
                if pred.late_finish == 0 or new_late_finish < pred.late_finish:
                    pred.late_finish = new_late_finish
                    pred.
Compartilhar X LinkedIn