kerykeion 5.0.0a11__py3-none-any.whl → 5.0.0a12__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.

@@ -1,275 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- """
3
- This is part of Kerykeion (C) 2025 Giacomo Battaglia
4
- """
5
-
6
- import logging
7
- from typing import Union, List, Optional
8
-
9
- from kerykeion.astrological_subject_factory import AstrologicalSubjectFactory
10
- from kerykeion.aspects.aspects_utils import get_aspect_from_two_points, get_active_points_list
11
- from kerykeion.kr_types.kr_models import AstrologicalSubjectModel, AspectModel, ActiveAspect, CompositeSubjectModel, PlanetReturnModel, SynastryAspectsModel
12
- from kerykeion.settings.config_constants import DEFAULT_ACTIVE_ASPECTS, DEFAULT_AXIS_ORBIT
13
- from kerykeion.settings.legacy.legacy_celestial_points_settings import DEFAULT_CELESTIAL_POINTS_SETTINGS
14
- from kerykeion.settings.legacy.legacy_chart_aspects_settings import DEFAULT_CHART_ASPECTS_SETTINGS
15
- from kerykeion.kr_types.kr_literals import AstrologicalPoint
16
- from kerykeion.utilities import find_common_active_points
17
-
18
-
19
- # Axes constants for orb filtering
20
- AXES_LIST = [
21
- "Ascendant",
22
- "Medium_Coeli",
23
- "Descendant",
24
- "Imum_Coeli",
25
- ]
26
-
27
-
28
- class SynastryAspectsFactory:
29
- """
30
- Factory class for creating synastry aspects analysis between two astrological subjects.
31
-
32
- This factory calculates all astrological aspects (angular relationships) between
33
- planets and points in two different charts. It's primarily used for relationship
34
- compatibility analysis, transit calculations, and comparative astrology.
35
-
36
- The factory provides both comprehensive aspect lists and filtered relevant aspects
37
- based on orb settings and chart axes considerations.
38
-
39
- Key Features:
40
- - Calculates all aspects between two subjects
41
- - Filters aspects based on orb thresholds
42
- - Applies stricter orb limits for chart axes (ASC, MC, DSC, IC)
43
- - Supports multiple subject types (natal, composite, planetary returns)
44
-
45
- Example:
46
- >>> john = AstrologicalSubjectFactory.from_birth_data("John", 1990, 1, 1, 12, 0, "London", "GB")
47
- >>> jane = AstrologicalSubjectFactory.from_birth_data("Jane", 1992, 6, 15, 14, 30, "Paris", "FR")
48
- >>> synastry = SynastryAspectsFactory.from_subjects(john, jane)
49
- >>> print(f"Found {len(synastry.relevant_aspects)} relevant aspects")
50
- """
51
-
52
- @staticmethod
53
- def from_subjects(
54
- first_subject: Union[AstrologicalSubjectModel, CompositeSubjectModel, PlanetReturnModel],
55
- second_subject: Union[AstrologicalSubjectModel, CompositeSubjectModel, PlanetReturnModel],
56
- *,
57
- active_points: Optional[List[AstrologicalPoint]] = None,
58
- active_aspects: Optional[List[ActiveAspect]] = None,
59
- ) -> SynastryAspectsModel:
60
- """
61
- Create synastry aspects analysis between two astrological subjects.
62
-
63
- This method calculates all astrological aspects (angular relationships)
64
- between planets and points in two different birth charts, commonly used
65
- for relationship compatibility analysis.
66
-
67
- Args:
68
- first_subject: The first astrological subject (person, composite chart, or planetary return)
69
- second_subject: The second astrological subject to compare with the first
70
- active_points: Optional list of celestial points to include in calculations.
71
- If None, uses common points between both subjects.
72
- active_aspects: Optional list of aspect types with their orb settings.
73
- If None, uses default aspect configuration.
74
-
75
- Returns:
76
- SynastryAspectsModel: Complete model containing all calculated aspects data,
77
- including both comprehensive and filtered relevant aspects.
78
-
79
- Example:
80
- >>> john = AstrologicalSubjectFactory.from_birth_data("John", 1990, 1, 1, 12, 0, "London", "GB")
81
- >>> jane = AstrologicalSubjectFactory.from_birth_data("Jane", 1992, 6, 15, 14, 30, "Paris", "FR")
82
- >>> synastry = SynastryAspectsFactory.from_subjects(john, jane)
83
- >>> print(f"Found {len(synastry.relevant_aspects)} relevant aspects")
84
- """
85
- # Initialize settings and configurations
86
- celestial_points = DEFAULT_CELESTIAL_POINTS_SETTINGS
87
- aspects_settings = DEFAULT_CHART_ASPECTS_SETTINGS
88
- axes_orbit_settings = DEFAULT_AXIS_ORBIT
89
-
90
- # Set active aspects with default fallback
91
- active_aspects_resolved = active_aspects if active_aspects is not None else DEFAULT_ACTIVE_ASPECTS
92
-
93
- # Determine active points to use - find common points between both subjects
94
- if active_points is None:
95
- active_points_resolved = first_subject.active_points
96
- else:
97
- active_points_resolved = find_common_active_points(
98
- first_subject.active_points,
99
- active_points,
100
- )
101
-
102
- # Further filter with second subject's active points
103
- active_points_resolved = find_common_active_points(
104
- second_subject.active_points,
105
- active_points_resolved,
106
- )
107
-
108
- return SynastryAspectsFactory._create_synastry_aspects_model(
109
- first_subject, second_subject, active_points_resolved, active_aspects_resolved,
110
- aspects_settings, axes_orbit_settings, celestial_points
111
- )
112
-
113
- @staticmethod
114
- def _create_synastry_aspects_model(
115
- first_subject: Union[AstrologicalSubjectModel, CompositeSubjectModel, PlanetReturnModel],
116
- second_subject: Union[AstrologicalSubjectModel, CompositeSubjectModel, PlanetReturnModel],
117
- active_points_resolved: List[AstrologicalPoint],
118
- active_aspects_resolved: List[ActiveAspect],
119
- aspects_settings: List[dict],
120
- axes_orbit_settings: float,
121
- celestial_points: List[dict]
122
- ) -> SynastryAspectsModel:
123
- """
124
- Create the complete synastry aspects model with all calculations.
125
-
126
- Args:
127
- first_subject: First astrological subject
128
- second_subject: Second astrological subject
129
- active_points_resolved: Resolved list of active celestial points
130
- active_aspects_resolved: Resolved list of active aspects with orbs
131
- aspects_settings: Chart aspect configuration settings
132
- axes_orbit_settings: Orb threshold for chart axes
133
- celestial_points: Celestial points configuration
134
-
135
- Returns:
136
- SynastryAspectsModel: Complete model containing all aspects data
137
- """
138
- all_aspects = SynastryAspectsFactory._calculate_all_aspects(
139
- first_subject, second_subject, active_points_resolved, active_aspects_resolved,
140
- aspects_settings, celestial_points
141
- )
142
- relevant_aspects = SynastryAspectsFactory._filter_relevant_aspects(all_aspects, axes_orbit_settings)
143
-
144
- return SynastryAspectsModel(
145
- first_subject=first_subject,
146
- second_subject=second_subject,
147
- all_aspects=all_aspects,
148
- relevant_aspects=relevant_aspects,
149
- active_points=active_points_resolved,
150
- active_aspects=active_aspects_resolved,
151
- )
152
-
153
- @staticmethod
154
- def _calculate_all_aspects(
155
- first_subject: Union[AstrologicalSubjectModel, CompositeSubjectModel, PlanetReturnModel],
156
- second_subject: Union[AstrologicalSubjectModel, CompositeSubjectModel, PlanetReturnModel],
157
- active_points: List[AstrologicalPoint],
158
- active_aspects: List[ActiveAspect],
159
- aspects_settings: List[dict],
160
- celestial_points: List[dict]
161
- ) -> List[AspectModel]:
162
- """
163
- Calculate all synastry aspects between two subjects.
164
-
165
- This method performs comprehensive aspect calculations between all active points
166
- of both subjects, applying the specified orb settings and creating detailed
167
- aspect models with planet IDs and positional information.
168
-
169
- Args:
170
- first_subject: First astrological subject
171
- second_subject: Second astrological subject
172
- active_points: List of celestial points to include in calculations
173
- active_aspects: List of aspect types with their orb settings
174
- aspects_settings: Base aspect configuration settings
175
- celestial_points: Celestial points configuration with IDs
176
-
177
- Returns:
178
- List[AspectModel]: Complete list of all calculated aspect instances
179
- """
180
- # Get active points lists for both subjects
181
- first_active_points_list = get_active_points_list(first_subject, active_points)
182
- second_active_points_list = get_active_points_list(second_subject, active_points)
183
-
184
- # Create a lookup dictionary for planet IDs to optimize performance
185
- planet_id_lookup = {planet["name"]: planet["id"] for planet in celestial_points}
186
-
187
- # Update aspects settings with active aspects orbs
188
- filtered_settings = []
189
- for aspect_setting in aspects_settings:
190
- for active_aspect in active_aspects:
191
- if aspect_setting["name"] == active_aspect["name"]:
192
- aspect_setting = aspect_setting.copy() # Don't modify original
193
- aspect_setting["orb"] = active_aspect["orb"]
194
- filtered_settings.append(aspect_setting)
195
- break
196
-
197
- all_aspects_list = []
198
- for first in range(len(first_active_points_list)):
199
- # Generate aspects list between all points of first and second subjects
200
- for second in range(len(second_active_points_list)):
201
- aspect = get_aspect_from_two_points(
202
- filtered_settings,
203
- first_active_points_list[first]["abs_pos"],
204
- second_active_points_list[second]["abs_pos"],
205
- )
206
-
207
- if aspect["verdict"]:
208
- first_name = first_active_points_list[first]["name"]
209
- second_name = second_active_points_list[second]["name"]
210
-
211
- # Get planet IDs using lookup dictionary for better performance
212
- first_planet_id = planet_id_lookup.get(first_name, 0)
213
- second_planet_id = planet_id_lookup.get(second_name, 0)
214
-
215
- aspect_model = AspectModel(
216
- p1_name=first_name,
217
- p1_owner=first_subject.name,
218
- p1_abs_pos=first_active_points_list[first]["abs_pos"],
219
- p2_name=second_name,
220
- p2_owner=second_subject.name,
221
- p2_abs_pos=second_active_points_list[second]["abs_pos"],
222
- aspect=aspect["name"],
223
- orbit=aspect["orbit"],
224
- aspect_degrees=aspect["aspect_degrees"],
225
- diff=aspect["diff"],
226
- p1=first_planet_id,
227
- p2=second_planet_id,
228
- )
229
- all_aspects_list.append(aspect_model)
230
-
231
- return all_aspects_list
232
-
233
- @staticmethod
234
- def _filter_relevant_aspects(all_aspects: List[AspectModel], axes_orbit_settings: float) -> List[AspectModel]:
235
- """
236
- Filter aspects based on orb thresholds for axes and comprehensive criteria.
237
-
238
- This method consolidates all filtering logic including axes checks and orb thresholds
239
- for synastry aspects in a single comprehensive filtering method.
240
-
241
- Args:
242
- all_aspects: Complete list of calculated aspects
243
- axes_orbit_settings: Orb threshold for axes aspects
244
-
245
- Returns:
246
- Filtered list of relevant aspects
247
- """
248
- logging.debug("Calculating relevant synastry aspects by filtering orbs...")
249
-
250
- relevant_aspects = []
251
-
252
- for aspect in all_aspects:
253
- # Check if aspect involves any of the chart axes and apply stricter orb limits
254
- aspect_involves_axes = (aspect.p1_name in AXES_LIST or aspect.p2_name in AXES_LIST)
255
-
256
- if aspect_involves_axes and abs(aspect.orbit) >= axes_orbit_settings:
257
- continue
258
-
259
- relevant_aspects.append(aspect)
260
-
261
- return relevant_aspects
262
-
263
-
264
- if __name__ == "__main__":
265
- from kerykeion.utilities import setup_logging
266
-
267
- setup_logging(level="debug")
268
-
269
- john = AstrologicalSubjectFactory.from_birth_data("John", 1940, 10, 9, 10, 30, "Liverpool", "GB")
270
- yoko = AstrologicalSubjectFactory.from_birth_data("Yoko", 1933, 2, 18, 10, 30, "Tokyo", "JP")
271
-
272
- # Create synastry aspects analysis using the factory
273
- synastry_aspects_model = SynastryAspectsFactory.from_subjects(john, yoko)
274
- print(f"All synastry aspects: {len(synastry_aspects_model.all_aspects)}")
275
- print(f"Relevant synastry aspects: {len(synastry_aspects_model.relevant_aspects)}")