kerykeion 3.1.1__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 (75) hide show
  1. kerykeion/__init__.py +58 -141
  2. kerykeion/aspects/__init__.py +14 -0
  3. kerykeion/aspects/aspects_factory.py +568 -0
  4. kerykeion/aspects/aspects_utils.py +164 -0
  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 +5 -0
  9. kerykeion/charts/chart_drawer.py +2794 -0
  10. kerykeion/charts/charts_utils.py +1840 -0
  11. kerykeion/charts/draw_planets.py +658 -0
  12. kerykeion/charts/templates/aspect_grid_only.xml +596 -0
  13. kerykeion/charts/templates/chart.xml +741 -0
  14. kerykeion/charts/templates/wheel_only.xml +653 -0
  15. kerykeion/charts/themes/black-and-white.css +148 -0
  16. kerykeion/charts/themes/classic.css +113 -0
  17. kerykeion/charts/themes/dark-high-contrast.css +159 -0
  18. kerykeion/charts/themes/dark.css +160 -0
  19. kerykeion/charts/themes/light.css +160 -0
  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 +105 -61
  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 +70 -0
  28. kerykeion/kr_types/chart_template_model.py +20 -0
  29. kerykeion/kr_types/kerykeion_exception.py +20 -0
  30. kerykeion/kr_types/kr_literals.py +20 -0
  31. kerykeion/kr_types/kr_models.py +20 -0
  32. kerykeion/kr_types/settings_models.py +20 -0
  33. kerykeion/planetary_return_factory.py +805 -0
  34. kerykeion/relationship_score_factory.py +301 -0
  35. kerykeion/report.py +779 -0
  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 -0
  43. kerykeion/settings/chart_defaults.py +444 -0
  44. kerykeion/settings/config_constants.py +152 -0
  45. kerykeion/settings/kerykeion_settings.py +51 -0
  46. kerykeion/settings/translation_strings.py +1499 -0
  47. kerykeion/settings/translations.py +74 -0
  48. kerykeion/sweph/README.md +3 -0
  49. kerykeion/sweph/ast136/s136108s.se1 +0 -0
  50. kerykeion/sweph/ast136/s136199s.se1 +0 -0
  51. kerykeion/sweph/ast136/s136472s.se1 +0 -0
  52. kerykeion/sweph/ast28/se28978s.se1 +0 -0
  53. kerykeion/sweph/ast50/se50000s.se1 +0 -0
  54. kerykeion/sweph/ast90/se90377s.se1 +0 -0
  55. kerykeion/sweph/ast90/se90482s.se1 +0 -0
  56. kerykeion/sweph/seas_18.se1 +0 -0
  57. kerykeion/sweph/sefstars.txt +1602 -0
  58. kerykeion/transits_time_range_factory.py +302 -0
  59. kerykeion/utilities.py +762 -130
  60. kerykeion-5.1.9.dist-info/METADATA +1793 -0
  61. kerykeion-5.1.9.dist-info/RECORD +63 -0
  62. {kerykeion-3.1.1.dist-info → kerykeion-5.1.9.dist-info}/WHEEL +1 -2
  63. kerykeion-5.1.9.dist-info/licenses/LICENSE +661 -0
  64. kerykeion/aspects.py +0 -331
  65. kerykeion/charts/charts_svg.py +0 -1607
  66. kerykeion/charts/templates/basic.xml +0 -285
  67. kerykeion/charts/templates/extended.xml +0 -294
  68. kerykeion/kr.config.json +0 -464
  69. kerykeion/main.py +0 -595
  70. kerykeion/print_all_data.py +0 -44
  71. kerykeion/relationship_score.py +0 -219
  72. kerykeion/types.py +0 -190
  73. kerykeion-3.1.1.dist-info/METADATA +0 -204
  74. kerykeion-3.1.1.dist-info/RECORD +0 -17
  75. kerykeion-3.1.1.dist-info/top_level.txt +0 -1
@@ -0,0 +1,408 @@
1
+ """
2
+ Composite Subject Factory Module
3
+
4
+ This module provides functionality for creating composite astrological charts from two
5
+ individual astrological subjects. A composite chart represents the relationship between
6
+ two people by calculating midpoint positions between corresponding planetary placements
7
+ and house cusps.
8
+
9
+ The module implements the midpoint composite technique, which is the most commonly used
10
+ method for relationship astrology. This technique creates a single chart that symbolizes
11
+ the energy and dynamics of the relationship itself, rather than comparing individual charts.
12
+
13
+ Key Features:
14
+ - Midpoint calculation for all planetary positions
15
+ - Midpoint calculation for house cusp positions
16
+ - Proper handling of zodiacal boundary crossings (0°/360°)
17
+ - Validation of compatible astrological settings between subjects
18
+ - Lunar phase calculation for composite charts
19
+ - Support for all standard astrological points and house systems
20
+
21
+ Classes:
22
+ CompositeSubjectFactory: Main factory class for creating composite charts
23
+
24
+ Dependencies:
25
+ - AstrologicalSubjectFactory: For working with individual astrological subjects
26
+ - Various schemas modules: For type definitions and models
27
+ - utilities module: For astrological calculations and helper functions
28
+
29
+ Example Usage:
30
+ >>> from kerykeion import AstrologicalSubjectFactory, CompositeSubjectFactory
31
+ >>> person1 = AstrologicalSubjectFactory.from_birth_data(...)
32
+ >>> person2 = AstrologicalSubjectFactory.from_birth_data(...)
33
+ >>> composite = CompositeSubjectFactory(person1, person2)
34
+ >>> composite_chart = composite.get_midpoint_composite_subject_model()
35
+
36
+ Author: Giacomo Battaglia
37
+ Copyright: (C) 2025 Kerykeion Project
38
+ License: AGPL-3.0
39
+ """
40
+
41
+ from typing import Union
42
+
43
+ # Fix the circular import by changing this import
44
+ from kerykeion.astrological_subject_factory import AstrologicalSubjectFactory
45
+ from kerykeion.schemas.kerykeion_exception import KerykeionException
46
+ from kerykeion.schemas.kr_models import CompositeSubjectModel, AstrologicalSubjectModel
47
+ from kerykeion.schemas.kr_literals import ZodiacType, PerspectiveType, HousesSystemIdentifier, SiderealMode, AstrologicalPoint, Houses, CompositeChartType
48
+ from kerykeion.utilities import (
49
+ get_kerykeion_point_from_degree,
50
+ get_planet_house,
51
+ circular_mean,
52
+ calculate_moon_phase,
53
+ circular_sort,
54
+ find_common_active_points
55
+ )
56
+
57
+
58
+ class CompositeSubjectFactory:
59
+ """
60
+ Factory class to create composite astrological charts from two astrological subjects.
61
+
62
+ A composite chart represents the relationship between two people by calculating the midpoint
63
+ between corresponding planetary positions and house cusps. This creates a single chart
64
+ that symbolizes the energy of the relationship itself.
65
+
66
+ Currently supports the midpoint method for composite chart calculation, where:
67
+ - Planetary positions are calculated as the circular mean of corresponding planets
68
+ - House cusps are calculated as the circular mean of corresponding houses
69
+ - Houses are reordered to maintain consistency with the original house system
70
+ - Only common active points between both subjects are included
71
+
72
+ The resulting composite chart maintains the zodiac type, sidereal mode, houses system,
73
+ and perspective type of the input subjects (which must be identical between subjects).
74
+
75
+ Attributes:
76
+ model (CompositeSubjectModel | None): The generated composite subject model
77
+ first_subject (AstrologicalSubjectModel): First astrological subject
78
+ second_subject (AstrologicalSubjectModel): Second astrological subject
79
+ name (str): Name of the composite chart
80
+ composite_chart_type (CompositeChartType): Type of composite chart (currently "Midpoint")
81
+ zodiac_type (ZodiacType): Zodiac system used (Tropical or Sidereal)
82
+ sidereal_mode (SiderealMode | None): Sidereal calculation mode if applicable
83
+ houses_system_identifier (HousesSystemIdentifier): House system identifier
84
+ houses_system_name (str): Human-readable house system name
85
+ perspective_type (PerspectiveType): Astrological perspective type
86
+ houses_names_list (list[Houses]): List of house names
87
+ active_points (list[AstrologicalPoint]): Common active planetary points
88
+
89
+ Example:
90
+ >>> first_person = AstrologicalSubjectFactory.from_birth_data(
91
+ ... "John", 1990, 1, 1, 12, 0, "New York", "US"
92
+ ... )
93
+ >>> second_person = AstrologicalSubjectFactory.from_birth_data(
94
+ ... "Jane", 1992, 6, 15, 14, 30, "Los Angeles", "US"
95
+ ... )
96
+ >>> composite = CompositeSubjectFactory(first_person, second_person)
97
+ >>> composite_model = composite.get_midpoint_composite_subject_model()
98
+
99
+ Raises:
100
+ KerykeionException: When subjects have incompatible settings (different zodiac types,
101
+ sidereal modes, house systems, or perspective types)
102
+ """
103
+
104
+ model: Union[CompositeSubjectModel, None]
105
+ first_subject: AstrologicalSubjectModel
106
+ second_subject: AstrologicalSubjectModel
107
+ name: str
108
+ composite_chart_type: CompositeChartType
109
+ zodiac_type: ZodiacType
110
+ sidereal_mode: Union[SiderealMode, None]
111
+ houses_system_identifier: HousesSystemIdentifier
112
+ houses_system_name: str
113
+ perspective_type: PerspectiveType
114
+ houses_names_list: list[Houses]
115
+ active_points: list[AstrologicalPoint]
116
+
117
+ def __init__(
118
+ self,
119
+ first_subject: AstrologicalSubjectModel,
120
+ second_subject: AstrologicalSubjectModel,
121
+ chart_name: Union[str, None] = None
122
+ ):
123
+ """
124
+ Initialize the composite subject factory with two astrological subjects.
125
+
126
+ Validates that both subjects have compatible settings and extracts common
127
+ active points for composite chart calculation.
128
+
129
+ Args:
130
+ first_subject (AstrologicalSubjectModel): First astrological subject for the composite
131
+ second_subject (AstrologicalSubjectModel): Second astrological subject for the composite
132
+ chart_name (str | None, optional): Custom name for the composite chart.
133
+ If None, generates name from subject names.
134
+ Defaults to None.
135
+
136
+ Raises:
137
+ KerykeionException: If subjects have different zodiac types, sidereal modes,
138
+ house systems, house system names, or perspective types.
139
+
140
+ Note:
141
+ Both subjects must have identical astrological calculation settings to ensure
142
+ meaningful composite chart calculations.
143
+ """
144
+ self.model: Union[CompositeSubjectModel, None] = None
145
+ self.composite_chart_type = "Midpoint"
146
+
147
+ self.first_subject = first_subject
148
+ self.second_subject = second_subject
149
+ self.active_points = find_common_active_points(
150
+ first_subject.active_points,
151
+ second_subject.active_points
152
+ )
153
+
154
+ # Name
155
+ if chart_name is None:
156
+ self.name = f"{first_subject.name} and {second_subject.name} Composite Chart"
157
+ else:
158
+ self.name = chart_name
159
+
160
+ # Zodiac Type
161
+ if first_subject.zodiac_type != second_subject.zodiac_type:
162
+ raise KerykeionException("Both subjects must have the same zodiac type")
163
+ self.zodiac_type = first_subject.zodiac_type
164
+
165
+ # Sidereal Mode
166
+ if first_subject.sidereal_mode != second_subject.sidereal_mode:
167
+ raise KerykeionException("Both subjects must have the same sidereal mode")
168
+
169
+ if first_subject.sidereal_mode is not None:
170
+ self.sidereal_mode = first_subject.sidereal_mode
171
+ else:
172
+ self.sidereal_mode = None
173
+
174
+ # Houses System
175
+ if first_subject.houses_system_identifier != second_subject.houses_system_identifier:
176
+ raise KerykeionException("Both subjects must have the same houses system")
177
+ self.houses_system_identifier = first_subject.houses_system_identifier
178
+
179
+ # Houses System Name
180
+ if first_subject.houses_system_name != second_subject.houses_system_name:
181
+ raise KerykeionException("Both subjects must have the same houses system name")
182
+ self.houses_system_name = first_subject.houses_system_name
183
+
184
+ # Perspective Type
185
+ if first_subject.perspective_type != second_subject.perspective_type:
186
+ raise KerykeionException("Both subjects must have the same perspective type")
187
+ self.perspective_type = first_subject.perspective_type
188
+
189
+ # Planets Names List
190
+ self.active_points = []
191
+ for planet in first_subject.active_points:
192
+ if planet in second_subject.active_points:
193
+ self.active_points.append(planet)
194
+
195
+ # Houses Names List
196
+ self.houses_names_list = self.first_subject.houses_names_list
197
+
198
+ def __str__(self):
199
+ """
200
+ Return string representation of the composite subject.
201
+
202
+ Returns:
203
+ str: Human-readable string describing the composite chart.
204
+ """
205
+ return f"Composite Chart Data for {self.name}"
206
+
207
+ def __repr__(self):
208
+ """
209
+ Return detailed string representation of the composite subject.
210
+
211
+ Returns:
212
+ str: Detailed string representation for debugging purposes.
213
+ """
214
+ return f"Composite Chart Data for {self.name}"
215
+
216
+ def __eq__(self, other):
217
+ """
218
+ Check equality with another composite subject.
219
+
220
+ Args:
221
+ other (CompositeSubjectFactory): Another composite subject to compare with.
222
+
223
+ Returns:
224
+ bool: True if both subjects and chart name are identical.
225
+ """
226
+ return self.first_subject == other.first_subject and self.second_subject == other.second_subject and self.name == other.chart_name
227
+
228
+ def __ne__(self, other):
229
+ """
230
+ Check inequality with another composite subject.
231
+
232
+ Args:
233
+ other (CompositeSubjectFactory): Another composite subject to compare with.
234
+
235
+ Returns:
236
+ bool: True if subjects or chart name are different.
237
+ """
238
+ return not self.__eq__(other)
239
+
240
+ def __hash__(self):
241
+ """
242
+ Generate hash for the composite subject.
243
+
244
+ Returns:
245
+ int: Hash value based on both subjects and chart name.
246
+ """
247
+ return hash((self.first_subject, self.second_subject, self.name))
248
+
249
+ def __copy__(self):
250
+ """
251
+ Create a shallow copy of the composite subject.
252
+
253
+ Returns:
254
+ CompositeSubjectFactory: New instance with the same subjects and name.
255
+ """
256
+ return CompositeSubjectFactory(self.first_subject, self.second_subject, self.name)
257
+
258
+ def __setitem__(self, key, value):
259
+ """
260
+ Set an attribute using dictionary-style access.
261
+
262
+ Args:
263
+ key (str): Attribute name to set.
264
+ value: Value to assign to the attribute.
265
+ """
266
+ setattr(self, key, value)
267
+
268
+ def __getitem__(self, key):
269
+ """
270
+ Get an attribute using dictionary-style access.
271
+
272
+ Args:
273
+ key (str): Attribute name to retrieve.
274
+
275
+ Returns:
276
+ Any: Value of the requested attribute.
277
+
278
+ Raises:
279
+ AttributeError: If the attribute doesn't exist.
280
+ """
281
+ return getattr(self, key)
282
+
283
+ def _calculate_midpoint_composite_points_and_houses(self):
284
+ """
285
+ Calculate midpoint positions for all planets and house cusps in the composite chart.
286
+
287
+ This method implements the midpoint composite technique by:
288
+ 1. Computing circular means of house cusp positions from both subjects
289
+ 2. Sorting house positions to maintain proper house order
290
+ 3. Creating composite house cusps with calculated positions
291
+ 4. Computing circular means of planetary positions for common active points
292
+ 5. Assigning planets to their appropriate houses in the composite chart
293
+
294
+ The circular mean calculation ensures proper handling of zodiacal positions
295
+ around the 360-degree boundary (e.g., when one position is at 350° and
296
+ another at 10°, the midpoint is correctly calculated as 0°).
297
+
298
+ Side Effects:
299
+ - Updates instance attributes with calculated house cusp positions
300
+ - Updates instance attributes with calculated planetary positions
301
+ - Sets house assignments for each planetary position
302
+
303
+ Note:
304
+ This is an internal method called by get_midpoint_composite_subject_model().
305
+ Only planets that exist in both subjects' active_points are included.
306
+ """
307
+ # Houses
308
+ house_degree_list_ut = []
309
+ for house in self.first_subject.houses_names_list:
310
+ house_lower = house.lower()
311
+ house_degree_list_ut.append(
312
+ circular_mean(
313
+ self.first_subject[house_lower]["abs_pos"],
314
+ self.second_subject[house_lower]["abs_pos"]
315
+ )
316
+ )
317
+ house_degree_list_ut = circular_sort(house_degree_list_ut)
318
+
319
+ for house_index, house_name in enumerate(self.first_subject.houses_names_list):
320
+ house_lower = house_name.lower()
321
+ self[house_lower] = get_kerykeion_point_from_degree(
322
+ house_degree_list_ut[house_index],
323
+ house_name,
324
+ "House"
325
+ )
326
+
327
+
328
+ # Planets
329
+ common_planets = []
330
+ for planet in self.first_subject.active_points:
331
+ if planet in self.second_subject.active_points:
332
+ common_planets.append(planet)
333
+
334
+ planets = {}
335
+ for planet in common_planets:
336
+ planet_lower = planet.lower()
337
+ planets[planet_lower] = {}
338
+ planets[planet_lower]["abs_pos"] = circular_mean(
339
+ self.first_subject[planet_lower]["abs_pos"],
340
+ self.second_subject[planet_lower]["abs_pos"]
341
+ )
342
+ self[planet_lower] = get_kerykeion_point_from_degree(planets[planet_lower]["abs_pos"], planet, "AstrologicalPoint")
343
+ self[planet_lower]["house"] = get_planet_house(self[planet_lower]['abs_pos'], house_degree_list_ut)
344
+
345
+ def _calculate_composite_lunar_phase(self):
346
+ """
347
+ Calculate the lunar phase for the composite chart based on Sun-Moon midpoints.
348
+
349
+ Uses the composite positions of the Sun and Moon to determine the lunar phase
350
+ angle, representing the relationship's emotional and instinctual dynamics.
351
+
352
+ Side Effects:
353
+ Sets the lunar_phase attribute with the calculated phase information.
354
+
355
+ Note:
356
+ This method should be called after _calculate_midpoint_composite_points_and_houses()
357
+ to ensure Sun and Moon composite positions are available.
358
+ """
359
+ self.lunar_phase = calculate_moon_phase(
360
+ self['moon'].abs_pos,
361
+ self['sun'].abs_pos
362
+ )
363
+
364
+ def get_midpoint_composite_subject_model(self):
365
+ """
366
+ Generate the complete composite chart model using the midpoint technique.
367
+
368
+ This is the main public method for creating a composite chart. It orchestrates
369
+ the calculation of all composite positions and creates a complete CompositeSubjectModel
370
+ containing all necessary astrological data for the relationship chart.
371
+
372
+ The process includes:
373
+ 1. Calculating midpoint positions for all planets and house cusps
374
+ 2. Computing the composite lunar phase
375
+ 3. Assembling all data into a comprehensive model
376
+
377
+ Returns:
378
+ CompositeSubjectModel: Complete composite chart data model containing:
379
+ - All calculated planetary positions and their house placements
380
+ - House cusp positions maintaining proper house system order
381
+ - Lunar phase information for the composite chart
382
+ - All metadata from the original subjects (names, chart type, etc.)
383
+
384
+ Example:
385
+ >>> composite = CompositeSubjectFactory(person1, person2, "Our Relationship")
386
+ >>> model = composite.get_midpoint_composite_subject_model()
387
+ >>> print(f"Composite Sun at {model.sun.abs_pos}° in House {model.sun.house}")
388
+
389
+ Note:
390
+ This method performs all calculations internally and returns a complete,
391
+ ready-to-use composite chart model suitable for analysis or chart drawing.
392
+ """
393
+ self._calculate_midpoint_composite_points_and_houses()
394
+ self._calculate_composite_lunar_phase()
395
+
396
+ return CompositeSubjectModel(
397
+ **self.__dict__
398
+ )
399
+
400
+
401
+ if __name__ == "__main__":
402
+ from kerykeion.astrological_subject_factory import AstrologicalSubjectFactory
403
+
404
+ first = AstrologicalSubjectFactory.from_birth_data("John Lennon", 1940, 10, 9, 18, 30, "Liverpool", "GB")
405
+ second = AstrologicalSubjectFactory.from_birth_data("Paul McCartney", 1942, 6, 18, 15, 30, "Liverpool", "GB")
406
+
407
+ composite_chart = CompositeSubjectFactory(first, second)
408
+ print(composite_chart.get_midpoint_composite_subject_model().model_dump_json(indent=4))