Spaces:
Sleeping
Sleeping
import gradio as gr | |
import matplotlib.pyplot as plt | |
from skyfield.api import load, Topos | |
from datetime import datetime | |
from dateutil import parser | |
from io import BytesIO | |
from PIL import Image | |
from langfuse import Langfuse | |
langfuse = Langfuse( | |
secret_key="sk-lf-f8fe856f-7569-4aec-9a08-dabbac9e83b9", | |
public_key="pk-lf-23dd0190-7c1d-4ac9-be62-9aaf1370ef6d", | |
host="https://cloud.langfuse.com" | |
) | |
# Russian translations for planets | |
planet_ru = { | |
'Sun': 'Солнце', 'Moon': 'Луна', 'Mercury': 'Меркурий', 'Venus': 'Венера', | |
'Mars': 'Марс', 'Jupiter': 'Юпитер', 'Saturn': 'Сатурн' | |
} | |
# Planet symbols for plotting | |
planet_symbols = { | |
'Sun': '☉', 'Moon': '☾', 'Mercury': '☿', 'Venus': '♀', | |
'Mars': '♂', 'Jupiter': '♃', 'Saturn': '♄' | |
} | |
# Parse date-time into ISO format | |
def parse_date_time(date_time_str): | |
try: | |
dt = parser.parse(date_time_str) | |
return dt.isoformat() | |
except ValueError: | |
return None | |
# Convert longitude to zodiac sign and degrees | |
def lon_to_sign(lon): | |
signs = ["Овен", "Телец", "Близнецы", "Рак", "Лев", "Дева", | |
"Весы", "Скорпион", "Стрелец", "Козерог", "Водолей", "Рыбы"] | |
sign_index = int(lon // 30) | |
degrees = int(lon % 30) | |
minutes = int((lon % 1) * 60) | |
return f"{signs[sign_index]} {degrees}°{minutes}'" | |
# Calculate PLadder and zone sizes | |
def PLadder_ZSizes(date_time_iso, lat, lon): | |
try: | |
dt = datetime.fromisoformat(date_time_iso) | |
if not 1900 <= dt.year <= 2050: | |
return {"error": "Дата вне диапазона (1900–2050)."} | |
planets = load('de421.bsp') | |
earth = planets['earth'] | |
observer = earth + Topos(latitude_degrees=float(lat), longitude_degrees=float(lon)) | |
planet_objects = { | |
'Sun': planets['sun'], 'Moon': planets['moon'], 'Mercury': planets['mercury'], | |
'Venus': planets['venus'], 'Mars': planets['mars'], | |
'Jupiter': planets['jupiter barycenter'], 'Saturn': planets['saturn barycenter'] | |
} | |
ts = load.timescale() | |
t = ts.utc(dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second) | |
longitudes = {} | |
for planet, obj in planet_objects.items(): | |
astrometric = observer.at(t).observe(obj) | |
_, lon, _ = astrometric.apparent().ecliptic_latlon() | |
longitudes[planet] = lon.degrees | |
sorted_planets = sorted(longitudes.items(), key=lambda x: x[1]) | |
PLadder = [p for p, _ in sorted_planets] | |
sorted_lons = [lon for _, lon in sorted_planets] | |
zone_sizes = [sorted_lons[0]] + [sorted_lons[i+1] - sorted_lons[i] for i in range(6)] + [360 - sorted_lons[6]] | |
bordering = [[PLadder[0]]] + [[PLadder[i-1], PLadder[i]] for i in range(1, 7)] + [[PLadder[6]]] | |
ZSizes = [] | |
for i, size in enumerate(zone_sizes): | |
bord = bordering[i] | |
X = 7 if any(p in ['Sun', 'Moon'] for p in bord) else 6 if any(p in ['Mercury', 'Venus', 'Mars'] for p in bord) else 5 | |
classification = ('Swallowed' if size <= 1 else 'Tiny' if size <= X else 'Small' if size <= 40 else | |
'Ideal' if 50 <= size <= 52 else 'Normal' if size < 60 else 'Big') | |
d, m = int(size), int((size - int(size)) * 60) | |
ZSizes.append((f"{d}°{m}'", classification)) | |
return {'PLadder': PLadder, 'ZSizes': ZSizes, 'longitudes': longitudes} | |
except ValueError: | |
return {"error": "Неверный формат даты и времени."} | |
except Exception as e: | |
return {"error": f"Ошибка: {str(e)}"} | |
# Plot the planetary ladder | |
def plot_pladder(PLadder): | |
fig, ax = plt.subplots() | |
ax.plot([0, 1.5, 3, 0], [0, 3, 0, 0], 'k-') # Triangle | |
ax.plot([0, 3], [1, 1], 'k--') # Horizontal lines | |
ax.plot([0, 3], [2, 2], 'k--') | |
positions = [(0.2, 0.2), (0.2, 1.2), (0.2, 2.2), (1.5, 3.2), (2.8, 2.2), (2.8, 1.2), (2.8, 0.2)] | |
for i, (x, y) in enumerate(positions): | |
ax.text(x, y, planet_symbols[PLadder[i]], ha='center', va='center', fontsize=24) | |
ax.set_xlim(-0.5, 3.5) | |
ax.set_ylim(-0.5, 3.5) | |
ax.set_aspect('equal') | |
ax.axis('off') | |
return fig | |
# Main interface function | |
def chat_interface(query, lat, lon): | |
if not query.startswith("PLadder "): | |
return "Запрос должен начинаться с 'PLadder' и содержать дату/время.", None | |
date_time_str = query.split(" ", 1)[1] | |
date_time_iso = parse_date_time(date_time_str) | |
if not date_time_iso: | |
return "Неверный формат даты и времени.", None | |
result = PLadder_ZSizes(date_time_iso, lat, lon) | |
if "error" in result: | |
return result["error"], None | |
PLadder = result["PLadder"] | |
ZSizes = result["ZSizes"] | |
longitudes = result["longitudes"] | |
planet_list = "\n".join([f"{planet_ru[p]}: {lon_to_sign(longitudes[p])}" for p in PLadder]) | |
zones_text = "\n".join([f"Зона {i+1}: {size} ({cls})" for i, (size, cls) in enumerate(ZSizes)]) | |
fig = plot_pladder(PLadder) | |
buf = BytesIO() | |
fig.savefig(buf, format='png', bbox_inches='tight') | |
buf.seek(0) | |
img = Image.open(buf) | |
plt.close(fig) | |
text = f"Планетарная лестница:\n{planet_list}\n\nРазмеры зон:\n{zones_text}" | |
return text, img | |
# Gradio UI | |
with gr.Blocks() as interface: | |
with gr.Row(): | |
with gr.Column(scale=2): | |
output_text = gr.Textbox(label="Ответ", lines=10) | |
with gr.Column(scale=1): | |
output_image = gr.Image(label="График планетарной лестницы") | |
with gr.Row(): | |
with gr.Column(scale=1): | |
query_text = gr.Textbox(label="Запрос", placeholder="Пример: PLadder 2023-10-10 12:00") | |
location_lat = gr.Textbox(label="Широта", placeholder="Пример: 37.7749") | |
location_lon = gr.Textbox(label="Долгота", placeholder="Пример: -122.4194") | |
submit_button = gr.Button("Отправить") | |
submit_button.click( | |
fn=chat_interface, | |
inputs=[query_text, location_lat, location_lon], | |
outputs=[output_text, output_image] | |
) | |
interface.launch() |