cwicr-location-factor
Apply geographic location factors to CWICR estimates. Adjust costs for regional labor rates, material prices, and market conditions.
What this skill does
# CWICR Location Factor
## Business Case
### Problem Statement
Construction costs vary by location:
- Labor rates differ by region
- Material prices vary geographically
- Market conditions affect costs
- Remote locations have premiums
### Solution
Apply location-based cost factors to CWICR estimates, adjusting for regional differences in labor, materials, and overall market conditions.
### Business Value
- **Regional accuracy** - Location-specific estimates
- **Market awareness** - Current conditions
- **Comparison support** - Normalize across locations
- **Planning** - Multi-location projects
## Technical Implementation
```python
import pandas as pd
from typing import Dict, Any, List, Optional
from dataclasses import dataclass
from enum import Enum
class CostComponent(Enum):
"""Cost components for factors."""
LABOR = "labor"
MATERIAL = "material"
EQUIPMENT = "equipment"
TOTAL = "total"
@dataclass
class LocationFactor:
"""Location adjustment factor."""
location_code: str
location_name: str
country: str
region: str
labor_factor: float
material_factor: float
equipment_factor: float
total_factor: float
currency: str
notes: str = ""
@dataclass
class AdjustedEstimate:
"""Estimate with location adjustment."""
base_cost: float
base_location: str
target_location: str
labor_adjustment: float
material_adjustment: float
equipment_adjustment: float
total_adjustment: float
adjusted_cost: float
adjustment_percent: float
# Location factors (relative to US national average = 1.00)
LOCATION_FACTORS = {
# USA
'US-NYC': LocationFactor('US-NYC', 'New York City', 'USA', 'Northeast', 1.35, 1.15, 1.10, 1.22, 'USD'),
'US-LA': LocationFactor('US-LA', 'Los Angeles', 'USA', 'West', 1.25, 1.10, 1.05, 1.15, 'USD'),
'US-CHI': LocationFactor('US-CHI', 'Chicago', 'USA', 'Midwest', 1.20, 1.05, 1.05, 1.12, 'USD'),
'US-HOU': LocationFactor('US-HOU', 'Houston', 'USA', 'South', 0.95, 0.98, 0.95, 0.96, 'USD'),
'US-PHX': LocationFactor('US-PHX', 'Phoenix', 'USA', 'Southwest', 0.90, 0.95, 0.95, 0.93, 'USD'),
'US-DEN': LocationFactor('US-DEN', 'Denver', 'USA', 'Mountain', 1.00, 1.02, 1.00, 1.01, 'USD'),
'US-SEA': LocationFactor('US-SEA', 'Seattle', 'USA', 'Northwest', 1.18, 1.08, 1.05, 1.12, 'USD'),
'US-MIA': LocationFactor('US-MIA', 'Miami', 'USA', 'Southeast', 0.98, 1.05, 1.00, 1.01, 'USD'),
'US-ATL': LocationFactor('US-ATL', 'Atlanta', 'USA', 'Southeast', 0.92, 0.98, 0.95, 0.95, 'USD'),
'US-NAT': LocationFactor('US-NAT', 'US National Average', 'USA', 'National', 1.00, 1.00, 1.00, 1.00, 'USD'),
# Europe
'UK-LON': LocationFactor('UK-LON', 'London', 'UK', 'Southeast', 1.45, 1.20, 1.15, 1.30, 'GBP'),
'DE-BER': LocationFactor('DE-BER', 'Berlin', 'Germany', 'East', 1.15, 1.10, 1.10, 1.12, 'EUR'),
'DE-MUN': LocationFactor('DE-MUN', 'Munich', 'Germany', 'South', 1.25, 1.15, 1.12, 1.18, 'EUR'),
'FR-PAR': LocationFactor('FR-PAR', 'Paris', 'France', 'Ile-de-France', 1.30, 1.18, 1.15, 1.22, 'EUR'),
'NL-AMS': LocationFactor('NL-AMS', 'Amsterdam', 'Netherlands', 'North Holland', 1.20, 1.12, 1.10, 1.15, 'EUR'),
# Middle East
'AE-DXB': LocationFactor('AE-DXB', 'Dubai', 'UAE', 'Dubai', 0.85, 1.25, 1.10, 1.05, 'AED'),
'SA-RIY': LocationFactor('SA-RIY', 'Riyadh', 'Saudi Arabia', 'Central', 0.80, 1.20, 1.05, 1.00, 'SAR'),
'QA-DOH': LocationFactor('QA-DOH', 'Doha', 'Qatar', 'Qatar', 0.88, 1.30, 1.12, 1.08, 'QAR'),
# Asia
'SG-SIN': LocationFactor('SG-SIN', 'Singapore', 'Singapore', 'Central', 1.10, 1.15, 1.08, 1.12, 'SGD'),
'HK-HKG': LocationFactor('HK-HKG', 'Hong Kong', 'Hong Kong', 'Hong Kong', 1.20, 1.25, 1.15, 1.20, 'HKD'),
'JP-TKY': LocationFactor('JP-TKY', 'Tokyo', 'Japan', 'Kanto', 1.35, 1.20, 1.18, 1.25, 'JPY'),
# Australia
'AU-SYD': LocationFactor('AU-SYD', 'Sydney', 'Australia', 'NSW', 1.25, 1.15, 1.12, 1.18, 'AUD'),
'AU-MEL': LocationFactor('AU-MEL', 'Melbourne', 'Australia', 'Victoria', 1.20, 1.12, 1.10, 1.15, 'AUD'),
}
class CWICRLocationFactor:
"""Apply location factors to CWICR estimates."""
def __init__(self,
cwicr_data: pd.DataFrame = None,
base_location: str = 'US-NAT'):
self.cwicr = cwicr_data
self.base_location = base_location
self._factors = LOCATION_FACTORS.copy()
if cwicr_data is not None:
self._index_cwicr()
def _index_cwicr(self):
"""Index CWICR data."""
if 'work_item_code' in self.cwicr.columns:
self._cwicr_index = self.cwicr.set_index('work_item_code')
else:
self._cwicr_index = None
def get_factor(self, location_code: str) -> Optional[LocationFactor]:
"""Get location factor."""
return self._factors.get(location_code)
def list_locations(self, country: str = None) -> List[Dict[str, Any]]:
"""List available locations."""
factors = self._factors.values()
if country:
factors = [f for f in factors if f.country.lower() == country.lower()]
return [
{
'code': f.location_code,
'name': f.location_name,
'country': f.country,
'region': f.region,
'total_factor': f.total_factor,
'currency': f.currency
}
for f in factors
]
def add_location(self, factor: LocationFactor):
"""Add custom location factor."""
self._factors[factor.location_code] = factor
def adjust_cost(self,
base_cost: float,
target_location: str,
cost_breakdown: Dict[str, float] = None) -> AdjustedEstimate:
"""Adjust cost from base to target location."""
base_factor = self._factors.get(self.base_location)
target_factor = self._factors.get(target_location)
if not base_factor or not target_factor:
return AdjustedEstimate(
base_cost=base_cost,
base_location=self.base_location,
target_location=target_location,
labor_adjustment=0,
material_adjustment=0,
equipment_adjustment=0,
total_adjustment=0,
adjusted_cost=base_cost,
adjustment_percent=0
)
if cost_breakdown is None:
# Default breakdown
cost_breakdown = {
'labor': base_cost * 0.40,
'material': base_cost * 0.45,
'equipment': base_cost * 0.15
}
# Calculate relative factors
labor_rel = target_factor.labor_factor / base_factor.labor_factor
material_rel = target_factor.material_factor / base_factor.material_factor
equipment_rel = target_factor.equipment_factor / base_factor.equipment_factor
# Apply adjustments
labor_adjusted = cost_breakdown.get('labor', 0) * labor_rel
material_adjusted = cost_breakdown.get('material', 0) * material_rel
equipment_adjusted = cost_breakdown.get('equipment', 0) * equipment_rel
adjusted_total = labor_adjusted + material_adjusted + equipment_adjusted
total_adjustment = adjusted_total - base_cost
adjustment_pct = (total_adjustment / base_cost * 100) if base_cost > 0 else 0
return AdjustedEstimate(
base_cost=round(base_cost, 2),
base_location=self.base_location,
target_location=target_location,
labor_adjustment=round(labor_adjusted - cost_breakdown.get('labor', 0), 2),
material_adjustment=round(material_adjusted - cost_breakdown.get('material', 0), 2),
equipment_adjustment=round(equipment_adjusted - cost_breakdown.get('equipment', 0), 2),
total_adjustment=round(total_adjustment, 2),
adjusted_cost=round(adjusted_total, 2),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.