import datetime import re from modules.plant import Plant from modules.weather_utils import did_or_will_rain, weather_values # ── 1. Watering frequency parser ─────────────────────────────────────────────── _RECOMMENDATION_MAP: dict[str, int] = { # ── Fixed cadence ────────────────────────────────────────────────────── "water weekly": 7, # ── Always moist ────────────────────────────────────────────────────── "keep soil moist": 2, "keep soil consistently moist": 2, "keep soil evenly moist": 2, "keep soil slightly moist": 3, "regular, moist soil": 3, # ── Regular ─────────────────────────────────────────────────────────── "regular watering": 4, "regular, well-drained soil": 4, # ── Water when dry ──────────────────────────────────────────────────── "water when soil is dry": 5, "water when topsoil is dry": 5, "water when soil feels dry": 5, "let soil dry between watering": 7, } _FALLBACK_RULES: list[tuple[str, int | None]] = [ (r"daily", 1), (r"twice.{0,10}week", 3), (r"every\s+(\d+)\s+day", None), # extracted dynamically (r"once.{0,10}week|weekly", 7), (r"once.{0,10}fortnight|every.{0,6}two.{0,6}week", 14), (r"monthly", 30), (r"moist", 2), (r"dry", 6), (r"regular", 4), ] DEFAULT_INTERVAL = 4 def _parse_watering_frequency(recommendation: str) -> int: """Map a free-text watering recommendation to a watering interval in days. Args: recommendation: e.g. "Water weekly", "Keep soil moist" Returns: Watering interval in days (>= 1). """ if not recommendation or not recommendation.strip(): return DEFAULT_INTERVAL cleaned = recommendation.strip().lower() if cleaned in _RECOMMENDATION_MAP: return _RECOMMENDATION_MAP[cleaned] for key, days in _RECOMMENDATION_MAP.items(): if cleaned.startswith(key) or key.startswith(cleaned): return days for pattern, days in _FALLBACK_RULES: m = re.search(pattern, cleaned) if m: if days is None: try: return max(1, int(m.group(1))) except (IndexError, ValueError): return DEFAULT_INTERVAL return days return DEFAULT_INTERVAL def get_watering_frequency(plant: Plant) -> int: """Determine the watering frequency for a plant, in days. Args: plant: The plant to determine the watering frequency for. Returns: Watering frequency in days (>= 1). """ if plant.watering_frequency: return _parse_watering_frequency(plant.watering_frequency) return DEFAULT_INTERVAL def should_water(plant: Plant,last_watered: datetime.date | None, date: datetime.date, LAT: float, LON: float) -> bool: """Determine if a plant should be watered on a given date. # returns true if its not raining and the plant wasnt watered since the last watering frequency interval Args: plant: The plant to check. last_watered: The date the plant was last watered. date: The date to check for. LAT: The latitude of the plant's location. LON: The longitude of the plant's location. """ if last_watered is None: return True frequency = get_watering_frequency(plant) # convert last_watered to datetime.date if it's a string if isinstance(last_watered, str): last_watered = datetime.datetime.strptime(last_watered, "%Y-%m-%d").date() next_watering_date = last_watered + datetime.timedelta(days=frequency) if date >= next_watering_date and not did_or_will_rain(date, LAT, LON, forecast_threshold=50): return True return False