You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
90 lines
3.3 KiB
90 lines
3.3 KiB
from datetime import datetime
|
|
from astral import LocationInfo as AstralLoc
|
|
from astral.sun import sun
|
|
from astral.moon import phase
|
|
from ..models import LocationInfo
|
|
|
|
class AstroProvider:
|
|
def get_astro_info(self, loc: LocationInfo) -> str:
|
|
parts = []
|
|
try:
|
|
# Astral uses its own LocationInfo
|
|
City = AstralLoc(loc.city, loc.region, loc.timezone, loc.latitude, loc.longitude)
|
|
s = sun(City.observer, date=datetime.now(), tzinfo=City.timezone)
|
|
|
|
sunrise = s['sunrise'].strftime("%I %M %p")
|
|
sunset = s['sunset'].strftime("%I %M %p")
|
|
parts.append(f"Sunrise is at {sunrise}. Sunset is at {sunset}.")
|
|
|
|
# Moon phase: 0..27
|
|
ph = phase(datetime.now())
|
|
moon_desc = self._describe_phase(ph)
|
|
parts.append(f"The moon is {moon_desc}.")
|
|
|
|
# Solar Flux
|
|
sfi_msg = self.get_solar_flux()
|
|
if sfi_msg:
|
|
parts.append(sfi_msg)
|
|
|
|
return " ".join(parts)
|
|
|
|
except Exception as e:
|
|
# logger.error(f"Astro error: {e}")
|
|
return ""
|
|
|
|
def get_solar_flux(self) -> str:
|
|
"""
|
|
Fetches the latest Solar Flux Index (SFI) from NOAA SWPC.
|
|
"SFI taken (date time) from the Penticton Radio Observatory in Penticton, British Columbia,
|
|
as reported by the National Weather Service Space Weather Prediction Center."
|
|
"""
|
|
import requests
|
|
from dateutil.parser import parse as parse_date
|
|
import pytz
|
|
|
|
url = "https://services.swpc.noaa.gov/products/summary/10cm-flux.json"
|
|
try:
|
|
resp = requests.get(url, timeout=5)
|
|
resp.raise_for_status()
|
|
data = resp.json()
|
|
# Expected format: {"Flux": "165", "Time": "2025-01-29 18:00"} OR similar
|
|
|
|
flux = data.get('Flux')
|
|
timestamp_str = data.get('TimeStamp') # Verify key case
|
|
|
|
if not flux or not timestamp_str:
|
|
return ""
|
|
|
|
# Parse time
|
|
# Timestamp is usually UTC.
|
|
dt = parse_date(timestamp_str)
|
|
# Format: Month Day, Hour Minute UTC?
|
|
# Or just "Date Time" as user asked? User said "SFI taken (date time)..."
|
|
# Let's format nicely: "January 29 at 10 00 UTC"
|
|
|
|
# Ensure it is treated as UTC if naive
|
|
if dt.tzinfo is None:
|
|
dt = dt.replace(tzinfo=pytz.UTC)
|
|
|
|
dt_fmt = dt.strftime("%B %d at %H %M UTC")
|
|
|
|
return (
|
|
f"Solar Flux Index is {flux}. "
|
|
f"S F I taken {dt_fmt} from the Penticton Radio Observatory in Penticton, British Columbia, "
|
|
f"as reported by the National Weather Service Space Weather Prediction Center."
|
|
)
|
|
|
|
except Exception as e:
|
|
# Fail silently for SFI
|
|
return ""
|
|
|
|
def _describe_phase(self, day: float) -> str:
|
|
if day < 1: return "New"
|
|
if day < 7: return "Waxing Crescent"
|
|
if day < 8: return "First Quarter"
|
|
if day < 14: return "Waxing Gibbous"
|
|
if day < 15: return "Full"
|
|
if day < 21: return "Waning Gibbous"
|
|
if day < 22: return "Last Quarter"
|
|
return "Waning Crescent"
|