kerykeion 4.18.3__py3-none-any.whl → 5.1.9__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 (76) hide show
  1. kerykeion/__init__.py +56 -11
  2. kerykeion/aspects/__init__.py +7 -4
  3. kerykeion/aspects/aspects_factory.py +568 -0
  4. kerykeion/aspects/aspects_utils.py +86 -13
  5. kerykeion/astrological_subject_factory.py +1901 -0
  6. kerykeion/backword.py +820 -0
  7. kerykeion/chart_data_factory.py +552 -0
  8. kerykeion/charts/__init__.py +2 -2
  9. kerykeion/charts/chart_drawer.py +2794 -0
  10. kerykeion/charts/charts_utils.py +1066 -309
  11. kerykeion/charts/draw_planets.py +602 -351
  12. kerykeion/charts/templates/aspect_grid_only.xml +337 -193
  13. kerykeion/charts/templates/chart.xml +441 -240
  14. kerykeion/charts/templates/wheel_only.xml +365 -211
  15. kerykeion/charts/themes/black-and-white.css +148 -0
  16. kerykeion/charts/themes/classic.css +107 -76
  17. kerykeion/charts/themes/dark-high-contrast.css +145 -107
  18. kerykeion/charts/themes/dark.css +146 -107
  19. kerykeion/charts/themes/light.css +146 -103
  20. kerykeion/charts/themes/strawberry.css +158 -0
  21. kerykeion/composite_subject_factory.py +408 -0
  22. kerykeion/ephemeris_data_factory.py +443 -0
  23. kerykeion/fetch_geonames.py +81 -21
  24. kerykeion/house_comparison/__init__.py +6 -0
  25. kerykeion/house_comparison/house_comparison_factory.py +103 -0
  26. kerykeion/house_comparison/house_comparison_utils.py +126 -0
  27. kerykeion/kr_types/__init__.py +66 -6
  28. kerykeion/kr_types/chart_template_model.py +20 -0
  29. kerykeion/kr_types/kerykeion_exception.py +15 -9
  30. kerykeion/kr_types/kr_literals.py +14 -106
  31. kerykeion/kr_types/kr_models.py +14 -179
  32. kerykeion/kr_types/settings_models.py +15 -152
  33. kerykeion/planetary_return_factory.py +805 -0
  34. kerykeion/relationship_score_factory.py +301 -0
  35. kerykeion/report.py +750 -65
  36. kerykeion/schemas/__init__.py +106 -0
  37. kerykeion/schemas/chart_template_model.py +367 -0
  38. kerykeion/schemas/kerykeion_exception.py +20 -0
  39. kerykeion/schemas/kr_literals.py +181 -0
  40. kerykeion/schemas/kr_models.py +603 -0
  41. kerykeion/schemas/settings_models.py +188 -0
  42. kerykeion/settings/__init__.py +20 -1
  43. kerykeion/settings/chart_defaults.py +444 -0
  44. kerykeion/settings/config_constants.py +152 -0
  45. kerykeion/settings/kerykeion_settings.py +36 -61
  46. kerykeion/settings/translation_strings.py +1499 -0
  47. kerykeion/settings/translations.py +74 -0
  48. kerykeion/sweph/ast136/s136108s.se1 +0 -0
  49. kerykeion/sweph/ast136/s136199s.se1 +0 -0
  50. kerykeion/sweph/ast136/s136472s.se1 +0 -0
  51. kerykeion/sweph/ast28/se28978s.se1 +0 -0
  52. kerykeion/sweph/ast50/se50000s.se1 +0 -0
  53. kerykeion/sweph/ast90/se90377s.se1 +0 -0
  54. kerykeion/sweph/ast90/se90482s.se1 +0 -0
  55. kerykeion/sweph/sefstars.txt +1602 -0
  56. kerykeion/transits_time_range_factory.py +302 -0
  57. kerykeion/utilities.py +626 -125
  58. kerykeion-5.1.9.dist-info/METADATA +1793 -0
  59. kerykeion-5.1.9.dist-info/RECORD +63 -0
  60. {kerykeion-4.18.3.dist-info → kerykeion-5.1.9.dist-info}/WHEEL +1 -1
  61. kerykeion/aspects/natal_aspects.py +0 -143
  62. kerykeion/aspects/synastry_aspects.py +0 -113
  63. kerykeion/astrological_subject.py +0 -818
  64. kerykeion/charts/kerykeion_chart_svg.py +0 -894
  65. kerykeion/enums.py +0 -51
  66. kerykeion/ephemeris_data.py +0 -178
  67. kerykeion/kr_types/chart_types.py +0 -88
  68. kerykeion/relationship_score/__init__.py +0 -2
  69. kerykeion/relationship_score/relationship_score.py +0 -175
  70. kerykeion/relationship_score/relationship_score_factory.py +0 -275
  71. kerykeion/settings/kr.config.json +0 -721
  72. kerykeion-4.18.3.dist-info/LICENSE +0 -661
  73. kerykeion-4.18.3.dist-info/METADATA +0 -396
  74. kerykeion-4.18.3.dist-info/RECORD +0 -42
  75. kerykeion-4.18.3.dist-info/entry_points.txt +0 -3
  76. /LICENSE → /kerykeion-5.1.9.dist-info/licenses/LICENSE +0 -0
@@ -1,818 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- """
3
- This is part of Kerykeion (C) 2024 Giacomo Battaglia
4
- """
5
-
6
- import pytz
7
- import swisseph as swe
8
- import logging
9
- import warnings
10
-
11
- from datetime import datetime
12
- from functools import cached_property
13
- from kerykeion.fetch_geonames import FetchGeonames
14
- from kerykeion.kr_types import (
15
- KerykeionException,
16
- ZodiacType,
17
- AstrologicalSubjectModel,
18
- LunarPhaseModel,
19
- KerykeionPointModel,
20
- PointType,
21
- SiderealMode,
22
- HousesSystemIdentifier,
23
- PerspectiveType,
24
- Planet,
25
- Houses
26
- )
27
- from kerykeion.utilities import (
28
- get_number_from_name,
29
- get_kerykeion_point_from_degree,
30
- get_planet_house,
31
- get_moon_emoji_from_phase_int,
32
- get_moon_phase_name_from_phase_int,
33
- check_and_adjust_polar_latitude
34
- )
35
- from pathlib import Path
36
- from typing import Union, get_args
37
-
38
- DEFAULT_GEONAMES_USERNAME = "century.boy"
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"
43
- GEONAMES_DEFAULT_USERNAME_WARNING = (
44
- "\n********\n"
45
- "NO GEONAMES USERNAME SET!\n"
46
- "Using the default geonames username is not recommended, please set a custom one!\n"
47
- "You can get one for free here:\n"
48
- "https://www.geonames.org/login\n"
49
- "Keep in mind that the default username is limited to 2000 requests per hour and is shared with everyone else using this library.\n"
50
- "********"
51
- )
52
-
53
- NOW = datetime.now()
54
-
55
-
56
- class AstrologicalSubject:
57
- """
58
- Calculates all the astrological information, the coordinates,
59
- it's utc and julian day and returns an object with all that data.
60
-
61
- Args:
62
- - name (str, optional): The name of the subject. Defaults to "Now".
63
- - year (int, optional): The year of birth. Defaults to the current year.
64
- - month (int, optional): The month of birth. Defaults to the current month.
65
- - day (int, optional): The day of birth. Defaults to the current day.
66
- - hour (int, optional): The hour of birth. Defaults to the current hour.
67
- - minute (int, optional): Defaults to the current minute.
68
- - city (str, optional): City or location of birth. Defaults to "London", which is GMT time.
69
- The city argument is used to get the coordinates and timezone from geonames just in case
70
- you don't insert them manually (see _get_tz).
71
- If you insert the coordinates and timezone manually, the city argument is not used for calculations
72
- but it's still used as a value for the city attribute.
73
- - nat (str, optional): _ Defaults to "".
74
- - lng (Union[int, float], optional): Longitude of the birth location. Defaults to 0 (Greenwich, London).
75
- - lat (Union[int, float], optional): Latitude of the birth location. Defaults to 51.5074 (Greenwich, London).
76
- - tz_str (Union[str, bool], optional): Timezone of the birth location. Defaults to "GMT".
77
- - geonames_username (str, optional): The username for the geonames API. Note: Change this to your own username to avoid rate limits!
78
- You can get one for free here: https://www.geonames.org/login
79
- - online (bool, optional): Sets if you want to use the online mode, which fetches the timezone and coordinates from geonames.
80
- If you already have the coordinates and timezone, set this to False. Defaults to True.
81
- - disable_chiron: Deprecated, use disable_chiron_and_lilith instead.
82
- - sidereal_mode (SiderealMode, optional): Also known as Ayanamsa.
83
- The mode to use for the sidereal zodiac, according to the Swiss Ephemeris.
84
- Defaults to "FAGAN_BRADLEY".
85
- Available modes are visible in the SiderealMode Literal.
86
- - houses_system_identifier (HousesSystemIdentifier, optional): The system to use for the calculation of the houses.
87
- Defaults to "P" (Placidus).
88
- Available systems are visible in the HousesSystemIdentifier Literal.
89
- - perspective_type (PerspectiveType, optional): The perspective to use for the calculation of the chart.
90
- Defaults to "Apparent Geocentric".
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.
97
- """
98
-
99
- # Defined by the user
100
- name: str
101
- year: int
102
- month: int
103
- day: int
104
- hour: int
105
- minute: int
106
- city: str
107
- nation: str
108
- lng: Union[int, float]
109
- lat: Union[int, float]
110
- tz_str: str
111
- geonames_username: str
112
- online: bool
113
- zodiac_type: ZodiacType
114
- sidereal_mode: Union[SiderealMode, None]
115
- houses_system_identifier: HousesSystemIdentifier
116
- houses_system_name: str
117
- perspective_type: PerspectiveType
118
- is_dst: Union[None, bool]
119
-
120
- # Generated internally
121
- city_data: dict[str, str]
122
- julian_day: Union[int, float]
123
- json_dir: Path
124
- iso_formatted_local_datetime: str
125
- iso_formatted_utc_datetime: str
126
-
127
- # Planets
128
- sun: KerykeionPointModel
129
- moon: KerykeionPointModel
130
- mercury: KerykeionPointModel
131
- venus: KerykeionPointModel
132
- mars: KerykeionPointModel
133
- jupiter: KerykeionPointModel
134
- saturn: KerykeionPointModel
135
- uranus: KerykeionPointModel
136
- neptune: KerykeionPointModel
137
- pluto: KerykeionPointModel
138
- true_node: KerykeionPointModel
139
- mean_node: KerykeionPointModel
140
- chiron: Union[KerykeionPointModel, None]
141
- mean_lilith: Union[KerykeionPointModel, None]
142
-
143
- # Houses
144
- first_house: KerykeionPointModel
145
- second_house: KerykeionPointModel
146
- third_house: KerykeionPointModel
147
- fourth_house: KerykeionPointModel
148
- fifth_house: KerykeionPointModel
149
- sixth_house: KerykeionPointModel
150
- seventh_house: KerykeionPointModel
151
- eighth_house: KerykeionPointModel
152
- ninth_house: KerykeionPointModel
153
- tenth_house: KerykeionPointModel
154
- eleventh_house: KerykeionPointModel
155
- twelfth_house: KerykeionPointModel
156
-
157
- # Lists
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]
167
-
168
- def __init__(
169
- self,
170
- name="Now",
171
- year: int = NOW.year,
172
- month: int = NOW.month,
173
- day: int = NOW.day,
174
- hour: int = NOW.hour,
175
- minute: int = NOW.minute,
176
- city: Union[str, None] = None,
177
- nation: Union[str, None] = None,
178
- lng: Union[int, float, None] = None,
179
- lat: Union[int, float, None] = None,
180
- tz_str: Union[str, None] = None,
181
- geonames_username: Union[str, None] = None,
182
- zodiac_type: ZodiacType = DEFAULT_ZODIAC_TYPE,
183
- online: bool = True,
184
- disable_chiron: Union[None, bool] = None, # Deprecated
185
- sidereal_mode: Union[SiderealMode, None] = None,
186
- houses_system_identifier: HousesSystemIdentifier = DEFAULT_HOUSES_SYSTEM_IDENTIFIER,
187
- perspective_type: PerspectiveType = DEFAULT_PERSPECTIVE_TYPE,
188
- is_dst: Union[None, bool] = None,
189
- disable_chiron_and_lilith: bool = False
190
- ) -> None:
191
- logging.debug("Starting Kerykeion")
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
-
207
- self.name = name
208
- self.year = year
209
- self.month = month
210
- self.day = day
211
- self.hour = hour
212
- self.minute = minute
213
- self.zodiac_type = zodiac_type
214
- self.online = online
215
- self.json_dir = Path.home()
216
- self.disable_chiron = disable_chiron
217
- self.sidereal_mode = sidereal_mode
218
- self.houses_system_identifier = houses_system_identifier
219
- self.perspective_type = perspective_type
220
- self.is_dst = is_dst
221
- self.disable_chiron_and_lilith = disable_chiron_and_lilith
222
-
223
- #---------------#
224
- # General setup #
225
- #---------------#
226
-
227
- # Geonames username
228
- if geonames_username is None and online:
229
- logging.warning(GEONAMES_DEFAULT_USERNAME_WARNING)
230
- self.geonames_username = DEFAULT_GEONAMES_USERNAME
231
- else:
232
- self.geonames_username = geonames_username # type: ignore
233
-
234
- # City
235
- if not city:
236
- self.city = "London"
237
- logging.info("No city specified, using London as default")
238
- else:
239
- self.city = city
240
-
241
- # Nation
242
- if not nation:
243
- self.nation = "GB"
244
- logging.info("No nation specified, using GB as default")
245
- else:
246
- self.nation = nation
247
-
248
- # Latitude
249
- if not lat and not self.online:
250
- self.lat = 51.5074
251
- logging.info("No latitude specified, using London as default")
252
- else:
253
- self.lat = lat # type: ignore
254
-
255
- # Longitude
256
- if not lng and not self.online:
257
- self.lng = 0
258
- logging.info("No longitude specified, using London as default")
259
- else:
260
- self.lng = lng # type: ignore
261
-
262
- # Timezone
263
- if (not self.online) and (not tz_str):
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
267
-
268
- #-----------------------#
269
- # Swiss Ephemeris setup #
270
- #-----------------------#
271
-
272
- # We set the swisseph path to the current directory
273
- swe.set_ephe_path(str(Path(__file__).parent.absolute() / "sweph"))
274
-
275
- # Flags for the Swiss Ephemeris
276
- self._iflag = swe.FLG_SWIEPH + swe.FLG_SPEED
277
-
278
- # Chart Perspective check and setup --->
279
- if self.perspective_type not in get_args(PerspectiveType):
280
- raise KerykeionException(f"\n* ERROR: '{self.perspective_type}' is NOT a valid chart perspective! Available perspectives are: *" + "\n" + str(get_args(PerspectiveType)))
281
-
282
- if self.perspective_type == "True Geocentric":
283
- self._iflag += swe.FLG_TRUEPOS
284
- elif self.perspective_type == "Heliocentric":
285
- self._iflag += swe.FLG_HELCTR
286
- elif self.perspective_type == "Topocentric":
287
- self._iflag += swe.FLG_TOPOCTR
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()
291
- swe.set_topo(self.lng, self.lat, 0)
292
- # <--- Chart Perspective check and setup
293
-
294
- # House System check and setup --->
295
- if self.houses_system_identifier not in get_args(HousesSystemIdentifier):
296
- raise KerykeionException(f"\n* ERROR: '{self.houses_system_identifier}' is NOT a valid house system! Available systems are: *" + "\n" + str(get_args(HousesSystemIdentifier)))
297
-
298
- self.houses_system_name = swe.house_name(self.houses_system_identifier.encode('ascii'))
299
- # <--- House System check and setup
300
-
301
- # Zodiac Type and Sidereal mode checks and setup --->
302
- if zodiac_type and not zodiac_type in get_args(ZodiacType):
303
- raise KerykeionException(f"\n* ERROR: '{zodiac_type}' is NOT a valid zodiac type! Available types are: *" + "\n" + str(get_args(ZodiacType)))
304
-
305
- if self.sidereal_mode and self.zodiac_type == "Tropic":
306
- raise KerykeionException("You can't set a sidereal mode with a Tropic zodiac type!")
307
-
308
- if self.zodiac_type == "Sidereal" and not self.sidereal_mode:
309
- self.sidereal_mode = DEFAULT_SIDEREAL_MODE
310
- logging.info("No sidereal mode set, using default FAGAN_BRADLEY")
311
-
312
- if self.zodiac_type == "Sidereal":
313
- # Check if the sidereal mode is valid
314
-
315
- if not self.sidereal_mode or not self.sidereal_mode in get_args(SiderealMode):
316
- raise KerykeionException(f"\n* ERROR: '{self.sidereal_mode}' is NOT a valid sidereal mode! Available modes are: *" + "\n" + str(get_args(SiderealMode)))
317
-
318
- self._iflag += swe.FLG_SIDEREAL
319
- mode = "SIDM_" + self.sidereal_mode
320
- swe.set_sid_mode(getattr(swe, mode))
321
- logging.debug(f"Using sidereal mode: {mode}")
322
- # <--- Zodiac Type and Sidereal mode checks and setup
323
-
324
- #------------------------#
325
- # Start the calculations #
326
- #------------------------#
327
-
328
- # UTC, julian day and local time setup --->
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)
333
-
334
- # Local time to UTC
335
- local_time = pytz.timezone(self.tz_str)
336
- naive_datetime = datetime(self.year, self.month, self.day, self.hour, self.minute, 0)
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
-
343
- utc_object = local_datetime.astimezone(pytz.utc)
344
- self.iso_formatted_utc_datetime = utc_object.isoformat()
345
-
346
- # ISO formatted local datetime
347
- self.iso_formatted_local_datetime = local_datetime.isoformat()
348
-
349
- # Julian day calculation
350
- utc_float_hour_with_minutes = utc_object.hour + (utc_object.minute / 60)
351
- self.julian_day = float(swe.julday(utc_object.year, utc_object.month, utc_object.day, utc_float_hour_with_minutes))
352
- # <--- UTC, julian day and local time setup
353
-
354
- self._initialize_houses()
355
- self._initialize_planets()
356
- self._initialize_moon_phase()
357
-
358
- # Deprecated properties
359
- self.utc_time
360
- self.local_time
361
-
362
- def __str__(self) -> str:
363
- return f"Astrological data for: {self.name}, {self.iso_formatted_utc_datetime} UTC\nBirth location: {self.city}, Lat {self.lat}, Lon {self.lng}"
364
-
365
- def __repr__(self) -> str:
366
- return f"Astrological data for: {self.name}, {self.iso_formatted_utc_datetime} UTC\nBirth location: {self.city}, Lat {self.lat}, Lon {self.lng}"
367
-
368
- def __getitem__(self, item):
369
- return getattr(self, item)
370
-
371
- def get(self, item, default=None):
372
- return getattr(self, item, default)
373
-
374
- def _fetch_and_set_tz_and_coordinates_from_geonames(self) -> None:
375
- """Gets the nearest time zone for the calculation"""
376
- logging.info("Fetching timezone/coordinates from geonames")
377
-
378
- geonames = FetchGeonames(
379
- self.city,
380
- self.nation,
381
- username=self.geonames_username,
382
- )
383
- self.city_data: dict[str, str] = geonames.get_serialized_data()
384
-
385
- if (
386
- not "countryCode" in self.city_data
387
- or not "timezonestr" in self.city_data
388
- or not "lat" in self.city_data
389
- or not "lng" in self.city_data
390
- ):
391
- raise KerykeionException("No data found for this city, try again! Maybe check your connection?")
392
-
393
- self.nation = self.city_data["countryCode"]
394
- self.lng = float(self.city_data["lng"])
395
- self.lat = float(self.city_data["lat"])
396
- self.tz_str = self.city_data["timezonestr"]
397
-
398
- def _initialize_houses(self) -> None:
399
- """
400
- Calculate positions and store them in dictionaries
401
-
402
- https://www.astro.com/faq/fq_fh_owhouse_e.htm
403
- https://github.com/jwmatthys/pd-swisseph/blob/master/swehouse.c#L685
404
- hsys = letter code for house system;
405
- A equal
406
- E equal
407
- B Alcabitius
408
- C Campanus
409
- D equal (MC)
410
- F Carter "Poli-Equatorial"
411
- G 36 Gauquelin sectors
412
- H horizon / azimut
413
- I Sunshine solution Treindl
414
- i Sunshine solution Makransky
415
- K Koch
416
- L Pullen SD "sinusoidal delta", ex Neo-Porphyry
417
- M Morinus
418
- N equal/1=Aries
419
- O Porphyry
420
- P Placidus
421
- Q Pullen SR "sinusoidal ratio"
422
- R Regiomontanus
423
- S Sripati
424
- T Polich/Page ("topocentric")
425
- U Krusinski-Pisa-Goelzer
426
- V equal Vehlow
427
- W equal, whole sign
428
- X axial rotation system/ Meridian houses
429
- Y APC houses
430
- """
431
-
432
- if self.zodiac_type == "Sidereal":
433
- self._houses_degree_ut = swe.houses_ex(
434
- tjdut=self.julian_day,
435
- lat=self.lat, lon=self.lng,
436
- hsys=str.encode(self.houses_system_identifier),
437
- flags=swe.FLG_SIDEREAL
438
- )[0]
439
-
440
- elif self.zodiac_type == "Tropic":
441
- self._houses_degree_ut = swe.houses(
442
- tjdut=self.julian_day, lat=self.lat,
443
- lon=self.lng,
444
- hsys=str.encode(self.houses_system_identifier)
445
- )[0]
446
-
447
- point_type: PointType = "House"
448
-
449
- # stores the house in singular dictionaries.
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 = [
467
- self.first_house,
468
- self.second_house,
469
- self.third_house,
470
- self.fourth_house,
471
- self.fifth_house,
472
- self.sixth_house,
473
- self.seventh_house,
474
- self.eighth_house,
475
- self.ninth_house,
476
- self.tenth_house,
477
- self.eleventh_house,
478
- self.twelfth_house,
479
- ]
480
-
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"
486
-
487
- sun_deg = swe.calc(self.julian_day, 0, self._iflag)[0][0]
488
- moon_deg = swe.calc(self.julian_day, 1, self._iflag)[0][0]
489
- mercury_deg = swe.calc(self.julian_day, 2, self._iflag)[0][0]
490
- venus_deg = swe.calc(self.julian_day, 3, self._iflag)[0][0]
491
- mars_deg = swe.calc(self.julian_day, 4, self._iflag)[0][0]
492
- jupiter_deg = swe.calc(self.julian_day, 5, self._iflag)[0][0]
493
- saturn_deg = swe.calc(self.julian_day, 6, self._iflag)[0][0]
494
- uranus_deg = swe.calc(self.julian_day, 7, self._iflag)[0][0]
495
- neptune_deg = swe.calc(self.julian_day, 8, self._iflag)[0][0]
496
- pluto_deg = swe.calc(self.julian_day, 9, self._iflag)[0][0]
497
- mean_node_deg = swe.calc(self.julian_day, 10, self._iflag)[0][0]
498
- true_node_deg = swe.calc(self.julian_day, 11, self._iflag)[0][0]
499
-
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 = [
528
- self.sun,
529
- self.moon,
530
- self.mercury,
531
- self.venus,
532
- self.mars,
533
- self.jupiter,
534
- self.saturn,
535
- self.uranus,
536
- self.neptune,
537
- self.pluto,
538
- self.mean_node,
539
- self.true_node,
540
- ]
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]
562
-
563
- # Check in retrograde or not:
564
- for planet in planets_list:
565
- planet_number = get_number_from_name(planet["name"])
566
- if swe.calc(self.julian_day, planet_number, self._iflag)[0][3] < 0:
567
- planet["retrograde"] = True
568
- else:
569
- planet["retrograde"] = False
570
-
571
-
572
- def _initialize_moon_phase(self) -> None:
573
- """
574
- Calculate and initialize the lunar phase based on the positions of the moon and sun.
575
-
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
581
-
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
585
-
586
- # Calculate the moon phase (1-28) based on the degrees between the sun and moon
587
- step = 360.0 / 28.0
588
- moon_phase = int(degrees_between // step) + 1
589
-
590
- # Define the sun phase steps
591
- sunstep = [
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
594
- ]
595
-
596
- # Calculate the sun phase (1-28) based on the degrees between the sun and moon
597
- for x in range(len(sunstep)):
598
- low = sunstep[x]
599
- high = sunstep[x + 1] if x < len(sunstep) - 1 else 360
600
- if low <= degrees_between < high:
601
- sun_phase = x + 1
602
- break
603
-
604
- # Create a dictionary with the lunar phase information
605
- lunar_phase_dictionary = {
606
- "degrees_between_s_m": degrees_between,
607
- "moon_phase": moon_phase,
608
- "sun_phase": sun_phase,
609
- "moon_emoji": get_moon_emoji_from_phase_int(moon_phase),
610
- "moon_phase_name": get_moon_phase_name_from_phase_int(moon_phase)
611
- }
612
-
613
- # Initialize the lunar phase model with the calculated values
614
- self.lunar_phase = LunarPhaseModel(**lunar_phase_dictionary)
615
-
616
- def json(self, dump=False, destination_folder: Union[str, None] = None, indent: Union[int, None] = None) -> str:
617
- """
618
- Dumps the Kerykeion object to a json string foramt,
619
- if dump=True also dumps to file located in destination
620
- or the home folder.
621
- """
622
-
623
- KrData = AstrologicalSubjectModel(**self.__dict__)
624
- json_string = KrData.model_dump_json(exclude_none=True, indent=indent)
625
-
626
- if dump:
627
- if destination_folder:
628
- destination_path = Path(destination_folder)
629
- json_path = destination_path / f"{self.name}_kerykeion.json"
630
-
631
- else:
632
- json_path = self.json_dir / f"{self.name}_kerykeion.json"
633
-
634
- with open(json_path, "w", encoding="utf-8") as file:
635
- file.write(json_string)
636
- logging.info(f"JSON file dumped in {json_path}.")
637
-
638
- return json_string
639
-
640
- def model(self) -> AstrologicalSubjectModel:
641
- """
642
- Creates a Pydantic model of the Kerykeion object.
643
- """
644
-
645
- return AstrologicalSubjectModel(**self.__dict__)
646
-
647
- @cached_property
648
- def utc_time(self) -> float:
649
- """
650
- Deprecated property, use iso_formatted_utc_datetime instead, will be removed in the future.
651
- Returns the UTC time as a float.
652
- """
653
- dt = datetime.fromisoformat(self.iso_formatted_utc_datetime)
654
-
655
- # Extract the hours, minutes, and seconds
656
- hours = dt.hour
657
- minutes = dt.minute
658
- seconds = dt.second + dt.microsecond / 1_000_000
659
-
660
- # Convert time to float hours
661
- float_time = hours + minutes / 60 + seconds / 3600
662
-
663
- return float_time
664
-
665
- @cached_property
666
- def local_time(self) -> float:
667
- """
668
- Deprecated property, use iso_formatted_local_datetime instead, will be removed in the future.
669
- Returns the local time as a float.
670
- """
671
- dt = datetime.fromisoformat(self.iso_formatted_local_datetime)
672
-
673
- # Extract the hours, minutes, and seconds
674
- hours = dt.hour
675
- minutes = dt.minute
676
- seconds = dt.second + dt.microsecond / 1_000_000
677
-
678
- # Convert time to float hours
679
- float_time = hours + minutes / 60 + seconds / 3600
680
-
681
- return float_time
682
-
683
-
684
- @staticmethod
685
- def get_from_iso_utc_time(
686
- name: str,
687
- iso_utc_time: str,
688
- city: str = "Greenwich",
689
- nation: str = "GB",
690
- tz_str: str = "Etc/GMT",
691
- online: bool = False,
692
- lng: Union[int, float] = 0,
693
- lat: Union[int, float] = 51.5074,
694
- geonames_username: str = DEFAULT_GEONAMES_USERNAME,
695
- zodiac_type: ZodiacType = DEFAULT_ZODIAC_TYPE,
696
- disable_chiron_and_lilith: bool = False,
697
- sidereal_mode: Union[SiderealMode, None] = None,
698
- houses_system_identifier: HousesSystemIdentifier = DEFAULT_HOUSES_SYSTEM_IDENTIFIER,
699
- perspective_type: PerspectiveType = DEFAULT_PERSPECTIVE_TYPE
700
-
701
- ) -> "AstrologicalSubject":
702
- """
703
- Creates an AstrologicalSubject object from an iso formatted UTC time.
704
- This method is offline by default, set online=True to fetch the timezone and coordinates from geonames.
705
-
706
- Args:
707
- - name (str): The name of the subject.
708
- - iso_utc_time (str): The iso formatted UTC time.
709
- - city (str, optional): City or location of birth. Defaults to "Greenwich".
710
- - nation (str, optional): Nation of birth. Defaults to "GB".
711
- - tz_str (str, optional): Timezone of the birth location. Defaults to "Etc/GMT".
712
- - online (bool, optional): Sets if you want to use the online mode, which fetches the timezone and coordinates from geonames.
713
- If you already have the coordinates and timezone, set this to False. Defaults to False.
714
- - lng (Union[int, float], optional): Longitude of the birth location. Defaults to 0 (Greenwich, London).
715
- - lat (Union[int, float], optional): Latitude of the birth location. Defaults to 51.5074 (Greenwich, London).
716
- - geonames_username (str, optional): The username for the geonames API. Note: Change this to your own username to avoid rate limits!
717
- You can get one for free here: https://www.geonames.org/login
718
- - zodiac_type (ZodiacType, optional): The zodiac type to use. Defaults to "Tropic".
719
- - disable_chiron_and_lilith: boolean representing if Chiron and Lilith should be disabled. Default is False.
720
- Chiron calculation can create some issues with the Swiss Ephemeris when the date is too far in the past.
721
- - sidereal_mode (SiderealMode, optional): Also known as Ayanamsa.
722
- The mode to use for the sidereal zodiac, according to the Swiss Ephemeris.
723
- Defaults to None.
724
- Available modes are visible in the SiderealMode Literal.
725
- - houses_system_identifier (HousesSystemIdentifier, optional): The system to use for the calculation of the houses.
726
- Defaults to "P" (Placidus).
727
- Available systems are visible in the HousesSystemIdentifier Literal.
728
- - perspective_type (PerspectiveType, optional): The perspective to use for the calculation of the chart.
729
- Defaults to "Apparent Geocentric".
730
-
731
- Returns:
732
- - AstrologicalSubject: The AstrologicalSubject object.
733
- """
734
- dt = datetime.fromisoformat(iso_utc_time)
735
-
736
- if online == True:
737
- if geonames_username == DEFAULT_GEONAMES_USERNAME:
738
- logging.warning(GEONAMES_DEFAULT_USERNAME_WARNING)
739
-
740
- geonames = FetchGeonames(
741
- city,
742
- nation,
743
- username=geonames_username,
744
- )
745
-
746
- city_data: dict[str, str] = geonames.get_serialized_data()
747
- lng = float(city_data["lng"])
748
- lat = float(city_data["lat"])
749
-
750
- subject = AstrologicalSubject(
751
- name=name,
752
- year=dt.year,
753
- month=dt.month,
754
- day=dt.day,
755
- hour=dt.hour,
756
- minute=dt.minute,
757
- city=city,
758
- nation=city,
759
- lng=lng,
760
- lat=lat,
761
- tz_str=tz_str,
762
- online=False,
763
- geonames_username=geonames_username,
764
- zodiac_type=zodiac_type,
765
- sidereal_mode=sidereal_mode,
766
- houses_system_identifier=houses_system_identifier,
767
- perspective_type=perspective_type,
768
- disable_chiron_and_lilith=disable_chiron_and_lilith
769
- )
770
-
771
- return subject
772
-
773
- if __name__ == "__main__":
774
- import json
775
- from kerykeion.utilities import setup_logging
776
-
777
- setup_logging(level="debug")
778
-
779
- # With Chiron enabled
780
- johnny = AstrologicalSubject("Johnny Depp", 1963, 6, 9, 0, 0, "Owensboro", "US")
781
- print(json.loads(johnny.json(dump=True)))
782
-
783
- print('\n')
784
- print(johnny.chiron)
785
-
786
- # With Chiron disabled
787
- johnny = AstrologicalSubject("Johnny Depp", 1963, 6, 9, 0, 0, "Owensboro", "US", disable_chiron=True)
788
- print(json.loads(johnny.json(dump=True)))
789
-
790
- print('\n')
791
- print(johnny.chiron)
792
-
793
- # With Sidereal Zodiac
794
- johnny = AstrologicalSubject("Johnny Depp", 1963, 6, 9, 0, 0, "Owensboro", "US", zodiac_type="Sidereal", sidereal_mode="LAHIRI")
795
- print(johnny.json(dump=True, indent=2))
796
-
797
- # With Morinus Houses
798
- johnny = AstrologicalSubject("Johnny Depp", 1963, 6, 9, 0, 0, "Owensboro", "US", houses_system_identifier="M")
799
- print(johnny.json(dump=True, indent=2))
800
-
801
- # With True Geocentric Perspective
802
- johnny = AstrologicalSubject("Johnny Depp", 1963, 6, 9, 0, 0, "Owensboro", "US", perspective_type="True Geocentric")
803
- print(johnny.json(dump=True, indent=2))
804
-
805
- # With Heliocentric Perspective
806
- johnny = AstrologicalSubject("Johnny Depp", 1963, 6, 9, 0, 0, "Owensboro", "US", perspective_type="Heliocentric")
807
- print(johnny.json(dump=True, indent=2))
808
-
809
- # With Topocentric Perspective
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))