import logging from datetime import datetime, timezone from pyproj import Transformer from providers.base import BaseProvider, BaseCountyProvider logger = logging.getLogger(__name__) class NiscCountyProvider(BaseCountyProvider): """ Handles county data from NISC-hosted cloud sources. """ def fetch(self): url = self.config.get('county_url') state = self.config.get('state_filter') try: resp = self.session.get(url, verify=False) if not resp.ok: return [] data = resp.json() results = [] # The structure is typically a list containing one object with a 'boundaries' key for boundary_group in data: for item in boundary_group.get('boundaries', []): results.append({ 'outages': item.get('customersOutNow'), 'served': item.get('customersServed'), 'county': item.get('name'), 'state': state, 'company': self.name }) return results except Exception as e: logger.error(f"Error fetching NISC county data for {self.name}: {e}") return [] class NiscHostedProvider(BaseProvider): """ Handles NISC Cloud Coop format (JSON with PROJ coordinate strings). Example: Buckeye REC """ def __init__(self, config, session): super().__init__(config, session) self.transformer = None proj_str = config.get('proj_string') if proj_str: try: self.transformer = Transformer.from_proj(proj_str, "EPSG:4326", always_xy=True) except Exception as e: logger.error(f"Failed to initialize projection for {self.name}: {e}") def fetch(self): url = self.config.get('url') try: resp = self.session.get(url, verify=False) if not resp.ok: logger.error(f"{self.name} HTTP {resp.status_code}") return [] data = resp.json() return [self._normalize(item) for item in data.get('outages', [])] except Exception as e: logger.error(f"Error fetching {self.name}: {e}") return [] def _normalize(self, item): x, y = item.get('x'), item.get('y') lat, lon = None, None if x is not None and y is not None and self.transformer: try: lon, lat = self.transformer.transform(x, y) except: pass time_off = item.get('timeOff') start_ts = datetime.fromtimestamp(time_off / 1000, tz=timezone.utc) if time_off else None return { 'incidentid': str(item.get('id')), 'utility': self.name, 'lat': lat, 'lon': lon, 'pointgeom': f"{lat},{lon}" if lat else None, 'start': start_ts, 'outagen': item.get('nbrOut', 1), 'cause': "Unknown", 'crew_status': "Unknown", 'active': True, 'last_change': datetime.now(timezone.utc) }