cwicr-comparison-tool
Compare cost estimates across projects, versions, and scenarios. Identify variances, benchmark against standards, and generate comparison reports.
What this skill does
# CWICR Comparison Tool
## Business Case
### Problem Statement
Project stakeholders need to compare:
- Alternative design options
- Estimate versions over time
- Projects against benchmarks
- Actual vs estimated costs
### Solution
Structured comparison of CWICR-based estimates with variance analysis, benchmarking, and visual reporting.
### Business Value
- **Decision support** - Compare alternatives objectively
- **Version control** - Track estimate evolution
- **Benchmarking** - Compare against standards
- **Audit** - Document estimate changes
## Technical Implementation
```python
import pandas as pd
import numpy as np
from typing import Dict, Any, List, Optional, Tuple
from dataclasses import dataclass, field
from datetime import datetime
from enum import Enum
class ComparisonType(Enum):
"""Types of comparisons."""
VERSION = "version" # Same project, different versions
ALTERNATIVE = "alternative" # Same project, design alternatives
BENCHMARK = "benchmark" # Project vs standard/benchmark
ACTUAL = "actual" # Estimate vs actual costs
PROJECT = "project" # Different projects
class VarianceSignificance(Enum):
"""Significance level of variance."""
CRITICAL = "critical" # >20% variance
HIGH = "high" # 10-20%
MEDIUM = "medium" # 5-10%
LOW = "low" # <5%
NONE = "none" # No variance
@dataclass
class ComparisonItem:
"""Single item comparison."""
work_item_code: str
description: str
base_quantity: float
base_cost: float
compare_quantity: float
compare_cost: float
quantity_variance: float
quantity_variance_pct: float
cost_variance: float
cost_variance_pct: float
significance: VarianceSignificance
@dataclass
class ComparisonResult:
"""Complete comparison result."""
comparison_type: ComparisonType
base_name: str
compare_name: str
base_total: float
compare_total: float
total_variance: float
total_variance_pct: float
items: List[ComparisonItem]
summary_by_category: Dict[str, Dict[str, float]]
created_at: datetime
class CWICRComparisonTool:
"""Compare CWICR-based estimates."""
SIGNIFICANCE_THRESHOLDS = {
VarianceSignificance.CRITICAL: 0.20,
VarianceSignificance.HIGH: 0.10,
VarianceSignificance.MEDIUM: 0.05,
VarianceSignificance.LOW: 0.01
}
def __init__(self):
pass
def _get_significance(self, variance_pct: float) -> VarianceSignificance:
"""Determine variance significance."""
abs_var = abs(variance_pct) / 100
if abs_var >= self.SIGNIFICANCE_THRESHOLDS[VarianceSignificance.CRITICAL]:
return VarianceSignificance.CRITICAL
elif abs_var >= self.SIGNIFICANCE_THRESHOLDS[VarianceSignificance.HIGH]:
return VarianceSignificance.HIGH
elif abs_var >= self.SIGNIFICANCE_THRESHOLDS[VarianceSignificance.MEDIUM]:
return VarianceSignificance.MEDIUM
elif abs_var >= self.SIGNIFICANCE_THRESHOLDS[VarianceSignificance.LOW]:
return VarianceSignificance.LOW
else:
return VarianceSignificance.NONE
def compare_estimates(self,
base_df: pd.DataFrame,
compare_df: pd.DataFrame,
base_name: str = "Base",
compare_name: str = "Compare",
comparison_type: ComparisonType = ComparisonType.VERSION,
code_column: str = 'work_item_code',
quantity_column: str = 'quantity',
cost_column: str = 'total_cost') -> ComparisonResult:
"""Compare two estimates."""
# Merge on code
merged = base_df.merge(
compare_df,
on=code_column,
how='outer',
suffixes=('_base', '_compare')
)
items = []
for _, row in merged.iterrows():
base_qty = float(row.get(f'{quantity_column}_base', 0) or 0)
base_cost = float(row.get(f'{cost_column}_base', 0) or 0)
compare_qty = float(row.get(f'{quantity_column}_compare', 0) or 0)
compare_cost = float(row.get(f'{cost_column}_compare', 0) or 0)
qty_variance = compare_qty - base_qty
qty_variance_pct = (qty_variance / base_qty * 100) if base_qty > 0 else (100 if compare_qty > 0 else 0)
cost_variance = compare_cost - base_cost
cost_variance_pct = (cost_variance / base_cost * 100) if base_cost > 0 else (100 if compare_cost > 0 else 0)
items.append(ComparisonItem(
work_item_code=str(row.get(code_column, '')),
description=str(row.get('description_base', row.get('description_compare', ''))),
base_quantity=base_qty,
base_cost=base_cost,
compare_quantity=compare_qty,
compare_cost=compare_cost,
quantity_variance=round(qty_variance, 2),
quantity_variance_pct=round(qty_variance_pct, 1),
cost_variance=round(cost_variance, 2),
cost_variance_pct=round(cost_variance_pct, 1),
significance=self._get_significance(cost_variance_pct)
))
# Totals
base_total = sum(i.base_cost for i in items)
compare_total = sum(i.compare_cost for i in items)
total_variance = compare_total - base_total
total_variance_pct = (total_variance / base_total * 100) if base_total > 0 else 0
# Summary by category
summary_by_category = self._summarize_by_category(items, merged)
return ComparisonResult(
comparison_type=comparison_type,
base_name=base_name,
compare_name=compare_name,
base_total=round(base_total, 2),
compare_total=round(compare_total, 2),
total_variance=round(total_variance, 2),
total_variance_pct=round(total_variance_pct, 1),
items=items,
summary_by_category=summary_by_category,
created_at=datetime.now()
)
def _summarize_by_category(self,
items: List[ComparisonItem],
merged_df: pd.DataFrame) -> Dict[str, Dict[str, float]]:
"""Summarize comparison by category."""
summary = {}
# Try to extract category from work item code prefix
for item in items:
code = item.work_item_code
category = code.split('-')[0] if '-' in code else 'Other'
if category not in summary:
summary[category] = {
'base_cost': 0,
'compare_cost': 0,
'variance': 0,
'variance_pct': 0,
'item_count': 0
}
summary[category]['base_cost'] += item.base_cost
summary[category]['compare_cost'] += item.compare_cost
summary[category]['variance'] += item.cost_variance
summary[category]['item_count'] += 1
# Calculate percentages
for category in summary:
base = summary[category]['base_cost']
if base > 0:
summary[category]['variance_pct'] = round(
summary[category]['variance'] / base * 100, 1
)
return summary
def get_significant_variances(self,
result: ComparisonResult,
min_significance: VarianceSignificance = VarianceSignificance.MEDIUM) -> List[ComparisonItem]:
"""Get items with significant variances."""
significance_order = [
VarianceSignificance.CRITICAL,
VarianceSignificance.HIGH,
VarianceSignificance.MEDIUM,
VarianceSignificance.LOW,
Related in General
modeling-omnistudio-epc-catalog
IncludedSalesforce Industries CME EPC product-modeling skill for Product2-based catalog creation. Use when creating EPC products, configuring product attributes, building offer bundles with Product Child Items, or reviewing EPC DataPack JSON metadata for product catalog changes. TRIGGER when: user creates or updates Product2 EPC records, AttributeAssignment payloads, AttributeMetadata/AttributeDefaultValues, Offer bundles, or ProductChildItem relationships. DO NOT TRIGGER when: designing OmniScripts/FlexCards/Integration Procedures (use building-omnistudio-omniscript, building-omnistudio-flexcard, or building-omnistudio-integration-procedure), implementing Apex business logic (use generating-apex), or troubleshooting deployment pipelines (use deploying-metadata).
relationship-science-coach
IncludedUse this skill for direct, practical adult relationship coaching: couples conflict, repair, trust, marriage, dating, flirting, attachment patterns, emotional connection, sex, desire differences, eroticism, kink negotiation, affection, love languages, breakups, and long-term passion. Draw on Gottman, EFT and Hold Me Tight, attachment science, modern sex research, Perel, Nagoski, Kerner, Schnarch, Love and Stosny, and flexible love-language tools. Be concrete and low-hedge. Redirect only for imminent danger, abuse, coercive control, minors, non-consent, self-harm, stalking, or medical/legal/psychiatric decisions.
building-sf-integrations
IncludedSalesforce integration architecture and runtime plumbing with 120-point scoring. Use this skill to set up Named Credentials, External Credentials, External Services, REST/SOAP callout patterns, Platform Events, and Change Data Capture. TRIGGER when: user sets up Named Credentials, External Services, REST/SOAP callouts, Platform Events, CDC, or touches .namedCredential-meta.xml files. DO NOT TRIGGER when: Connected App/OAuth config (use configuring-connected-apps), Apex-only logic (use generating-apex), or data import/export (use handling-sf-data).
venue-templates
IncludedAccess comprehensive LaTeX templates, formatting requirements, and submission guidelines for major scientific publication venues (Nature, Science, PLOS, IEEE, ACM), academic conferences (NeurIPS, ICML, CVPR, CHI), research posters, and grant proposals (NSF, NIH, DOE, DARPA). This skill should be used when preparing manuscripts for journal submission, conference papers, research posters, or grant proposals and need venue-specific formatting requirements and templates.
let-fate-decide
IncludedDraws the 12 Houses of the Zodiac Tarot spread to inject entropy into planning when prompts are vague, ambiguous, or casually delegated. Interprets the spread to guide next steps. Use when the user says 'let fate decide', 'YOLO', 'whatever', 'idk', or other nonchalant phrases, makes Yu-Gi-Oh references, or when you are about to arbitrarily pick between multiple reasonable approaches. Prefer over ask-questions-if-underspecified when the user's tone is casual or playful rather than precision-seeking.
net-ops
IncludedCross-platform network troubleshooting (Windows, macOS, Linux) via local or remote shell. Use for: DNS broken, can't resolve hostnames, nslookup/dig works but apps fail, NRPT, WFP, scutil, /etc/resolver, systemd-resolved, /etc/resolv.conf, NetworkManager, VPN DNS leak residue (ProtonVPN/Mullvad/WireGuard/AnyConnect), AV/firewall blocking DNS or DoH, Tailscale DNS interaction, intermittent connectivity, remote diagnostics over SSH.