kerykeion 4.26.2__py3-none-any.whl → 5.0.0a1__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 (48) hide show
  1. kerykeion/__init__.py +8 -5
  2. kerykeion/aspects/aspects_utils.py +14 -8
  3. kerykeion/aspects/natal_aspects.py +26 -17
  4. kerykeion/aspects/synastry_aspects.py +32 -15
  5. kerykeion/aspects/transits_time_range.py +2 -2
  6. kerykeion/astrological_subject_factory.py +1132 -0
  7. kerykeion/charts/charts_utils.py +583 -85
  8. kerykeion/charts/draw_planets.py +9 -8
  9. kerykeion/charts/draw_planets_v2.py +639 -0
  10. kerykeion/charts/kerykeion_chart_svg.py +1289 -592
  11. kerykeion/charts/templates/chart.xml +178 -79
  12. kerykeion/charts/templates/wheel_only.xml +13 -12
  13. kerykeion/charts/themes/classic.css +91 -76
  14. kerykeion/charts/themes/dark-high-contrast.css +129 -107
  15. kerykeion/charts/themes/dark.css +130 -107
  16. kerykeion/charts/themes/light.css +130 -103
  17. kerykeion/charts/themes/strawberry.css +143 -0
  18. kerykeion/composite_subject_factory.py +26 -43
  19. kerykeion/ephemeris_data.py +6 -10
  20. kerykeion/house_comparison/__init__.py +3 -0
  21. kerykeion/house_comparison/house_comparison_factory.py +70 -0
  22. kerykeion/house_comparison/house_comparison_models.py +38 -0
  23. kerykeion/house_comparison/house_comparison_utils.py +98 -0
  24. kerykeion/kr_types/chart_types.py +9 -3
  25. kerykeion/kr_types/kr_literals.py +34 -6
  26. kerykeion/kr_types/kr_models.py +122 -160
  27. kerykeion/kr_types/settings_models.py +107 -143
  28. kerykeion/planetary_return_factory.py +299 -0
  29. kerykeion/relationship_score/relationship_score.py +3 -3
  30. kerykeion/relationship_score/relationship_score_factory.py +9 -12
  31. kerykeion/report.py +4 -4
  32. kerykeion/settings/config_constants.py +35 -6
  33. kerykeion/settings/kerykeion_settings.py +1 -0
  34. kerykeion/settings/kr.config.json +1301 -1255
  35. kerykeion/settings/legacy/__init__.py +0 -0
  36. kerykeion/settings/legacy/legacy_celestial_points_settings.py +299 -0
  37. kerykeion/settings/legacy/legacy_chart_aspects_settings.py +71 -0
  38. kerykeion/settings/legacy/legacy_color_settings.py +42 -0
  39. kerykeion/transits_time_range.py +13 -9
  40. kerykeion/utilities.py +228 -31
  41. {kerykeion-4.26.2.dist-info → kerykeion-5.0.0a1.dist-info}/METADATA +3 -3
  42. kerykeion-5.0.0a1.dist-info/RECORD +56 -0
  43. {kerykeion-4.26.2.dist-info → kerykeion-5.0.0a1.dist-info}/WHEEL +1 -1
  44. kerykeion/astrological_subject.py +0 -841
  45. kerykeion-4.26.2.dist-info/LICENSE +0 -661
  46. kerykeion-4.26.2.dist-info/RECORD +0 -46
  47. /LICENSE → /kerykeion-5.0.0a1.dist-info/LICENSE +0 -0
  48. {kerykeion-4.26.2.dist-info → kerykeion-5.0.0a1.dist-info}/entry_points.txt +0 -0
@@ -18,69 +18,10 @@ class KerykeionSettingsCelestialPointModel(SubscriptableBaseModel):
18
18
  name: str = Field(title="Celestial Point Name", description="Celestial Point Name")
19
19
  color: str = Field(title="Celestial Point Color", description="Celestial Point Color, used in the chart")
20
20
  element_points: int = Field(title="Celestial Point Element Points", description="Element Points given to the celestial point")
21
- related_zodiac_signs: List[int] = Field(title="Celestial Point Related Zodiac Signs", description="Zodiac Signs related to the celestial point")
22
21
  label: str = Field(title="Celestial Point Label", description="The name of the celestial point in the chart, it can be different from the name")
23
22
  is_active: Optional[bool] = Field(title="Celestial Point is Active", description="Indicates if the celestial point is active in the chart", default=None)
24
23
 
25
24
 
26
- # Chart Colors Settings
27
- class KerykeionSettingsChartColorsModel(SubscriptableBaseModel):
28
- """
29
- Defines the model for the chart colors.
30
- """
31
-
32
- paper_0: str = Field(title="Paper Color 0", description="Paper Color 0")
33
- paper_1: str = Field(title="Paper Color 1", description="Paper Color 1")
34
- zodiac_bg_0: str = Field(title="Zodiac Background Color 0", description="Zodiac Background Color 0")
35
- zodiac_bg_1: str = Field(title="Zodiac Background Color 1", description="Zodiac Background Color 1")
36
- zodiac_bg_2: str = Field(title="Zodiac Background Color 2", description="Zodiac Background Color 2")
37
- zodiac_bg_3: str = Field(title="Zodiac Background Color 3", description="Zodiac Background Color 3")
38
- zodiac_bg_4: str = Field(title="Zodiac Background Color 4", description="Zodiac Background Color 4")
39
- zodiac_bg_5: str = Field(title="Zodiac Background Color 5", description="Zodiac Background Color 5")
40
- zodiac_bg_6: str = Field(title="Zodiac Background Color 6", description="Zodiac Background Color 6")
41
- zodiac_bg_7: str = Field(title="Zodiac Background Color 7", description="Zodiac Background Color 7")
42
- zodiac_bg_8: str = Field(title="Zodiac Background Color 8", description="Zodiac Background Color 8")
43
- zodiac_bg_9: str = Field(title="Zodiac Background Color 9", description="Zodiac Background Color 9")
44
- zodiac_bg_10: str = Field(title="Zodiac Background Color 10", description="Zodiac Background Color 10")
45
- zodiac_bg_11: str = Field(title="Zodiac Background Color 11", description="Zodiac Background Color 11")
46
- zodiac_icon_0: str = Field(title="Zodiac Icon Color 0", description="Zodiac Icon Color 0")
47
- zodiac_icon_1: str = Field(title="Zodiac Icon Color 1", description="Zodiac Icon Color 1")
48
- zodiac_icon_2: str = Field(title="Zodiac Icon Color 2", description="Zodiac Icon Color 2")
49
- zodiac_icon_3: str = Field(title="Zodiac Icon Color 3", description="Zodiac Icon Color 3")
50
- zodiac_icon_4: str = Field(title="Zodiac Icon Color 4", description="Zodiac Icon Color 4")
51
- zodiac_icon_5: str = Field(title="Zodiac Icon Color 5", description="Zodiac Icon Color 5")
52
- zodiac_icon_6: str = Field(title="Zodiac Icon Color 6", description="Zodiac Icon Color 6")
53
- zodiac_icon_7: str = Field(title="Zodiac Icon Color 7", description="Zodiac Icon Color 7")
54
- zodiac_icon_8: str = Field(title="Zodiac Icon Color 8", description="Zodiac Icon Color 8")
55
- zodiac_icon_9: str = Field(title="Zodiac Icon Color 9", description="Zodiac Icon Color 9")
56
- zodiac_icon_10: str = Field(title="Zodiac Icon Color 10", description="Zodiac Icon Color 10")
57
- zodiac_icon_11: str = Field(title="Zodiac Icon Color 11", description="Zodiac Icon Color 11")
58
- zodiac_radix_ring_0: str = Field(title="Zodiac Radix Ring Color 0", description="Zodiac Radix Ring Color 0")
59
- zodiac_radix_ring_1: str = Field(title="Zodiac Radix Ring Color 1", description="Zodiac Radix Ring Color 1")
60
- zodiac_radix_ring_2: str = Field(title="Zodiac Radix Ring Color 2", description="Zodiac Radix Ring Color 2")
61
- zodiac_transit_ring_0: str = Field(title="Zodiac Transit Ring Color 0", description="Zodiac Transit Ring Color 0")
62
- zodiac_transit_ring_1: str = Field(title="Zodiac Transit Ring Color 1", description="Zodiac Transit Ring Color 1")
63
- zodiac_transit_ring_2: str = Field(title="Zodiac Transit Ring Color 2", description="Zodiac Transit Ring Color 2")
64
- zodiac_transit_ring_3: str = Field(title="Zodiac Transit Ring Color 3", description="Zodiac Transit Ring Color 3")
65
- houses_radix_line: str = Field(title="Houses Radix Line Color", description="Houses Radix Line Color")
66
- houses_transit_line: str = Field(title="Houses Transit Line Color", description="Houses Transit Line Color")
67
-
68
- # Deprecated: Not used anymore
69
- lunar_phase_0: str = Field(title="Lunar Phase Color 0", description="Lunar Phase Color 0")
70
- lunar_phase_1: str = Field(title="Lunar Phase Color 1", description="Lunar Phase Color 1")
71
-
72
-
73
- # Aspect Settings
74
- class KerykeionSettingsAspectModel(SubscriptableBaseModel):
75
- """
76
- Defines the model for an aspect.
77
- """
78
-
79
- degree: int = Field(title="Aspect Degrees", description="The degree of the aspect")
80
- name: str = Field(title="Aspect Name", description="The name of the aspect")
81
- color: str = Field(title="Aspect Color", description="The color of the aspect")
82
- orb: Optional[int] = Field(title="Aspect Orb", description="The orb of the aspect", default=None)
83
-
84
25
  # Language Settings
85
26
  class KerykeionLanguageCelestialPointModel(SubscriptableBaseModel):
86
27
  """
@@ -108,7 +49,28 @@ class KerykeionLanguageCelestialPointModel(SubscriptableBaseModel):
108
49
  Mean_Lilith: str = Field(title="Mean Lilith", description="The name of Mean Lilith in the chart, in the language")
109
50
  True_South_Node: str = Field(title="True South Node", description="The name of True South Node in the chart, in the language")
110
51
  Mean_South_Node: str = Field(title="Mean South Node", description="The name of Mean South Node in the chart, in the language")
111
-
52
+ True_Lilith: str = Field(title="True Lilith", description="The name of True Lilith in the chart, in the language")
53
+ Earth: str = Field(title="Earth", description="The name of Earth in the chart, in the language")
54
+ Pholus: str = Field(title="Pholus", description="The name of Pholus in the chart, in the language")
55
+ Ceres: str = Field(title="Ceres", description="The name of Ceres in the chart, in the language")
56
+ Pallas: str = Field(title="Pallas", description="The name of Pallas in the chart, in the language")
57
+ Juno: str = Field(title="Juno", description="The name of Juno in the chart, in the language")
58
+ Vesta: str = Field(title="Vesta", description="The name of Vesta in the chart, in the language")
59
+ Eris: str = Field(title="Eris", description="The name of Eris in the chart, in the language")
60
+ Sedna: str = Field(title="Sedna", description="The name of Sedna in the chart, in the language")
61
+ Haumea: str = Field(title="Haumea", description="The name of Haumea in the chart, in the language")
62
+ Makemake: str = Field(title="Makemake", description="The name of Makemake in the chart, in the language")
63
+ Ixion: str = Field(title="Ixion", description="The name of Ixion in the chart, in the language")
64
+ Orcus: str = Field(title="Orcus", description="The name of Orcus in the chart, in the language")
65
+ Quaoar: str = Field(title="Quaoar", description="The name of Quaoar in the chart, in the language")
66
+ Regulus: str = Field(title="Regulus", description="The name of Regulus in the chart, in the language")
67
+ Spica: str = Field(title="Spica", description="The name of Spica in the chart, in the language")
68
+ Pars_Fortunae: str = Field(title="Part of Fortune", description="The name of Part of Fortune in the chart, in the language")
69
+ Pars_Spiritus: str = Field(title="Part of Spirit", description="The name of Part of Spirit in the chart, in the language")
70
+ Pars_Amoris: str = Field(title="Part of Love", description="The name of Part of Love in the chart, in the language")
71
+ Pars_Fidei: str = Field(title="Part of Faith", description="The name of Part of Faith in the chart, in the language")
72
+ Vertex: str = Field(title="Vertex", description="The name of Vertex in the chart, in the language")
73
+ Anti_Vertex: str = Field(title="Anti-Vertex", description="The name of Anti-Vertex in the chart, in the language")
112
74
 
113
75
  class KerykeionLanguageModel(SubscriptableBaseModel):
114
76
  """
@@ -116,93 +78,95 @@ class KerykeionLanguageModel(SubscriptableBaseModel):
116
78
  it's used to translate the celestial points and the other labels
117
79
  """
118
80
 
119
- info: str
120
- cusp: str
121
- longitude: str
122
- latitude: str
123
- north: str
124
- east: str
125
- south: str
126
- west: str
127
- fire: str
128
- earth: str
129
- air: str
130
- water: str
131
- and_word: str
132
- transits: str
133
- type: str
134
- couple_aspects: str
135
- transit_aspects: str
136
- planets_and_house: str
137
- transit_name: str
138
- lunar_phase: str
139
- day: str
140
- celestial_points: KerykeionLanguageCelestialPointModel
141
- composite_chart: str
142
- midpoints: str
143
- north_letter: str
144
- east_letter: str
145
- south_letter: str
146
- west_letter: str
147
- tropical: str
148
- sidereal: str
149
- zodiac: str
150
- ayanamsa: str
151
- apparent_geocentric: str
152
- heliocentric: str
153
- topocentric: str
154
- true_geocentric: str
155
- new_moon: str
156
- waxing_crescent: str
157
- first_quarter: str
158
- waxing_gibbous: str
159
- full_moon: str
160
- waning_gibbous: str
161
- last_quarter: str
162
- waning_crescent: str
163
- houses: str
164
- houses_system_A: str
165
- houses_system_B: str
166
- houses_system_C: str
167
- houses_system_D: str
168
- houses_system_F: str
169
- houses_system_H: str
170
- houses_system_I: str
171
- houses_system_i: str
172
- houses_system_K: str
173
- houses_system_L: str
174
- houses_system_M: str
175
- houses_system_N: str
176
- houses_system_O: str
177
- houses_system_P: str
178
- houses_system_Q: str
179
- houses_system_R: str
180
- houses_system_S: str
181
- houses_system_T: str
182
- houses_system_U: str
183
- houses_system_V: str
184
- houses_system_W: str
185
- houses_system_X: str
186
- houses_system_Y: str
187
- Natal: str
188
- ExternalNatal: str
189
- Synastry: str
190
- Transit: str
191
- Composite: str
192
-
193
-
194
- class KerykeionGeneralSettingsModel(SubscriptableBaseModel):
195
- axes_orbit: int = Field(title="Axes Orbit", description="The orbit of the axes in the chart")
196
-
81
+ info: str = Field(title="Info", description="The info label in the chart, in the language")
82
+ cusp: str = Field(title="Cusp", description="The cusp label in the chart, in the language")
83
+ longitude: str = Field(title="Longitude", description="The longitude label in the chart, in the language")
84
+ latitude: str = Field(title="Latitude", description="The latitude label in the chart, in the language")
85
+ north: str = Field(title="North", description="The north label in the chart, in the language")
86
+ east: str = Field(title="East", description="The east label in the chart, in the language")
87
+ south: str = Field(title="South", description="The south label in the chart, in the language")
88
+ west: str = Field(title="West", description="The west label in the chart, in the language")
89
+ fire: str = Field(title="Fire", description="The fire element label in the chart, in the language")
90
+ earth: str = Field(title="Earth", description="The earth element label in the chart, in the language")
91
+ air: str = Field(title="Air", description="The air element label in the chart, in the language")
92
+ water: str = Field(title="Water", description="The water element label in the chart, in the language")
93
+ and_word: str = Field(title="And", description="The 'and' word in the chart, in the language")
94
+ transits: str = Field(title="Transits", description="The transits label in the chart, in the language")
95
+ type: str = Field(title="Type", description="The type label in the chart, in the language")
96
+ couple_aspects: str = Field(title="Couple Aspects", description="The couple aspects label in the chart, in the language")
97
+ transit_aspects: str = Field(title="Transit Aspects", description="The transit aspects label in the chart, in the language")
98
+ planets_and_house: str = Field(title="Planets and House", description="The planets and house label in the chart, in the language")
99
+ transit_name: str = Field(title="Transit Name", description="The transit name label in the chart, in the language")
100
+ lunar_phase: str = Field(title="Lunar Phase", description="The lunar phase label in the chart, in the language")
101
+ lunation_day: str = Field(title="Lunation Day", description="The lunation day label in the chart, in the language")
102
+ day: str = Field(title="Day", description="The day label in the chart, in the language")
103
+ celestial_points: KerykeionLanguageCelestialPointModel = Field(title="Celestial Points", description="The celestial points translations in the chart, in the language")
104
+ composite_chart: str = Field(title="Composite Chart", description="The composite chart label in the chart, in the language")
105
+ midpoints: str = Field(title="Midpoints", description="The midpoints label in the chart, in the language")
106
+ north_letter: str = Field(title="North Letter", description="The north letter in the chart, in the language")
107
+ east_letter: str = Field(title="East Letter", description="The east letter in the chart, in the language")
108
+ south_letter: str = Field(title="South Letter", description="The south letter in the chart, in the language")
109
+ west_letter: str = Field(title="West Letter", description="The west letter in the chart, in the language")
110
+ tropical: str = Field(title="Tropical", description="The tropical zodiac label in the chart, in the language")
111
+ sidereal: str = Field(title="Sidereal", description="The sidereal zodiac label in the chart, in the language")
112
+ zodiac: str = Field(title="Zodiac", description="The zodiac label in the chart, in the language")
113
+ ayanamsa: str = Field(title="Ayanamsa", description="The ayanamsa label in the chart, in the language")
114
+ apparent_geocentric: str = Field(title="Apparent Geocentric", description="The apparent geocentric label in the chart, in the language")
115
+ heliocentric: str = Field(title="Heliocentric", description="The heliocentric label in the chart, in the language")
116
+ topocentric: str = Field(title="Topocentric", description="The topocentric label in the chart, in the language")
117
+ true_geocentric: str = Field(title="True Geocentric", description="The true geocentric label in the chart, in the language")
118
+ new_moon: str = Field(title="New Moon", description="The new moon label in the chart, in the language")
119
+ waxing_crescent: str = Field(title="Waxing Crescent", description="The waxing crescent label in the chart, in the language")
120
+ first_quarter: str = Field(title="First Quarter", description="The first quarter label in the chart, in the language")
121
+ waxing_gibbous: str = Field(title="Waxing Gibbous", description="The waxing gibbous label in the chart, in the language")
122
+ full_moon: str = Field(title="Full Moon", description="The full moon label in the chart, in the language")
123
+ waning_gibbous: str = Field(title="Waning Gibbous", description="The waning gibbous label in the chart, in the language")
124
+ last_quarter: str = Field(title="Last Quarter", description="The last quarter label in the chart, in the language")
125
+ waning_crescent: str = Field(title="Waning Crescent", description="The waning crescent label in the chart, in the language")
126
+ houses: str = Field(title="Houses", description="The houses label in the chart, in the language")
127
+ domification: str = Field(title="Domification", description="The domification label in the chart, in the language")
128
+ houses_system_A: str = Field(title="Houses System A", description="The houses system A label in the chart, in the language")
129
+ houses_system_B: str = Field(title="Houses System B", description="The houses system B label in the chart, in the language")
130
+ houses_system_C: str = Field(title="Houses System C", description="The houses system C label in the chart, in the language")
131
+ houses_system_D: str = Field(title="Houses System D", description="The houses system D label in the chart, in the language")
132
+ houses_system_F: str = Field(title="Houses System F", description="The houses system F label in the chart, in the language")
133
+ houses_system_H: str = Field(title="Houses System H", description="The houses system H label in the chart, in the language")
134
+ houses_system_I: str = Field(title="Houses System I", description="The houses system I label in the chart, in the language")
135
+ houses_system_i: str = Field(title="Houses System i", description="The houses system i label in the chart, in the language")
136
+ houses_system_K: str = Field(title="Houses System K", description="The houses system K label in the chart, in the language")
137
+ houses_system_L: str = Field(title="Houses System L", description="The houses system L label in the chart, in the language")
138
+ houses_system_M: str = Field(title="Houses System M", description="The houses system M label in the chart, in the language")
139
+ houses_system_N: str = Field(title="Houses System N", description="The houses system N label in the chart, in the language")
140
+ houses_system_O: str = Field(title="Houses System O", description="The houses system O label in the chart, in the language")
141
+ houses_system_P: str = Field(title="Houses System P", description="The houses system P label in the chart, in the language")
142
+ houses_system_Q: str = Field(title="Houses System Q", description="The houses system Q label in the chart, in the language")
143
+ houses_system_R: str = Field(title="Houses System R", description="The houses system R label in the chart, in the language")
144
+ houses_system_S: str = Field(title="Houses System S", description="The houses system S label in the chart, in the language")
145
+ houses_system_T: str = Field(title="Houses System T", description="The houses system T label in the chart, in the language")
146
+ houses_system_U: str = Field(title="Houses System U", description="The houses system U label in the chart, in the language")
147
+ houses_system_V: str = Field(title="Houses System V", description="The houses system V label in the chart, in the language")
148
+ houses_system_W: str = Field(title="Houses System W", description="The houses system W label in the chart, in the language")
149
+ houses_system_X: str = Field(title="Houses System X", description="The houses system X label in the chart, in the language")
150
+ houses_system_Y: str = Field(title="Houses System Y", description="The houses system Y label in the chart, in the language")
151
+ Natal: str = Field(title="Natal", description="The natal chart label in the chart, in the language")
152
+ ExternalNatal: str = Field(title="External Natal", description="The external natal chart label in the chart, in the language")
153
+ Synastry: str = Field(title="Synastry", description="The synastry chart label in the chart, in the language")
154
+ Transit: str = Field(title="Transit", description="The transit chart label in the chart, in the language")
155
+ Composite: str = Field(title="Composite", description="The composite chart label in the chart, in the language")
156
+ Return: str = Field(title="Return", description="The return chart label in the chart, in the language")
157
+ return_aspects: str = Field(title="Return Aspects", description="The return aspects label in the chart, in the language")
158
+ solar_return: str = Field(title="Solar Return", description="The solar return label in the chart, in the language")
159
+ lunar_return: str = Field(title="Lunar Return", description="The lunar return label in the chart, in the language")
160
+ inner_wheel: str = Field(title="Inner Wheel", description="The inner wheel label in the chart, in the language")
161
+ outer_wheel: str = Field(title="Outer Wheel", description="The outer wheel label in the chart, in the language")
162
+ house_position_comparison: str = Field(title="House Position Comparison", description="The house position comparison label in the chart, in the language")
163
+ return_point: str = Field(title="Return Point", description="The return point label in the chart, in the language")
164
+ natal: str = Field(title="Natal", description="The natal label in the chart, in the language")
165
+ perspective_type: str = Field(title="Perspective Type", description="The perspective type label in the chart, in the language")
197
166
 
198
167
  # Settings Model
199
168
  class KerykeionSettingsModel(SubscriptableBaseModel):
200
169
  """
201
170
  This class is used to define the global settings for the Kerykeion.
202
171
  """
203
-
204
- chart_colors: KerykeionSettingsChartColorsModel = Field(title="Chart Colors", description="The colors of the chart")
205
- celestial_points: List[KerykeionSettingsCelestialPointModel] = Field(title="Celestial Points", description="The list of the celestial points of the chart")
206
- aspects: List[KerykeionSettingsAspectModel] = Field(title="Aspects", description="The list of the aspects of the chart")
207
172
  language_settings: dict[str, KerykeionLanguageModel] = Field(title="Language Settings", description="The language settings of the chart")
208
- general_settings: KerykeionGeneralSettingsModel = Field(title="General Settings", description="The general settings of the chart")
@@ -0,0 +1,299 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Module for generating Planetary Return charts using the Swiss Ephemeris.
4
+
5
+ This module provides the PlanetaryReturnFactory class, which computes
6
+ the return times and chart data for solar and lunar returns of a natal
7
+ astrological subject.
8
+ """
9
+ import logging
10
+ import swisseph as swe
11
+
12
+ from datetime import datetime, timezone
13
+ from typing import Optional, Union
14
+
15
+ from kerykeion.kr_types import KerykeionException
16
+ from kerykeion.fetch_geonames import FetchGeonames
17
+ from kerykeion.utilities import julian_to_datetime, datetime_to_julian
18
+ from kerykeion.astrological_subject_factory import (
19
+ GEONAMES_DEFAULT_USERNAME_WARNING,
20
+ DEFAULT_GEONAMES_CACHE_EXPIRE_AFTER_DAYS,
21
+ )
22
+ from kerykeion.astrological_subject_factory import AstrologicalSubjectFactory
23
+ from kerykeion.kr_types.kr_literals import ReturnType
24
+ from kerykeion.kr_types.kr_models import PlanetReturnModel, AstrologicalSubjectModel
25
+
26
+
27
+ class PlanetaryReturnFactory:
28
+ """
29
+ Calculator class to generate Solar and Lunar Return charts.
30
+ """
31
+
32
+ def __init__(
33
+ self,
34
+ subject: AstrologicalSubjectModel,
35
+ city: Union[str, None] = None,
36
+ nation: Union[str, None] = None,
37
+ lng: Union[int, float, None] = None,
38
+ lat: Union[int, float, None] = None,
39
+ tz_str: Union[str, None] = None,
40
+ online: bool = True,
41
+ geonames_username: Union[str, None] = None,
42
+ *,
43
+ cache_expire_after_days: int = DEFAULT_GEONAMES_CACHE_EXPIRE_AFTER_DAYS,
44
+ altitude: Union[float, int, None] = None,
45
+ ):
46
+
47
+ """
48
+ Initialize a PlanetaryReturnFactory instance.
49
+
50
+ Args:
51
+ subject (AstrologicalSubject): The natal astrological subject.
52
+ city (Optional[str]): City name for return location (online mode).
53
+ nation (Optional[str]): Nation code for return location (online mode).
54
+ lng (Optional[Union[int, float]]): Longitude for return location.
55
+ lat (Optional[Union[int, float]]): Latitude for return location.
56
+ tz_str (Optional[str]): Timezone string for return location.
57
+ online (bool): Whether to fetch location data online via Geonames.
58
+ geonames_username (Optional[str]): Username for Geonames API.
59
+ cache_expire_after_days (int): Days to expire Geonames cache.
60
+ altitude (Optional[float]): Altitude of the location (reserved for future use).
61
+ """
62
+ # Store basic configuration
63
+ self.subject = subject
64
+ self.online = online
65
+ self.cache_expire_after_days = cache_expire_after_days
66
+ self.altitude = altitude
67
+
68
+ # Geonames username
69
+ if geonames_username is None and online and (not lat or not lng or not tz_str):
70
+ logging.warning(GEONAMES_DEFAULT_USERNAME_WARNING)
71
+ self.geonames_username = DEFAULT_GEONAMES_USERNAME
72
+ else:
73
+ self.geonames_username = geonames_username # type: ignore
74
+
75
+ # City
76
+ if not city and online:
77
+ raise KerykeionException("You need to set the city if you want to use the online mode!")
78
+ else:
79
+ self.city = city
80
+
81
+ # Nation
82
+ if not nation and online:
83
+ raise KerykeionException("You need to set the nation if you want to use the online mode!")
84
+ else:
85
+ self.nation = nation
86
+
87
+ # Latitude
88
+ if not lat and not online:
89
+ raise KerykeionException("You need to set the coordinates and timezone if you want to use the offline mode!")
90
+ else:
91
+ self.lat = lat # type: ignore
92
+
93
+ # Longitude
94
+ if not lng and not online:
95
+ raise KerykeionException("You need to set the coordinates and timezone if you want to use the offline mode!")
96
+ else:
97
+ self.lng = lng # type: ignore
98
+
99
+ # Timezone
100
+ if (not online) and (not tz_str):
101
+ raise KerykeionException("You need to set the coordinates and timezone if you want to use the offline mode!")
102
+ else:
103
+ self.tz_str = tz_str # type: ignore
104
+
105
+ # Online mode
106
+ if (self.online) and (not self.tz_str) and (not self.lat) and (not self.lng):
107
+ logging.info("Fetching timezone/coordinates from geonames")
108
+
109
+ if not self.city or not self.nation or not self.geonames_username:
110
+ raise KerykeionException("You need to set the city and nation if you want to use the online mode!")
111
+
112
+ geonames = FetchGeonames(
113
+ self.city,
114
+ self.nation,
115
+ username=self.geonames_username,
116
+ cache_expire_after_days=self.cache_expire_after_days
117
+ )
118
+ self.city_data: dict[str, str] = geonames.get_serialized_data()
119
+
120
+ if (
121
+ not "countryCode" in self.city_data
122
+ or not "timezonestr" in self.city_data
123
+ or not "lat" in self.city_data
124
+ or not "lng" in self.city_data
125
+ ):
126
+ raise KerykeionException("No data found for this city, try again! Maybe check your connection?")
127
+
128
+ self.nation = self.city_data["countryCode"]
129
+ self.lng = float(self.city_data["lng"])
130
+ self.lat = float(self.city_data["lat"])
131
+ self.tz_str = self.city_data["timezonestr"]
132
+
133
+ def next_return_from_iso_formatted_time(
134
+ self,
135
+ iso_formatted_time: str,
136
+ return_type: ReturnType
137
+ ) -> PlanetReturnModel:
138
+ """
139
+ Get the next Return for the provided ISO-formatted time.
140
+
141
+ Args:
142
+ iso_formatted_time (str): ISO-formatted datetime string.
143
+
144
+ Returns:
145
+ PlanetReturnModel: Pydantic model containing the return chart data.
146
+ """
147
+
148
+ date = datetime.fromisoformat(iso_formatted_time)
149
+ julian_day = datetime_to_julian(date)
150
+
151
+ return_julian_date = None
152
+ if return_type == "Solar":
153
+ return_julian_date = swe.solcross_ut(
154
+ self.subject.sun.abs_pos,
155
+ julian_day,
156
+ )
157
+ elif return_type == "Lunar":
158
+ return_julian_date = swe.mooncross_ut(
159
+ self.subject.moon.abs_pos,
160
+ julian_day,
161
+ )
162
+ else:
163
+ raise KerykeionException(f"Invalid return type {return_type}. Use 'Solar' or 'Lunar'.")
164
+
165
+ solar_return_date_utc = julian_to_datetime(return_julian_date)
166
+ solar_return_date_utc = solar_return_date_utc.replace(tzinfo=timezone.utc)
167
+
168
+ solar_return_astrological_subject = AstrologicalSubjectFactory.from_iso_utc_time(
169
+ name=self.subject.name,
170
+ iso_utc_time=solar_return_date_utc.isoformat(),
171
+ lng=self.lng, # type: ignore
172
+ lat=self.lat, # type: ignore
173
+ tz_str=self.tz_str, # type: ignore
174
+ city=self.city, # type: ignore
175
+ nation=self.nation, # type: ignore
176
+ online=False,
177
+ altitude=self.altitude,
178
+ )
179
+
180
+ model_data = solar_return_astrological_subject.model_dump()
181
+ model_data['name'] = f"{self.subject.name} {return_type} Return"
182
+ model_data['return_type'] = return_type
183
+
184
+ return PlanetReturnModel(
185
+ **model_data,
186
+ )
187
+
188
+ def next_return_from_year(
189
+ self,
190
+ year: int,
191
+ return_type: ReturnType
192
+ ) -> PlanetReturnModel:
193
+ """
194
+ Get the next Return for the specified year.
195
+
196
+ This method finds the first return that occurs in the given year.
197
+
198
+ Args:
199
+ year (int): The year to search for the return
200
+ return_type (ReturnType): The type of return ("Solar" or "Lunar")
201
+
202
+ Returns:
203
+ PlanetReturnModel: Pydantic model containing the return chart data
204
+ """
205
+ # Create datetime for January 1st of the specified year (UTC)
206
+ start_date = datetime(year, 1, 1, 0, 0, tzinfo=timezone.utc)
207
+
208
+ # Get the return using the existing method
209
+ return self.next_return_from_iso_formatted_time(
210
+ start_date.isoformat(),
211
+ return_type
212
+ )
213
+
214
+ def next_return_from_month_and_year(
215
+ self,
216
+ year: int,
217
+ month: int,
218
+ return_type: ReturnType
219
+ ) -> PlanetReturnModel:
220
+ """
221
+ Get the next Return for the specified month and year.
222
+
223
+ This method finds the first return that occurs after the first day
224
+ of the specified month and year.
225
+
226
+ Args:
227
+ year (int): The year to search for the return
228
+ month (int): The month to search for the return (1-12)
229
+ return_type (ReturnType): The type of return ("Solar" or "Lunar")
230
+
231
+ Returns:
232
+ PlanetReturnModel: Pydantic model containing the return chart data
233
+ """
234
+ # Validate month input
235
+ if month < 1 or month > 12:
236
+ raise KerykeionException(f"Invalid month {month}. Month must be between 1 and 12.")
237
+
238
+ # Create datetime for the first day of the specified month and year (UTC)
239
+ start_date = datetime(year, month, 1, 0, 0, tzinfo=timezone.utc)
240
+
241
+ # Get the return using the existing method
242
+ return self.next_return_from_iso_formatted_time(
243
+ start_date.isoformat(),
244
+ return_type
245
+ )
246
+
247
+
248
+ if __name__ == "__main__":
249
+ import json
250
+ # Example usage
251
+ subject = AstrologicalSubjectFactory.from_birth_data(
252
+ name="Test Subject",
253
+ lng=-122.4194,
254
+ lat=37.7749,
255
+ tz_str="America/Los_Angeles",
256
+ )
257
+
258
+ print("=== Planet Return Calculator ===")
259
+ calculator = PlanetaryReturnFactory(
260
+ subject,
261
+ city="San Francisco",
262
+ nation="USA",
263
+ online=True,
264
+ geonames_username="century.boy",
265
+ )
266
+ date = datetime(2026, 1, 1, 0, 0, tzinfo=timezone.utc)
267
+ print(f"INITIAL DATE: {date.isoformat()}")
268
+ print(f"INITIAL DATE JULIAN: {datetime_to_julian(date)}")
269
+ print(f"INITIAL DATE REVERSED: {julian_to_datetime(datetime_to_julian(date)).isoformat()}")
270
+ solar_return = calculator.next_return_from_iso_formatted_time(
271
+ date.isoformat(),
272
+ return_type="Lunar",
273
+ )
274
+ print("--- After ---")
275
+ print(f"Solar Return Date UTC: {solar_return.iso_formatted_utc_datetime}")
276
+ print(f"Solar Return Date Local: {solar_return.iso_formatted_local_datetime}")
277
+ print(f"Solar Return JSON: {json.dumps(solar_return.model_dump(), indent=4)}")
278
+ print(f"Solar Return Julian Data: {solar_return.julian_day}")
279
+ print(f"ISO UTC: {solar_return.iso_formatted_utc_datetime}")
280
+
281
+ ## From Year
282
+ print("=== Planet Return Calculator ===")
283
+ solar_return = calculator.next_return_from_year(
284
+ 2026,
285
+ return_type="Lunar",
286
+ )
287
+ print("--- From Year ---")
288
+ print(f"Solar Return Julian Data: {solar_return.julian_day}")
289
+ print(f"Solar Return Date UTC: {solar_return.iso_formatted_utc_datetime}")
290
+ ## From Month and Year
291
+ print("=== Planet Return Calculator ===")
292
+ solar_return = calculator.next_return_from_month_and_year(
293
+ 2026,
294
+ 1,
295
+ return_type="Lunar",
296
+ )
297
+ print("--- From Month and Year ---")
298
+ print(f"Solar Return Julian Data: {solar_return.julian_day}")
299
+ print(f"Solar Return Date UTC: {solar_return.iso_formatted_utc_datetime}")
@@ -3,7 +3,7 @@
3
3
  This is part of Kerykeion (C) 2025 Giacomo Battaglia
4
4
  """
5
5
 
6
- from kerykeion import AstrologicalSubject
6
+ from kerykeion import AstrologicalSubjectFactory
7
7
  from kerykeion.aspects.synastry_aspects import SynastryAspects
8
8
  import logging
9
9
  from pathlib import Path
@@ -33,8 +33,8 @@ class RelationshipScore:
33
33
 
34
34
  def __init__(
35
35
  self,
36
- first_subject: Union[AstrologicalSubject, AstrologicalSubjectModel],
37
- second_subject: Union[AstrologicalSubject, AstrologicalSubjectModel],
36
+ first_subject: AstrologicalSubjectModel,
37
+ second_subject: AstrologicalSubjectModel,
38
38
  new_settings_file: Union[Path, None] = None,
39
39
  ):
40
40
  warnings.warn(
@@ -3,7 +3,7 @@
3
3
  This is part of Kerykeion (C) 2025 Giacomo Battaglia
4
4
  """
5
5
 
6
- from kerykeion import AstrologicalSubject
6
+ from kerykeion import AstrologicalSubjectFactory
7
7
  from kerykeion.aspects.synastry_aspects import SynastryAspects
8
8
  import logging
9
9
  from pathlib import Path
@@ -27,8 +27,8 @@ class RelationshipScoreFactory:
27
27
  Documentation: http://www.cirodiscepolo.it/Articoli/Discepoloele.htm
28
28
 
29
29
  Args:
30
- first_subject (Union[AstrologicalSubject, AstrologicalSubjectModel]): First subject instance
31
- second_subject (Union[AstrologicalSubject, AstrologicalSubjectModel]): Second subject instance
30
+ first_subject (AstrologicalSubjectModel): First subject instance
31
+ second_subject (AstrologicalSubjectModel): Second subject instance
32
32
  """
33
33
 
34
34
  SCORE_MAPPING = [
@@ -44,16 +44,13 @@ class RelationshipScoreFactory:
44
44
 
45
45
  def __init__(
46
46
  self,
47
- first_subject: Union[AstrologicalSubject, AstrologicalSubjectModel],
48
- second_subject: Union[AstrologicalSubject, AstrologicalSubjectModel],
47
+ first_subject: AstrologicalSubjectModel,
48
+ second_subject: AstrologicalSubjectModel,
49
49
  use_only_major_aspects: bool = True,
50
50
  ):
51
- if isinstance(first_subject, AstrologicalSubject):
52
- self.first_subject = first_subject.model()
53
- if isinstance(second_subject, AstrologicalSubject):
54
- self.second_subject = second_subject.model()
55
-
56
51
  self.use_only_major_aspects = use_only_major_aspects
52
+ self.first_subject: AstrologicalSubjectModel = first_subject
53
+ self.second_subject: AstrologicalSubjectModel = second_subject
57
54
 
58
55
  self.score_value = 0
59
56
  self.relationship_score_description: RelationshipScoreDescription = "Minimal"
@@ -221,8 +218,8 @@ if __name__ == "__main__":
221
218
 
222
219
  setup_logging(level="critical")
223
220
 
224
- giacomo = AstrologicalSubject("Giacomo", 1993, 6, 10, 12, 15, "Montichiari", "IT")
225
- yoko = AstrologicalSubject("Susie Cox", 1949, 6, 17, 9, 40, "Tucson", "US")
221
+ giacomo = AstrologicalSubjectFactory.from_birth_data("Giacomo", 1993, 6, 10, 12, 15, "Montichiari", "IT")
222
+ yoko = AstrologicalSubjectFactory.from_birth_data("Susie Cox", 1949, 6, 17, 9, 40, "Tucson", "US")
226
223
 
227
224
  factory = RelationshipScoreFactory(giacomo, yoko)
228
225
  score = factory.get_relationship_score()