kerykeion 4.12.3__py3-none-any.whl → 4.18.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of kerykeion might be problematic. Click here for more details.

Files changed (36) hide show
  1. kerykeion/__init__.py +3 -1
  2. kerykeion/aspects/aspects_utils.py +40 -123
  3. kerykeion/aspects/natal_aspects.py +34 -25
  4. kerykeion/aspects/synastry_aspects.py +34 -28
  5. kerykeion/astrological_subject.py +199 -196
  6. kerykeion/charts/charts_utils.py +701 -62
  7. kerykeion/charts/draw_planets.py +407 -0
  8. kerykeion/charts/kerykeion_chart_svg.py +534 -1140
  9. kerykeion/charts/templates/aspect_grid_only.xml +452 -0
  10. kerykeion/charts/templates/chart.xml +88 -70
  11. kerykeion/charts/templates/wheel_only.xml +499 -0
  12. kerykeion/charts/themes/classic.css +82 -0
  13. kerykeion/charts/themes/dark-high-contrast.css +121 -0
  14. kerykeion/charts/themes/dark.css +121 -0
  15. kerykeion/charts/themes/light.css +117 -0
  16. kerykeion/enums.py +1 -0
  17. kerykeion/ephemeris_data.py +178 -0
  18. kerykeion/fetch_geonames.py +2 -3
  19. kerykeion/kr_types/chart_types.py +6 -16
  20. kerykeion/kr_types/kr_literals.py +12 -3
  21. kerykeion/kr_types/kr_models.py +77 -32
  22. kerykeion/kr_types/settings_models.py +4 -10
  23. kerykeion/relationship_score/__init__.py +2 -0
  24. kerykeion/relationship_score/relationship_score.py +175 -0
  25. kerykeion/relationship_score/relationship_score_factory.py +275 -0
  26. kerykeion/report.py +6 -3
  27. kerykeion/settings/kerykeion_settings.py +6 -1
  28. kerykeion/settings/kr.config.json +256 -102
  29. kerykeion/utilities.py +122 -217
  30. {kerykeion-4.12.3.dist-info → kerykeion-4.18.0.dist-info}/METADATA +40 -10
  31. kerykeion-4.18.0.dist-info/RECORD +42 -0
  32. kerykeion/relationship_score.py +0 -205
  33. kerykeion-4.12.3.dist-info/RECORD +0 -32
  34. {kerykeion-4.12.3.dist-info → kerykeion-4.18.0.dist-info}/LICENSE +0 -0
  35. {kerykeion-4.12.3.dist-info → kerykeion-4.18.0.dist-info}/WHEEL +0 -0
  36. {kerykeion-4.12.3.dist-info → kerykeion-4.18.0.dist-info}/entry_points.txt +0 -0
@@ -6,6 +6,7 @@
6
6
  import pytz
7
7
  import swisseph as swe
8
8
  import logging
9
+ import warnings
9
10
 
10
11
  from datetime import datetime
11
12
  from functools import cached_property
@@ -19,11 +20,13 @@ from kerykeion.kr_types import (
19
20
  PointType,
20
21
  SiderealMode,
21
22
  HousesSystemIdentifier,
22
- PerspectiveType
23
+ PerspectiveType,
24
+ Planet,
25
+ Houses
23
26
  )
24
27
  from kerykeion.utilities import (
25
28
  get_number_from_name,
26
- calculate_position,
29
+ get_kerykeion_point_from_degree,
27
30
  get_planet_house,
28
31
  get_moon_emoji_from_phase_int,
29
32
  get_moon_phase_name_from_phase_int,
@@ -33,10 +36,10 @@ from pathlib import Path
33
36
  from typing import Union, get_args
34
37
 
35
38
  DEFAULT_GEONAMES_USERNAME = "century.boy"
36
- DEFAULT_SIDEREAL_MODE = "FAGAN_BRADLEY"
37
- DEFAULT_HOUSES_SYSTEM_IDENTIFIER = "P"
38
- DEFAULT_ZODIAC_TYPE = "Tropic"
39
- DEFAULT_PERSPECTIVE_TYPE = "Apparent Geocentric"
39
+ DEFAULT_SIDEREAL_MODE: SiderealMode = "FAGAN_BRADLEY"
40
+ DEFAULT_HOUSES_SYSTEM_IDENTIFIER: HousesSystemIdentifier = "P"
41
+ DEFAULT_ZODIAC_TYPE: ZodiacType = "Tropic"
42
+ DEFAULT_PERSPECTIVE_TYPE: PerspectiveType = "Apparent Geocentric"
40
43
  GEONAMES_DEFAULT_USERNAME_WARNING = (
41
44
  "\n********\n"
42
45
  "NO GEONAMES USERNAME SET!\n"
@@ -75,8 +78,7 @@ class AstrologicalSubject:
75
78
  You can get one for free here: https://www.geonames.org/login
76
79
  - online (bool, optional): Sets if you want to use the online mode, which fetches the timezone and coordinates from geonames.
77
80
  If you already have the coordinates and timezone, set this to False. Defaults to True.
78
- - disable_chiron (bool, optional): Disables the calculation of Chiron. Defaults to False.
79
- Chiron calculation can create some issues with the Swiss Ephemeris when the date is too far in the past.
81
+ - disable_chiron: Deprecated, use disable_chiron_and_lilith instead.
80
82
  - sidereal_mode (SiderealMode, optional): Also known as Ayanamsa.
81
83
  The mode to use for the sidereal zodiac, according to the Swiss Ephemeris.
82
84
  Defaults to "FAGAN_BRADLEY".
@@ -87,6 +89,11 @@ class AstrologicalSubject:
87
89
  - perspective_type (PerspectiveType, optional): The perspective to use for the calculation of the chart.
88
90
  Defaults to "Apparent Geocentric".
89
91
  Available perspectives are visible in the PerspectiveType Literal.
92
+ - is_dst (Union[None, bool], optional): Specify if the time is in DST. Defaults to None.
93
+ By default (None), the library will try to guess if the time is in DST or not and raise an AmbiguousTimeError
94
+ if it can't guess. If you know the time is in DST, set this to True, if you know it's not, set it to False.
95
+ - disable_chiron_and_lilith (bool, optional): boolean representing if Chiron and Lilith should be disabled. Default is False.
96
+ Chiron calculation can create some issues with the Swiss Ephemeris when the date is too far in the past.
90
97
  """
91
98
 
92
99
  # Defined by the user
@@ -104,10 +111,11 @@ class AstrologicalSubject:
104
111
  geonames_username: str
105
112
  online: bool
106
113
  zodiac_type: ZodiacType
107
- sidereal_mode: SiderealMode
114
+ sidereal_mode: Union[SiderealMode, None]
108
115
  houses_system_identifier: HousesSystemIdentifier
109
116
  houses_system_name: str
110
117
  perspective_type: PerspectiveType
118
+ is_dst: Union[None, bool]
111
119
 
112
120
  # Generated internally
113
121
  city_data: dict[str, str]
@@ -130,6 +138,7 @@ class AstrologicalSubject:
130
138
  true_node: KerykeionPointModel
131
139
  mean_node: KerykeionPointModel
132
140
  chiron: Union[KerykeionPointModel, None]
141
+ mean_lilith: Union[KerykeionPointModel, None]
133
142
 
134
143
  # Houses
135
144
  first_house: KerykeionPointModel
@@ -146,10 +155,15 @@ class AstrologicalSubject:
146
155
  twelfth_house: KerykeionPointModel
147
156
 
148
157
  # Lists
149
- houses_list: list[KerykeionPointModel]
150
- planets_list: list[KerykeionPointModel]
151
- planets_degrees_ut: list[float]
152
- houses_degree_ut: list[float]
158
+ _houses_list: list[KerykeionPointModel]
159
+ _houses_degree_ut: list[float]
160
+
161
+ # Enable or disable features
162
+ disable_chiron: Union[None, bool]
163
+ disable_chiron_and_lilith: bool
164
+
165
+ planets_names_list: list[Planet]
166
+ houses_names_list: list[Houses]
153
167
 
154
168
  def __init__(
155
169
  self,
@@ -167,63 +181,89 @@ class AstrologicalSubject:
167
181
  geonames_username: Union[str, None] = None,
168
182
  zodiac_type: ZodiacType = DEFAULT_ZODIAC_TYPE,
169
183
  online: bool = True,
170
- disable_chiron: bool = False,
184
+ disable_chiron: Union[None, bool] = None, # Deprecated
171
185
  sidereal_mode: Union[SiderealMode, None] = None,
172
186
  houses_system_identifier: HousesSystemIdentifier = DEFAULT_HOUSES_SYSTEM_IDENTIFIER,
173
- perspective_type: PerspectiveType = DEFAULT_PERSPECTIVE_TYPE
187
+ perspective_type: PerspectiveType = DEFAULT_PERSPECTIVE_TYPE,
188
+ is_dst: Union[None, bool] = None,
189
+ disable_chiron_and_lilith: bool = False
174
190
  ) -> None:
175
191
  logging.debug("Starting Kerykeion")
176
192
 
193
+ # Deprecation warnings --->
194
+ if disable_chiron is not None:
195
+ warnings.warn(
196
+ "The 'disable_chiron' argument is deprecated and will be removed in a future version. "
197
+ "Please use 'disable_chiron' instead.",
198
+ DeprecationWarning
199
+ )
200
+
201
+ if disable_chiron_and_lilith:
202
+ raise ValueError("Cannot specify both 'disable_chiron' and 'disable_chiron_and_lilith'. Use 'disable_chiron_and_lilith' only.")
203
+
204
+ self.disable_chiron_and_lilith = disable_chiron
205
+ # <--- Deprecation warnings
206
+
177
207
  self.name = name
178
208
  self.year = year
179
209
  self.month = month
180
210
  self.day = day
181
211
  self.hour = hour
182
212
  self.minute = minute
183
- self.city = city
184
- self.nation = nation
185
- self.lng = lng
186
- self.lat = lat
187
- self.tz_str = tz_str
188
213
  self.zodiac_type = zodiac_type
189
214
  self.online = online
190
215
  self.json_dir = Path.home()
191
- self.geonames_username = geonames_username
192
216
  self.disable_chiron = disable_chiron
193
217
  self.sidereal_mode = sidereal_mode
194
218
  self.houses_system_identifier = houses_system_identifier
195
219
  self.perspective_type = perspective_type
220
+ self.is_dst = is_dst
221
+ self.disable_chiron_and_lilith = disable_chiron_and_lilith
196
222
 
197
223
  #---------------#
198
224
  # General setup #
199
225
  #---------------#
200
226
 
201
- # This message is set to encourage the user to set a custom geonames username
227
+ # Geonames username
202
228
  if geonames_username is None and online:
203
- logging.warning(
204
-
205
- )
206
-
229
+ logging.warning(GEONAMES_DEFAULT_USERNAME_WARNING)
207
230
  self.geonames_username = DEFAULT_GEONAMES_USERNAME
231
+ else:
232
+ self.geonames_username = geonames_username # type: ignore
208
233
 
209
- if not self.city:
234
+ # City
235
+ if not city:
210
236
  self.city = "London"
211
237
  logging.info("No city specified, using London as default")
238
+ else:
239
+ self.city = city
212
240
 
213
- if not self.nation:
241
+ # Nation
242
+ if not nation:
214
243
  self.nation = "GB"
215
244
  logging.info("No nation specified, using GB as default")
245
+ else:
246
+ self.nation = nation
216
247
 
217
- if not self.lat:
248
+ # Latitude
249
+ if not lat and not self.online:
218
250
  self.lat = 51.5074
219
251
  logging.info("No latitude specified, using London as default")
220
-
221
- if not self.lng:
252
+ else:
253
+ self.lat = lat # type: ignore
254
+
255
+ # Longitude
256
+ if not lng and not self.online:
222
257
  self.lng = 0
223
258
  logging.info("No longitude specified, using London as default")
259
+ else:
260
+ self.lng = lng # type: ignore
224
261
 
262
+ # Timezone
225
263
  if (not self.online) and (not tz_str):
226
264
  raise KerykeionException("You need to set the coordinates and timezone if you want to use the offline mode!")
265
+ else:
266
+ self.tz_str = tz_str # type: ignore
227
267
 
228
268
  #-----------------------#
229
269
  # Swiss Ephemeris setup #
@@ -246,6 +286,8 @@ class AstrologicalSubject:
246
286
  elif self.perspective_type == "Topocentric":
247
287
  self._iflag += swe.FLG_TOPOCTR
248
288
  # geopos_is_set, for topocentric
289
+ if (self.online) and (not self.tz_str) and (not self.lat) and (not self.lng):
290
+ self._fetch_and_set_tz_and_coordinates_from_geonames()
249
291
  swe.set_topo(self.lng, self.lat, 0)
250
292
  # <--- Chart Perspective check and setup
251
293
 
@@ -269,7 +311,8 @@ class AstrologicalSubject:
269
311
 
270
312
  if self.zodiac_type == "Sidereal":
271
313
  # Check if the sidereal mode is valid
272
- if not self.sidereal_mode in get_args(SiderealMode):
314
+
315
+ if not self.sidereal_mode or not self.sidereal_mode in get_args(SiderealMode):
273
316
  raise KerykeionException(f"\n* ERROR: '{self.sidereal_mode}' is NOT a valid sidereal mode! Available modes are: *" + "\n" + str(get_args(SiderealMode)))
274
317
 
275
318
  self._iflag += swe.FLG_SIDEREAL
@@ -281,17 +324,22 @@ class AstrologicalSubject:
281
324
  #------------------------#
282
325
  # Start the calculations #
283
326
  #------------------------#
284
-
285
- check_and_adjust_polar_latitude(self.lat, self.lng)
286
327
 
287
328
  # UTC, julian day and local time setup --->
288
- if (self.online) and (not self.tz_str):
289
- self._fetch_tz_from_geonames()
329
+ if (self.online) and (not self.tz_str) and (not self.lat) and (not self.lng):
330
+ self._fetch_and_set_tz_and_coordinates_from_geonames()
331
+
332
+ self.lat = check_and_adjust_polar_latitude(self.lat)
290
333
 
291
334
  # Local time to UTC
292
335
  local_time = pytz.timezone(self.tz_str)
293
336
  naive_datetime = datetime(self.year, self.month, self.day, self.hour, self.minute, 0)
294
- local_datetime = local_time.localize(naive_datetime, is_dst=None)
337
+
338
+ try:
339
+ local_datetime = local_time.localize(naive_datetime, is_dst=self.is_dst)
340
+ except pytz.exceptions.AmbiguousTimeError:
341
+ raise KerykeionException("Ambiguous time! Please specify if the time is in DST or not with the is_dst argument.")
342
+
295
343
  utc_object = local_datetime.astimezone(pytz.utc)
296
344
  self.iso_formatted_utc_datetime = utc_object.isoformat()
297
345
 
@@ -303,11 +351,9 @@ class AstrologicalSubject:
303
351
  self.julian_day = float(swe.julday(utc_object.year, utc_object.month, utc_object.day, utc_float_hour_with_minutes))
304
352
  # <--- UTC, julian day and local time setup
305
353
 
306
- self._planets_degrees_lister()
307
- self._planets()
308
- self._houses()
309
- self._planets_in_houses()
310
- self._lunar_phase_calc()
354
+ self._initialize_houses()
355
+ self._initialize_planets()
356
+ self._initialize_moon_phase()
311
357
 
312
358
  # Deprecated properties
313
359
  self.utc_time
@@ -325,7 +371,7 @@ class AstrologicalSubject:
325
371
  def get(self, item, default=None):
326
372
  return getattr(self, item, default)
327
373
 
328
- def _fetch_tz_from_geonames(self) -> None:
374
+ def _fetch_and_set_tz_and_coordinates_from_geonames(self) -> None:
329
375
  """Gets the nearest time zone for the calculation"""
330
376
  logging.info("Fetching timezone/coordinates from geonames")
331
377
 
@@ -349,9 +395,7 @@ class AstrologicalSubject:
349
395
  self.lat = float(self.city_data["lat"])
350
396
  self.tz_str = self.city_data["timezonestr"]
351
397
 
352
- check_and_adjust_polar_latitude(self.lat, self.lng)
353
-
354
- def _houses(self) -> None:
398
+ def _initialize_houses(self) -> None:
355
399
  """
356
400
  Calculate positions and store them in dictionaries
357
401
 
@@ -386,7 +430,7 @@ class AstrologicalSubject:
386
430
  """
387
431
 
388
432
  if self.zodiac_type == "Sidereal":
389
- self.houses_degree_ut = swe.houses_ex(
433
+ self._houses_degree_ut = swe.houses_ex(
390
434
  tjdut=self.julian_day,
391
435
  lat=self.lat, lon=self.lng,
392
436
  hsys=str.encode(self.houses_system_identifier),
@@ -394,7 +438,7 @@ class AstrologicalSubject:
394
438
  )[0]
395
439
 
396
440
  elif self.zodiac_type == "Tropic":
397
- self.houses_degree_ut = swe.houses(
441
+ self._houses_degree_ut = swe.houses(
398
442
  tjdut=self.julian_day, lat=self.lat,
399
443
  lon=self.lng,
400
444
  hsys=str.encode(self.houses_system_identifier)
@@ -403,20 +447,23 @@ class AstrologicalSubject:
403
447
  point_type: PointType = "House"
404
448
 
405
449
  # stores the house in singular dictionaries.
406
- self.first_house = calculate_position(self.houses_degree_ut[0], "First_House", point_type=point_type)
407
- self.second_house = calculate_position(self.houses_degree_ut[1], "Second_House", point_type=point_type)
408
- self.third_house = calculate_position(self.houses_degree_ut[2], "Third_House", point_type=point_type)
409
- self.fourth_house = calculate_position(self.houses_degree_ut[3], "Fourth_House", point_type=point_type)
410
- self.fifth_house = calculate_position(self.houses_degree_ut[4], "Fifth_House", point_type=point_type)
411
- self.sixth_house = calculate_position(self.houses_degree_ut[5], "Sixth_House", point_type=point_type)
412
- self.seventh_house = calculate_position(self.houses_degree_ut[6], "Seventh_House", point_type=point_type)
413
- self.eighth_house = calculate_position(self.houses_degree_ut[7], "Eighth_House", point_type=point_type)
414
- self.ninth_house = calculate_position(self.houses_degree_ut[8], "Ninth_House", point_type=point_type)
415
- self.tenth_house = calculate_position(self.houses_degree_ut[9], "Tenth_House", point_type=point_type)
416
- self.eleventh_house = calculate_position(self.houses_degree_ut[10], "Eleventh_House", point_type=point_type)
417
- self.twelfth_house = calculate_position(self.houses_degree_ut[11], "Twelfth_House", point_type=point_type)
418
-
419
- self.houses_list = [
450
+ self.first_house = get_kerykeion_point_from_degree(self._houses_degree_ut[0], "First_House", point_type=point_type)
451
+ self.second_house = get_kerykeion_point_from_degree(self._houses_degree_ut[1], "Second_House", point_type=point_type)
452
+ self.third_house = get_kerykeion_point_from_degree(self._houses_degree_ut[2], "Third_House", point_type=point_type)
453
+ self.fourth_house = get_kerykeion_point_from_degree(self._houses_degree_ut[3], "Fourth_House", point_type=point_type)
454
+ self.fifth_house = get_kerykeion_point_from_degree(self._houses_degree_ut[4], "Fifth_House", point_type=point_type)
455
+ self.sixth_house = get_kerykeion_point_from_degree(self._houses_degree_ut[5], "Sixth_House", point_type=point_type)
456
+ self.seventh_house = get_kerykeion_point_from_degree(self._houses_degree_ut[6], "Seventh_House", point_type=point_type)
457
+ self.eighth_house = get_kerykeion_point_from_degree(self._houses_degree_ut[7], "Eighth_House", point_type=point_type)
458
+ self.ninth_house = get_kerykeion_point_from_degree(self._houses_degree_ut[8], "Ninth_House", point_type=point_type)
459
+ self.tenth_house = get_kerykeion_point_from_degree(self._houses_degree_ut[9], "Tenth_House", point_type=point_type)
460
+ self.eleventh_house = get_kerykeion_point_from_degree(self._houses_degree_ut[10], "Eleventh_House", point_type=point_type)
461
+ self.twelfth_house = get_kerykeion_point_from_degree(self._houses_degree_ut[11], "Twelfth_House", point_type=point_type)
462
+
463
+ self.houses_names_list = list(get_args(Houses))
464
+
465
+ # Deprecated
466
+ self._houses_list = [
420
467
  self.first_house,
421
468
  self.second_house,
422
469
  self.third_house,
@@ -431,10 +478,12 @@ class AstrologicalSubject:
431
478
  self.twelfth_house,
432
479
  ]
433
480
 
434
- def _planets_degrees_lister(self):
435
- """Sidereal or tropic mode."""
481
+ def _initialize_planets(self) -> None:
482
+ """Defines body positon in signs and information and
483
+ stores them in dictionaries"""
484
+
485
+ point_type: PointType = "Planet"
436
486
 
437
- # Calculates the position of the planets and stores it in a list.
438
487
  sun_deg = swe.calc(self.julian_day, 0, self._iflag)[0][0]
439
488
  moon_deg = swe.calc(self.julian_day, 1, self._iflag)[0][0]
440
489
  mercury_deg = swe.calc(self.julian_day, 2, self._iflag)[0][0]
@@ -447,77 +496,35 @@ class AstrologicalSubject:
447
496
  pluto_deg = swe.calc(self.julian_day, 9, self._iflag)[0][0]
448
497
  mean_node_deg = swe.calc(self.julian_day, 10, self._iflag)[0][0]
449
498
  true_node_deg = swe.calc(self.julian_day, 11, self._iflag)[0][0]
450
-
451
- if not self.disable_chiron:
452
- chiron_deg = swe.calc(self.julian_day, 15, self._iflag)[0][0]
453
- else:
454
- chiron_deg = 0
455
-
456
- self.planets_degrees_ut = [
457
- sun_deg,
458
- moon_deg,
459
- mercury_deg,
460
- venus_deg,
461
- mars_deg,
462
- jupiter_deg,
463
- saturn_deg,
464
- uranus_deg,
465
- neptune_deg,
466
- pluto_deg,
467
- mean_node_deg,
468
- true_node_deg,
469
- ]
470
-
471
- if not self.disable_chiron:
472
- self.planets_degrees_ut.append(chiron_deg)
473
499
 
474
- def _planets(self) -> None:
475
- """Defines body positon in signs and information and
476
- stores them in dictionaries"""
477
-
478
- point_type: PointType = "Planet"
479
- # stores the planets in singular dictionaries.
480
- self.sun = calculate_position(self.planets_degrees_ut[0], "Sun", point_type=point_type)
481
- self.moon = calculate_position(self.planets_degrees_ut[1], "Moon", point_type=point_type)
482
- self.mercury = calculate_position(self.planets_degrees_ut[2], "Mercury", point_type=point_type)
483
- self.venus = calculate_position(self.planets_degrees_ut[3], "Venus", point_type=point_type)
484
- self.mars = calculate_position(self.planets_degrees_ut[4], "Mars", point_type=point_type)
485
- self.jupiter = calculate_position(self.planets_degrees_ut[5], "Jupiter", point_type=point_type)
486
- self.saturn = calculate_position(self.planets_degrees_ut[6], "Saturn", point_type=point_type)
487
- self.uranus = calculate_position(self.planets_degrees_ut[7], "Uranus", point_type=point_type)
488
- self.neptune = calculate_position(self.planets_degrees_ut[8], "Neptune", point_type=point_type)
489
- self.pluto = calculate_position(self.planets_degrees_ut[9], "Pluto", point_type=point_type)
490
- self.mean_node = calculate_position(self.planets_degrees_ut[10], "Mean_Node", point_type=point_type)
491
- self.true_node = calculate_position(self.planets_degrees_ut[11], "True_Node", point_type=point_type)
492
-
493
- if not self.disable_chiron:
494
- self.chiron = calculate_position(self.planets_degrees_ut[12], "Chiron", point_type=point_type)
495
- else:
496
- self.chiron = None
497
-
498
- def _planets_in_houses(self) -> None:
499
- """Calculates the house of the planet and updates
500
- the planets dictionary."""
501
-
502
- self.sun.house = get_planet_house(self.planets_degrees_ut[0], self.houses_degree_ut)
503
- self.moon.house = get_planet_house(self.planets_degrees_ut[1], self.houses_degree_ut)
504
- self.mercury.house = get_planet_house(self.planets_degrees_ut[2], self.houses_degree_ut)
505
- self.venus.house = get_planet_house(self.planets_degrees_ut[3], self.houses_degree_ut)
506
- self.mars.house = get_planet_house(self.planets_degrees_ut[4], self.houses_degree_ut)
507
- self.jupiter.house = get_planet_house(self.planets_degrees_ut[5], self.houses_degree_ut)
508
- self.saturn.house = get_planet_house(self.planets_degrees_ut[6], self.houses_degree_ut)
509
- self.uranus.house = get_planet_house(self.planets_degrees_ut[7], self.houses_degree_ut)
510
- self.neptune.house = get_planet_house(self.planets_degrees_ut[8], self.houses_degree_ut)
511
- self.pluto.house = get_planet_house(self.planets_degrees_ut[9], self.houses_degree_ut)
512
- self.mean_node.house = get_planet_house(self.planets_degrees_ut[10], self.houses_degree_ut)
513
- self.true_node.house = get_planet_house(self.planets_degrees_ut[11], self.houses_degree_ut)
514
-
515
- if not self.disable_chiron:
516
- self.chiron.house = get_planet_house(self.planets_degrees_ut[12], self.houses_degree_ut)
517
- else:
518
- self.chiron = None
519
-
520
- self.planets_list = [
500
+ self.sun = get_kerykeion_point_from_degree(sun_deg, "Sun", point_type=point_type)
501
+ self.moon = get_kerykeion_point_from_degree(moon_deg, "Moon", point_type=point_type)
502
+ self.mercury = get_kerykeion_point_from_degree(mercury_deg, "Mercury", point_type=point_type)
503
+ self.venus = get_kerykeion_point_from_degree(venus_deg, "Venus", point_type=point_type)
504
+ self.mars = get_kerykeion_point_from_degree(mars_deg, "Mars", point_type=point_type)
505
+ self.jupiter = get_kerykeion_point_from_degree(jupiter_deg, "Jupiter", point_type=point_type)
506
+ self.saturn = get_kerykeion_point_from_degree(saturn_deg, "Saturn", point_type=point_type)
507
+ self.uranus = get_kerykeion_point_from_degree(uranus_deg, "Uranus", point_type=point_type)
508
+ self.neptune = get_kerykeion_point_from_degree(neptune_deg, "Neptune", point_type=point_type)
509
+ self.pluto = get_kerykeion_point_from_degree(pluto_deg, "Pluto", point_type=point_type)
510
+ self.mean_node = get_kerykeion_point_from_degree(mean_node_deg, "Mean_Node", point_type=point_type)
511
+ self.true_node = get_kerykeion_point_from_degree(true_node_deg, "True_Node", point_type=point_type)
512
+
513
+ self.sun.house = get_planet_house(sun_deg, self._houses_degree_ut)
514
+ self.moon.house = get_planet_house(moon_deg, self._houses_degree_ut)
515
+ self.mercury.house = get_planet_house(mercury_deg, self._houses_degree_ut)
516
+ self.venus.house = get_planet_house(venus_deg, self._houses_degree_ut)
517
+ self.mars.house = get_planet_house(mars_deg, self._houses_degree_ut)
518
+ self.jupiter.house = get_planet_house(jupiter_deg, self._houses_degree_ut)
519
+ self.saturn.house = get_planet_house(saturn_deg, self._houses_degree_ut)
520
+ self.uranus.house = get_planet_house(uranus_deg, self._houses_degree_ut)
521
+ self.neptune.house = get_planet_house(neptune_deg, self._houses_degree_ut)
522
+ self.pluto.house = get_planet_house(pluto_deg, self._houses_degree_ut)
523
+ self.mean_node.house = get_planet_house(mean_node_deg, self._houses_degree_ut)
524
+ self.true_node.house = get_planet_house(true_node_deg, self._houses_degree_ut)
525
+
526
+ # Deprecated
527
+ planets_list = [
521
528
  self.sun,
522
529
  self.moon,
523
530
  self.mercury,
@@ -531,83 +538,70 @@ class AstrologicalSubject:
531
538
  self.mean_node,
532
539
  self.true_node,
533
540
  ]
534
-
535
- if not self.disable_chiron:
536
- self.planets_list.append(self.chiron)
541
+
542
+ if not self.disable_chiron_and_lilith:
543
+ chiron_deg = swe.calc(self.julian_day, 15, self._iflag)[0][0]
544
+ mean_lilith_deg = swe.calc(self.julian_day, 12, self._iflag)[0][0]
545
+
546
+ self.chiron = get_kerykeion_point_from_degree(chiron_deg, "Chiron", point_type=point_type)
547
+ self.mean_lilith = get_kerykeion_point_from_degree(mean_lilith_deg, "Mean_Lilith", point_type=point_type)
548
+
549
+ self.chiron.house = get_planet_house(chiron_deg, self._houses_degree_ut)
550
+ self.mean_lilith.house = get_planet_house(mean_lilith_deg, self._houses_degree_ut)
551
+
552
+ # Deprecated
553
+ planets_list.append(self.chiron)
554
+ planets_list.append(self.mean_lilith)
555
+
556
+ else:
557
+ self.chiron = None
558
+ self.mean_lilith = None
559
+
560
+ # FIXME: Update after removing planets_list
561
+ self.planets_names_list = [planet["name"] for planet in planets_list]
537
562
 
538
563
  # Check in retrograde or not:
539
- planets_ret = []
540
- for planet in self.planets_list:
564
+ for planet in planets_list:
541
565
  planet_number = get_number_from_name(planet["name"])
542
566
  if swe.calc(self.julian_day, planet_number, self._iflag)[0][3] < 0:
543
567
  planet["retrograde"] = True
544
568
  else:
545
569
  planet["retrograde"] = False
546
- planets_ret.append(planet)
547
570
 
548
- def _lunar_phase_calc(self) -> None:
549
- """Function to calculate the lunar phase"""
550
571
 
551
- # If ther's an error:
552
- moon_phase, sun_phase = None, None
572
+ def _initialize_moon_phase(self) -> None:
573
+ """
574
+ Calculate and initialize the lunar phase based on the positions of the moon and sun.
553
575
 
554
- # anti-clockwise degrees between sun and moon
555
- moon, sun = self.planets_degrees_ut[1], self.planets_degrees_ut[0]
556
- degrees_between = moon - sun
576
+ This function calculates the degrees between the moon and the sun, determines the moon phase
577
+ and sun phase, and initializes the lunar phase model with the calculated values.
578
+ """
579
+ # Initialize moon_phase and sun_phase to None in case of an error
580
+ moon_phase, sun_phase = None, None
557
581
 
558
- if degrees_between < 0:
559
- degrees_between += 360.0
582
+ # Calculate the anti-clockwise degrees between the sun and moon
583
+ moon, sun = self.moon.abs_pos, self.sun.abs_pos
584
+ degrees_between = (moon - sun) % 360
560
585
 
586
+ # Calculate the moon phase (1-28) based on the degrees between the sun and moon
561
587
  step = 360.0 / 28.0
588
+ moon_phase = int(degrees_between // step) + 1
562
589
 
563
- for x in range(28):
564
- low = x * step
565
- high = (x + 1) * step
566
-
567
- if degrees_between >= low and degrees_between < high:
568
- moon_phase = x + 1
569
-
590
+ # Define the sun phase steps
570
591
  sunstep = [
571
- 0,
572
- 30,
573
- 40,
574
- 50,
575
- 60,
576
- 70,
577
- 80,
578
- 90,
579
- 120,
580
- 130,
581
- 140,
582
- 150,
583
- 160,
584
- 170,
585
- 180,
586
- 210,
587
- 220,
588
- 230,
589
- 240,
590
- 250,
591
- 260,
592
- 270,
593
- 300,
594
- 310,
595
- 320,
596
- 330,
597
- 340,
598
- 350,
592
+ 0, 30, 40, 50, 60, 70, 80, 90, 120, 130, 140, 150, 160, 170, 180,
593
+ 210, 220, 230, 240, 250, 260, 270, 300, 310, 320, 330, 340, 350
599
594
  ]
600
595
 
596
+ # Calculate the sun phase (1-28) based on the degrees between the sun and moon
601
597
  for x in range(len(sunstep)):
602
598
  low = sunstep[x]
603
-
604
- if x == 27:
605
- high = 360
606
- else:
607
- high = sunstep[x + 1]
608
- if degrees_between >= low and degrees_between < high:
599
+ high = sunstep[x + 1] if x < len(sunstep) - 1 else 360
600
+ if low <= degrees_between < high:
609
601
  sun_phase = x + 1
602
+ break
610
603
 
604
+ # Create a dictionary with the lunar phase information
611
605
  lunar_phase_dictionary = {
612
606
  "degrees_between_s_m": degrees_between,
613
607
  "moon_phase": moon_phase,
@@ -616,6 +610,7 @@ class AstrologicalSubject:
616
610
  "moon_phase_name": get_moon_phase_name_from_phase_int(moon_phase)
617
611
  }
618
612
 
613
+ # Initialize the lunar phase model with the calculated values
619
614
  self.lunar_phase = LunarPhaseModel(**lunar_phase_dictionary)
620
615
 
621
616
  def json(self, dump=False, destination_folder: Union[str, None] = None, indent: Union[int, None] = None) -> str:
@@ -698,7 +693,7 @@ class AstrologicalSubject:
698
693
  lat: Union[int, float] = 51.5074,
699
694
  geonames_username: str = DEFAULT_GEONAMES_USERNAME,
700
695
  zodiac_type: ZodiacType = DEFAULT_ZODIAC_TYPE,
701
- disable_chiron: bool = False,
696
+ disable_chiron_and_lilith: bool = False,
702
697
  sidereal_mode: Union[SiderealMode, None] = None,
703
698
  houses_system_identifier: HousesSystemIdentifier = DEFAULT_HOUSES_SYSTEM_IDENTIFIER,
704
699
  perspective_type: PerspectiveType = DEFAULT_PERSPECTIVE_TYPE
@@ -721,7 +716,7 @@ class AstrologicalSubject:
721
716
  - geonames_username (str, optional): The username for the geonames API. Note: Change this to your own username to avoid rate limits!
722
717
  You can get one for free here: https://www.geonames.org/login
723
718
  - zodiac_type (ZodiacType, optional): The zodiac type to use. Defaults to "Tropic".
724
- - disable_chiron (bool, optional): Disables the calculation of Chiron. Defaults to False.
719
+ - disable_chiron_and_lilith: boolean representing if Chiron and Lilith should be disabled. Default is False.
725
720
  Chiron calculation can create some issues with the Swiss Ephemeris when the date is too far in the past.
726
721
  - sidereal_mode (SiderealMode, optional): Also known as Ayanamsa.
727
722
  The mode to use for the sidereal zodiac, according to the Swiss Ephemeris.
@@ -767,10 +762,10 @@ class AstrologicalSubject:
767
762
  online=False,
768
763
  geonames_username=geonames_username,
769
764
  zodiac_type=zodiac_type,
770
- disable_chiron=disable_chiron,
771
765
  sidereal_mode=sidereal_mode,
772
766
  houses_system_identifier=houses_system_identifier,
773
- perspective_type=perspective_type
767
+ perspective_type=perspective_type,
768
+ disable_chiron_and_lilith=disable_chiron_and_lilith
774
769
  )
775
770
 
776
771
  return subject
@@ -812,4 +807,12 @@ if __name__ == "__main__":
812
807
  print(johnny.json(dump=True, indent=2))
813
808
 
814
809
  # With Topocentric Perspective
815
- johnny = AstrologicalSubject("Johnny Depp", 1963, 6, 9, 0, 0, "Owensboro", "US", perspective_type="Topocentric")
810
+ johnny = AstrologicalSubject("Johnny Depp", 1963, 6, 9, 0, 0, "Owensboro", "US", perspective_type="Topocentric")
811
+
812
+ # Test Mean Lilith
813
+ johnny = AstrologicalSubject("Johnny Depp", 1963, 6, 9, 0, 0, "Owensboro", "US", disable_chiron_and_lilith=True)
814
+ print(johnny.mean_lilith)
815
+
816
+ # Offline mode
817
+ johnny = AstrologicalSubject("Johnny Depp", 1963, 6, 9, 0, 0, "Owensboro", "US", online=False, tz_str="America/New_York", lng=-87.1111, lat=37.7711)
818
+ print(johnny.json(dump=True, indent=2))