Spaces:
Sleeping
Sleeping
| import json | |
| import gradio as gr | |
| import datetime | |
| import pytz | |
| from skyfield.api import load, Topos, load_file | |
| from skyfield import almanac | |
| # Load ephemeris and timescale | |
| planets = load('https://naif.jpl.nasa.gov/pub/naif/generic_kernels/spk/planets/de440.bsp') | |
| ts = load.timescale() | |
| # [Keep all your constants and coefficient dictionaries here - they remain unchanged] | |
| # Define Zodiac signs and their boundaries (0° to 360° ecliptic longitude) | |
| ZODIAC_SIGNS = [ | |
| ("Aries", 0, 30), | |
| ("Taurus", 30, 60), | |
| ("Gemini", 60, 90), | |
| ("Cancer", 90, 120), | |
| ("Leo", 120, 150), | |
| ("Virgo", 150, 180), | |
| ("Libra", 180, 210), | |
| ("Scorpio", 210, 240), | |
| ("Sagittarius", 240, 270), | |
| ("Capricorn", 270, 300), | |
| ("Aquarius", 300, 330), | |
| ("Pisces", 330, 360), | |
| ] | |
| # Moon phase boundaries (0° to 360° phase angle) for display purposes | |
| MOON_PHASES = [ | |
| ("New Moon", 0, 45), | |
| ("Waxing Crescent", 45, 90), | |
| ("First Quarter", 90, 135), | |
| ("Waxing Gibbous", 135, 180), | |
| ("Full Moon", 180, 225), | |
| ("Waning Gibbous", 225, 270), | |
| ("Last Quarter", 270, 315), | |
| ("Waning Crescent", 315, 360), | |
| ] | |
| # Fertility sign coefficients (applicable to all plants) | |
| FERTILITY_SIGN_COEFFS = { | |
| "Aries": 1, | |
| "Taurus": 2, | |
| "Gemini": 0, | |
| "Cancer": 2, | |
| "Leo": 1, | |
| "Virgo": 0, | |
| "Libra": 0.5, | |
| "Scorpio": 1.5, | |
| "Sagittarius": 1, | |
| "Capricorn": 1, | |
| "Aquarius": 0, | |
| "Pisces": 2, | |
| } | |
| # Pruning sign coefficients (applicable to all plants) | |
| PRUNING_SIGN_COEFFS = { | |
| "Aries": 1, | |
| "Taurus": 0, | |
| "Gemini": 2, | |
| "Cancer": 0, | |
| "Leo": 1, | |
| "Virgo": 2, | |
| "Libra": 1.5, | |
| "Scorpio": 0.5, | |
| "Sagittarius": 1, | |
| "Capricorn": 1, | |
| "Aquarius": 2, | |
| "Pisces": 0, | |
| } | |
| # Fertility phase coefficients for above-ground plants | |
| FERTILITY_PHASE_COEFFS_ABOVE = { | |
| "New Moon": 0, | |
| "Waxing Moon": 1, | |
| "Full Moon": 0, | |
| "Waning Moon": 0.5, | |
| } | |
| # Fertility phase coefficients for root crops | |
| FERTILITY_PHASE_COEFFS_ROOT = { | |
| "New Moon": 0, | |
| "Waxing Moon": 0.5, | |
| "Full Moon": 0, | |
| "Waning Moon": 1, | |
| } | |
| # Pruning phase coefficients | |
| PRUNING_PHASE_COEFFS = { | |
| "New Moon": 0, | |
| "Waxing Moon": 1, | |
| "Full Moon": 0, | |
| "Waning Moon": 0.5, | |
| } | |
| def get_moon_info(date_input) -> str: | |
| """ | |
| Returns Moon's Zodiac position, phase, and fertility and pruning indices for the given date/time. | |
| The fertility and pruning indices are calculated as sum of sign and phase fertility values of the Moon position. Moon sign fertility | |
| amounts up to 2.0 value and phase fertility value could be 1.0 max. | |
| It is observed that when Moon is in different Zodiac signs, the fertility of new plants and impact of pruning differs. | |
| When Moon is in fertile sign the plant is in the active phase, when all processes are particularly intense, and any intervention | |
| such as pruning can be very traumatic for the plant. | |
| Fertility indices ranges from 0.0 to 3.0 where proportionaly | |
| 0 - minimal expected fertility | |
| 3.0 - most favorable fertility for platining, | |
| and depends on type of plant (root crop or produce above ground). | |
| Pruning indices ranges from 0 to 3 where proportionaly: | |
| 0 - pruning is not recommended as it causes most damage to tree and can lead to: | |
| Increased sap production from the cut points | |
| Increased vulnerability to infections | |
| Delayed wound healing | |
| Possible weakening of the plant. | |
| Instead of pruning into fertile signs, you can do: | |
| Crown formation | |
| Pinching the shoots | |
| Removing dead branches | |
| Sanitary treatment | |
| 1.0 - pruning is not recommended, | |
| 2.0 - allowed only minimum or sanitary pruning, | |
| 3.0 - most favorable time for pruning. | |
| Args: | |
| date_input: Can be a datetime object, float (timestamp), or string, depending on Gradio configuration | |
| Returns: | |
| A JSON string containing zodiac_position, moon_phase, fertility_above_ground, fertility_root_crop, pruning | |
| """ | |
| try: | |
| # Handle different input types from Gradio DateTime component | |
| if isinstance(date_input, datetime.datetime): | |
| # Already a datetime object - ensure it's UTC | |
| if date_input.tzinfo is None: | |
| user_time = pytz.utc.localize(date_input) | |
| else: | |
| user_time = date_input.astimezone(pytz.UTC) | |
| elif isinstance(date_input, (float, int)): | |
| # Unix timestamp in milliseconds - convert to datetime | |
| user_time = datetime.datetime.fromtimestamp(date_input / 1000, pytz.UTC) | |
| elif isinstance(date_input, str): | |
| # String input - parse accordingly | |
| try: | |
| user_time = datetime.datetime.fromisoformat(date_input.replace('Z', '+00:00')) | |
| if user_time.tzinfo is None: | |
| user_time = pytz.utc.localize(user_time) | |
| else: | |
| user_time = user_time.astimezone(pytz.UTC) | |
| except (ValueError, AttributeError): | |
| raise ValueError(f"Unsupported input format: {date_input}") | |
| else: | |
| raise ValueError(f"Unsupported input type: {type(date_input)}") | |
| # Use loaded ephemeris and timescale | |
| t = ts.from_datetime(user_time) | |
| # Define celestial bodies | |
| earth = planets['earth'] | |
| moon = planets['moon'] | |
| # Calculate Moon's ecliptic longitude | |
| astrometric = earth.at(t).observe(moon) | |
| ecliptic_lat, ecliptic_lon, distance = astrometric.ecliptic_latlon() | |
| lon_deg = ecliptic_lon.degrees % 360 | |
| # Calculate the phase angle using almanac.moon_phase | |
| phase = almanac.moon_phase(planets, t) | |
| phase_angle = phase.degrees | |
| # Determine Zodiac sign and position | |
| zodiac_sign = "Unknown" | |
| position_degrees = 0 | |
| for sign, start, end in ZODIAC_SIGNS: | |
| if start <= lon_deg < end: | |
| zodiac_sign = sign | |
| position_degrees = lon_deg - start | |
| break | |
| # Format position to degrees and minutes | |
| degrees = int(position_degrees) | |
| minutes = int((position_degrees % 1) * 60) | |
| position_str = f"{zodiac_sign} {degrees}°{minutes:02}'" | |
| # Determine moon phase for display | |
| moon_phase = "Unknown" | |
| for phase, start, end in MOON_PHASES: | |
| if start <= phase_angle < end: | |
| moon_phase = phase | |
| break | |
| # Determine phase category for indices with 15° orbis for New and Full Moon | |
| if (phase_angle >= 345 or phase_angle < 15): | |
| phase_category = "New Moon" # 345° to 15° (30° total orbis) | |
| elif 15 <= phase_angle < 165: | |
| phase_category = "Waxing Moon" | |
| elif 165 <= phase_angle < 195: | |
| phase_category = "Full Moon" # 165° to 195° (30° total orbis) | |
| elif 195 <= phase_angle < 345: | |
| phase_category = "Waning Moon" | |
| else: | |
| phase_category = "Unknown" | |
| # Calculate fertility and pruning indices | |
| if zodiac_sign in FERTILITY_SIGN_COEFFS and phase_category in FERTILITY_PHASE_COEFFS_ABOVE: | |
| fertility_above_ground = FERTILITY_SIGN_COEFFS[zodiac_sign] + FERTILITY_PHASE_COEFFS_ABOVE[phase_category] | |
| fertility_root_crop = FERTILITY_SIGN_COEFFS[zodiac_sign] + FERTILITY_PHASE_COEFFS_ROOT[phase_category] | |
| pruning = PRUNING_SIGN_COEFFS[zodiac_sign] + PRUNING_PHASE_COEFFS[phase_category] | |
| else: | |
| fertility_above_ground = None | |
| fertility_root_crop = None | |
| pruning = None | |
| result = { | |
| "zodiac_position": position_str, | |
| "moon_phase": moon_phase, | |
| "fertility_above_ground": fertility_above_ground, | |
| "fertility_root_crop": fertility_root_crop, | |
| "pruning": pruning | |
| } | |
| return json.dumps(result) | |
| except Exception as e: | |
| print(f"Error: {str(e)}") | |
| raise ValueError(f"Error in get_moon_info: {str(e)}") | |
| # Create the Gradio interface with explicit type specification | |
| demo = gr.Interface( | |
| fn=get_moon_info, | |
| inputs=gr.DateTime( | |
| label="Date and Time", | |
| type="datetime", # Explicitly set to return datetime object | |
| timezone="UTC" | |
| ), | |
| outputs=gr.Textbox(), | |
| title="Garden Magus", | |
| description="Enter date and time to calculate Moon's position and phase, fertility and pruning indices." | |
| ) | |
| # Launch the interface | |
| if __name__ == "__main__": | |
| demo.launch(mcp_server=True) |