Spaces:
Runtime error
Runtime error
| 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 |