kerykeion 5.0.0a12__py3-none-any.whl → 5.0.0b2__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 (51) hide show
  1. kerykeion/__init__.py +30 -6
  2. kerykeion/aspects/aspects_factory.py +40 -24
  3. kerykeion/aspects/aspects_utils.py +75 -6
  4. kerykeion/astrological_subject_factory.py +377 -226
  5. kerykeion/backword.py +680 -0
  6. kerykeion/chart_data_factory.py +484 -0
  7. kerykeion/charts/{kerykeion_chart_svg.py → chart_drawer.py} +688 -440
  8. kerykeion/charts/charts_utils.py +157 -94
  9. kerykeion/charts/draw_planets.py +38 -28
  10. kerykeion/charts/templates/aspect_grid_only.xml +188 -17
  11. kerykeion/charts/templates/chart.xml +153 -47
  12. kerykeion/charts/templates/wheel_only.xml +195 -24
  13. kerykeion/charts/themes/classic.css +11 -0
  14. kerykeion/charts/themes/dark-high-contrast.css +11 -0
  15. kerykeion/charts/themes/dark.css +11 -0
  16. kerykeion/charts/themes/light.css +11 -0
  17. kerykeion/charts/themes/strawberry.css +10 -0
  18. kerykeion/composite_subject_factory.py +4 -4
  19. kerykeion/ephemeris_data_factory.py +12 -9
  20. kerykeion/house_comparison/__init__.py +0 -3
  21. kerykeion/house_comparison/house_comparison_factory.py +3 -3
  22. kerykeion/house_comparison/house_comparison_utils.py +3 -4
  23. kerykeion/planetary_return_factory.py +8 -4
  24. kerykeion/relationship_score_factory.py +3 -3
  25. kerykeion/report.py +748 -67
  26. kerykeion/{kr_types → schemas}/__init__.py +44 -4
  27. kerykeion/schemas/chart_template_model.py +367 -0
  28. kerykeion/{kr_types → schemas}/kr_literals.py +7 -3
  29. kerykeion/{kr_types → schemas}/kr_models.py +220 -11
  30. kerykeion/{kr_types → schemas}/settings_models.py +7 -7
  31. kerykeion/settings/config_constants.py +75 -8
  32. kerykeion/settings/kerykeion_settings.py +1 -1
  33. kerykeion/settings/kr.config.json +132 -42
  34. kerykeion/settings/legacy/legacy_celestial_points_settings.py +8 -8
  35. kerykeion/sweph/ast136/s136108s.se1 +0 -0
  36. kerykeion/sweph/ast136/s136199s.se1 +0 -0
  37. kerykeion/sweph/ast136/s136472s.se1 +0 -0
  38. kerykeion/sweph/ast28/se28978s.se1 +0 -0
  39. kerykeion/sweph/ast50/se50000s.se1 +0 -0
  40. kerykeion/sweph/ast90/se90377s.se1 +0 -0
  41. kerykeion/sweph/ast90/se90482s.se1 +0 -0
  42. kerykeion/transits_time_range_factory.py +7 -7
  43. kerykeion/utilities.py +61 -38
  44. {kerykeion-5.0.0a12.dist-info → kerykeion-5.0.0b2.dist-info}/METADATA +507 -120
  45. kerykeion-5.0.0b2.dist-info/RECORD +58 -0
  46. kerykeion/house_comparison/house_comparison_models.py +0 -76
  47. kerykeion/kr_types/chart_types.py +0 -106
  48. kerykeion-5.0.0a12.dist-info/RECORD +0 -50
  49. /kerykeion/{kr_types → schemas}/kerykeion_exception.py +0 -0
  50. {kerykeion-5.0.0a12.dist-info → kerykeion-5.0.0b2.dist-info}/WHEEL +0 -0
  51. {kerykeion-5.0.0a12.dist-info → kerykeion-5.0.0b2.dist-info}/licenses/LICENSE +0 -0
kerykeion/__init__.py CHANGED
@@ -8,31 +8,55 @@ This is part of Kerykeion (C) 2025 Giacomo Battaglia
8
8
  # Local
9
9
  from .aspects import AspectsFactory
10
10
  from .astrological_subject_factory import AstrologicalSubjectFactory
11
- from .charts.kerykeion_chart_svg import KerykeionChartSVG
11
+ from .chart_data_factory import ChartDataFactory
12
+ from .schemas import KerykeionException
13
+ from .schemas.kr_models import (
14
+ ChartDataModel,
15
+ SingleChartDataModel,
16
+ DualChartDataModel,
17
+ ElementDistributionModel,
18
+ QualityDistributionModel,
19
+ HouseComparisonModel,
20
+ )
21
+ from .charts.chart_drawer import ChartDrawer
12
22
  from .composite_subject_factory import CompositeSubjectFactory
13
23
  from .ephemeris_data_factory import EphemerisDataFactory
14
24
  from .house_comparison.house_comparison_factory import HouseComparisonFactory
15
- from .house_comparison.house_comparison_models import HouseComparisonModel
16
- from .kr_types import *
17
25
  from .planetary_return_factory import PlanetaryReturnFactory, PlanetReturnModel
18
26
  from .relationship_score_factory import RelationshipScoreFactory
19
- from .report import Report
27
+ from .report import ReportGenerator
20
28
  from .settings import KerykeionSettingsModel, get_settings
21
29
  from .transits_time_range_factory import TransitsTimeRangeFactory
30
+ from .backword import (
31
+ AstrologicalSubject, # Legacy wrapper
32
+ KerykeionChartSVG, # Legacy wrapper
33
+ SynastryAspects, # Legacy wrapper
34
+ )
22
35
 
23
36
  __all__ = [
24
37
  "AspectsFactory",
25
38
  "AstrologicalSubjectFactory",
26
- "KerykeionChartSVG",
39
+ "ChartDataFactory",
40
+ "ChartDataModel",
41
+ "SingleChartDataModel",
42
+ "DualChartDataModel",
43
+ "ElementDistributionModel",
44
+ "QualityDistributionModel",
45
+ "ChartDrawer",
27
46
  "CompositeSubjectFactory",
28
47
  "EphemerisDataFactory",
29
48
  "HouseComparisonFactory",
30
49
  "HouseComparisonModel",
50
+ "KerykeionException",
31
51
  "PlanetaryReturnFactory",
32
52
  "PlanetReturnModel",
33
53
  "RelationshipScoreFactory",
34
- "Report",
54
+ "ReportGenerator",
35
55
  "KerykeionSettingsModel",
36
56
  "get_settings",
37
57
  "TransitsTimeRangeFactory",
58
+ # Legacy (v4) exported names for backward compatibility
59
+ "AstrologicalSubject",
60
+ "KerykeionChartSVG",
61
+ "SynastryAspects",
38
62
  ]
@@ -7,20 +7,20 @@ import logging
7
7
  from typing import Union, List, Optional
8
8
 
9
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 (
12
- AstrologicalSubjectModel,
13
- AspectModel,
14
- ActiveAspect,
15
- CompositeSubjectModel,
16
- PlanetReturnModel,
10
+ from kerykeion.aspects.aspects_utils import get_aspect_from_two_points, get_active_points_list, calculate_aspect_movement
11
+ from kerykeion.schemas.kr_models import (
12
+ AstrologicalSubjectModel,
13
+ AspectModel,
14
+ ActiveAspect,
15
+ CompositeSubjectModel,
16
+ PlanetReturnModel,
17
17
  SingleChartAspectsModel,
18
18
  DualChartAspectsModel,
19
19
  # Legacy aliases for backward compatibility
20
20
  NatalAspectsModel,
21
21
  SynastryAspectsModel
22
22
  )
23
- from kerykeion.kr_types.kr_literals import AstrologicalPoint
23
+ from kerykeion.schemas.kr_literals import AstrologicalPoint
24
24
  from kerykeion.settings.config_constants import DEFAULT_ACTIVE_ASPECTS, DEFAULT_AXIS_ORBIT
25
25
  from kerykeion.settings.legacy.legacy_celestial_points_settings import DEFAULT_CELESTIAL_POINTS_SETTINGS
26
26
  from kerykeion.settings.legacy.legacy_chart_aspects_settings import DEFAULT_CHART_ASPECTS_SETTINGS
@@ -39,8 +39,8 @@ class AspectsFactory:
39
39
  """
40
40
  Unified factory class for creating both single chart and dual chart aspects analysis.
41
41
 
42
- This factory provides methods to calculate all aspects within a single chart or
43
- between two charts. It consolidates the common functionality between different
42
+ This factory provides methods to calculate all aspects within a single chart or
43
+ between two charts. It consolidates the common functionality between different
44
44
  types of aspect calculations while providing specialized methods for each type.
45
45
 
46
46
  The factory provides both comprehensive and filtered aspect lists based on orb settings
@@ -57,7 +57,7 @@ class AspectsFactory:
57
57
  >>> # For single chart aspects (natal, returns, etc.)
58
58
  >>> johnny = AstrologicalSubjectFactory.from_birth_data("Johnny", 1963, 6, 9, 0, 0, "Owensboro", "US")
59
59
  >>> single_chart_aspects = AspectsFactory.single_chart_aspects(johnny)
60
- >>>
60
+ >>>
61
61
  >>> # For dual chart aspects (synastry, comparisons, etc.)
62
62
  >>> john = AstrologicalSubjectFactory.from_birth_data("John", 1990, 1, 1, 12, 0, "London", "GB")
63
63
  >>> jane = AstrologicalSubjectFactory.from_birth_data("Jane", 1992, 6, 15, 14, 30, "Paris", "FR")
@@ -77,7 +77,7 @@ class AspectsFactory:
77
77
  This method calculates all astrological aspects (angular relationships)
78
78
  within a single chart. Can be used for any type of chart including:
79
79
  - Natal charts
80
- - Planetary return charts
80
+ - Planetary return charts
81
81
  - Composite charts
82
82
  - Any other single chart type
83
83
 
@@ -227,7 +227,7 @@ class AspectsFactory:
227
227
 
228
228
  Args:
229
229
  first_subject: First astrological subject
230
- second_subject: Second astrological subject
230
+ second_subject: Second astrological subject
231
231
  active_points_resolved: Resolved list of active celestial points
232
232
  active_aspects_resolved: Resolved list of active aspects with orbs
233
233
  aspects_settings: Chart aspect configuration settings
@@ -284,10 +284,10 @@ class AspectsFactory:
284
284
  ("Descendant", "Ascendant"),
285
285
  ("Medium_Coeli", "Imum_Coeli"),
286
286
  ("Imum_Coeli", "Medium_Coeli"),
287
- ("True_Node", "True_South_Node"),
288
- ("Mean_Node", "Mean_South_Node"),
289
- ("True_South_Node", "True_Node"),
290
- ("Mean_South_Node", "Mean_Node"),
287
+ ("True_North_Lunar_Node", "True_South_Lunar_Node"),
288
+ ("Mean_North_Lunar_Node", "Mean_South_Lunar_Node"),
289
+ ("True_South_Lunar_Node", "True_North_Lunar_Node"),
290
+ ("Mean_South_Lunar_Node", "Mean_North_Lunar_Node"),
291
291
  }
292
292
 
293
293
  all_aspects_list = []
@@ -313,6 +313,13 @@ class AspectsFactory:
313
313
  first_planet_id = planet_id_lookup.get(first_name, 0)
314
314
  second_planet_id = planet_id_lookup.get(second_name, 0)
315
315
 
316
+ # Calculate aspect movement (applying/separating/exact)
317
+ aspect_movement = calculate_aspect_movement(
318
+ active_points_list[first]["abs_pos"],
319
+ active_points_list[second]["abs_pos"],
320
+ aspect["aspect_degrees"]
321
+ )
322
+
316
323
  aspect_model = AspectModel(
317
324
  p1_name=first_name,
318
325
  p1_owner=subject.name,
@@ -326,6 +333,7 @@ class AspectsFactory:
326
333
  diff=aspect["diff"],
327
334
  p1=first_planet_id,
328
335
  p2=second_planet_id,
336
+ aspect_movement=aspect_movement,
329
337
  )
330
338
  all_aspects_list.append(aspect_model)
331
339
 
@@ -382,11 +390,18 @@ class AspectsFactory:
382
390
  if aspect["verdict"]:
383
391
  first_name = first_active_points_list[first]["name"]
384
392
  second_name = second_active_points_list[second]["name"]
385
-
393
+
386
394
  # Get planet IDs using lookup dictionary for better performance
387
395
  first_planet_id = planet_id_lookup.get(first_name, 0)
388
396
  second_planet_id = planet_id_lookup.get(second_name, 0)
389
397
 
398
+ # Calculate aspect movement (applying/separating/exact)
399
+ aspect_movement = calculate_aspect_movement(
400
+ first_active_points_list[first]["abs_pos"],
401
+ second_active_points_list[second]["abs_pos"],
402
+ aspect["aspect_degrees"]
403
+ )
404
+
390
405
  aspect_model = AspectModel(
391
406
  p1_name=first_name,
392
407
  p1_owner=first_subject.name,
@@ -400,6 +415,7 @@ class AspectsFactory:
400
415
  diff=aspect["diff"],
401
416
  p1=first_planet_id,
402
417
  p2=second_planet_id,
418
+ aspect_movement=aspect_movement,
403
419
  )
404
420
  all_aspects_list.append(aspect_model)
405
421
 
@@ -407,18 +423,18 @@ class AspectsFactory:
407
423
 
408
424
  @staticmethod
409
425
  def _update_aspect_settings(
410
- aspects_settings: List[dict],
426
+ aspects_settings: List[dict],
411
427
  active_aspects: List[ActiveAspect]
412
428
  ) -> List[dict]:
413
429
  """
414
430
  Update aspects settings with active aspects orbs.
415
-
431
+
416
432
  This is a common utility method used by both single chart and dual chart calculations.
417
-
433
+
418
434
  Args:
419
435
  aspects_settings: Base aspect settings
420
436
  active_aspects: Active aspects with their orb configurations
421
-
437
+
422
438
  Returns:
423
439
  List of filtered and updated aspect settings
424
440
  """
@@ -472,7 +488,7 @@ class AspectsFactory:
472
488
  ) -> NatalAspectsModel:
473
489
  """
474
490
  Legacy method - use single_chart_aspects() instead.
475
-
491
+
476
492
  ⚠️ DEPRECATION WARNING ⚠️
477
493
  This method is deprecated. Use AspectsFactory.single_chart_aspects() instead.
478
494
  """
@@ -488,7 +504,7 @@ class AspectsFactory:
488
504
  ) -> SynastryAspectsModel:
489
505
  """
490
506
  Legacy method - use dual_chart_aspects() instead.
491
-
507
+
492
508
  ⚠️ DEPRECATION WARNING ⚠️
493
509
  This method is deprecated. Use AspectsFactory.dual_chart_aspects() instead.
494
510
  """
@@ -5,15 +5,13 @@
5
5
  # TODO: Better documentation and unit tests
6
6
 
7
7
  from swisseph import difdeg2n
8
- from typing import Union, TYPE_CHECKING
9
- from kerykeion.kr_types.kr_models import AstrologicalSubjectModel, CompositeSubjectModel, PlanetReturnModel
10
- from kerykeion.kr_types.settings_models import KerykeionSettingsCelestialPointModel
8
+ from typing import Union
9
+ from kerykeion.schemas.kr_models import AstrologicalSubjectModel, CompositeSubjectModel, PlanetReturnModel
10
+ from kerykeion.schemas.kr_literals import AspectMovementType
11
+ from kerykeion.schemas.settings_models import KerykeionSettingsCelestialPointModel
11
12
  from kerykeion.settings.legacy.legacy_celestial_points_settings import DEFAULT_CELESTIAL_POINTS_SETTINGS
12
13
 
13
14
 
14
- if TYPE_CHECKING:
15
- pass
16
-
17
15
  def get_aspect_from_two_points(
18
16
  aspects_settings: Union[list[dict], list[dict]],
19
17
  point_one: Union[float, int],
@@ -58,6 +56,77 @@ def get_aspect_from_two_points(
58
56
  }
59
57
 
60
58
 
59
+ def calculate_aspect_movement(
60
+ point_one_abs_pos: float,
61
+ point_two_abs_pos: float,
62
+ aspect_degrees: int,
63
+ exact_orb_threshold: float = 0.05
64
+ ) -> AspectMovementType:
65
+ """
66
+ Calculate whether an aspect is applying, separating, or exact.
67
+
68
+ An aspect is:
69
+ - "Exact": When the orb is very tight (default < 0.17°)
70
+ - "Applying": When the faster planet is moving toward the exact aspect
71
+ - "Separating": When the faster planet is moving away from the exact aspect
72
+
73
+ For simplicity, we assume the first planet (p1) is faster than the second (p2).
74
+ This is generally true for:
75
+ - Moon vs outer planets
76
+ - Inner planets vs outer planets
77
+ - Transits (transiting planet vs natal planet)
78
+
79
+ Args:
80
+ point_one_abs_pos: Absolute position of first point (0-360°)
81
+ point_two_abs_pos: Absolute position of second point (0-360°)
82
+ aspect_degrees: The exact degree of the aspect (0, 60, 90, 120, 180, etc.)
83
+ exact_orb_threshold: Maximum orb to consider aspect "exact" (default 0.17°)
84
+
85
+ Returns:
86
+ "Applying", "Separating", or "Exact"
87
+
88
+ Example:
89
+ >>> # Moon at 45° applying to Sun at 50° (conjunction at 0°/360°)
90
+ >>> calculate_aspect_movement(45, 50, 0)
91
+ 'Applying'
92
+ >>> # Moon at 55° separating from Sun at 50° (conjunction)
93
+ >>> calculate_aspect_movement(55, 50, 0)
94
+ 'Separating'
95
+ """
96
+
97
+ # Calculate the angular distance
98
+ distance = abs(difdeg2n(point_one_abs_pos, point_two_abs_pos))
99
+ orbit = abs(distance - aspect_degrees)
100
+
101
+ # Check if aspect is exact (within tight orb)
102
+ if orbit <= exact_orb_threshold:
103
+ return "Exact"
104
+
105
+ # Calculate if p1 is ahead or behind p2 relative to the aspect
106
+ # We need to determine the direction of movement
107
+ diff = difdeg2n(point_one_abs_pos, point_two_abs_pos)
108
+
109
+ # For conjunction (0°) or opposition (180°)
110
+ if aspect_degrees == 0 or aspect_degrees == 360:
111
+ # If p1 is behind p2 (negative diff), it's applying
112
+ # If p1 is ahead of p2 (positive diff), it's separating
113
+ return "Applying" if diff < 0 else "Separating"
114
+
115
+ elif aspect_degrees == 180:
116
+ # For opposition, the logic is reversed
117
+ return "Applying" if abs(diff) < 180 else "Separating"
118
+
119
+ else:
120
+ # For other aspects (60°, 90°, 120°, 150°)
121
+ # Check if the distance is increasing or decreasing
122
+ # If distance < aspect_degrees and diff < 0: applying
123
+ # If distance > aspect_degrees or diff > 0: separating
124
+ if abs(diff) < aspect_degrees:
125
+ return "Applying" if diff < 0 else "Separating"
126
+ else:
127
+ return "Separating" if diff > 0 else "Applying"
128
+
129
+
61
130
  def planet_id_decoder(planets_settings: list[KerykeionSettingsCelestialPointModel], name: str) -> int:
62
131
  """
63
132
  Check if the name of the planet is the same in the settings and return