| """ |
| Download IMS weather data for station 43 (Sde Boker) and cache to Data/ims/. |
| Resamples 10min to 15min. Use --list-channels to discover channel IDs for RH, Rain, WS, BP. |
| |
| Usage: |
| python -m scripts.download_ims_data --list-channels |
| python -m scripts.download_ims_data --from 2024-01-01 --to 2024-12-31 |
| python -m scripts.download_ims_data --years 2 |
| """ |
|
|
| from __future__ import annotations |
|
|
| import argparse |
| import sys |
| from pathlib import Path |
|
|
| |
| PROJECT_ROOT = Path(__file__).resolve().parent.parent |
| if str(PROJECT_ROOT) not in sys.path: |
| sys.path.insert(0, str(PROJECT_ROOT)) |
|
|
| try: |
| from dotenv import load_dotenv |
| load_dotenv(PROJECT_ROOT / ".env") |
| except ImportError: |
| pass |
|
|
|
|
| def main() -> None: |
| parser = argparse.ArgumentParser( |
| description="Fetch IMS station 43 data and cache to Data/ims/ (15min resolution)." |
| ) |
| parser.add_argument( |
| "--list-channels", |
| action="store_true", |
| help="List channel IDs and names for station 43 (to find RH, Rain, WS, BP).", |
| ) |
| parser.add_argument( |
| "--from", |
| dest="from_date", |
| metavar="YYYY-MM-DD", |
| help="Start date (inclusive).", |
| ) |
| parser.add_argument( |
| "--to", |
| dest="to_date", |
| metavar="YYYY-MM-DD", |
| help="End date (inclusive).", |
| ) |
| parser.add_argument( |
| "--years", |
| type=float, |
| default=2, |
| metavar="N", |
| help="If --from/--to not set, fetch last N years (default: 2).", |
| ) |
| parser.add_argument( |
| "--chunk-days", |
| type=int, |
| default=7, |
| metavar="D", |
| help="Split range into D-day chunks (default: 7 for better API success). Use 0 to disable chunking.", |
| ) |
| args = parser.parse_args() |
|
|
| from src.ims_client import IMSClient |
| from config import settings |
|
|
| client = IMSClient() |
|
|
| if args.list_channels: |
| try: |
| channels = client.list_channels(settings.IMS_STATION_ID) |
| except Exception as e: |
| print(f"Error fetching station metadata: {e}", file=sys.stderr) |
| sys.exit(1) |
| if not channels: |
| print("No channels returned. Check API response structure.") |
| sys.exit(0) |
| print(f"Station {settings.IMS_STATION_ID} channels:") |
| print("-" * 60) |
| for ch in sorted(channels, key=lambda x: (x.get("channelId") or 0)): |
| cid = ch.get("channelId", "?") |
| name = ch.get("name", "?") |
| units = ch.get("units", "") |
| active = ch.get("active", True) |
| print(f" {cid:>4} {name:<12} {units:<8} active={active}") |
| return |
|
|
| |
| from datetime import datetime, timedelta, timezone |
|
|
| end = datetime.now(timezone.utc).date() |
| if args.from_date and args.to_date: |
| start = datetime.strptime(args.from_date, "%Y-%m-%d").date() |
| end = datetime.strptime(args.to_date, "%Y-%m-%d").date() |
| elif args.from_date: |
| start = datetime.strptime(args.from_date, "%Y-%m-%d").date() |
| else: |
| start = end - timedelta(days=int(args.years * 365.25)) |
|
|
| from_s = start.strftime("%Y-%m-%d") |
| to_s = end.strftime("%Y-%m-%d") |
| chunk_days = args.chunk_days if args.chunk_days else None |
|
|
| print(f"Fetching IMS station {settings.IMS_STATION_ID} from {from_s} to {to_s} (chunk_days={chunk_days})...") |
| try: |
| df = client.fetch_and_cache(from_s, to_s, chunk_days=chunk_days) |
| except Exception as e: |
| print(f"Error: {e}", file=sys.stderr) |
| sys.exit(1) |
|
|
| if df.empty: |
| print("No data returned. Check token and date range.") |
| sys.exit(1) |
|
|
| out_path = settings.IMS_CACHE_DIR / "ims_merged_15min.csv" |
| print(f"Saved {len(df)} rows (15min) to {out_path}") |
| print(f"Columns: {list(df.columns)}") |
|
|
|
|
| if __name__ == "__main__": |
| main() |
|
|