SergeyO7 commited on
Commit
e7652e0
·
verified ·
1 Parent(s): f08be6b

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +103 -450
app.py CHANGED
@@ -1,500 +1,153 @@
1
- from smolagents import CodeAgent, DuckDuckGoSearchTool, HfApiModel, load_tool, tool
2
- import smolagents # Added for aliasing
3
- # from smolagents.security import E2BSandbox
4
- import datetime
5
- import pytz
6
- import yaml
7
- from skyfield.api import load, Topos, load_file
8
- from skyfield import almanac
9
- from tools.final_answer import FinalAnswerTool
10
- from Gradio_UI import GradioUI
11
- import os
12
- import base64
13
-
14
- # Add the alias before instrumentation
15
- smolagents.ApiModel = smolagents.HfApiModel
16
-
17
- LANGFUSE_PUBLIC_KEY="pk-lf-133099c7-8644-49e8-8f6e-ec8bd6d543fd"
18
- LF_SECRET_KEY = os.environ["LANGFUSE_SECRET_KEY"]
19
- LANGFUSE_AUTH=base64.b64encode(f"{LANGFUSE_PUBLIC_KEY}:{LF_SECRET_KEY}".encode()).decode()
20
-
21
- os.environ["OTEL_EXPORTER_OTLP_ENDPOINT"] = "https://cloud.langfuse.com/api/public/otel" # EU data region
22
- # os.environ["OTEL_EXPORTER_OTLP_ENDPOINT"] = "https://us.cloud.langfuse.com/api/public/otel" # US data region
23
- os.environ["OTEL_EXPORTER_OTLP_HEADERS"] = f"Authorization=Basic {LANGFUSE_AUTH}"
24
-
25
- from opentelemetry.sdk.trace import TracerProvider
26
- from openinference.instrumentation.smolagents import SmolagentsInstrumentor
27
- from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
28
- from opentelemetry.sdk.trace.export import SimpleSpanProcessor
29
-
30
- trace_provider = TracerProvider()
31
- trace_provider.add_span_processor(SimpleSpanProcessor(OTLPSpanExporter()))
32
-
33
- SmolagentsInstrumentor().instrument(tracer_provider=trace_provider)
34
-
35
- # Load ephemeris and timescale
36
- planets = load('https://naif.jpl.nasa.gov/pub/naif/generic_kernels/spk/planets/de440.bsp')
37
- ts = load.timescale()
38
-
39
- # Define Zodiac signs and their boundaries (0° to 360° ecliptic longitude)
40
- ZODIAC_SIGNS = [
41
- ("Aries", 0, 30),
42
- ("Taurus", 30, 60),
43
- ("Gemini", 60, 90),
44
- ("Cancer", 90, 120),
45
- ("Leo", 120, 150),
46
- ("Virgo"
47
- , 150, 180),
48
- ("Libra", 180, 210),
49
- ("Scorpio", 210, 240),
50
- ("Sagittarius", 240, 270),
51
- ("Capricorn", 270, 300),
52
- ("Aquarius", 300, 330),
53
- ("Pisces", 330, 360),
54
- ]
55
-
56
- # Moon phase boundaries (0° to 360° phase angle) for display purposes
57
- MOON_PHASES = [
58
- ("New Moon", 0, 45),
59
- ("Waxing Crescent", 45, 90),
60
- ("First Quarter", 90, 135),
61
- ("Waxing Gibbous", 135, 180),
62
- ("Full Moon", 180, 225),
63
- ("Waning Gibbous", 225, 270),
64
- ("Last Quarter", 270, 315),
65
- ("Waning Crescent", 315, 360),
66
- ]
67
-
68
- # Fertility sign coefficients (applicable to all plants)
69
- FERTILITY_SIGN_COEFFS = {
70
- "Aries": 1,
71
- "Taurus": 2,
72
- "Gemini": 0,
73
- "Cancer": 2,
74
- "Leo": 1,
75
- "Virgo": 0,
76
- "Libra": 0.5,
77
- "Scorpio": 1.5,
78
- "Sagittarius": 1,
79
- "Capricorn": 1,
80
- "Aquarius": 0,
81
- "Pisces": 2,
82
- }
83
-
84
- # Pruning sign coefficients (applicable to all plants)
85
- PRUNING_SIGN_COEFFS = {
86
- "Aries": 1,
87
- "Taurus": 0,
88
- "Gemini": 2,
89
- "Cancer": 0,
90
- "Leo": 1,
91
- "Virgo": 2,
92
- "Libra": 1.5,
93
- "Scorpio": 0.5,
94
- "Sagittarius": 1,
95
- "Capricorn": 1,
96
- "Aquarius": 2,
97
- "Pisces": 0,
98
- }
99
-
100
- # Fertility phase coefficients for above-ground plants
101
- FERTILITY_PHASE_COEFFS_ABOVE = {
102
- "New Moon": 0,
103
- "Waxing Moon": 1,
104
- "Full Moon": 0,
105
- "Waning Moon": 0.5,
106
- }
107
-
108
- # Fertility phase coefficients for root crops
109
- FERTILITY_PHASE_COEFFS_ROOT = {
110
- "New Moon": 0,
111
- "Waxing Moon": 0.5,
112
- "Full Moon": 0,
113
- "Waning Moon": 1,
114
- }
115
-
116
- # Pruning phase coefficients
117
- PRUNING_PHASE_COEFFS = {
118
- "New Moon": 0,
119
- "Waxing Moon": 1,
120
- "Full Moon": 0,
121
- "Waning Moon": 0.5,
122
- }
123
-
124
- # Translation dictionaries
125
- classification_ru = {
126
- 'Swallowed': 'проглоченная',
127
- 'Tiny': 'сверхмалая',
128
- 'Small': 'малая',
129
- 'Normal': 'нормальная',
130
- 'Ideal': 'идеальная',
131
- 'Big': 'большая'
132
- }
133
-
134
  planet_ru = {
135
- 'Sun': 'Солнце',
136
- 'Moon': 'Луна',
137
- 'Mercury': 'Меркурий',
138
- 'Venus': 'Венера',
139
- 'Mars': 'Марс',
140
- 'Jupiter': 'Юпитер',
141
- 'Saturn': 'Сатурн'
142
  }
143
 
 
144
  planet_symbols = {
145
- 'Sun': '☉',
146
- 'Moon': '',
147
- 'Mercury': '☿',
148
- 'Venus': '♀',
149
- 'Mars': '♂',
150
- 'Jupiter': '♃',
151
- 'Saturn': '♄'
152
  }
153
 
154
-
155
-
156
- @tool
157
- def get_moon_info(date_time: str) -> dict:
158
- """
159
- Returns Moon's Zodiac position, phase, and fertility and pruning indices for the given date/time.
160
-
161
- The fertility and pruning indices are calculated as sum of sign and phase fertility values of the Moon position. Moon sign fertility
162
- amounts up to 2.0 value and phase fertility value could be 1.0 max.
163
- It is observed that when Moon is in different Zodiac signs, the fertility of new plants and impact of pruning differs.
164
- When Moon is in fertile sign the plant is in the active phase, when all processes are particularly intense, and any intervention
165
- such as pruning can be very traumatic for the plant. Here:
166
- Most fertile signs: Taurus, Pisces, Cancer - Plants are in the active growth phase, juices and nutrients actively circulate
167
- in the plant, and it is best time for fertilizers, harvasting cutting, vaccination, rooting.
168
- Conditionally fertile: Scorpio
169
- Neutral: Aries, Leo, Sagittarius, Capricorn
170
- Conditionally sterile: Libra
171
- Sterile: Gemini, Virgo, Aquarius
172
-
173
- Fertility indices ranges from 0.0 to 3.0 where proportionaly
174
- 0 - minimal expected fertility
175
- 3.0 - most favorable fertility for platining,
176
- and depends on type of plant (root crop or produce above ground).
177
-
178
- Pruning indices ranges from 0 to 3 where proportionaly:
179
- 0 - pruning is not recommended as it causes most damage to tree and can lead to:
180
- Increased sap production from the cut points
181
- Increased vulnerability to infections
182
- Delayed wound healing
183
- Possible weakening of the plant.
184
- Instead of pruning into fertile signs, you can do:
185
- Crown formation
186
- Pinching the shoots
187
- Removing dead branches
188
- Sanitary treatment
189
- 1.0 - pruning is not recommended,
190
- 2.0 - allowed only minimum or sanitary pruning,
191
- 3.0 - most favorable time for pruning.
192
-
193
- Args:
194
- date_time (str): ISO 8601 formatted datetime (YYYY-MM-DDTHH:MM:SS)
195
-
196
- Returns:
197
- dict: {
198
- "zodiac_position": "Leo 15°30'",
199
- "moon_phase": "Waxing Gibbous",
200
- "fertility_above_ground": 2.0,
201
- "fertility_root_crop": 1.5,
202
- "pruning": 2.0
203
- }
204
- """
205
- try:
206
- # Parse input datetime and localize to UTC
207
- user_time = datetime.datetime.strptime(date_time, "%Y-%m-%dT%H:%M:%S")
208
- user_time = pytz.utc.localize(user_time)
209
-
210
- # Use loaded ephemeris and timescale
211
- t = ts.from_datetime(user_time)
212
-
213
- # Define celestial bodies
214
- earth = planets['earth']
215
- moon = planets['moon']
216
- sun = planets['sun']
217
-
218
- # Calculate Moon's ecliptic longitude
219
- astrometric = earth.at(t).observe(moon)
220
- ecliptic_lat, ecliptic_lon, distance = astrometric.ecliptic_latlon()
221
- lon_deg = ecliptic_lon.degrees % 360
222
-
223
- # Calculate the phase angle using almanac.moon_phase
224
- phase = almanac.moon_phase(planets, t)
225
- phase_angle = phase.degrees
226
-
227
- # Determine Zodiac sign and position
228
- zodiac_sign = "Unknown"
229
- position_degrees = 0
230
- for sign, start, end in ZODIAC_SIGNS:
231
- if start <= lon_deg < end:
232
- zodiac_sign = sign
233
- position_degrees = lon_deg - start
234
- break
235
-
236
- # Format position to degrees and minutes
237
- degrees = int(position_degrees)
238
- minutes = int((position_degrees % 1) * 60)
239
- position_str = f"{zodiac_sign} {degrees}°{minutes:02}'"
240
-
241
- # Determine moon phase for display
242
- moon_phase = "Unknown"
243
- for phase, start, end in MOON_PHASES:
244
- if start <= phase_angle < end:
245
- moon_phase = phase
246
- break
247
-
248
- # Determine phase category for indices with 15° orbis for New and Full Moon
249
- if (phase_angle >= 345 or phase_angle < 15):
250
- phase_category = "New Moon" # 345° to 15° (30° total orbis)
251
- elif 15 <= phase_angle < 165:
252
- phase_category = "Waxing Moon"
253
- elif 165 <= phase_angle < 195:
254
- phase_category = "Full Moon" # 165° to 195° (30° total orbis)
255
- elif 195 <= phase_angle < 345:
256
- phase_category = "Waning Moon"
257
- else:
258
- phase_category = "Unknown"
259
-
260
- # Calculate fertility and pruning indices
261
- if zodiac_sign in FERTILITY_SIGN_COEFFS and phase_category in FERTILITY_PHASE_COEFFS_ABOVE:
262
- fertility_above_ground = FERTILITY_SIGN_COEFFS[zodiac_sign] + FERTILITY_PHASE_COEFFS_ABOVE[phase_category]
263
- fertility_root_crop = FERTILITY_SIGN_COEFFS[zodiac_sign] + FERTILITY_PHASE_COEFFS_ROOT[phase_category]
264
- pruning = PRUNING_SIGN_COEFFS[zodiac_sign] + PRUNING_PHASE_COEFFS[phase_category]
265
- else:
266
- fertility_above_ground = None
267
- fertility_root_crop = None
268
- pruning = None
269
-
270
- return {
271
- "zodiac_position": position_str,
272
- "moon_phase": moon_phase,
273
- "fertility_above_ground": fertility_above_ground,
274
- "fertility_root_crop": fertility_root_crop,
275
- "pruning": pruning
276
- }
277
-
278
- except Exception as e:
279
- raise ValueError(f"Error in get_moon_info: {str(e)}")
280
-
281
- @tool
282
- def get_current_time_in_timezone(timezone: str) -> str:
283
- """
284
- Returns the current local time in the specified timezone with description.
285
-
286
- Args:
287
- timezone (str): A string representing a valid timezone (e.g., 'UTC')
288
-
289
-
290
- Returns:
291
- str: Formatted local time with timezone description
292
- """
293
- try:
294
- tz = pytz.timezone(timezone)
295
- now = datetime.datetime.now(tz)
296
- return f"Local time in {timezone}: {now.strftime('%Y-%m-%d %H:%M:%S')}"
297
- except Exception as e:
298
- return f"Error: {str(e)}"
299
-
300
- @tool
301
- def get_current_time_raw(timezone: str) -> str:
302
- """
303
- Returns current local time in specified timezone as ISO 8601 string.
304
-
305
- Args:
306
- timezone (str): A string representing a valid timezone (e.g., 'UTC')
307
-
308
- Returns:
309
- str: Datetime in ISO 8601 format (YYYY-MM-DDTHH:MM:SS)
310
- """
311
  try:
312
- tz = pytz.timezone(timezone)
313
- now = datetime.datetime.now(tz)
314
- return now.strftime("%Y-%m-%dT%H:%M:%S")
315
- except Exception as e:
316
- return f"Error: {str(e)}"
317
 
318
- # Model configuration
319
- final_answer = FinalAnswerTool()
320
- model = HfApiModel(
321
- max_tokens=2096,
322
- temperature=0.5,
323
- model_id="https://pflgm2locj2t89co.us-east-1.aws.endpoints.huggingface.cloud/",
324
- custom_role_conversions=None,
325
- )
326
 
327
- # Function to calculate PLadder and zone sizes
328
- @tool
329
- def PLadder_ZSizes(date_time_iso: str):
330
- """
331
- Calculate the planetary ladder and zone sizes for a given date and time.
332
-
333
- Args:
334
- date_time_iso (str): Date and time in ISO format (e.g., '2023-10-10T12:00:00')
335
-
336
- Returns:
337
- dict: Contains 'PLadder' (list of planets) and 'ZSizes' (list of zone sizes with classifications)
338
- or an error message if unsuccessful
339
- """
340
  try:
341
  dt = datetime.fromisoformat(date_time_iso)
342
- if dt.year < 1900 or dt.year > 2050:
343
- return {"error": "Дата вне диапазона. Должна быть между 1900 и 2050 годами."}
344
 
345
- # Load ephemeris
346
  planets = load('de421.bsp')
347
  earth = planets['earth']
 
348
 
349
- # Define planet objects
350
  planet_objects = {
351
- 'Sun': planets['sun'],
352
- 'Moon': planets['moon'],
353
- 'Mercury': planets['mercury'],
354
- 'Venus': planets['venus'],
355
- 'Mars': planets['mars'],
356
- 'Jupiter': planets['jupiter barycenter'],
357
- 'Saturn': planets['saturn barycenter']
358
  }
359
 
360
- # Create time object
361
  ts = load.timescale()
362
  t = ts.utc(dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second)
363
 
364
- # Compute ecliptic longitudes
365
  longitudes = {}
366
- for planet in planet_objects:
367
- apparent = earth.at(t).observe(planet_objects[planet]).apparent()
368
- _, lon, _ = apparent.ecliptic_latlon()
369
  longitudes[planet] = lon.degrees
370
 
371
- # Sort planets by longitude to form PLadder
372
  sorted_planets = sorted(longitudes.items(), key=lambda x: x[1])
373
  PLadder = [p for p, _ in sorted_planets]
374
  sorted_lons = [lon for _, lon in sorted_planets]
375
 
376
- # Calculate zone sizes
377
  zone_sizes = [sorted_lons[0]] + [sorted_lons[i+1] - sorted_lons[i] for i in range(6)] + [360 - sorted_lons[6]]
378
-
379
- # Determine bordering planets for classification
380
  bordering = [[PLadder[0]]] + [[PLadder[i-1], PLadder[i]] for i in range(1, 7)] + [[PLadder[6]]]
381
 
382
- # Classify each zone
383
  ZSizes = []
384
  for i, size in enumerate(zone_sizes):
385
  bord = bordering[i]
386
- if any(p in ['Sun', 'Moon'] for p in bord):
387
- X = 7
388
- elif any(p in ['Mercury', 'Venus', 'Mars'] for p in bord):
389
- X = 6
390
- else:
391
- X = 5
392
-
393
- if size <= 1:
394
- classification = 'Swallowed'
395
- elif size <= X:
396
- classification = 'Tiny'
397
- elif size <= 40:
398
- classification = 'Small'
399
- elif size < 60:
400
- if 50 <= size <= 52:
401
- classification = 'Ideal'
402
- else:
403
- classification = 'Normal'
404
- else:
405
- classification = 'Big'
406
-
407
- # Convert size to degrees and minutes
408
- d = int(size)
409
- m = int((size - d) * 60)
410
- size_str = f"{d}°{m}'"
411
- ZSizes.append((size_str, classification))
412
 
413
- return {'PLadder': PLadder, 'ZSizes': ZSizes}
414
 
415
  except ValueError:
416
- return {"error": "Неверный формат даты и времени. Используйте ISO формат, например, '2023-10-10T12:00:00'"}
417
  except Exception as e:
418
- return {"error": f"Ошибка при вычислении: {str(e)}"}
419
 
420
-
421
- # Function to parse date and time into ISO format
422
- def parse_date_time(date_time_str):
423
- try:
424
- dt = parser.parse(date_time_str)
425
- return dt.isoformat()
426
- except ValueError:
427
- return None
428
-
429
- # Function to convert longitude to zodiac sign and degrees
430
- def lon_to_sign(lon):
431
- signs = ["Овен", "Телец", "Близнецы", "Рак", "Лев", "Дева",
432
- "Весы", "Скорпион", "Стрелец", "Козерог", "Водолей", "Рыбы"]
433
- sign_index = int(lon // 30)
434
- sign = signs[sign_index]
435
- degrees = int(lon % 30)
436
- minutes = int((lon % 1) * 60)
437
- return f"{sign} {degrees}°{minutes}'"
438
-
439
-
440
- @tool
441
  def plot_pladder(PLadder):
442
- """
443
- Plot the planetary ladder as a right triangle with planet symbols.
444
-
445
- Args:
446
- PLadder (list): List of planet names in order
447
-
448
- Returns:
449
- matplotlib.figure.Figure: The generated plot
450
- """
451
  fig, ax = plt.subplots()
452
- # Plot triangle with right angle on top: vertices at (0,0), (1.5,3), (3,0)
453
- ax.plot([0, 1.5, 3, 0], [0, 3, 0, 0], 'k-')
454
- # Draw horizontal lines dividing height into three equal parts
455
- ax.plot([0, 3], [1, 1], 'k--')
456
  ax.plot([0, 3], [2, 2], 'k--')
457
- # Define positions for planets 1 to 7, adjusted to avoid overlap
458
  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)]
459
- for i, pos in enumerate(positions):
460
- symbol = planet_symbols[PLadder[i]]
461
- ax.text(pos[0], pos[1], symbol, ha='center', va='center', fontsize=24) # Doubled font size
462
  ax.set_xlim(-0.5, 3.5)
463
  ax.set_ylim(-0.5, 3.5)
464
  ax.set_aspect('equal')
465
  ax.axis('off')
466
  return fig
467
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
468
 
469
-
470
- # Load image tool from Hub
471
- image_generation_tool = load_tool("agents-course/text-to-image", trust_remote_code=True)
472
-
473
- # Load prompt templates
474
- with open("prompts.yaml", 'r') as stream:
475
- prompt_templates = yaml.safe_load(stream)
476
-
477
- # Initialize agent with all tools
478
- agent = CodeAgent(
479
- model=model,
480
- tools=[final_answer, get_moon_info, get_current_time_in_timezone, get_current_time_raw, plot_pladder, PLadder_ZSizes, image_generation_tool],
481
- max_steps=6,
482
- verbosity_level=1,
483
- prompt_templates=prompt_templates
484
- # execution_env=E2BSandbox(
485
- # allowed_imports=["numpy", "pandas"], # Explicitly permitted packages
486
- # blocked_imports=["subprocess"], # Prevent system access
487
- # ),
488
- # safe_mode=True, # Enable safe code execution
489
- # timeout=10, # Seconds before execution timeout
490
- # max_memory=512, # MB memory limit
491
- # file_system_access=False, # Disable disk write access
492
- # network_access=False, # Block network operations
493
- # max_code_iterations=100, # Prevent infinite loops
494
- )
495
-
496
- if __name__ == "__main__":
497
- GradioUI(agent).launch()
498
-
499
- # Change to your username and repo name
500
- # agent.push_to_hub('sergeyo7/Garden_Magus')
 
1
+ import gradio as gr
2
+ import matplotlib.pyplot as plt
3
+ from skyfield.api import load, Topos
4
+ from datetime import datetime
5
+ from dateutil import parser
6
+ from io import BytesIO
7
+ from PIL import Image
8
+
9
+ # Russian translations for planets
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  planet_ru = {
11
+ 'Sun': 'Солнце', 'Moon': 'Луна', 'Mercury': 'Меркурий', 'Venus': 'Венера',
12
+ 'Mars': 'Марс', 'Jupiter': 'Юпитер', 'Saturn': 'Сатурн'
 
 
 
 
 
13
  }
14
 
15
+ # Planet symbols for plotting
16
  planet_symbols = {
17
+ 'Sun': '☉', 'Moon': '☾', 'Mercury': '☿', 'Venus': '♀',
18
+ 'Mars': '', 'Jupiter': '♃', 'Saturn': '♄'
 
 
 
 
 
19
  }
20
 
21
+ # Parse date-time into ISO format
22
+ def parse_date_time(date_time_str):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
  try:
24
+ dt = parser.parse(date_time_str)
25
+ return dt.isoformat()
26
+ except ValueError:
27
+ return None
 
28
 
29
+ # Convert longitude to zodiac sign and degrees
30
+ def lon_to_sign(lon):
31
+ signs = ["Овен", "Телец", "Близнецы", "Рак", "Лев", "Дева",
32
+ "Весы", "Скорпион", "Стрелец", "Козерог", "Водолей", "Рыбы"]
33
+ sign_index = int(lon // 30)
34
+ degrees = int(lon % 30)
35
+ minutes = int((lon % 1) * 60)
36
+ return f"{signs[sign_index]} {degrees}°{minutes}'"
37
 
38
+ # Calculate PLadder and zone sizes
39
+ def PLadder_ZSizes(date_time_iso, lat, lon):
 
 
 
 
 
 
 
 
 
 
 
40
  try:
41
  dt = datetime.fromisoformat(date_time_iso)
42
+ if not 1900 <= dt.year <= 2050:
43
+ return {"error": "Дата вне диапазона (19002050)."}
44
 
 
45
  planets = load('de421.bsp')
46
  earth = planets['earth']
47
+ observer = earth + Topos(latitude_degrees=float(lat), longitude_degrees=float(lon))
48
 
 
49
  planet_objects = {
50
+ 'Sun': planets['sun'], 'Moon': planets['moon'], 'Mercury': planets['mercury'],
51
+ 'Venus': planets['venus'], 'Mars': planets['mars'],
52
+ 'Jupiter': planets['jupiter barycenter'], 'Saturn': planets['saturn barycenter']
 
 
 
 
53
  }
54
 
 
55
  ts = load.timescale()
56
  t = ts.utc(dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second)
57
 
 
58
  longitudes = {}
59
+ for planet, obj in planet_objects.items():
60
+ astrometric = observer.at(t).observe(obj)
61
+ _, lon, _ = astrometric.apparent().ecliptic_latlon()
62
  longitudes[planet] = lon.degrees
63
 
 
64
  sorted_planets = sorted(longitudes.items(), key=lambda x: x[1])
65
  PLadder = [p for p, _ in sorted_planets]
66
  sorted_lons = [lon for _, lon in sorted_planets]
67
 
 
68
  zone_sizes = [sorted_lons[0]] + [sorted_lons[i+1] - sorted_lons[i] for i in range(6)] + [360 - sorted_lons[6]]
 
 
69
  bordering = [[PLadder[0]]] + [[PLadder[i-1], PLadder[i]] for i in range(1, 7)] + [[PLadder[6]]]
70
 
 
71
  ZSizes = []
72
  for i, size in enumerate(zone_sizes):
73
  bord = bordering[i]
74
+ 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
75
+ classification = ('Swallowed' if size <= 1 else 'Tiny' if size <= X else 'Small' if size <= 40 else
76
+ 'Ideal' if 50 <= size <= 52 else 'Normal' if size < 60 else 'Big')
77
+ d, m = int(size), int((size - int(size)) * 60)
78
+ ZSizes.append((f"{d}°{m}'", classification))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79
 
80
+ return {'PLadder': PLadder, 'ZSizes': ZSizes, 'longitudes': longitudes}
81
 
82
  except ValueError:
83
+ return {"error": "Неверный формат даты и времени."}
84
  except Exception as e:
85
+ return {"error": f"Ошибка: {str(e)}"}
86
 
87
+ # Plot the planetary ladder
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
  def plot_pladder(PLadder):
 
 
 
 
 
 
 
 
 
89
  fig, ax = plt.subplots()
90
+ ax.plot([0, 1.5, 3, 0], [0, 3, 0, 0], 'k-') # Triangle
91
+ ax.plot([0, 3], [1, 1], 'k--') # Horizontal lines
 
 
92
  ax.plot([0, 3], [2, 2], 'k--')
 
93
  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)]
94
+ for i, (x, y) in enumerate(positions):
95
+ ax.text(x, y, planet_symbols[PLadder[i]], ha='center', va='center', fontsize=24)
 
96
  ax.set_xlim(-0.5, 3.5)
97
  ax.set_ylim(-0.5, 3.5)
98
  ax.set_aspect('equal')
99
  ax.axis('off')
100
  return fig
101
 
102
+ # Main interface function
103
+ def chat_interface(query, lat, lon):
104
+ if not query.startswith("PLadder "):
105
+ return "Запрос должен начинаться с 'PLadder' и содержать дату/время.", None
106
+
107
+ date_time_str = query.split(" ", 1)[1]
108
+ date_time_iso = parse_date_time(date_time_str)
109
+ if not date_time_iso:
110
+ return "Неверный формат даты и времени.", None
111
+
112
+ result = PLadder_ZSizes(date_time_iso, lat, lon)
113
+ if "error" in result:
114
+ return result["error"], None
115
+
116
+ PLadder = result["PLadder"]
117
+ ZSizes = result["ZSizes"]
118
+ longitudes = result["longitudes"]
119
+
120
+ planet_list = "\n".join([f"{planet_ru[p]}: {lon_to_sign(longitudes[p])}" for p in PLadder])
121
+ zones_text = "\n".join([f"Зона {i+1}: {size} ({cls})" for i, (size, cls) in enumerate(ZSizes)])
122
+
123
+ fig = plot_pladder(PLadder)
124
+ buf = BytesIO()
125
+ fig.savefig(buf, format='png', bbox_inches='tight')
126
+ buf.seek(0)
127
+ img = Image.open(buf)
128
+ plt.close(fig)
129
+
130
+ text = f"Планетарная лестница:\n{planet_list}\n\nРазмеры зон:\n{zones_text}"
131
+ return text, img
132
+
133
+ # Gradio UI
134
+ with gr.Blocks() as interface:
135
+ with gr.Row():
136
+ with gr.Column(scale=2):
137
+ output_text = gr.Textbox(label="Ответ", lines=10)
138
+ with gr.Column(scale=1):
139
+ output_image = gr.Image(label="График планетарной лестницы")
140
+ with gr.Row():
141
+ with gr.Column(scale=1):
142
+ query_text = gr.Textbox(label="Запрос", placeholder="Пример: PLadder 2023-10-10 12:00")
143
+ location_lat = gr.Textbox(label="Широта", placeholder="Пример: 37.7749")
144
+ location_lon = gr.Textbox(label="Долгота", placeholder="Пример: -122.4194")
145
+ submit_button = gr.Button("Отправить")
146
+
147
+ submit_button.click(
148
+ fn=chat_interface,
149
+ inputs=[query_text, location_lat, location_lon],
150
+ outputs=[output_text, output_image]
151
+ )
152
 
153
+ interface.launch()