[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6FK8D57AX18PTBKW85SCZZ7":3,"skill-same:01M6FK8D57AX18PTBKW85SCZZ7":67},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":15,"categories":16,"keywords":17,"publishedAt":34,"linkOnly":35,"markdownContent":36,"markdownBody":37,"frontMatter":38,"refName":40,"commitSha":41,"skillDirectory":42,"forksCount":43,"repositoryHtmlUrl":44,"repositoryHomepageUrl":6,"ownerHtmlUrl":45,"ownerBlogUrl":6,"ownerDescription":6,"updatedAt":46,"install":47,"resources":60},"01M6FK8D57AX18PTBKW85SCZZ7","weather-impact-analysis",null,"Analyze weather data impact on construction schedules. Predict weather delays, optimize work scheduling based on forecasts, and calculate weather-related risk factors for project planning.","kbarbel640-del\u002Fskills","kbarbel640-del","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F255315490?v=4","01M6CJPPQGCCESXMG3K7BBGQ5J","01M6CJPPX2PB2BCZERFVM2C2B3",1,"MIT","https:\u002F\u002Fgithub.com\u002Fkbarbel640-del\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fdatadrivenconstruction\u002Fweather-impact-analysis\u002FSKILL.md",[],[18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33],"weather","analysis","impact analysis","impact","weather impact analysis","planning","data","risk","analyze","calculate","work","scheduling","project planning","optimize","project","impact-analysis","2026-11-10 10:12:30",false,"---\r\nslug: \"weather-impact-analysis\"\r\ndisplay_name: \"Weather Impact Analysis\"\r\ndescription: \"Analyze weather data impact on construction schedules. Predict weather delays, optimize work scheduling based on forecasts, and calculate weather-related risk factors for project planning.\"\r\n---\r\n\r\n# Weather Impact Analysis\r\n\r\n## Overview\r\n\r\nThis skill implements weather data analysis for construction project management. Integrate weather forecasts, historical data, and activity sensitivity to predict delays and optimize scheduling.\r\n\r\n**Capabilities:**\r\n- Weather forecast integration\r\n- Activity weather sensitivity mapping\r\n- Delay prediction and quantification\r\n- Schedule optimization based on weather\r\n- Historical weather impact analysis\r\n- Risk factor calculation\r\n\r\n## Quick Start\r\n\r\n```python\r\nfrom dataclasses import dataclass\r\nfrom datetime import date, datetime, timedelta\r\nfrom typing import List, Dict, Optional\r\nfrom enum import Enum\r\nimport requests\r\n\r\nclass WeatherCondition(Enum):\r\n    CLEAR = \"clear\"\r\n    CLOUDY = \"cloudy\"\r\n    RAIN = \"rain\"\r\n    HEAVY_RAIN = \"heavy_rain\"\r\n    SNOW = \"snow\"\r\n    FROST = \"frost\"\r\n    HIGH_WIND = \"high_wind\"\r\n    EXTREME_HEAT = \"extreme_heat\"\r\n    EXTREME_COLD = \"extreme_cold\"\r\n\r\n@dataclass\r\nclass WeatherDay:\r\n    date: date\r\n    condition: WeatherCondition\r\n    temp_high: float\r\n    temp_low: float\r\n    precipitation_mm: float\r\n    wind_speed_kmh: float\r\n    humidity_pct: float\r\n\r\n@dataclass\r\nclass ActivitySensitivity:\r\n    activity_type: str\r\n    min_temp: float\r\n    max_temp: float\r\n    max_wind: float\r\n    max_precipitation: float\r\n    can_work_in_rain: bool\r\n\r\ndef check_work_day(weather: WeatherDay, activity: ActivitySensitivity) -> Dict:\r\n    \"\"\"Check if work is possible for given weather and activity\"\"\"\r\n    can_work = True\r\n    reasons = []\r\n\r\n    if weather.temp_low \u003C activity.min_temp:\r\n        can_work = False\r\n        reasons.append(f\"Temperature too low: {weather.temp_low}°C \u003C {activity.min_temp}°C\")\r\n\r\n    if weather.temp_high > activity.max_temp:\r\n        can_work = False\r\n        reasons.append(f\"Temperature too high: {weather.temp_high}°C > {activity.max_temp}°C\")\r\n\r\n    if weather.wind_speed_kmh > activity.max_wind:\r\n        can_work = False\r\n        reasons.append(f\"Wind too strong: {weather.wind_speed_kmh} km\u002Fh > {activity.max_wind} km\u002Fh\")\r\n\r\n    if weather.precipitation_mm > activity.max_precipitation and not activity.can_work_in_rain:\r\n        can_work = False\r\n        reasons.append(f\"Precipitation: {weather.precipitation_mm}mm\")\r\n\r\n    return {\r\n        'date': weather.date,\r\n        'can_work': can_work,\r\n        'reasons': reasons,\r\n        'productivity_factor': 1.0 if can_work else 0.0\r\n    }\r\n\r\n# Example\r\nconcrete_work = ActivitySensitivity(\r\n    activity_type=\"concrete_placement\",\r\n    min_temp=5,\r\n    max_temp=35,\r\n    max_wind=40,\r\n    max_precipitation=2,\r\n    can_work_in_rain=False\r\n)\r\n\r\ntoday_weather = WeatherDay(\r\n    date=date.today(),\r\n    condition=WeatherCondition.RAIN,\r\n    temp_high=15,\r\n    temp_low=8,\r\n    precipitation_mm=10,\r\n    wind_speed_kmh=20,\r\n    humidity_pct=80\r\n)\r\n\r\nresult = check_work_day(today_weather, concrete_work)\r\nprint(f\"Can work: {result['can_work']}, Reasons: {result['reasons']}\")\r\n```\r\n\r\n## Comprehensive Weather Analysis System\r\n\r\n### Weather Data Integration\r\n\r\n```python\r\nfrom dataclasses import dataclass, field\r\nfrom datetime import date, datetime, timedelta\r\nfrom typing import List, Dict, Optional, Tuple\r\nfrom enum import Enum\r\nimport requests\r\nimport json\r\n\r\nclass WeatherSeverity(Enum):\r\n    NORMAL = 1\r\n    CAUTION = 2\r\n    WARNING = 3\r\n    SEVERE = 4\r\n    EXTREME = 5\r\n\r\n@dataclass\r\nclass HourlyWeather:\r\n    datetime: datetime\r\n    temperature: float\r\n    feels_like: float\r\n    humidity: float\r\n    wind_speed: float\r\n    wind_direction: float\r\n    precipitation: float\r\n    precipitation_probability: float\r\n    condition: WeatherCondition\r\n    visibility: float\r\n    uv_index: float\r\n\r\n@dataclass\r\nclass DailyForecast:\r\n    date: date\r\n    temp_high: float\r\n    temp_low: float\r\n    sunrise: datetime\r\n    sunset: datetime\r\n    precipitation_total: float\r\n    precipitation_probability: float\r\n    primary_condition: WeatherCondition\r\n    hourly: List[HourlyWeather] = field(default_factory=list)\r\n    severity: WeatherSeverity = WeatherSeverity.NORMAL\r\n\r\nclass WeatherDataService:\r\n    \"\"\"Weather data integration service\"\"\"\r\n\r\n    def __init__(self, api_key: str = None, provider: str = \"openweathermap\"):\r\n        self.api_key = api_key\r\n        self.provider = provider\r\n        self.cache: Dict[str, Dict] = {}\r\n        self.cache_duration = timedelta(hours=1)\r\n\r\n    def get_forecast(self, latitude: float, longitude: float,\r\n                    days: int = 14) -> List[DailyForecast]:\r\n        \"\"\"Get weather forecast for location\"\"\"\r\n        cache_key = f\"{latitude},{longitude}\"\r\n\r\n        if cache_key in self.cache:\r\n            cached = self.cache[cache_key]\r\n            if datetime.now() - cached['timestamp'] \u003C self.cache_duration:\r\n                return cached['data']\r\n\r\n        if self.provider == \"openweathermap\":\r\n            forecast = self._fetch_openweathermap(latitude, longitude, days)\r\n        else:\r\n            forecast = self._generate_sample_forecast(days)\r\n\r\n        self.cache[cache_key] = {\r\n            'timestamp': datetime.now(),\r\n            'data': forecast\r\n        }\r\n\r\n        return forecast\r\n\r\n    def _fetch_openweathermap(self, lat: float, lon: float,\r\n                              days: int) -> List[DailyForecast]:\r\n        \"\"\"Fetch from OpenWeatherMap API\"\"\"\r\n        url = f\"https:\u002F\u002Fapi.openweathermap.org\u002Fdata\u002F2.5\u002Fforecast\"\r\n        params = {\r\n            'lat': lat,\r\n            'lon': lon,\r\n            'appid': self.api_key,\r\n            'units': 'metric'\r\n        }\r\n\r\n        try:\r\n            response = requests.get(url, params=params)\r\n            data = response.json()\r\n            return self._parse_openweathermap(data)\r\n        except Exception as e:\r\n            print(f\"Weather API error: {e}\")\r\n            return self._generate_sample_forecast(days)\r\n\r\n    def _parse_openweathermap(self, data: Dict) -> List[DailyForecast]:\r\n        \"\"\"Parse OpenWeatherMap response\"\"\"\r\n        forecasts = []\r\n        daily_data = {}\r\n\r\n        for item in data.get('list', []):\r\n            dt = datetime.fromtimestamp(item['dt'])\r\n            day = dt.date()\r\n\r\n            if day not in daily_data:\r\n                daily_data[day] = {\r\n                    'temps': [],\r\n                    'precipitation': 0,\r\n                    'conditions': [],\r\n                    'hourly': []\r\n                }\r\n\r\n            daily_data[day]['temps'].append(item['main']['temp'])\r\n            daily_data[day]['precipitation'] += item.get('rain', {}).get('3h', 0)\r\n\r\n            condition = self._map_condition(item['weather'][0]['main'])\r\n            daily_data[day]['conditions'].append(condition)\r\n\r\n            daily_data[day]['hourly'].append(HourlyWeather(\r\n                datetime=dt,\r\n                temperature=item['main']['temp'],\r\n                feels_like=item['main']['feels_like'],\r\n                humidity=item['main']['humidity'],\r\n                wind_speed=item['wind']['speed'] * 3.6,  # m\u002Fs to km\u002Fh\r\n                wind_direction=item['wind'].get('deg', 0),\r\n                precipitation=item.get('rain', {}).get('3h', 0),\r\n                precipitation_probability=item.get('pop', 0) * 100,\r\n                condition=condition,\r\n                visibility=item.get('visibility', 10000) \u002F 1000,\r\n                uv_index=0\r\n            ))\r\n\r\n        for day, data in daily_data.items():\r\n            primary_condition = max(set(data['conditions']), key=data['conditions'].count)\r\n\r\n            forecasts.append(DailyForecast(\r\n                date=day,\r\n                temp_high=max(data['temps']),\r\n                temp_low=min(data['temps']),\r\n                sunrise=datetime.combine(day, datetime.min.time().replace(hour=6)),\r\n                sunset=datetime.combine(day, datetime.min.time().replace(hour=18)),\r\n                precipitation_total=data['precipitation'],\r\n                precipitation_probability=max(h.precipitation_probability for h in data['hourly']),\r\n                primary_condition=primary_condition,\r\n                hourly=data['hourly'],\r\n                severity=self._calculate_severity(primary_condition, data)\r\n            ))\r\n\r\n        return sorted(forecasts, key=lambda x: x.date)\r\n\r\n    def _map_condition(self, condition_str: str) -> WeatherCondition:\r\n        \"\"\"Map API condition to enum\"\"\"\r\n        mapping = {\r\n            'Clear': WeatherCondition.CLEAR,\r\n            'Clouds': WeatherCondition.CLOUDY,\r\n            'Rain': WeatherCondition.RAIN,\r\n            'Drizzle': WeatherCondition.RAIN,\r\n            'Thunderstorm': WeatherCondition.HEAVY_RAIN,\r\n            'Snow': WeatherCondition.SNOW,\r\n            'Mist': WeatherCondition.CLOUDY,\r\n            'Fog': WeatherCondition.CLOUDY\r\n        }\r\n        return mapping.get(condition_str, WeatherCondition.CLEAR)\r\n\r\n    def _calculate_severity(self, condition: WeatherCondition,\r\n                           data: Dict) -> WeatherSeverity:\r\n        \"\"\"Calculate weather severity\"\"\"\r\n        max_temp = max(data['temps'])\r\n        min_temp = min(data['temps'])\r\n        precip = data['precipitation']\r\n\r\n        if condition in [WeatherCondition.HEAVY_RAIN, WeatherCondition.SNOW]:\r\n            if precip > 50:\r\n                return WeatherSeverity.EXTREME\r\n            elif precip > 25:\r\n                return WeatherSeverity.SEVERE\r\n\r\n        if max_temp > 40 or min_temp \u003C -15:\r\n            return WeatherSeverity.SEVERE\r\n\r\n        if max_temp > 35 or min_temp \u003C -5:\r\n            return WeatherSeverity.WARNING\r\n\r\n        if condition == WeatherCondition.RAIN:\r\n            return WeatherSeverity.CAUTION\r\n\r\n        return WeatherSeverity.NORMAL\r\n\r\n    def _generate_sample_forecast(self, days: int) -> List[DailyForecast]:\r\n        \"\"\"Generate sample forecast for testing\"\"\"\r\n        import random\r\n        forecasts = []\r\n\r\n        for i in range(days):\r\n            day = date.today() + timedelta(days=i)\r\n            temp_base = 15 + random.uniform(-5, 10)\r\n            condition = random.choice(list(WeatherCondition))\r\n\r\n            forecasts.append(DailyForecast(\r\n                date=day,\r\n                temp_high=temp_base + random.uniform(3, 8),\r\n                temp_low=temp_base - random.uniform(3, 8),\r\n                sunrise=datetime.combine(day, datetime.min.time().replace(hour=6)),\r\n                sunset=datetime.combine(day, datetime.min.time().replace(hour=18)),\r\n                precipitation_total=random.uniform(0, 20) if condition == WeatherCondition.RAIN else 0,\r\n                precipitation_probability=random.uniform(0, 100) if condition == WeatherCondition.RAIN else 10,\r\n                primary_condition=condition,\r\n                severity=WeatherSeverity.NORMAL\r\n            ))\r\n\r\n        return forecasts\r\n```\r\n\r\n### Activity Weather Sensitivity\r\n\r\n```python\r\n@dataclass\r\nclass WeatherThresholds:\r\n    min_temp: float = -10\r\n    max_temp: float = 45\r\n    max_wind: float = 50\r\n    max_precipitation: float = 50\r\n    max_snow_depth: float = 20\r\n    min_visibility: float = 0.5  # km\r\n\r\n@dataclass\r\nclass ConstructionActivity:\r\n    activity_id: str\r\n    activity_name: str\r\n    category: str\r\n    thresholds: WeatherThresholds\r\n    indoor: bool = False\r\n    rain_sensitive: bool = True\r\n    frost_sensitive: bool = False\r\n    productivity_factors: Dict[WeatherCondition, float] = field(default_factory=dict)\r\n\r\n    def __post_init__(self):\r\n        if not self.productivity_factors:\r\n            self.productivity_factors = {\r\n                WeatherCondition.CLEAR: 1.0,\r\n                WeatherCondition.CLOUDY: 0.95,\r\n                WeatherCondition.RAIN: 0.3 if self.rain_sensitive else 0.8,\r\n                WeatherCondition.HEAVY_RAIN: 0.0 if self.rain_sensitive else 0.5,\r\n                WeatherCondition.SNOW: 0.2,\r\n                WeatherCondition.FROST: 0.5 if self.frost_sensitive else 0.8,\r\n                WeatherCondition.HIGH_WIND: 0.3,\r\n                WeatherCondition.EXTREME_HEAT: 0.6,\r\n                WeatherCondition.EXTREME_COLD: 0.4\r\n            }\r\n\r\nclass ActivityWeatherAnalyzer:\r\n    \"\"\"Analyze weather impact on construction activities\"\"\"\r\n\r\n    # Default activity definitions\r\n    ACTIVITY_TEMPLATES = {\r\n        'concrete_placement': ConstructionActivity(\r\n            activity_id='ACT-001',\r\n            activity_name='Concrete Placement',\r\n            category='structural',\r\n            thresholds=WeatherThresholds(min_temp=5, max_temp=35, max_precipitation=2, max_wind=40),\r\n            rain_sensitive=True,\r\n            frost_sensitive=True\r\n        ),\r\n        'steel_erection': ConstructionActivity(\r\n            activity_id='ACT-002',\r\n            activity_name='Steel Erection',\r\n            category='structural',\r\n            thresholds=WeatherThresholds(max_wind=35, max_precipitation=10),\r\n            rain_sensitive=False\r\n        ),\r\n        'roofing': ConstructionActivity(\r\n            activity_id='ACT-003',\r\n            activity_name='Roofing',\r\n            category='envelope',\r\n            thresholds=WeatherThresholds(min_temp=0, max_precipitation=0, max_wind=30),\r\n            rain_sensitive=True\r\n        ),\r\n        'excavation': ConstructionActivity(\r\n            activity_id='ACT-004',\r\n            activity_name='Excavation',\r\n            category='earthwork',\r\n            thresholds=WeatherThresholds(min_temp=-5, max_precipitation=25),\r\n            rain_sensitive=True,\r\n            frost_sensitive=True\r\n        ),\r\n        'painting_exterior': ConstructionActivity(\r\n            activity_id='ACT-005',\r\n            activity_name='Exterior Painting',\r\n            category='finishing',\r\n            thresholds=WeatherThresholds(min_temp=10, max_temp=35, max_precipitation=0, max_wind=25),\r\n            rain_sensitive=True\r\n        ),\r\n        'masonry': ConstructionActivity(\r\n            activity_id='ACT-006',\r\n            activity_name='Masonry Work',\r\n            category='structural',\r\n            thresholds=WeatherThresholds(min_temp=5, max_temp=32, max_precipitation=5),\r\n            rain_sensitive=True,\r\n            frost_sensitive=True\r\n        ),\r\n        'crane_operations': ConstructionActivity(\r\n            activity_id='ACT-007',\r\n            activity_name='Crane Operations',\r\n            category='equipment',\r\n            thresholds=WeatherThresholds(max_wind=30, min_visibility=1.0),\r\n            rain_sensitive=False\r\n        ),\r\n        'electrical_exterior': ConstructionActivity(\r\n            activity_id='ACT-008',\r\n            activity_name='Exterior Electrical',\r\n            category='MEP',\r\n            thresholds=WeatherThresholds(max_precipitation=0),\r\n            rain_sensitive=True\r\n        ),\r\n        'interior_work': ConstructionActivity(\r\n            activity_id='ACT-009',\r\n            activity_name='Interior Work',\r\n            category='finishing',\r\n            thresholds=WeatherThresholds(),\r\n            indoor=True,\r\n            rain_sensitive=False\r\n        )\r\n    }\r\n\r\n    def __init__(self):\r\n        self.activities = dict(self.ACTIVITY_TEMPLATES)\r\n\r\n    def add_activity(self, activity: ConstructionActivity):\r\n        \"\"\"Add custom activity\"\"\"\r\n        self.activities[activity.activity_id] = activity\r\n\r\n    def analyze_day(self, weather: DailyForecast,\r\n                   activities: List[str]) -> Dict[str, Dict]:\r\n        \"\"\"Analyze weather impact for specific day and activities\"\"\"\r\n        results = {}\r\n\r\n        for activity_id in activities:\r\n            activity = self.activities.get(activity_id)\r\n            if not activity:\r\n                continue\r\n\r\n            impact = self._calculate_impact(weather, activity)\r\n            results[activity_id] = impact\r\n\r\n        return results\r\n\r\n    def _calculate_impact(self, weather: DailyForecast,\r\n                         activity: ConstructionActivity) -> Dict:\r\n        \"\"\"Calculate weather impact on activity\"\"\"\r\n        if activity.indoor:\r\n            return {\r\n                'can_work': True,\r\n                'productivity': 1.0,\r\n                'issues': [],\r\n                'recommendations': []\r\n            }\r\n\r\n        issues = []\r\n        productivity = 1.0\r\n\r\n        # Temperature check\r\n        if weather.temp_low \u003C activity.thresholds.min_temp:\r\n            issues.append(f\"Low temperature: {weather.temp_low}°C\")\r\n            if activity.frost_sensitive:\r\n                productivity *= 0.0\r\n            else:\r\n                productivity *= 0.5\r\n\r\n        if weather.temp_high > activity.thresholds.max_temp:\r\n            issues.append(f\"High temperature: {weather.temp_high}°C\")\r\n            productivity *= 0.6\r\n\r\n        # Precipitation check\r\n        if weather.precipitation_total > activity.thresholds.max_precipitation:\r\n            issues.append(f\"Precipitation: {weather.precipitation_total}mm\")\r\n            if activity.rain_sensitive:\r\n                productivity *= 0.0\r\n            else:\r\n                productivity *= 0.7\r\n\r\n        # Condition-based productivity\r\n        condition_factor = activity.productivity_factors.get(\r\n            weather.primary_condition, 1.0\r\n        )\r\n        productivity *= condition_factor\r\n\r\n        # Generate recommendations\r\n        recommendations = []\r\n        if productivity \u003C 0.5 and productivity > 0:\r\n            recommendations.append(\"Consider rescheduling to more favorable day\")\r\n        if weather.temp_low \u003C activity.thresholds.min_temp + 5:\r\n            recommendations.append(\"Plan for cold weather precautions\")\r\n        if weather.precipitation_probability > 50:\r\n            recommendations.append(\"Have rain contingency plan ready\")\r\n\r\n        can_work = productivity > 0\r\n\r\n        return {\r\n            'activity_name': activity.activity_name,\r\n            'can_work': can_work,\r\n            'productivity': round(productivity, 2),\r\n            'issues': issues,\r\n            'recommendations': recommendations,\r\n            'weather_condition': weather.primary_condition.value,\r\n            'temperature_range': f\"{weather.temp_low}°C - {weather.temp_high}°C\"\r\n        }\r\n\r\n    def find_optimal_days(self, forecast: List[DailyForecast],\r\n                         activity_id: str,\r\n                         min_productivity: float = 0.8) -> List[date]:\r\n        \"\"\"Find optimal days for an activity\"\"\"\r\n        activity = self.activities.get(activity_id)\r\n        if not activity:\r\n            return []\r\n\r\n        optimal = []\r\n        for day in forecast:\r\n            impact = self._calculate_impact(day, activity)\r\n            if impact['productivity'] >= min_productivity:\r\n                optimal.append(day.date)\r\n\r\n        return optimal\r\n```\r\n\r\n### Schedule Weather Integration\r\n\r\n```python\r\nfrom datetime import date, timedelta\r\nfrom typing import List, Dict\r\nimport pandas as pd\r\n\r\n@dataclass\r\nclass ScheduledActivity:\r\n    activity_id: str\r\n    activity_name: str\r\n    activity_type: str  # Maps to ACTIVITY_TEMPLATES\r\n    planned_start: date\r\n    planned_end: date\r\n    duration_days: int\r\n    is_critical: bool = False\r\n\r\nclass ScheduleWeatherOptimizer:\r\n    \"\"\"Optimize construction schedule based on weather\"\"\"\r\n\r\n    def __init__(self, weather_service: WeatherDataService,\r\n                 activity_analyzer: ActivityWeatherAnalyzer):\r\n        self.weather = weather_service\r\n        self.analyzer = activity_analyzer\r\n\r\n    def analyze_schedule(self, schedule: List[ScheduledActivity],\r\n                        location: Tuple[float, float]) -> Dict:\r\n        \"\"\"Analyze schedule against weather forecast\"\"\"\r\n        forecast = self.weather.get_forecast(location[0], location[1])\r\n        forecast_dict = {f.date: f for f in forecast}\r\n\r\n        analysis = {\r\n            'activities': [],\r\n            'weather_delays': 0,\r\n            'risk_days': [],\r\n            'recommendations': []\r\n        }\r\n\r\n        for activity in schedule:\r\n            activity_analysis = self._analyze_activity(\r\n                activity, forecast_dict\r\n            )\r\n            analysis['activities'].append(activity_analysis)\r\n\r\n            if activity_analysis['expected_delay'] > 0:\r\n                analysis['weather_delays'] += activity_analysis['expected_delay']\r\n\r\n            analysis['risk_days'].extend(activity_analysis['risk_days'])\r\n\r\n        # Generate overall recommendations\r\n        if analysis['weather_delays'] > 5:\r\n            analysis['recommendations'].append(\r\n                f\"Schedule shows {analysis['weather_delays']} potential weather delay days. \"\r\n                \"Consider buffer time or alternative scheduling.\"\r\n            )\r\n\r\n        return analysis\r\n\r\n    def _analyze_activity(self, activity: ScheduledActivity,\r\n                         forecast: Dict[date, DailyForecast]) -> Dict:\r\n        \"\"\"Analyze single activity against weather\"\"\"\r\n        result = {\r\n            'activity_id': activity.activity_id,\r\n            'activity_name': activity.activity_name,\r\n            'planned_start': activity.planned_start,\r\n            'planned_end': activity.planned_end,\r\n            'day_analysis': [],\r\n            'risk_days': [],\r\n            'expected_delay': 0,\r\n            'avg_productivity': 1.0\r\n        }\r\n\r\n        current_date = activity.planned_start\r\n        productivities = []\r\n        delay_days = 0\r\n\r\n        while current_date \u003C= activity.planned_end:\r\n            if current_date in forecast:\r\n                weather = forecast[current_date]\r\n                impact = self.analyzer._calculate_impact(\r\n                    weather,\r\n                    self.analyzer.activities.get(activity.activity_type,\r\n                        self.analyzer.ACTIVITY_TEMPLATES.get('interior_work'))\r\n                )\r\n\r\n                productivities.append(impact['productivity'])\r\n\r\n                day_info = {\r\n                    'date': current_date,\r\n                    'can_work': impact['can_work'],\r\n                    'productivity': impact['productivity'],\r\n                    'weather': weather.primary_condition.value\r\n                }\r\n                result['day_analysis'].append(day_info)\r\n\r\n                if not impact['can_work']:\r\n                    delay_days += 1\r\n                    result['risk_days'].append({\r\n                        'date': current_date,\r\n                        'activity': activity.activity_name,\r\n                        'reason': weather.primary_condition.value\r\n                    })\r\n                elif impact['productivity'] \u003C 0.7:\r\n                    delay_days += (1 - impact['productivity'])\r\n\r\n            current_date += timedelta(days=1)\r\n\r\n        result['expected_delay'] = round(delay_days)\r\n        result['avg_productivity'] = sum(productivities) \u002F len(productivities) if productivities else 1.0\r\n\r\n        return result\r\n\r\n    def suggest_reschedule(self, activity: ScheduledActivity,\r\n                          location: Tuple[float, float],\r\n                          flexibility_days: int = 7) -> Optional[date]:\r\n        \"\"\"Suggest better start date for activity\"\"\"\r\n        forecast = self.weather.get_forecast(location[0], location[1])\r\n\r\n        best_start = None\r\n        best_avg_productivity = 0\r\n\r\n        for offset in range(-flexibility_days, flexibility_days + 1):\r\n            test_start = activity.planned_start + timedelta(days=offset)\r\n            test_end = test_start + timedelta(days=activity.duration_days - 1)\r\n\r\n            productivities = []\r\n            for f in forecast:\r\n                if test_start \u003C= f.date \u003C= test_end:\r\n                    act_template = self.analyzer.activities.get(activity.activity_type)\r\n                    if act_template:\r\n                        impact = self.analyzer._calculate_impact(f, act_template)\r\n                        productivities.append(impact['productivity'])\r\n\r\n            if productivities:\r\n                avg = sum(productivities) \u002F len(productivities)\r\n                if avg > best_avg_productivity:\r\n                    best_avg_productivity = avg\r\n                    best_start = test_start\r\n\r\n        if best_start and best_start != activity.planned_start:\r\n            return best_start\r\n        return None\r\n\r\n    def generate_weather_report(self, schedule: List[ScheduledActivity],\r\n                               location: Tuple[float, float],\r\n                               output_path: str) -> str:\r\n        \"\"\"Generate weather impact report\"\"\"\r\n        analysis = self.analyze_schedule(schedule, location)\r\n\r\n        with pd.ExcelWriter(output_path, engine='openpyxl') as writer:\r\n            # Summary\r\n            summary = pd.DataFrame([{\r\n                'Total Activities': len(schedule),\r\n                'Weather Delay Days': analysis['weather_delays'],\r\n                'High Risk Days': len(analysis['risk_days']),\r\n                'Recommendations': len(analysis['recommendations'])\r\n            }])\r\n            summary.to_excel(writer, sheet_name='Summary', index=False)\r\n\r\n            # Activity details\r\n            activity_data = []\r\n            for act in analysis['activities']:\r\n                activity_data.append({\r\n                    'Activity': act['activity_name'],\r\n                    'Start': act['planned_start'],\r\n                    'End': act['planned_end'],\r\n                    'Avg Productivity': f\"{act['avg_productivity']:.0%}\",\r\n                    'Expected Delay': f\"{act['expected_delay']} days\"\r\n                })\r\n            pd.DataFrame(activity_data).to_excel(writer, sheet_name='Activities', index=False)\r\n\r\n            # Risk days\r\n            if analysis['risk_days']:\r\n                pd.DataFrame(analysis['risk_days']).to_excel(\r\n                    writer, sheet_name='Risk_Days', index=False\r\n                )\r\n\r\n        return output_path\r\n```\r\n\r\n## Quick Reference\r\n\r\n| Activity Type | Min Temp | Max Precip | Max Wind | Rain Sensitive |\r\n|---------------|----------|------------|----------|----------------|\r\n| Concrete | 5°C | 2mm | 40 km\u002Fh | Yes |\r\n| Steel Erection | -10°C | 10mm | 35 km\u002Fh | No |\r\n| Roofing | 0°C | 0mm | 30 km\u002Fh | Yes |\r\n| Excavation | -5°C | 25mm | 50 km\u002Fh | Partial |\r\n| Exterior Painting | 10°C | 0mm | 25 km\u002Fh | Yes |\r\n| Masonry | 5°C | 5mm | 40 km\u002Fh | Yes |\r\n| Crane Operations | -15°C | 20mm | 30 km\u002Fh | No |\r\n\r\n## Resources\r\n\r\n- **OpenWeatherMap API**: https:\u002F\u002Fopenweathermap.org\u002Fapi\r\n- **Weather Underground**: https:\u002F\u002Fwww.wunderground.com\u002Fweather\u002Fapi\r\n- **DDC Website**: https:\u002F\u002Fdatadrivenconstruction.io\r\n\r\n## Next Steps\r\n\r\n- See `4d-simulation` for schedule visualization\r\n- See `risk-assessment-ml` for weather risk prediction\r\n- See `site-logistics-optimization` for delivery scheduling\r\n","# Weather Impact Analysis\r\n\r\n## Overview\r\n\r\nThis skill implements weather data analysis for construction project management. Integrate weather forecasts, historical data, and activity sensitivity to predict delays and optimize scheduling.\r\n\r\n**Capabilities:**\r\n- Weather forecast integration\r\n- Activity weather sensitivity mapping\r\n- Delay prediction and quantification\r\n- Schedule optimization based on weather\r\n- Historical weather impact analysis\r\n- Risk factor calculation\r\n\r\n## Quick Start\r\n\r\n```python\r\nfrom dataclasses import dataclass\r\nfrom datetime import date, datetime, timedelta\r\nfrom typing import List, Dict, Optional\r\nfrom enum import Enum\r\nimport requests\r\n\r\nclass WeatherCondition(Enum):\r\n    CLEAR = \"clear\"\r\n    CLOUDY = \"cloudy\"\r\n    RAIN = \"rain\"\r\n    HEAVY_RAIN = \"heavy_rain\"\r\n    SNOW = \"snow\"\r\n    FROST = \"frost\"\r\n    HIGH_WIND = \"high_wind\"\r\n    EXTREME_HEAT = \"extreme_heat\"\r\n    EXTREME_COLD = \"extreme_cold\"\r\n\r\n@dataclass\r\nclass WeatherDay:\r\n    date: date\r\n    condition: WeatherCondition\r\n    temp_high: float\r\n    temp_low: float\r\n    precipitation_mm: float\r\n    wind_speed_kmh: float\r\n    humidity_pct: float\r\n\r\n@dataclass\r\nclass ActivitySensitivity:\r\n    activity_type: str\r\n    min_temp: float\r\n    max_temp: float\r\n    max_wind: float\r\n    max_precipitation: float\r\n    can_work_in_rain: bool\r\n\r\ndef check_work_day(weather: WeatherDay, activity: ActivitySensitivity) -> Dict:\r\n    \"\"\"Check if work is possible for given weather and activity\"\"\"\r\n    can_work = True\r\n    reasons = []\r\n\r\n    if weather.temp_low \u003C activity.min_temp:\r\n        can_work = False\r\n        reasons.append(f\"Temperature too low: {weather.temp_low}°C \u003C {activity.min_temp}°C\")\r\n\r\n    if weather.temp_high > activity.max_temp:\r\n        can_work = False\r\n        reasons.append(f\"Temperature too high: {weather.temp_high}°C > {activity.max_temp}°C\")\r\n\r\n    if weather.wind_speed_kmh > activity.max_wind:\r\n        can_work = False\r\n        reasons.append(f\"Wind too strong: {weather.wind_speed_kmh} km\u002Fh > {activity.max_wind} km\u002Fh\")\r\n\r\n    if weather.precipitation_mm > activity.max_precipitation and not activity.can_work_in_rain:\r\n        can_work = False\r\n        reasons.append(f\"Precipitation: {weather.precipitation_mm}mm\")\r\n\r\n    return {\r\n        'date': weather.date,\r\n        'can_work': can_work,\r\n        'reasons': reasons,\r\n        'productivity_factor': 1.0 if can_work else 0.0\r\n    }\r\n\r\n# Example\r\nconcrete_work = ActivitySensitivity(\r\n    activity_type=\"concrete_placement\",\r\n    min_temp=5,\r\n    max_temp=35,\r\n    max_wind=40,\r\n    max_precipitation=2,\r\n    can_work_in_rain=False\r\n)\r\n\r\ntoday_weather = WeatherDay(\r\n    date=date.today(),\r\n    condition=WeatherCondition.RAIN,\r\n    temp_high=15,\r\n    temp_low=8,\r\n    precipitation_mm=10,\r\n    wind_speed_kmh=20,\r\n    humidity_pct=80\r\n)\r\n\r\nresult = check_work_day(today_weather, concrete_work)\r\nprint(f\"Can work: {result['can_work']}, Reasons: {result['reasons']}\")\r\n```\r\n\r\n## Comprehensive Weather Analysis System\r\n\r\n### Weather Data Integration\r\n\r\n```python\r\nfrom dataclasses import dataclass, field\r\nfrom datetime import date, datetime, timedelta\r\nfrom typing import List, Dict, Optional, Tuple\r\nfrom enum import Enum\r\nimport requests\r\nimport json\r\n\r\nclass WeatherSeverity(Enum):\r\n    NORMAL = 1\r\n    CAUTION = 2\r\n    WARNING = 3\r\n    SEVERE = 4\r\n    EXTREME = 5\r\n\r\n@dataclass\r\nclass HourlyWeather:\r\n    datetime: datetime\r\n    temperature: float\r\n    feels_like: float\r\n    humidity: float\r\n    wind_speed: float\r\n    wind_direction: float\r\n    precipitation: float\r\n    precipitation_probability: float\r\n    condition: WeatherCondition\r\n    visibility: float\r\n    uv_index: float\r\n\r\n@dataclass\r\nclass DailyForecast:\r\n    date: date\r\n    temp_high: float\r\n    temp_low: float\r\n    sunrise: datetime\r\n    sunset: datetime\r\n    precipitation_total: float\r\n    precipitation_probability: float\r\n    primary_condition: WeatherCondition\r\n    hourly: List[HourlyWeather] = field(default_factory=list)\r\n    severity: WeatherSeverity = WeatherSeverity.NORMAL\r\n\r\nclass WeatherDataService:\r\n    \"\"\"Weather data integration service\"\"\"\r\n\r\n    def __init__(self, api_key: str = None, provider: str = \"openweathermap\"):\r\n        self.api_key = api_key\r\n        self.provider = provider\r\n        self.cache: Dict[str, Dict] = {}\r\n        self.cache_duration = timedelta(hours=1)\r\n\r\n    def get_forecast(self, latitude: float, longitude: float,\r\n                    days: int = 14) -> List[DailyForecast]:\r\n        \"\"\"Get weather forecast for location\"\"\"\r\n        cache_key = f\"{latitude},{longitude}\"\r\n\r\n        if cache_key in self.cache:\r\n            cached = self.cache[cache_key]\r\n            if datetime.now() - cached['timestamp'] \u003C self.cache_duration:\r\n                return cached['data']\r\n\r\n        if self.provider == \"openweathermap\":\r\n            forecast = self._fetch_openweathermap(latitude, longitude, days)\r\n        else:\r\n            forecast = self._generate_sample_forecast(days)\r\n\r\n        self.cache[cache_key] = {\r\n            'timestamp': datetime.now(),\r\n            'data': forecast\r\n        }\r\n\r\n        return forecast\r\n\r\n    def _fetch_openweathermap(self, lat: float, lon: float,\r\n                              days: int) -> List[DailyForecast]:\r\n        \"\"\"Fetch from OpenWeatherMap API\"\"\"\r\n        url = f\"https:\u002F\u002Fapi.openweathermap.org\u002Fdata\u002F2.5\u002Fforecast\"\r\n        params = {\r\n            'lat': lat,\r\n            'lon': lon,\r\n            'appid': self.api_key,\r\n            'units': 'metric'\r\n        }\r\n\r\n        try:\r\n            response = requests.get(url, params=params)\r\n            data = response.json()\r\n            return self._parse_openweathermap(data)\r\n        except Exception as e:\r\n            print(f\"Weather API error: {e}\")\r\n            return self._generate_sample_forecast(days)\r\n\r\n    def _parse_openweathermap(self, data: Dict) -> List[DailyForecast]:\r\n        \"\"\"Parse OpenWeatherMap response\"\"\"\r\n        forecasts = []\r\n        daily_data = {}\r\n\r\n        for item in data.get('list', []):\r\n            dt = datetime.fromtimestamp(item['dt'])\r\n            day = dt.date()\r\n\r\n            if day not in daily_data:\r\n                daily_data[day] = {\r\n                    'temps': [],\r\n                    'precipitation': 0,\r\n                    'conditions': [],\r\n                    'hourly': []\r\n                }\r\n\r\n            daily_data[day]['temps'].append(item['main']['temp'])\r\n            daily_data[day]['precipitation'] += item.get('rain', {}).get('3h', 0)\r\n\r\n            condition = self._map_condition(item['weather'][0]['main'])\r\n            daily_data[day]['conditions'].append(condition)\r\n\r\n            daily_data[day]['hourly'].append(HourlyWeather(\r\n                datetime=dt,\r\n                temperature=item['main']['temp'],\r\n                feels_like=item['main']['feels_like'],\r\n                humidity=item['main']['humidity'],\r\n                wind_speed=item['wind']['speed'] * 3.6,  # m\u002Fs to km\u002Fh\r\n                wind_direction=item['wind'].get('deg', 0),\r\n                precipitation=item.get('rain', {}).get('3h', 0),\r\n                precipitation_probability=item.get('pop', 0) * 100,\r\n                condition=condition,\r\n                visibility=item.get('visibility', 10000) \u002F 1000,\r\n                uv_index=0\r\n            ))\r\n\r\n        for day, data in daily_data.items():\r\n            primary_condition = max(set(data['conditions']), key=data['conditions'].count)\r\n\r\n            forecasts.append(DailyForecast(\r\n                date=day,\r\n                temp_high=max(data['temps']),\r\n                temp_low=min(data['temps']),\r\n                sunrise=datetime.combine(day, datetime.min.time().replace(hour=6)),\r\n                sunset=datetime.combine(day, datetime.min.time().replace(hour=18)),\r\n                precipitation_total=data['precipitation'],\r\n                precipitation_probability=max(h.precipitation_probability for h in data['hourly']),\r\n                primary_condition=primary_condition,\r\n                hourly=data['hourly'],\r\n                severity=self._calculate_severity(primary_condition, data)\r\n            ))\r\n\r\n        return sorted(forecasts, key=lambda x: x.date)\r\n\r\n    def _map_condition(self, condition_str: str) -> WeatherCondition:\r\n        \"\"\"Map API condition to enum\"\"\"\r\n        mapping = {\r\n            'Clear': WeatherCondition.CLEAR,\r\n            'Clouds': WeatherCondition.CLOUDY,\r\n            'Rain': WeatherCondition.RAIN,\r\n            'Drizzle': WeatherCondition.RAIN,\r\n            'Thunderstorm': WeatherCondition.HEAVY_RAIN,\r\n            'Snow': WeatherCondition.SNOW,\r\n            'Mist': WeatherCondition.CLOUDY,\r\n            'Fog': WeatherCondition.CLOUDY\r\n        }\r\n        return mapping.get(condition_str, WeatherCondition.CLEAR)\r\n\r\n    def _calculate_severity(self, condition: WeatherCondition,\r\n                           data: Dict) -> WeatherSeverity:\r\n        \"\"\"Calculate weather severity\"\"\"\r\n        max_temp = max(data['temps'])\r\n        min_temp = min(data['temps'])\r\n        precip = data['precipitation']\r\n\r\n        if condition in [WeatherCondition.HEAVY_RAIN, WeatherCondition.SNOW]:\r\n            if precip > 50:\r\n                return WeatherSeverity.EXTREME\r\n            elif precip > 25:\r\n                return WeatherSeverity.SEVERE\r\n\r\n        if max_temp > 40 or min_temp \u003C -15:\r\n            return WeatherSeverity.SEVERE\r\n\r\n        if max_temp > 35 or min_temp \u003C -5:\r\n            return WeatherSeverity.WARNING\r\n\r\n        if condition == WeatherCondition.RAIN:\r\n            return WeatherSeverity.CAUTION\r\n\r\n        return WeatherSeverity.NORMAL\r\n\r\n    def _generate_sample_forecast(self, days: int) -> List[DailyForecast]:\r\n        \"\"\"Generate sample forecast for testing\"\"\"\r\n        import random\r\n        forecasts = []\r\n\r\n        for i in range(days):\r\n            day = date.today() + timedelta(days=i)\r\n            temp_base = 15 + random.uniform(-5, 10)\r\n            condition = random.choice(list(WeatherCondition))\r\n\r\n            forecasts.append(DailyForecast(\r\n                date=day,\r\n                temp_high=temp_base + random.uniform(3, 8),\r\n                temp_low=temp_base - random.uniform(3, 8),\r\n                sunrise=datetime.combine(day, datetime.min.time().replace(hour=6)),\r\n                sunset=datetime.combine(day, datetime.min.time().replace(hour=18)),\r\n                precipitation_total=random.uniform(0, 20) if condition == WeatherCondition.RAIN else 0,\r\n                precipitation_probability=random.uniform(0, 100) if condition == WeatherCondition.RAIN else 10,\r\n                primary_condition=condition,\r\n                severity=WeatherSeverity.NORMAL\r\n            ))\r\n\r\n        return forecasts\r\n```\r\n\r\n### Activity Weather Sensitivity\r\n\r\n```python\r\n@dataclass\r\nclass WeatherThresholds:\r\n    min_temp: float = -10\r\n    max_temp: float = 45\r\n    max_wind: float = 50\r\n    max_precipitation: float = 50\r\n    max_snow_depth: float = 20\r\n    min_visibility: float = 0.5  # km\r\n\r\n@dataclass\r\nclass ConstructionActivity:\r\n    activity_id: str\r\n    activity_name: str\r\n    category: str\r\n    thresholds: WeatherThresholds\r\n    indoor: bool = False\r\n    rain_sensitive: bool = True\r\n    frost_sensitive: bool = False\r\n    productivity_factors: Dict[WeatherCondition, float] = field(default_factory=dict)\r\n\r\n    def __post_init__(self):\r\n        if not self.productivity_factors:\r\n            self.productivity_factors = {\r\n                WeatherCondition.CLEAR: 1.0,\r\n                WeatherCondition.CLOUDY: 0.95,\r\n                WeatherCondition.RAIN: 0.3 if self.rain_sensitive else 0.8,\r\n                WeatherCondition.HEAVY_RAIN: 0.0 if self.rain_sensitive else 0.5,\r\n                WeatherCondition.SNOW: 0.2,\r\n                WeatherCondition.FROST: 0.5 if self.frost_sensitive else 0.8,\r\n                WeatherCondition.HIGH_WIND: 0.3,\r\n                WeatherCondition.EXTREME_HEAT: 0.6,\r\n                WeatherCondition.EXTREME_COLD: 0.4\r\n            }\r\n\r\nclass ActivityWeatherAnalyzer:\r\n    \"\"\"Analyze weather impact on construction activities\"\"\"\r\n\r\n    # Default activity definitions\r\n    ACTIVITY_TEMPLATES = {\r\n        'concrete_placement': ConstructionActivity(\r\n            activity_id='ACT-001',\r\n            activity_name='Concrete Placement',\r\n            category='structural',\r\n            thresholds=WeatherThresholds(min_temp=5, max_temp=35, max_precipitation=2, max_wind=40),\r\n            rain_sensitive=True,\r\n            frost_sensitive=True\r\n        ),\r\n        'steel_erection': ConstructionActivity(\r\n            activity_id='ACT-002',\r\n            activity_name='Steel Erection',\r\n            category='structural',\r\n            thresholds=WeatherThresholds(max_wind=35, max_precipitation=10),\r\n            rain_sensitive=False\r\n        ),\r\n        'roofing': ConstructionActivity(\r\n            activity_id='ACT-003',\r\n            activity_name='Roofing',\r\n            category='envelope',\r\n            thresholds=WeatherThresholds(min_temp=0, max_precipitation=0, max_wind=30),\r\n            rain_sensitive=True\r\n        ),\r\n        'excavation': ConstructionActivity(\r\n            activity_id='ACT-004',\r\n            activity_name='Excavation',\r\n            category='earthwork',\r\n            thresholds=WeatherThresholds(min_temp=-5, max_precipitation=25),\r\n            rain_sensitive=True,\r\n            frost_sensitive=True\r\n        ),\r\n        'painting_exterior': ConstructionActivity(\r\n            activity_id='ACT-005',\r\n            activity_name='Exterior Painting',\r\n            category='finishing',\r\n            thresholds=WeatherThresholds(min_temp=10, max_temp=35, max_precipitation=0, max_wind=25),\r\n            rain_sensitive=True\r\n        ),\r\n        'masonry': ConstructionActivity(\r\n            activity_id='ACT-006',\r\n            activity_name='Masonry Work',\r\n            category='structural',\r\n            thresholds=WeatherThresholds(min_temp=5, max_temp=32, max_precipitation=5),\r\n            rain_sensitive=True,\r\n            frost_sensitive=True\r\n        ),\r\n        'crane_operations': ConstructionActivity(\r\n            activity_id='ACT-007',\r\n            activity_name='Crane Operations',\r\n            category='equipment',\r\n            thresholds=WeatherThresholds(max_wind=30, min_visibility=1.0),\r\n            rain_sensitive=False\r\n        ),\r\n        'electrical_exterior': ConstructionActivity(\r\n            activity_id='ACT-008',\r\n            activity_name='Exterior Electrical',\r\n            category='MEP',\r\n            thresholds=WeatherThresholds(max_precipitation=0),\r\n            rain_sensitive=True\r\n        ),\r\n        'interior_work': ConstructionActivity(\r\n            activity_id='ACT-009',\r\n            activity_name='Interior Work',\r\n            category='finishing',\r\n            thresholds=WeatherThresholds(),\r\n            indoor=True,\r\n            rain_sensitive=False\r\n        )\r\n    }\r\n\r\n    def __init__(self):\r\n        self.activities = dict(self.ACTIVITY_TEMPLATES)\r\n\r\n    def add_activity(self, activity: ConstructionActivity):\r\n        \"\"\"Add custom activity\"\"\"\r\n        self.activities[activity.activity_id] = activity\r\n\r\n    def analyze_day(self, weather: DailyForecast,\r\n                   activities: List[str]) -> Dict[str, Dict]:\r\n        \"\"\"Analyze weather impact for specific day and activities\"\"\"\r\n        results = {}\r\n\r\n        for activity_id in activities:\r\n            activity = self.activities.get(activity_id)\r\n            if not activity:\r\n                continue\r\n\r\n            impact = self._calculate_impact(weather, activity)\r\n            results[activity_id] = impact\r\n\r\n        return results\r\n\r\n    def _calculate_impact(self, weather: DailyForecast,\r\n                         activity: ConstructionActivity) -> Dict:\r\n        \"\"\"Calculate weather impact on activity\"\"\"\r\n        if activity.indoor:\r\n            return {\r\n                'can_work': True,\r\n                'productivity': 1.0,\r\n                'issues': [],\r\n                'recommendations': []\r\n            }\r\n\r\n        issues = []\r\n        productivity = 1.0\r\n\r\n        # Temperature check\r\n        if weather.temp_low \u003C activity.thresholds.min_temp:\r\n            issues.append(f\"Low temperature: {weather.temp_low}°C\")\r\n            if activity.frost_sensitive:\r\n                productivity *= 0.0\r\n            else:\r\n                productivity *= 0.5\r\n\r\n        if weather.temp_high > activity.thresholds.max_temp:\r\n            issues.append(f\"High temperature: {weather.temp_high}°C\")\r\n            productivity *= 0.6\r\n\r\n        # Precipitation check\r\n        if weather.precipitation_total > activity.thresholds.max_precipitation:\r\n            issues.append(f\"Precipitation: {weather.precipitation_total}mm\")\r\n            if activity.rain_sensitive:\r\n                productivity *= 0.0\r\n            else:\r\n                productivity *= 0.7\r\n\r\n        # Condition-based productivity\r\n        condition_factor = activity.productivity_factors.get(\r\n            weather.primary_condition, 1.0\r\n        )\r\n        productivity *= condition_factor\r\n\r\n        # Generate recommendations\r\n        recommendations = []\r\n        if productivity \u003C 0.5 and productivity > 0:\r\n            recommendations.append(\"Consider rescheduling to more favorable day\")\r\n        if weather.temp_low \u003C activity.thresholds.min_temp + 5:\r\n            recommendations.append(\"Plan for cold weather precautions\")\r\n        if weather.precipitation_probability > 50:\r\n            recommendations.append(\"Have rain contingency plan ready\")\r\n\r\n        can_work = productivity > 0\r\n\r\n        return {\r\n            'activity_name': activity.activity_name,\r\n            'can_work': can_work,\r\n            'productivity': round(productivity, 2),\r\n            'issues': issues,\r\n            'recommendations': recommendations,\r\n            'weather_condition': weather.primary_condition.value,\r\n            'temperature_range': f\"{weather.temp_low}°C - {weather.temp_high}°C\"\r\n        }\r\n\r\n    def find_optimal_days(self, forecast: List[DailyForecast],\r\n                         activity_id: str,\r\n                         min_productivity: float = 0.8) -> List[date]:\r\n        \"\"\"Find optimal days for an activity\"\"\"\r\n        activity = self.activities.get(activity_id)\r\n        if not activity:\r\n            return []\r\n\r\n        optimal = []\r\n        for day in forecast:\r\n            impact = self._calculate_impact(day, activity)\r\n            if impact['productivity'] >= min_productivity:\r\n                optimal.append(day.date)\r\n\r\n        return optimal\r\n```\r\n\r\n### Schedule Weather Integration\r\n\r\n```python\r\nfrom datetime import date, timedelta\r\nfrom typing import List, Dict\r\nimport pandas as pd\r\n\r\n@dataclass\r\nclass ScheduledActivity:\r\n    activity_id: str\r\n    activity_name: str\r\n    activity_type: str  # Maps to ACTIVITY_TEMPLATES\r\n    planned_start: date\r\n    planned_end: date\r\n    duration_days: int\r\n    is_critical: bool = False\r\n\r\nclass ScheduleWeatherOptimizer:\r\n    \"\"\"Optimize construction schedule based on weather\"\"\"\r\n\r\n    def __init__(self, weather_service: WeatherDataService,\r\n                 activity_analyzer: ActivityWeatherAnalyzer):\r\n        self.weather = weather_service\r\n        self.analyzer = activity_analyzer\r\n\r\n    def analyze_schedule(self, schedule: List[ScheduledActivity],\r\n                        location: Tuple[float, float]) -> Dict:\r\n        \"\"\"Analyze schedule against weather forecast\"\"\"\r\n        forecast = self.weather.get_forecast(location[0], location[1])\r\n        forecast_dict = {f.date: f for f in forecast}\r\n\r\n        analysis = {\r\n            'activities': [],\r\n            'weather_delays': 0,\r\n            'risk_days': [],\r\n            'recommendations': []\r\n        }\r\n\r\n        for activity in schedule:\r\n            activity_analysis = self._analyze_activity(\r\n                activity, forecast_dict\r\n            )\r\n            analysis['activities'].append(activity_analysis)\r\n\r\n            if activity_analysis['expected_delay'] > 0:\r\n                analysis['weather_delays'] += activity_analysis['expected_delay']\r\n\r\n            analysis['risk_days'].extend(activity_analysis['risk_days'])\r\n\r\n        # Generate overall recommendations\r\n        if analysis['weather_delays'] > 5:\r\n            analysis['recommendations'].append(\r\n                f\"Schedule shows {analysis['weather_delays']} potential weather delay days. \"\r\n                \"Consider buffer time or alternative scheduling.\"\r\n            )\r\n\r\n        return analysis\r\n\r\n    def _analyze_activity(self, activity: ScheduledActivity,\r\n                         forecast: Dict[date, DailyForecast]) -> Dict:\r\n        \"\"\"Analyze single activity against weather\"\"\"\r\n        result = {\r\n            'activity_id': activity.activity_id,\r\n            'activity_name': activity.activity_name,\r\n            'planned_start': activity.planned_start,\r\n            'planned_end': activity.planned_end,\r\n            'day_analysis': [],\r\n            'risk_days': [],\r\n            'expected_delay': 0,\r\n            'avg_productivity': 1.0\r\n        }\r\n\r\n        current_date = activity.planned_start\r\n        productivities = []\r\n        delay_days = 0\r\n\r\n        while current_date \u003C= activity.planned_end:\r\n            if current_date in forecast:\r\n                weather = forecast[current_date]\r\n                impact = self.analyzer._calculate_impact(\r\n                    weather,\r\n                    self.analyzer.activities.get(activity.activity_type,\r\n                        self.analyzer.ACTIVITY_TEMPLATES.get('interior_work'))\r\n                )\r\n\r\n                productivities.append(impact['productivity'])\r\n\r\n                day_info = {\r\n                    'date': current_date,\r\n                    'can_work': impact['can_work'],\r\n                    'productivity': impact['productivity'],\r\n                    'weather': weather.primary_condition.value\r\n                }\r\n                result['day_analysis'].append(day_info)\r\n\r\n                if not impact['can_work']:\r\n                    delay_days += 1\r\n                    result['risk_days'].append({\r\n                        'date': current_date,\r\n                        'activity': activity.activity_name,\r\n                        'reason': weather.primary_condition.value\r\n                    })\r\n                elif impact['productivity'] \u003C 0.7:\r\n                    delay_days += (1 - impact['productivity'])\r\n\r\n            current_date += timedelta(days=1)\r\n\r\n        result['expected_delay'] = round(delay_days)\r\n        result['avg_productivity'] = sum(productivities) \u002F len(productivities) if productivities else 1.0\r\n\r\n        return result\r\n\r\n    def suggest_reschedule(self, activity: ScheduledActivity,\r\n                          location: Tuple[float, float],\r\n                          flexibility_days: int = 7) -> Optional[date]:\r\n        \"\"\"Suggest better start date for activity\"\"\"\r\n        forecast = self.weather.get_forecast(location[0], location[1])\r\n\r\n        best_start = None\r\n        best_avg_productivity = 0\r\n\r\n        for offset in range(-flexibility_days, flexibility_days + 1):\r\n            test_start = activity.planned_start + timedelta(days=offset)\r\n            test_end = test_start + timedelta(days=activity.duration_days - 1)\r\n\r\n            productivities = []\r\n            for f in forecast:\r\n                if test_start \u003C= f.date \u003C= test_end:\r\n                    act_template = self.analyzer.activities.get(activity.activity_type)\r\n                    if act_template:\r\n                        impact = self.analyzer._calculate_impact(f, act_template)\r\n                        productivities.append(impact['productivity'])\r\n\r\n            if productivities:\r\n                avg = sum(productivities) \u002F len(productivities)\r\n                if avg > best_avg_productivity:\r\n                    best_avg_productivity = avg\r\n                    best_start = test_start\r\n\r\n        if best_start and best_start != activity.planned_start:\r\n            return best_start\r\n        return None\r\n\r\n    def generate_weather_report(self, schedule: List[ScheduledActivity],\r\n                               location: Tuple[float, float],\r\n                               output_path: str) -> str:\r\n        \"\"\"Generate weather impact report\"\"\"\r\n        analysis = self.analyze_schedule(schedule, location)\r\n\r\n        with pd.ExcelWriter(output_path, engine='openpyxl') as writer:\r\n            # Summary\r\n            summary = pd.DataFrame([{\r\n                'Total Activities': len(schedule),\r\n                'Weather Delay Days': analysis['weather_delays'],\r\n                'High Risk Days': len(analysis['risk_days']),\r\n                'Recommendations': len(analysis['recommendations'])\r\n            }])\r\n            summary.to_excel(writer, sheet_name='Summary', index=False)\r\n\r\n            # Activity details\r\n            activity_data = []\r\n            for act in analysis['activities']:\r\n                activity_data.append({\r\n                    'Activity': act['activity_name'],\r\n                    'Start': act['planned_start'],\r\n                    'End': act['planned_end'],\r\n                    'Avg Productivity': f\"{act['avg_productivity']:.0%}\",\r\n                    'Expected Delay': f\"{act['expected_delay']} days\"\r\n                })\r\n            pd.DataFrame(activity_data).to_excel(writer, sheet_name='Activities', index=False)\r\n\r\n            # Risk days\r\n            if analysis['risk_days']:\r\n                pd.DataFrame(analysis['risk_days']).to_excel(\r\n                    writer, sheet_name='Risk_Days', index=False\r\n                )\r\n\r\n        return output_path\r\n```\r\n\r\n## Quick Reference\r\n\r\n| Activity Type | Min Temp | Max Precip | Max Wind | Rain Sensitive |\r\n|---------------|----------|------------|----------|----------------|\r\n| Concrete | 5°C | 2mm | 40 km\u002Fh | Yes |\r\n| Steel Erection | -10°C | 10mm | 35 km\u002Fh | No |\r\n| Roofing | 0°C | 0mm | 30 km\u002Fh | Yes |\r\n| Excavation | -5°C | 25mm | 50 km\u002Fh | Partial |\r\n| Exterior Painting | 10°C | 0mm | 25 km\u002Fh | Yes |\r\n| Masonry | 5°C | 5mm | 40 km\u002Fh | Yes |\r\n| Crane Operations | -15°C | 20mm | 30 km\u002Fh | No |\r\n\r\n## Resources\r\n\r\n- **OpenWeatherMap API**: https:\u002F\u002Fopenweathermap.org\u002Fapi\r\n- **Weather Underground**: https:\u002F\u002Fwww.wunderground.com\u002Fweather\u002Fapi\r\n- **DDC Website**: https:\u002F\u002Fdatadrivenconstruction.io\r\n\r\n## Next Steps\r\n\r\n- See `4d-simulation` for schedule visualization\r\n- See `risk-assessment-ml` for weather risk prediction\r\n- See `site-logistics-optimization` for delivery scheduling\r\n",{"slug":5,"description":7,"display_name":39},"Weather Impact Analysis","main","e7e336ac628c3e3bd771aff1aaa469c93778f94a","skills\u002Fdatadrivenconstruction\u002Fweather-impact-analysis",0,"https:\u002F\u002Fgithub.com\u002Fkbarbel640-del\u002Fskills","https:\u002F\u002Fgithub.com\u002Fkbarbel640-del","2026-08-23 22:39:37",{"source":8,"skillName":5,"commands":48,"prompt":58,"zipAvailable":59},[49,52,55],{"tool":50,"command":51},"npx","npx skills add kbarbel640-del\u002Fskills --skill weather-impact-analysis",{"tool":53,"command":54},"bunx","bunx skills add kbarbel640-del\u002Fskills --skill weather-impact-analysis",{"tool":56,"command":57},"pnpm","pnpm dlx skills add kbarbel640-del\u002Fskills --skill weather-impact-analysis","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fkbarbel640-del\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fdatadrivenconstruction\u002Fweather-impact-analysis\u002FSKILL.md",true,[61],{"path":62,"resourceType":63,"resourceTypeDesc":64,"sizeBytes":65,"sourceHtmlUrl":66},"_meta.json","REFERENCE","参考文档",314,"https:\u002F\u002Fgithub.com\u002Fkbarbel640-del\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fdatadrivenconstruction\u002Fweather-impact-analysis\u002F_meta.json",[68,99,127,148,154,174],{"skillNo":69,"name":70,"nameZh":6,"description":71,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":72,"categories":73,"keywords":79,"publishedAt":34},"01M6G8J55967D6281ACMKAKTEQ","crowd-prompting","A marketplace where AI agents improve prompts, system instructions, tool descriptions, and other text-based content with domain expertise from real-world operations — and earn tokens for valuable contributions.","https:\u002F\u002Fgithub.com\u002Fkbarbel640-del\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fzzadrian\u002Fcrowd-prompting\u002FSKILL.md",[74],{"categoryNo":75,"categoryName":76,"taxonomyType":77,"taxonomyTypeDesc":78},"01JBSEEDCAT000000000000036","内容创作者","PROFESSION","适用职业",[80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98],"crowd prompting","prompting","api","with","tokens","domain","world","prompts","operations","instructions","text","tool","system","improve","productivity","marketplace","https","content","ai agents",{"skillNo":100,"name":101,"nameZh":6,"description":102,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":103,"categories":104,"keywords":110,"publishedAt":34},"01M6G8J0PKPAYVET6BWE8W4C26","dilemm-ai","Compete in DilemmAI, the prisoner's dilemma AI arena at dilemm.ai. Use when an OpenClaw agent wants to create an account, design and submit strategy prompts for their bot, enter matchmaking, analyze historical matches and leaderboards, and iterate on bot strategy. Requires headless browser automation (no API available yet).","https:\u002F\u002Fgithub.com\u002Fkbarbel640-del\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fzyori\u002Fdilemm-ai\u002FSKILL.md",[105],{"categoryNo":106,"categoryName":107,"taxonomyType":108,"taxonomyTypeDesc":109},"01JBSEEDCAT000000000000023","Skill与智能体开发","SCENARIO","使用场景",[111,82,112,26,113,114,115,116,117,118,119,120,121,122,123,124,125,87,126],"dilemm ai","arena","browser","openclaw agent","create","automation","browser automation","bot","openclaw","design","strategy","headless","compete","submit","matchmaking","use",{"skillNo":128,"name":129,"nameZh":6,"description":130,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":131,"categories":132,"keywords":136,"publishedAt":34},"01M6G8HWAF3K9SW7NP3Z83CPJ6","arxiv-paper-reviews","Interact with arXiv Crawler API to fetch papers, read reviews, and submit comments. Use when working with arXiv papers, fetching paper lists by date\u002Fcategory\u002Finterest, viewing paper details with comments, or submitting paper reviews via API at http:\u002F\u002F150.158.152.82:8000.","https:\u002F\u002Fgithub.com\u002Fkbarbel640-del\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fzxrys\u002Fweak-accept\u002FSKILL.md",[133],{"categoryNo":134,"categoryName":135,"taxonomyType":108,"taxonomyTypeDesc":109},"01JBSEEDCAT000000000000021","资料检索与研究",[137,138,139,140,141,142,143,83,144,124,126,145,82,146,147],"arxiv paper reviews","reviews","arxiv","paper","crawler","papers","comments","fetch","read","http","via",{"skillNo":149,"name":129,"nameZh":6,"description":130,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":150,"categories":151,"keywords":153,"publishedAt":34},"01M6G8HQJ7N7WMY6YV7D7SSJ16","https:\u002F\u002Fgithub.com\u002Fkbarbel640-del\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fzxrys\u002Farxiv-paper-reviews\u002FSKILL.md",[152],{"categoryNo":134,"categoryName":135,"taxonomyType":108,"taxonomyTypeDesc":109},[140,139,138,137,147,142,143,83,141,124,126,144,145,82,146],{"skillNo":155,"name":156,"nameZh":6,"description":157,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":158,"categories":159,"keywords":160,"publishedAt":34},"01M6G8HM95Y64JNVFC2HN4E2BG","git-summary","Get a quick summary of the current Git repository including status, recent commits, branches, and contributors.","https:\u002F\u002Fgithub.com\u002Fkbarbel640-del\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fzweack\u002Fgit-summary\u002FSKILL.md",[],[161,162,163,164,165,166,167,168,169,170,171,172,173],"git","summary","git summary","commits","repository","win32","status","linux","contributors","true","quick","get","branches",{"skillNo":175,"name":176,"nameZh":6,"description":177,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":178,"categories":179,"keywords":184,"publishedAt":34},"01M6G8HGEGF05CNEJCGVYFSC4R","agent-constitution","Interact with AgentConstitution governance contracts on Base Sepolia. Check compliance, read rules, log actions, query governance state.","https:\u002F\u002Fgithub.com\u002Fkbarbel640-del\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fztsalexey\u002Fagent-constitution\u002FSKILL.md",[180,181],{"categoryNo":106,"categoryName":107,"taxonomyType":108,"taxonomyTypeDesc":109},{"categoryNo":182,"categoryName":183,"taxonomyType":77,"taxonomyTypeDesc":78},"01JBSEEDCAT000000000000037","律师与法务",[185,186,187,188,83,189,190,40,191,145,192,193,194,195,196,96,197,198,199],"agent constitution","constitution","contracts","tree","query","base","log","check","Rules","projects","compliance","actions","state","governance","github"]