cryogenic22 commited on
Commit
c926334
·
verified ·
1 Parent(s): 62b7fb3

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +132 -189
app.py CHANGED
@@ -1,206 +1,149 @@
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
  import pytz
9
- from geopy.geocoders import Nominatim
10
- from geopy.exc import GeocoderTimedOut, GeocoderServiceError
11
- from timezonefinder import TimezoneFinder
12
- from dotenv import load_dotenv
13
- from astro_core import ChartCalculator
14
- from ai_interpreter import AstroAI
15
-
16
- # Load environment variables
17
- load_dotenv()
18
 
19
- def init_session_state():
20
- """Initialize session state variables"""
21
- if 'birth_chart' not in st.session_state:
22
- st.session_state.birth_chart = None
23
- if 'chat_history' not in st.session_state:
24
- st.session_state.chat_history = []
25
- if 'user_info' not in st.session_state:
26
- st.session_state.user_info = None
27
- if 'location_coords' not in st.session_state:
28
- st.session_state.location_coords = None
29
- if 'calculation_error' not in st.session_state:
30
- st.session_state.calculation_error = None
31
-
32
- def get_location_details(location_name: str) -> dict:
33
- """Get coordinates and timezone from location name"""
34
- try:
35
- # Initialize geocoder and timezone finder
36
- geolocator = Nominatim(user_agent="ai_astrology_app")
37
- tf = TimezoneFinder()
38
-
39
- # Get location
40
- location = geolocator.geocode(location_name)
41
- if location:
42
- latitude, longitude = location.latitude, location.longitude
43
-
44
- # Get timezone string for the coordinates
45
- timezone_str = tf.timezone_at(lat=latitude, lng=longitude)
46
- if not timezone_str:
47
- timezone_str = 'UTC' # Fallback to UTC if timezone not found
48
-
49
- return {
50
- 'latitude': latitude,
51
- 'longitude': longitude,
52
- 'timezone': timezone_str,
53
- 'error': None
54
- }
55
- return {'error': 'Location not found'}
56
- except (GeocoderTimedOut, GeocoderServiceError) as e:
57
- return {'error': f'Geocoding error: {str(e)}'}
58
- except Exception as e:
59
- return {'error': f'Unexpected error: {str(e)}'}
60
 
61
- def calculate_chart(user_data: dict):
62
- """Calculate birth chart based on user data"""
63
- try:
64
- calculator = ChartCalculator()
65
- birth_datetime = datetime.combine(user_data['birth_date'], user_data['birth_time'])
66
-
67
- chart_data = calculator.calculate_birth_chart(
68
- birth_datetime,
69
- user_data['latitude'],
70
- user_data['longitude'],
71
- user_data['timezone']
72
- )
73
-
74
- # Check for calculation errors
75
- if 'error' in chart_data:
76
- st.session_state.calculation_error = chart_data['error']
77
- return None
78
-
79
- return chart_data
80
- except Exception as e:
81
- st.session_state.calculation_error = str(e)
82
- return None
83
-
84
- def main():
85
- st.title("AI Astrology Assistant")
86
 
87
- # Initialize session state
88
- init_session_state()
 
 
 
89
 
90
- # User Information Form
91
- with st.form("user_info_form"):
92
- st.subheader("Personal Information")
93
-
94
- col1, col2 = st.columns(2)
95
- with col1:
96
- name = st.text_input("Full Name")
97
- gender = st.selectbox("Gender", ["Male", "Female", "Non-binary", "Prefer not to say"])
98
- birth_date = st.date_input("Birth Date", min_value=datetime(1900, 1, 1))
99
-
100
- with col2:
101
- birth_time = st.time_input("Birth Time")
102
- birth_place = st.text_input("Birth Place (City, Country)")
103
-
104
- submit_button = st.form_submit_button("Generate Birth Chart")
105
 
106
- if submit_button:
107
- if not birth_place:
108
- st.error("Please enter your birth place.")
109
- else:
110
- with st.spinner("Looking up location details..."):
111
- location_details = get_location_details(birth_place)
112
-
113
- if location_details.get('error'):
114
- st.error(location_details['error'])
115
- else:
116
- # Reset error state
117
- st.session_state.calculation_error = None
118
-
119
- # Store user information
120
- st.session_state.user_info = {
121
- 'name': name,
122
- 'gender': gender,
123
- 'birth_date': birth_date,
124
- 'birth_time': birth_time,
125
- 'birth_place': birth_place,
126
- 'latitude': location_details['latitude'],
127
- 'longitude': location_details['longitude'],
128
- 'timezone': location_details['timezone']
129
- }
130
-
131
- # Display detected timezone
132
- st.info(f"Detected timezone for {birth_place}: {location_details['timezone']}")
133
-
134
- # Calculate birth chart
135
- with st.spinner("Calculating your birth chart..."):
136
- st.session_state.birth_chart = calculate_chart(st.session_state.user_info)
137
-
138
- if st.session_state.birth_chart:
139
- st.success("Birth chart calculated successfully!")
140
- elif st.session_state.calculation_error:
141
- st.error(f"Error calculating birth chart: {st.session_state.calculation_error}")
142
 
143
- # Display areas (only show if birth chart is calculated)
144
- if st.session_state.birth_chart and st.session_state.user_info:
145
- col1, col2 = st.columns([2, 3])
146
-
147
- with col1:
148
- st.subheader(f"Birth Chart for {st.session_state.user_info['name']}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
149
 
150
- # Display birth details
151
- st.write("**Birth Details:**")
152
- st.write(f"Date: {st.session_state.user_info['birth_date'].strftime('%B %d, %Y')}")
153
- st.write(f"Time: {st.session_state.user_info['birth_time'].strftime('%H:%M')}")
154
- st.write(f"Location: {st.session_state.user_info['birth_place']}")
155
- st.write(f"Timezone: {st.session_state.user_info['timezone']}")
156
 
157
- # Display planetary positions
158
- st.write("**Planetary Positions:**")
159
- for planet, data in st.session_state.birth_chart['planets'].items():
160
- if 'error' in data:
161
- st.write(f"{planet}: Could not calculate position")
162
- else:
163
- st.write(f"{planet}: {data['degrees']:.2f}° {data['sign']} in House {data['house']}")
164
-
165
- with col2:
166
- st.subheader("AI Interpretation")
167
- # Suggested questions
168
- question_templates = [
169
- "What are the main themes in my birth chart?",
170
- f"How does my {st.session_state.user_info['gender'].lower()} energy manifest in this chart?",
171
- "What career paths might suit me based on my chart?",
172
- "What are my relationship patterns according to this chart?",
173
- "What are my greatest strengths based on this chart?"
174
- ]
175
 
176
- selected_question = st.selectbox(
177
- "Choose a question or type your own below:",
178
- [""] + question_templates
179
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
180
 
181
- custom_question = st.text_input(
182
- "Ask about your chart:",
183
- value=selected_question
184
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
185
 
186
- if custom_question:
187
- with st.spinner("Generating interpretation..."):
188
- interpreter = AstroAI()
189
- interpretation = interpreter.get_interpretation(
190
- st.session_state.birth_chart,
191
- custom_question
192
- )
193
- st.session_state.chat_history.append({
194
- "question": custom_question,
195
- "answer": interpretation
196
- })
197
 
198
- # Display chat history
199
- if st.session_state.chat_history:
200
- st.write("**Previous Interpretations:**")
201
- for exchange in st.session_state.chat_history:
202
- with st.expander(f"Q: {exchange['question']}", expanded=False):
203
- st.write(exchange['answer'])
204
-
205
- if __name__ == "__main__":
206
- main()
 
 
 
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 {}