cryogenic22 commited on
Commit
9d63cb3
·
verified ·
1 Parent(s): 47e4146

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +197 -143
app.py CHANGED
@@ -1,149 +1,203 @@
1
-
2
- # astro_core.py
3
  """
4
- Core astrology calculation module.
5
- Handles birth chart calculations using flatlib.
6
  """
7
 
 
8
  from datetime import datetime
9
- import pytz
10
- from typing import Dict, Any
11
- from flatlib.datetime import Datetime
12
- from flatlib.geopos import GeoPos
13
- from flatlib.chart import Chart
14
-
15
- # Define our own constants
16
- ZODIAC_SIGNS = [
17
- 'Aries', 'Taurus', 'Gemini', 'Cancer',
18
- 'Leo', 'Virgo', 'Libra', 'Scorpio',
19
- 'Sagittarius', 'Capricorn', 'Aquarius', 'Pisces'
20
- ]
21
-
22
- class ChartCalculator:
23
- """Handles astrological calculations"""
24
-
25
- def __init__(self):
26
- self.planets = [
27
- 'Sun', 'Moon', 'Mercury', 'Venus', 'Mars',
28
- 'Jupiter', 'Saturn', 'Uranus', 'Neptune', 'Pluto'
29
- ]
30
-
31
- def _get_house_number(self, lon: float, chart: Chart) -> int:
32
- """
33
- Determine house number for a given longitude
34
- """
35
- house_lons = []
36
- for i in range(1, 13):
37
- try:
38
- house = chart.houses.get(i)
39
- house_lons.append(house.lon if house else 0)
40
- except (KeyError, AttributeError):
41
- house_lons.append(0)
42
-
43
- # Convert longitude to house number
44
- for i in range(12):
45
- next_i = (i + 1) % 12
46
- if (house_lons[i] <= lon < house_lons[next_i]) or \
47
- (house_lons[i] > house_lons[next_i] and # Handle zodiac wrap
48
- (lon >= house_lons[i] or lon < house_lons[next_i])):
49
- return i + 1
50
- return 1 # Default to first house if no match found
51
-
52
- def _normalize_longitude(self, lon: float) -> float:
53
- """Normalize longitude to 0-360 range"""
54
- return lon % 360
55
-
56
- def _get_zodiac_sign(self, longitude: float) -> str:
57
- """
58
- Get zodiac sign from longitude
59
- """
60
- sign_num = int(self._normalize_longitude(longitude) / 30)
61
- return ZODIAC_SIGNS[sign_num]
62
-
63
- def calculate_birth_chart(self,
64
- birth_datetime: datetime,
65
- latitude: float,
66
- longitude: float,
67
- timezone: str) -> Dict[str, Any]:
68
- """
69
- Calculate birth chart positions
70
- Returns a dictionary of planetary positions and house cusps
71
- """
72
- try:
73
- # Ensure timezone is valid
74
- tz = pytz.timezone(timezone)
75
- birth_datetime = tz.localize(birth_datetime) if birth_datetime.tzinfo is None else birth_datetime
76
-
77
- # Extract date and time components from birth_datetime
78
- date_str = birth_datetime.strftime('%Y/%m/%d')
79
- time_str = birth_datetime.strftime('%H:%M')
80
-
81
- # Convert to flatlib datetime
82
- date = Datetime(date_str, time_str, timezone)
83
- pos = GeoPos(latitude, longitude)
84
-
85
- # Calculate chart
86
- chart = Chart(date, pos)
87
-
88
- # Extract planetary positions
89
- positions = {}
90
- for planet in self.planets:
91
- try:
92
- obj = chart.get(planet)
93
- if obj:
94
- # Normalize longitude
95
- lon = self._normalize_longitude(obj.lon)
96
-
97
- # Get sign
98
- sign = self._get_zodiac_sign(lon)
99
-
100
- # Get house
101
- house = self._get_house_number(lon, chart)
102
-
103
- positions[planet] = {
104
- 'sign': sign,
105
- 'degrees': lon,
106
- 'house': house
107
- }
108
- except Exception as e:
109
- positions[planet] = {
110
- 'sign': 'Unknown',
111
- 'degrees': 0,
112
- 'house': 1,
113
- 'error': str(e)
114
- }
115
-
116
- # Extract house cusps
117
- houses = {}
118
- for i in range(1, 13):
119
- try:
120
- house = chart.houses.get(i)
121
- if house:
122
- lon = self._normalize_longitude(house.lon)
123
- houses[f'House_{i}'] = {
124
- 'sign': self._get_zodiac_sign(lon),
125
- 'degrees': lon
126
- }
127
- except Exception as e:
128
- houses[f'House_{i}'] = {
129
- 'sign': 'Unknown',
130
- 'degrees': 0,
131
- 'error': str(e)
132
- }
133
-
134
- return {
135
- 'planets': positions,
136
- 'houses': houses
137
- }
138
-
139
- except Exception as e:
140
  return {
141
- 'error': f"Failed to calculate birth chart: {str(e)}",
142
- 'planets': {},
143
- 'houses': {}
 
144
  }
145
-
146
- def get_planet_aspects(self, chart_data: Dict[str, Any]) -> Dict[str, list]:
147
- """Calculate major aspects between planets"""
148
- # TODO: Implement aspect calculation
149
- return {}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # app.py
 
2
  """
3
+ Streamlined Streamlit application for AI Astrology app with automatic timezone detection.
 
4
  """
5
 
6
+ import streamlit as st
7
  from datetime import datetime
8
+ from geopy.geocoders import Nominatim
9
+ from geopy.exc import GeocoderTimedOut, GeocoderServiceError
10
+ from timezonefinder import TimezoneFinder
11
+ from dotenv import load_dotenv
12
+ from astro_core import ChartCalculator # Assuming you have the astro_core.py file
13
+ from ai_interpreter import AstroAI # Assuming you have the ai_interpreter.py file
14
+
15
+ # Load environment variables (if you are using .env for API keys etc.)
16
+ load_dotenv()
17
+
18
+ def init_session_state():
19
+ """Initialize session state variables"""
20
+ if 'birth_chart' not in st.session_state:
21
+ st.session_state.birth_chart = None
22
+ if 'chat_history' not in st.session_state:
23
+ st.session_state.chat_history = []
24
+ if 'user_info' not in st.session_state:
25
+ st.session_state.user_info = None
26
+ if 'calculation_error' not in st.session_state:
27
+ st.session_state.calculation_error = None
28
+
29
+ def get_location_details(location_name: str) -> dict:
30
+ """Get coordinates and timezone from location name"""
31
+ try:
32
+ # Initialize geocoder and timezone finder
33
+ geolocator = Nominatim(user_agent="ai_astrology_app")
34
+ tf = TimezoneFinder()
35
+
36
+ # Get location
37
+ location = geolocator.geocode(location_name)
38
+ if location:
39
+ latitude, longitude = location.latitude, location.longitude
40
+
41
+ # Get timezone string for the coordinates
42
+ timezone_str = tf.timezone_at(lng=longitude, lat=latitude)
43
+ if not timezone_str:
44
+ timezone_str = 'UTC' # Fallback to UTC if timezone not found
45
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
  return {
47
+ 'latitude': latitude,
48
+ 'longitude': longitude,
49
+ 'timezone': timezone_str,
50
+ 'error': None
51
  }
52
+ return {'error': 'Location not found'}
53
+ except (GeocoderTimedOut, GeocoderServiceError) as e:
54
+ return {'error': f'Geocoding error: {str(e)}'}
55
+ except Exception as e:
56
+ return {'error': f'Unexpected error: {str(e)}'}
57
+
58
+ def calculate_chart(user_data: dict):
59
+ """Calculate birth chart based on user data"""
60
+ try:
61
+ calculator = ChartCalculator()
62
+ birth_datetime = datetime.combine(user_data['birth_date'], user_data['birth_time'])
63
+
64
+ chart_data = calculator.calculate_birth_chart(
65
+ birth_datetime,
66
+ user_data['latitude'],
67
+ user_data['longitude'],
68
+ user_data['timezone']
69
+ )
70
+
71
+ # Check for calculation errors
72
+ if 'error' in chart_data:
73
+ st.session_state.calculation_error = chart_data['error']
74
+ return None
75
+
76
+ return chart_data
77
+ except Exception as e:
78
+ st.session_state.calculation_error = str(e)
79
+ return None
80
+
81
+ def main():
82
+ st.title("AI Astrology Assistant")
83
+
84
+ # Initialize session state
85
+ init_session_state()
86
+
87
+ # User Information Form
88
+ with st.form("user_info_form"):
89
+ st.subheader("Personal Information")
90
+
91
+ col1, col2 = st.columns(2)
92
+ with col1:
93
+ name = st.text_input("Full Name")
94
+ gender = st.selectbox("Gender", ["Male", "Female", "Non-binary", "Prefer not to say"])
95
+ birth_date = st.date_input("Birth Date", min_value=datetime(1900, 1, 1))
96
+
97
+ with col2:
98
+ birth_time = st.time_input("Birth Time")
99
+ birth_place = st.text_input("Birth Place (City, Country)")
100
+
101
+ submit_button = st.form_submit_button("Generate Birth Chart")
102
+
103
+ if submit_button:
104
+ if not birth_place:
105
+ st.error("Please enter your birth place.")
106
+ else:
107
+ with st.spinner("Looking up location details..."):
108
+ location_details = get_location_details(birth_place)
109
+
110
+ if location_details.get('error'):
111
+ st.error(location_details['error'])
112
+ else:
113
+ # Reset error state
114
+ st.session_state.calculation_error = None
115
+
116
+ # Store user information
117
+ st.session_state.user_info = {
118
+ 'name': name,
119
+ 'gender': gender,
120
+ 'birth_date': birth_date,
121
+ 'birth_time': birth_time,
122
+ 'birth_place': birth_place,
123
+ 'latitude': location_details['latitude'],
124
+ 'longitude': location_details['longitude'],
125
+ 'timezone': location_details['timezone']
126
+ }
127
+
128
+ # Display detected timezone
129
+ st.info(f"Detected timezone for {birth_place}: {location_details['timezone']}")
130
+
131
+ # Calculate birth chart
132
+ with st.spinner("Calculating your birth chart..."):
133
+ st.session_state.birth_chart = calculate_chart(st.session_state.user_info)
134
+
135
+ if st.session_state.birth_chart:
136
+ st.success("Birth chart calculated successfully!")
137
+ elif st.session_state.calculation_error:
138
+ st.error(f"Error calculating birth chart: {st.session_state.calculation_error}")
139
+
140
+ # Display areas (only show if birth chart is calculated)
141
+ if st.session_state.birth_chart and st.session_state.user_info:
142
+ col1, col2 = st.columns([2, 3])
143
+
144
+ with col1:
145
+ st.subheader(f"Birth Chart for {st.session_state.user_info['name']}")
146
+
147
+ # Display birth details
148
+ st.write("**Birth Details:**")
149
+ st.write(f"Date: {st.session_state.user_info['birth_date'].strftime('%B %d, %Y')}")
150
+ st.write(f"Time: {st.session_state.user_info['birth_time'].strftime('%H:%M')}")
151
+ st.write(f"Location: {st.session_state.user_info['birth_place']}")
152
+ st.write(f"Timezone: {st.session_state.user_info['timezone']}")
153
+
154
+ # Display planetary positions
155
+ st.write("**Planetary Positions:**")
156
+ for planet, data in st.session_state.birth_chart['planets'].items():
157
+ if 'error' in data:
158
+ st.write(f"{planet}: Could not calculate position")
159
+ else:
160
+ st.write(f"{planet}: {data['degrees']:.2f}° {data['sign']} in House {data['house']}")
161
+
162
+ with col2:
163
+ st.subheader("AI Interpretation")
164
+ # Suggested questions
165
+ question_templates = [
166
+ "What are the main themes in my birth chart?",
167
+ f"How does my {st.session_state.user_info['gender'].lower()} energy manifest in this chart?",
168
+ "What career paths might suit me based on my chart?",
169
+ "What are my relationship patterns according to this chart?",
170
+ "What are my greatest strengths based on this chart?"
171
+ ]
172
+
173
+ selected_question = st.selectbox(
174
+ "Choose a question or type your own below:",
175
+ [""] + question_templates
176
+ )
177
+
178
+ custom_question = st.text_input(
179
+ "Ask about your chart:",
180
+ value=selected_question
181
+ )
182
+
183
+ if custom_question:
184
+ with st.spinner("Generating interpretation..."):
185
+ interpreter = AstroAI() # Replace with your actual AI interpreter class
186
+ interpretation = interpreter.get_interpretation(
187
+ st.session_state.birth_chart,
188
+ custom_question
189
+ )
190
+ st.session_state.chat_history.append({
191
+ "question": custom_question,
192
+ "answer": interpretation
193
+ })
194
+
195
+ # Display chat history
196
+ if st.session_state.chat_history:
197
+ st.write("**Previous Interpretations:**")
198
+ for exchange in st.session_state.chat_history:
199
+ with st.expander(f"Q: {exchange['question']}", expanded=False):
200
+ st.write(exchange['answer'])
201
+
202
+ if __name__ == "__main__":
203
+ main()