kerykeion 5.0.0a11__py3-none-any.whl → 5.0.0b1__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 (56) hide show
  1. kerykeion/__init__.py +32 -9
  2. kerykeion/aspects/__init__.py +2 -4
  3. kerykeion/aspects/aspects_factory.py +530 -0
  4. kerykeion/aspects/aspects_utils.py +75 -6
  5. kerykeion/astrological_subject_factory.py +380 -229
  6. kerykeion/backword.py +680 -0
  7. kerykeion/chart_data_factory.py +484 -0
  8. kerykeion/charts/{kerykeion_chart_svg.py → chart_drawer.py} +612 -439
  9. kerykeion/charts/charts_utils.py +135 -94
  10. kerykeion/charts/draw_planets.py +38 -28
  11. kerykeion/charts/templates/aspect_grid_only.xml +188 -17
  12. kerykeion/charts/templates/chart.xml +104 -28
  13. kerykeion/charts/templates/wheel_only.xml +195 -24
  14. kerykeion/charts/themes/classic.css +11 -0
  15. kerykeion/charts/themes/dark-high-contrast.css +11 -0
  16. kerykeion/charts/themes/dark.css +11 -0
  17. kerykeion/charts/themes/light.css +11 -0
  18. kerykeion/charts/themes/strawberry.css +10 -0
  19. kerykeion/composite_subject_factory.py +4 -4
  20. kerykeion/ephemeris_data_factory.py +12 -9
  21. kerykeion/house_comparison/__init__.py +0 -3
  22. kerykeion/house_comparison/house_comparison_factory.py +51 -18
  23. kerykeion/house_comparison/house_comparison_utils.py +37 -8
  24. kerykeion/planetary_return_factory.py +8 -4
  25. kerykeion/relationship_score_factory.py +5 -5
  26. kerykeion/report.py +748 -67
  27. kerykeion/{kr_types → schemas}/__init__.py +44 -4
  28. kerykeion/schemas/chart_template_model.py +340 -0
  29. kerykeion/{kr_types → schemas}/kr_literals.py +7 -3
  30. kerykeion/{kr_types → schemas}/kr_models.py +247 -21
  31. kerykeion/{kr_types → schemas}/settings_models.py +7 -7
  32. kerykeion/settings/config_constants.py +75 -8
  33. kerykeion/settings/kerykeion_settings.py +1 -1
  34. kerykeion/settings/kr.config.json +130 -40
  35. kerykeion/settings/legacy/legacy_celestial_points_settings.py +8 -8
  36. kerykeion/sweph/ast136/s136108s.se1 +0 -0
  37. kerykeion/sweph/ast136/s136199s.se1 +0 -0
  38. kerykeion/sweph/ast136/s136472s.se1 +0 -0
  39. kerykeion/sweph/ast28/se28978s.se1 +0 -0
  40. kerykeion/sweph/ast50/se50000s.se1 +0 -0
  41. kerykeion/sweph/ast90/se90377s.se1 +0 -0
  42. kerykeion/sweph/ast90/se90482s.se1 +0 -0
  43. kerykeion/sweph/sefstars.txt +1602 -0
  44. kerykeion/transits_time_range_factory.py +11 -11
  45. kerykeion/utilities.py +61 -38
  46. kerykeion-5.0.0b1.dist-info/METADATA +1055 -0
  47. kerykeion-5.0.0b1.dist-info/RECORD +58 -0
  48. kerykeion/aspects/natal_aspects_factory.py +0 -235
  49. kerykeion/aspects/synastry_aspects_factory.py +0 -275
  50. kerykeion/house_comparison/house_comparison_models.py +0 -38
  51. kerykeion/kr_types/chart_types.py +0 -106
  52. kerykeion-5.0.0a11.dist-info/METADATA +0 -641
  53. kerykeion-5.0.0a11.dist-info/RECORD +0 -50
  54. /kerykeion/{kr_types → schemas}/kerykeion_exception.py +0 -0
  55. {kerykeion-5.0.0a11.dist-info → kerykeion-5.0.0b1.dist-info}/WHEEL +0 -0
  56. {kerykeion-5.0.0a11.dist-info → kerykeion-5.0.0b1.dist-info}/licenses/LICENSE +0 -0
@@ -22,9 +22,9 @@ Classes:
22
22
 
23
23
  Dependencies:
24
24
  - kerykeion.AstrologicalSubjectFactory: For creating astrological subjects
25
- - kerykeion.aspects.SynastryAspectsFactory: For calculating angular relationships
25
+ - kerykeion.aspects.AspectsFactory: For calculating angular relationships
26
26
  - kerykeion.ephemeris_data_factory: For generating time-series planetary positions
27
- - kerykeion.kr_types: For type definitions and model structures
27
+ - kerykeion.schemas: For type definitions and model structures
28
28
  - datetime: For date/time handling
29
29
 
30
30
  Example:
@@ -57,13 +57,13 @@ License: AGPL-3.0
57
57
 
58
58
  from typing import Union, List
59
59
  from datetime import datetime, timedelta
60
- from kerykeion.kr_types.kr_models import AstrologicalSubjectModel
60
+ from kerykeion.schemas.kr_models import AstrologicalSubjectModel
61
61
  from kerykeion.astrological_subject_factory import AstrologicalSubjectFactory
62
- from kerykeion.aspects import SynastryAspectsFactory
62
+ from kerykeion.aspects import AspectsFactory
63
63
  from kerykeion.ephemeris_data_factory import EphemerisDataFactory
64
- from kerykeion.kr_types.kr_literals import AstrologicalPoint
65
- from kerykeion.kr_types.kr_models import ActiveAspect, TransitMomentModel, TransitsTimeRangeModel
66
- from kerykeion.kr_types.settings_models import KerykeionSettingsModel
64
+ from kerykeion.schemas.kr_literals import AstrologicalPoint
65
+ from kerykeion.schemas.kr_models import ActiveAspect, TransitMomentModel, TransitsTimeRangeModel
66
+ from kerykeion.schemas.settings_models import KerykeionSettingsModel
67
67
  from kerykeion.settings.config_constants import DEFAULT_ACTIVE_POINTS, DEFAULT_ACTIVE_ASPECTS
68
68
  from pathlib import Path
69
69
 
@@ -115,8 +115,8 @@ class TransitsTimeRangeFactory:
115
115
 
116
116
  Custom configuration:
117
117
 
118
- >>> from kerykeion.kr_types import AstrologicalPoint, ActiveAspect
119
- >>> custom_points = [AstrologicalPoint.SUN, AstrologicalPoint.MOON]
118
+ >>> from kerykeion.schemas import AstrologicalPoint, ActiveAspect
119
+ >>> custom_points = ["Sun", "Moon"]
120
120
  >>> custom_aspects = [ActiveAspect.CONJUNCTION, ActiveAspect.OPPOSITION]
121
121
  >>> factory = TransitsTimeRangeFactory(
122
122
  ... natal_chart, ephemeris_data,
@@ -227,13 +227,13 @@ class TransitsTimeRangeFactory:
227
227
  See Also:
228
228
  TransitMomentModel: Individual transit moment structure
229
229
  TransitsTimeRangeModel: Complete transit dataset structure
230
- SynastryAspectsFactory: Underlying aspect calculation engine
230
+ AspectsFactory: Underlying aspect calculation engine
231
231
  """
232
232
  transit_moments = []
233
233
 
234
234
  for ephemeris_point in self.ephemeris_data_points:
235
235
  # Calculate aspects between transit positions and natal chart
236
- aspects = SynastryAspectsFactory.from_subjects(
236
+ aspects = AspectsFactory.dual_chart_aspects(
237
237
  ephemeris_point,
238
238
  self.natal_chart,
239
239
  active_points=self.active_points,
kerykeion/utilities.py CHANGED
@@ -4,7 +4,7 @@ Copyright: (C) 2025 Kerykeion Project
4
4
  License: AGPL-3.0
5
5
  """
6
6
 
7
- from kerykeion.kr_types import (
7
+ from kerykeion.schemas import (
8
8
  KerykeionPointModel,
9
9
  KerykeionException,
10
10
  ZodiacSignModel,
@@ -13,16 +13,13 @@ from kerykeion.kr_types import (
13
13
  CompositeSubjectModel,
14
14
  PlanetReturnModel,
15
15
  )
16
- from kerykeion.kr_types.kr_literals import LunarPhaseEmoji, LunarPhaseName, PointType, AstrologicalPoint, Houses
17
- from typing import Union, get_args, TYPE_CHECKING
16
+ from kerykeion.schemas.kr_literals import LunarPhaseEmoji, LunarPhaseName, PointType, AstrologicalPoint, Houses
17
+ from typing import Union, Optional, get_args
18
18
  import logging
19
19
  import math
20
20
  import re
21
21
  from datetime import datetime
22
22
 
23
- if TYPE_CHECKING:
24
- pass
25
-
26
23
 
27
24
  def get_number_from_name(name: AstrologicalPoint) -> int:
28
25
  """
@@ -58,14 +55,14 @@ def get_number_from_name(name: AstrologicalPoint) -> int:
58
55
  return 8
59
56
  elif name == "Pluto":
60
57
  return 9
61
- elif name == "Mean_Node":
58
+ elif name == "Mean_North_Lunar_Node":
62
59
  return 10
63
- elif name == "True_Node":
60
+ elif name == "True_North_Lunar_Node":
64
61
  return 11
65
62
  # Note: Swiss ephemeris library has no constants for south nodes. We're using integers >= 1000 for them.
66
- elif name == "Mean_South_Node":
63
+ elif name == "Mean_South_Lunar_Node":
67
64
  return 1000
68
- elif name == "True_South_Node":
65
+ elif name == "True_South_Lunar_Node":
69
66
  return 1100
70
67
  elif name == "Chiron":
71
68
  return 15
@@ -84,7 +81,7 @@ def get_number_from_name(name: AstrologicalPoint) -> int:
84
81
 
85
82
 
86
83
  def get_kerykeion_point_from_degree(
87
- degree: Union[int, float], name: Union[AstrologicalPoint, Houses], point_type: PointType
84
+ degree: Union[int, float], name: Union[AstrologicalPoint, Houses], point_type: PointType, speed: Optional[float] = None, declination: Optional[float] = None
88
85
  ) -> KerykeionPointModel:
89
86
  """
90
87
  Create a KerykeionPointModel from a degree position.
@@ -93,6 +90,8 @@ def get_kerykeion_point_from_degree(
93
90
  degree: The degree position (0-360, negative values are converted to positive)
94
91
  name: The name of the celestial point or house
95
92
  point_type: The type classification of the point
93
+ speed: The velocity/speed of the celestial point in degrees per day (optional)
94
+ declination: The declination of the celestial point in degrees (optional)
96
95
 
97
96
  Returns:
98
97
  A KerykeionPointModel with calculated zodiac sign, position, and properties
@@ -136,6 +135,8 @@ def get_kerykeion_point_from_degree(
136
135
  abs_pos=degree,
137
136
  emoji=zodiac_sign.emoji,
138
137
  point_type=point_type,
138
+ speed=speed,
139
+ declination=declination,
139
140
  )
140
141
 
141
142
 
@@ -416,9 +417,6 @@ def calculate_moon_phase(moon_abs_pos: float, sun_abs_pos: float) -> LunarPhaseM
416
417
  Returns:
417
418
  LunarPhaseModel containing phase data, emoji, and name
418
419
  """
419
- # Initialize moon_phase and sun_phase to None in case of an error
420
- moon_phase, sun_phase = None, None
421
-
422
420
  # Calculate the anti-clockwise degrees between the sun and moon
423
421
  degrees_between = (moon_abs_pos - sun_abs_pos) % 360
424
422
 
@@ -426,30 +424,12 @@ def calculate_moon_phase(moon_abs_pos: float, sun_abs_pos: float) -> LunarPhaseM
426
424
  step = 360.0 / 28.0
427
425
  moon_phase = int(degrees_between // step) + 1
428
426
 
429
- # Define the sun phase steps
430
- sunstep = [
431
- 0, 30, 40, 50, 60, 70, 80, 90, 120, 130, 140, 150, 160, 170, 180,
432
- 210, 220, 230, 240, 250, 260, 270, 300, 310, 320, 330, 340, 350
433
- ]
434
-
435
- # Calculate the sun phase (1-28) based on the degrees between the sun and moon
436
- for x in range(len(sunstep)):
437
- low = sunstep[x]
438
- high = sunstep[x + 1] if x < len(sunstep) - 1 else 360
439
- if low <= degrees_between < high:
440
- sun_phase = x + 1
441
- break
442
-
443
- # Create a dictionary with the lunar phase information
444
- lunar_phase_dictionary = {
445
- "degrees_between_s_m": degrees_between,
446
- "moon_phase": moon_phase,
447
- "sun_phase": sun_phase,
448
- "moon_emoji": get_moon_emoji_from_phase_int(moon_phase),
449
- "moon_phase_name": get_moon_phase_name_from_phase_int(moon_phase),
450
- }
451
-
452
- return LunarPhaseModel(**lunar_phase_dictionary)
427
+ return LunarPhaseModel(
428
+ degrees_between_s_m=degrees_between,
429
+ moon_phase=moon_phase,
430
+ moon_emoji=get_moon_emoji_from_phase_int(moon_phase),
431
+ moon_phase_name=get_moon_phase_name_from_phase_int(moon_phase)
432
+ )
453
433
 
454
434
 
455
435
  def circular_sort(degrees: list[Union[int, float]]) -> list[Union[int, float]]:
@@ -751,3 +731,46 @@ def find_common_active_points(first_points: list[AstrologicalPoint], second_poin
751
731
  common_points = list(set(first_points) & set(second_points))
752
732
 
753
733
  return common_points
734
+
735
+
736
+ def distribute_percentages_to_100(values: dict[str, float]) -> dict[str, int]:
737
+ """
738
+ Distribute percentages so they sum to exactly 100.
739
+
740
+ This function uses a largest remainder method to ensure that
741
+ the percentage total equals 100 even after rounding.
742
+
743
+ Args:
744
+ values: Dictionary with keys and their raw percentage values
745
+
746
+ Returns:
747
+ Dictionary with the same keys and integer percentages that sum to 100
748
+ """
749
+ if not values:
750
+ return {}
751
+
752
+ total = sum(values.values())
753
+ if total == 0:
754
+ return {key: 0 for key in values.keys()}
755
+
756
+ # Calculate base percentages
757
+ percentages = {key: value * 100 / total for key, value in values.items()}
758
+
759
+ # Get integer parts and remainders
760
+ integer_parts = {key: int(value) for key, value in percentages.items()}
761
+ remainders = {key: percentages[key] - integer_parts[key] for key in percentages.keys()}
762
+
763
+ # Calculate how many we need to add to reach 100
764
+ current_sum = sum(integer_parts.values())
765
+ needed = 100 - current_sum
766
+
767
+ # Sort by remainder (largest first) and add 1 to the largest remainders
768
+ sorted_by_remainder = sorted(remainders.items(), key=lambda x: x[1], reverse=True)
769
+
770
+ result = integer_parts.copy()
771
+ for i in range(needed):
772
+ if i < len(sorted_by_remainder):
773
+ key = sorted_by_remainder[i][0]
774
+ result[key] += 1
775
+
776
+ return result