kerykeion 5.0.0a8__py3-none-any.whl → 5.0.0a10__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 (38) hide show
  1. kerykeion/__init__.py +22 -1
  2. kerykeion/aspects/__init__.py +7 -2
  3. kerykeion/aspects/aspects_utils.py +1 -3
  4. kerykeion/aspects/natal_aspects_factory.py +236 -0
  5. kerykeion/aspects/synastry_aspects_factory.py +234 -0
  6. kerykeion/astrological_subject_factory.py +5 -9
  7. kerykeion/charts/charts_utils.py +27 -12
  8. kerykeion/charts/draw_planets.py +3 -4
  9. kerykeion/charts/draw_planets_v2.py +5 -6
  10. kerykeion/charts/kerykeion_chart_svg.py +52 -10
  11. kerykeion/charts/templates/aspect_grid_only.xml +1 -1
  12. kerykeion/charts/templates/chart.xml +7 -1
  13. kerykeion/charts/templates/wheel_only.xml +8 -2
  14. kerykeion/composite_subject_factory.py +1 -1
  15. kerykeion/house_comparison/__init__.py +6 -0
  16. kerykeion/house_comparison/house_comparison_factory.py +1 -1
  17. kerykeion/house_comparison/house_comparison_utils.py +0 -1
  18. kerykeion/kr_types/__init__.py +49 -0
  19. kerykeion/kr_types/chart_types.py +3 -0
  20. kerykeion/kr_types/kr_models.py +29 -0
  21. kerykeion/kr_types/settings_models.py +9 -1
  22. kerykeion/planetary_return_factory.py +6 -5
  23. kerykeion/relationship_score_factory.py +27 -17
  24. kerykeion/report.py +0 -1
  25. kerykeion/settings/__init__.py +5 -0
  26. kerykeion/settings/config_constants.py +20 -6
  27. kerykeion/settings/kr.config.json +80 -0
  28. kerykeion/transits_time_range.py +4 -4
  29. kerykeion/utilities.py +1 -1
  30. {kerykeion-5.0.0a8.dist-info → kerykeion-5.0.0a10.dist-info}/METADATA +9 -4
  31. kerykeion-5.0.0a10.dist-info/RECORD +53 -0
  32. kerykeion/aspects/natal_aspects.py +0 -181
  33. kerykeion/aspects/synastry_aspects.py +0 -141
  34. kerykeion/aspects/transits_time_range.py +0 -41
  35. kerykeion-5.0.0a8.dist-info/RECORD +0 -55
  36. kerykeion-5.0.0a8.dist-info/entry_points.txt +0 -2
  37. {kerykeion-5.0.0a8.dist-info → kerykeion-5.0.0a10.dist-info}/WHEEL +0 -0
  38. {kerykeion-5.0.0a8.dist-info → kerykeion-5.0.0a10.dist-info}/licenses/LICENSE +0 -0
@@ -88,7 +88,6 @@ def draw_planets(
88
88
  keys.sort()
89
89
  switch = 0
90
90
 
91
- planets_degrouped = {}
92
91
  groups = []
93
92
  planets_by_pos = list(range(len(planets_degut)))
94
93
  planet_drange = 3.4
@@ -186,7 +185,7 @@ def draw_planets(
186
185
  # position relative to next planets
187
186
  else:
188
187
  startA = (leftover / (xa + xb)) * xa
189
- startB = (leftover / (xa + xb)) * xb
188
+ (leftover / (xa + xb)) * xb
190
189
 
191
190
  if available > need:
192
191
  planets_delta[groups[a][0][0]] = startA - groups[a][0][1] + (1.5 * planet_drange)
@@ -265,7 +264,7 @@ def draw_planets(
265
264
 
266
265
  output += f'<g kr:node="ChartPoint" kr:house="{planet_details["house"]}" kr:sign="{planet_details["sign"]}" kr:slug="{planet_details["name"]}" transform="translate(-{12 * scale},-{12 * scale}) scale({scale})">'
267
266
  output += f'<use x="{planet_x * (1/scale)}" y="{planet_y * (1/scale)}" xlink:href="#{available_planets_setting[i]["name"]}" />'
268
- output += f"</g>"
267
+ output += "</g>"
269
268
 
270
269
  # make transit degut and display planets
271
270
  if chart_type == "Transit" or chart_type == "Synastry" or chart_type == "Return":
@@ -373,7 +372,7 @@ def draw_planets(
373
372
  xo = -1
374
373
  deg_x = sliceToX(0, (radius - rtext), t_offset + xo) + rtext
375
374
  deg_y = sliceToY(0, (radius - rtext), t_offset + xo) + rtext
376
- degree = int(t_offset)
375
+ int(t_offset)
377
376
  output += f'<g transform="translate({deg_x},{deg_y})">'
378
377
  output += f'<text transform="rotate({rotate})" text-anchor="{textanchor}'
379
378
  output += f'" style="fill: {available_planets_setting[i]["color"]}; font-size: 10px;">{convert_decimal_to_degree_string(t_points_deg[i], format_type="1")}'
@@ -50,15 +50,15 @@ def draw_planets_v2(
50
50
  # 1. Validate inputs and prepare data
51
51
  # -----------------------------------------------------------
52
52
  if chart_type == "Transit" and second_subject_available_kerykeion_celestial_points is None:
53
- raise KerykeionException(f"Secondary celestial points are required for Transit charts")
53
+ raise KerykeionException("Secondary celestial points are required for Transit charts")
54
54
  elif chart_type == "Synastry" and second_subject_available_kerykeion_celestial_points is None:
55
- raise KerykeionException(f"Secondary celestial points are required for Synastry charts")
55
+ raise KerykeionException("Secondary celestial points are required for Synastry charts")
56
56
  elif chart_type == "Return" and second_subject_available_kerykeion_celestial_points is None:
57
- raise KerykeionException(f"Secondary celestial points are required for Return charts")
57
+ raise KerykeionException("Secondary celestial points are required for Return charts")
58
58
 
59
59
  # Extract absolute and relative positions for main celestial points
60
60
  main_points_abs_positions = [planet.abs_pos for planet in available_kerykeion_celestial_points]
61
- main_points_rel_positions = [planet.position for planet in available_kerykeion_celestial_points]
61
+ [planet.position for planet in available_kerykeion_celestial_points]
62
62
 
63
63
  # Extract absolute and relative positions for secondary celestial points if needed
64
64
  secondary_points_abs_positions = []
@@ -87,7 +87,6 @@ def draw_planets_v2(
87
87
  # 3. Identify groups of celestial points that are close to each other
88
88
  # -----------------------------------------------------------
89
89
  point_groups = []
90
- current_group = []
91
90
  is_group_open = False
92
91
  planets_by_position = [None] * len(position_index_map)
93
92
 
@@ -573,7 +572,7 @@ def draw_secondary_points(
573
572
  # Draw point symbol
574
573
  point_x = sliceToX(0, radius - point_radius, point_offset) + point_radius
575
574
  point_y = sliceToY(0, radius - point_radius, point_offset) + point_radius
576
- output += f'<g class="transit-planet-name" transform="translate(-6,-6)"><g transform="scale(0.5)">'
575
+ output += '<g class="transit-planet-name" transform="translate(-6,-6)"><g transform="scale(0.5)">'
577
576
  output += f'<use x="{point_x*2}" y="{point_y*2}" xlink:href="#{points_settings[point_idx]["name"]}" /></g></g>'
578
577
 
579
578
  # Draw connecting line
@@ -9,8 +9,8 @@ import swisseph as swe
9
9
  from typing import get_args, Union, Optional
10
10
 
11
11
  from kerykeion.settings.kerykeion_settings import get_settings
12
- from kerykeion.aspects.synastry_aspects import SynastryAspects
13
- from kerykeion.aspects.natal_aspects import NatalAspects
12
+ from kerykeion.aspects.synastry_aspects_factory import SynastryAspectsFactory
13
+ from kerykeion.aspects.natal_aspects_factory import NatalAspectsFactory
14
14
  from kerykeion.house_comparison.house_comparison_factory import HouseComparisonFactory
15
15
  from kerykeion.kr_types import (
16
16
  KerykeionException,
@@ -42,6 +42,7 @@ from kerykeion.charts.charts_utils import (
42
42
  draw_transit_ring_degree_steps,
43
43
  draw_degree_ring,
44
44
  draw_transit_ring,
45
+ draw_background_circle,
45
46
  draw_first_circle,
46
47
  draw_house_comparison_grid,
47
48
  draw_second_circle,
@@ -72,7 +73,7 @@ from kerykeion.settings.legacy.legacy_chart_aspects_settings import DEFAULT_CHAR
72
73
  from pathlib import Path
73
74
  from scour.scour import scourString
74
75
  from string import Template
75
- from typing import Union, List, Literal
76
+ from typing import List, Literal
76
77
  from datetime import datetime
77
78
 
78
79
 
@@ -176,6 +177,7 @@ class KerykeionChartSVG:
176
177
  chart_language: KerykeionChartLanguage
177
178
  active_points: List[AstrologicalPoint]
178
179
  active_aspects: List[ActiveAspect]
180
+ transparent_background: bool
179
181
 
180
182
  # Internal properties
181
183
  fire: float
@@ -210,6 +212,7 @@ class KerykeionChartSVG:
210
212
  active_points: Optional[list[AstrologicalPoint]] = None,
211
213
  active_aspects: list[ActiveAspect]= DEFAULT_ACTIVE_ASPECTS,
212
214
  *,
215
+ transparent_background: bool = False,
213
216
  colors_settings: dict = DEFAULT_CHART_COLORS,
214
217
  celestial_points_settings: list[dict] = DEFAULT_CELESTIAL_POINTS_SETTINGS,
215
218
  aspects_settings: list[dict] = DEFAULT_CHART_ASPECTS_SETTINGS,
@@ -238,6 +241,8 @@ class KerykeionChartSVG:
238
241
  Celestial points to include in the chart visualization.
239
242
  active_aspects (List[ActiveAspect], optional):
240
243
  Aspects to calculate, each defined by name and orb.
244
+ transparent_background (bool, optional):
245
+ Whether to use a transparent background instead of the theme color. Defaults to False.
241
246
  """
242
247
  # --------------------
243
248
  # COMMON INITIALIZATION
@@ -248,6 +253,7 @@ class KerykeionChartSVG:
248
253
  self.active_aspects = active_aspects
249
254
  self.chart_type = chart_type
250
255
  self.double_chart_aspect_grid_type = double_chart_aspect_grid_type
256
+ self.transparent_background = transparent_background
251
257
  self.chart_colors_settings = colors_settings
252
258
  self.planets_settings = celestial_points_settings
253
259
  self.aspects_settings = aspects_settings
@@ -306,7 +312,7 @@ class KerykeionChartSVG:
306
312
  raise KerykeionException("First object must be an AstrologicalSubjectModel or AstrologicalSubject instance.")
307
313
 
308
314
  # Calculate aspects
309
- natal_aspects_instance = NatalAspects(
315
+ natal_aspects_instance = NatalAspectsFactory.from_subject(
310
316
  self.first_obj,
311
317
  new_settings_file=self.new_settings_file,
312
318
  active_points=self.active_points,
@@ -341,7 +347,7 @@ class KerykeionChartSVG:
341
347
  raise KerykeionException("First object must be a CompositeSubjectModel instance.")
342
348
 
343
349
  # Calculate aspects
344
- self.aspects_list = NatalAspects(self.first_obj, new_settings_file=self.new_settings_file, active_points=self.active_points).relevant_aspects
350
+ self.aspects_list = NatalAspectsFactory.from_subject(self.first_obj, new_settings_file=self.new_settings_file, active_points=self.active_points).relevant_aspects
345
351
 
346
352
  # Screen size
347
353
  self.height = self._DEFAULT_HEIGHT
@@ -372,7 +378,7 @@ class KerykeionChartSVG:
372
378
  self.second_obj = second_obj
373
379
 
374
380
  # Calculate aspects (transit to natal)
375
- synastry_aspects_instance = SynastryAspects(
381
+ synastry_aspects_instance = SynastryAspectsFactory.from_subjects(
376
382
  self.first_obj,
377
383
  self.second_obj,
378
384
  new_settings_file=self.new_settings_file,
@@ -417,7 +423,7 @@ class KerykeionChartSVG:
417
423
  self.second_obj = second_obj
418
424
 
419
425
  # Calculate aspects (natal to partner)
420
- synastry_aspects_instance = SynastryAspects(
426
+ synastry_aspects_instance = SynastryAspectsFactory.from_subjects(
421
427
  self.first_obj,
422
428
  self.second_obj,
423
429
  new_settings_file=self.new_settings_file,
@@ -458,7 +464,7 @@ class KerykeionChartSVG:
458
464
  self.second_obj = second_obj
459
465
 
460
466
  # Calculate aspects (natal to return)
461
- synastry_aspects_instance = SynastryAspects(
467
+ synastry_aspects_instance = SynastryAspectsFactory.from_subjects(
462
468
  self.first_obj,
463
469
  self.second_obj,
464
470
  new_settings_file=self.new_settings_file,
@@ -492,7 +498,7 @@ class KerykeionChartSVG:
492
498
  raise KerykeionException("First object must be an AstrologicalSubjectModel or AstrologicalSubject instance.")
493
499
 
494
500
  # Calculate aspects
495
- natal_aspects_instance = NatalAspects(
501
+ natal_aspects_instance = NatalAspectsFactory.from_subject(
496
502
  self.first_obj,
497
503
  new_settings_file=self.new_settings_file,
498
504
  active_points=self.active_points,
@@ -708,6 +714,12 @@ class KerykeionChartSVG:
708
714
  template_dict["paper_color_0"] = self.chart_colors_settings["paper_0"]
709
715
  template_dict["paper_color_1"] = self.chart_colors_settings["paper_1"]
710
716
 
717
+ # Set background color based on transparent_background setting
718
+ if self.transparent_background:
719
+ template_dict["background_color"] = "transparent"
720
+ else:
721
+ template_dict["background_color"] = self.chart_colors_settings["paper_1"]
722
+
711
723
  # Set planet colors
712
724
  for planet in self.planets_settings:
713
725
  planet_id = planet["id"]
@@ -769,6 +781,11 @@ class KerykeionChartSVG:
769
781
  self.first_obj.seventh_house.abs_pos,
770
782
  self.chart_colors_settings["paper_0"],
771
783
  )
784
+ template_dict["background_circle"] = draw_background_circle(
785
+ self.main_radius,
786
+ self.chart_colors_settings["paper_1"],
787
+ self.chart_colors_settings["paper_1"],
788
+ )
772
789
  template_dict["first_circle"] = draw_first_circle(
773
790
  self.main_radius,
774
791
  self.chart_colors_settings["zodiac_radix_ring_2"],
@@ -800,7 +817,7 @@ class KerykeionChartSVG:
800
817
  template_dict["makeAspects"] = self._draw_all_aspects_lines(self.main_radius, self.main_radius - self.third_circle_radius)
801
818
 
802
819
  # Chart title
803
- template_dict["stringTitle"] = f'{self.first_obj.name} - {self.language_settings.get("Birth Chart", "Birth Chart")}'
820
+ template_dict["stringTitle"] = f'{self.first_obj.name} - {self.language_settings.get("birth_chart", "Birth Chart")}'
804
821
 
805
822
  # Top left section
806
823
  latitude_string = convert_latitude_coordinate_to_string(self.geolat, self.language_settings["north"], self.language_settings["south"])
@@ -894,6 +911,11 @@ class KerykeionChartSVG:
894
911
  self.first_obj.seventh_house.abs_pos,
895
912
  self.chart_colors_settings["paper_0"],
896
913
  )
914
+ template_dict["background_circle"] = draw_background_circle(
915
+ self.main_radius,
916
+ self.chart_colors_settings["paper_1"],
917
+ self.chart_colors_settings["paper_1"],
918
+ )
897
919
  template_dict["first_circle"] = draw_first_circle(
898
920
  self.main_radius,
899
921
  self.chart_colors_settings["zodiac_radix_ring_2"],
@@ -1054,6 +1076,11 @@ class KerykeionChartSVG:
1054
1076
  self.chart_colors_settings["zodiac_transit_ring_3"],
1055
1077
  )
1056
1078
  template_dict["degreeRing"] = draw_transit_ring_degree_steps(self.main_radius, self.first_obj.seventh_house.abs_pos)
1079
+ template_dict["background_circle"] = draw_background_circle(
1080
+ self.main_radius,
1081
+ self.chart_colors_settings["paper_1"],
1082
+ self.chart_colors_settings["paper_1"],
1083
+ )
1057
1084
  template_dict["first_circle"] = draw_first_circle(
1058
1085
  self.main_radius,
1059
1086
  self.chart_colors_settings["zodiac_transit_ring_2"],
@@ -1224,6 +1251,11 @@ class KerykeionChartSVG:
1224
1251
  self.chart_colors_settings["zodiac_transit_ring_3"],
1225
1252
  )
1226
1253
  template_dict["degreeRing"] = draw_transit_ring_degree_steps(self.main_radius, self.first_obj.seventh_house.abs_pos)
1254
+ template_dict["background_circle"] = draw_background_circle(
1255
+ self.main_radius,
1256
+ self.chart_colors_settings["paper_1"],
1257
+ self.chart_colors_settings["paper_1"],
1258
+ )
1227
1259
  template_dict["first_circle"] = draw_first_circle(
1228
1260
  self.main_radius,
1229
1261
  self.chart_colors_settings["zodiac_transit_ring_2"],
@@ -1365,6 +1397,11 @@ class KerykeionChartSVG:
1365
1397
  self.chart_colors_settings["zodiac_transit_ring_3"],
1366
1398
  )
1367
1399
  template_dict["degreeRing"] = draw_transit_ring_degree_steps(self.main_radius, self.first_obj.seventh_house.abs_pos)
1400
+ template_dict["background_circle"] = draw_background_circle(
1401
+ self.main_radius,
1402
+ self.chart_colors_settings["paper_1"],
1403
+ self.chart_colors_settings["paper_1"],
1404
+ )
1368
1405
  template_dict["first_circle"] = draw_first_circle(
1369
1406
  self.main_radius,
1370
1407
  self.chart_colors_settings["zodiac_transit_ring_2"],
@@ -1547,6 +1584,11 @@ class KerykeionChartSVG:
1547
1584
  self.first_obj.seventh_house.abs_pos,
1548
1585
  self.chart_colors_settings["paper_0"],
1549
1586
  )
1587
+ template_dict["background_circle"] = draw_background_circle(
1588
+ self.main_radius,
1589
+ self.chart_colors_settings["paper_1"],
1590
+ self.chart_colors_settings["paper_1"],
1591
+ )
1550
1592
  template_dict["first_circle"] = draw_first_circle(
1551
1593
  self.main_radius,
1552
1594
  self.chart_colors_settings["zodiac_radix_ring_2"],
@@ -7,7 +7,7 @@
7
7
  height="100%"
8
8
  viewBox="28 20 255 250"
9
9
  preserveAspectRatio="xMidYMid"
10
- style="background-color: $paper_color_1"
10
+ style="background-color: $background_color"
11
11
  >
12
12
 
13
13
  <!-- Colors -->
@@ -7,7 +7,7 @@
7
7
  height="100%"
8
8
  viewBox="$viewbox"
9
9
  preserveAspectRatio="xMidYMid"
10
- style="background-color: $paper_color_1"
10
+ style="background-color: $background_color"
11
11
  >
12
12
  <title>$stringTitle</title>
13
13
 
@@ -60,6 +60,12 @@
60
60
 
61
61
  <!-- Full Wheel -->
62
62
  <g kr:node="Full_Wheel" transform="translate(100,50)">
63
+
64
+ <!-- Background Circle -->
65
+ <g kr:node='Background_Circle'>
66
+ $background_circle
67
+ </g>
68
+
63
69
  <!-- Zodiac -->
64
70
  <g kr:node="Zodiac">
65
71
  $makeZodiac
@@ -5,9 +5,9 @@
5
5
  xmlns:kr="https://www.kerykeion.net/"
6
6
  width="100%"
7
7
  height="100%"
8
- viewBox="40 40 500 500"
8
+ viewBox="85 40 500 500"
9
9
  preserveAspectRatio="xMidYMid"
10
- style="background-color: $paper_color_1"
10
+ style="background-color: $background_color"
11
11
  >
12
12
  <title>$stringTitle | Kerykeion</title>
13
13
 
@@ -21,6 +21,12 @@
21
21
 
22
22
  <!-- Full Wheel -->
23
23
  <g kr:node="Full_Wheel" transform="translate(100,50)">
24
+
25
+ <!-- Background Circle -->
26
+ <g kr:node='Background_Circle'>
27
+ $background_circle
28
+ </g>
29
+
24
30
  <!-- Zodiac -->
25
31
  <g kr:node="Zodiac">
26
32
  $makeZodiac
@@ -1,4 +1,4 @@
1
- from typing import Union, TYPE_CHECKING
1
+ from typing import Union
2
2
 
3
3
  # Fix the circular import by changing this import
4
4
  from kerykeion.astrological_subject_factory import AstrologicalSubjectFactory
@@ -1,3 +1,9 @@
1
1
 
2
2
  from .house_comparison_factory import HouseComparisonFactory
3
3
  from .house_comparison_models import HouseComparisonModel, PointInHouseModel
4
+
5
+ __all__ = [
6
+ "HouseComparisonFactory",
7
+ "HouseComparisonModel",
8
+ "PointInHouseModel",
9
+ ]
@@ -1,6 +1,6 @@
1
1
  from kerykeion.house_comparison.house_comparison_utils import calculate_points_in_reciprocal_houses
2
2
  from typing import Union
3
- from kerykeion.settings.config_constants import DEFAULT_ACTIVE_POINTS, DEFAULT_ACTIVE_ASPECTS
3
+ from kerykeion.settings.config_constants import DEFAULT_ACTIVE_POINTS
4
4
  from kerykeion.house_comparison.house_comparison_models import HouseComparisonModel
5
5
  from kerykeion.astrological_subject_factory import AstrologicalSubjectFactory
6
6
  from kerykeion.kr_types import AstrologicalSubjectModel, PlanetReturnModel
@@ -1,4 +1,3 @@
1
- from kerykeion.astrological_subject_factory import AstrologicalSubjectFactory
2
1
  from kerykeion.kr_types.kr_models import AstrologicalSubjectModel, PlanetReturnModel
3
2
  from kerykeion.kr_types.kr_literals import AstrologicalPoint
4
3
  from kerykeion.settings.config_constants import DEFAULT_ACTIVE_POINTS
@@ -8,3 +8,52 @@ from .kr_literals import *
8
8
  from .kr_models import *
9
9
  from .chart_types import ChartTemplateDictionary
10
10
  from .settings_models import KerykeionSettingsModel
11
+
12
+ __all__ = [
13
+ # Exceptions
14
+ "KerykeionException",
15
+
16
+ # Settings and Chart Types
17
+ "ChartTemplateDictionary",
18
+ "KerykeionSettingsModel",
19
+
20
+ # Main Literal Types (from kr_literals)
21
+ "ZodiacType",
22
+ "Sign",
23
+ "SignNumbers",
24
+ "Houses",
25
+ "HouseNumbers",
26
+ "AstrologicalPoint",
27
+ "Element",
28
+ "Quality",
29
+ "ChartType",
30
+ "PointType",
31
+ "LunarPhaseEmoji",
32
+ "LunarPhaseName",
33
+ "SiderealMode",
34
+ "HousesSystemIdentifier",
35
+ "PerspectiveType",
36
+ "SignsEmoji",
37
+ "KerykeionChartTheme",
38
+ "KerykeionChartLanguage",
39
+ "RelationshipScoreDescription",
40
+ "CompositeChartType",
41
+ "AspectName",
42
+
43
+ # Main Model Classes (from kr_models)
44
+ "SubscriptableBaseModel",
45
+ "LunarPhaseModel",
46
+ "KerykeionPointModel",
47
+ "AstrologicalBaseModel",
48
+ "AstrologicalSubjectModel",
49
+ "CompositeSubjectModel",
50
+ "PlanetReturnModel",
51
+ "EphemerisDictModel",
52
+ "AspectModel",
53
+ "ZodiacSignModel",
54
+ "RelationshipScoreAspectModel",
55
+ "RelationshipScoreModel",
56
+ "ActiveAspect",
57
+ "TransitMomentModel",
58
+ "TransitsTimeRangeModel",
59
+ ]
@@ -4,6 +4,7 @@ from typing_extensions import TypedDict
4
4
  class ChartTemplateDictionary(TypedDict):
5
5
  transitRing: str
6
6
  degreeRing: str
7
+ background_circle: str
7
8
  first_circle: str
8
9
  second_circle: str
9
10
  third_circle: str
@@ -31,6 +32,8 @@ class ChartTemplateDictionary(TypedDict):
31
32
  paper_color_0: str
32
33
  # Background color of the chart
33
34
  paper_color_1: str
35
+ # Dynamic background color (can be transparent or theme color)
36
+ background_color: str
34
37
 
35
38
  # Planets colors, from 0 to 16 (0 is the Sun)
36
39
  planets_color_0: str
@@ -285,6 +285,35 @@ class TransitMomentModel(SubscriptableBaseModel):
285
285
  aspects: List[AspectModel] = Field(description="List of aspects active at this specific moment.")
286
286
 
287
287
 
288
+ class NatalAspectsModel(SubscriptableBaseModel):
289
+ """
290
+ Model representing all aspects in a natal chart.
291
+
292
+ Contains both all calculated aspects and the filtered relevant aspects
293
+ for an astrological subject.
294
+ """
295
+ subject: Union["AstrologicalSubjectModel", "CompositeSubjectModel", "PlanetReturnModel"] = Field(description="The astrological subject for which aspects were calculated.")
296
+ all_aspects: List[AspectModel] = Field(description="Complete list of all calculated aspects.")
297
+ relevant_aspects: List[AspectModel] = Field(description="Filtered list of relevant aspects based on orb settings.")
298
+ active_points: List[AstrologicalPoint] = Field(description="List of active points used in the calculation.")
299
+ active_aspects: List["ActiveAspect"] = Field(description="List of active aspects with their orb settings.")
300
+
301
+
302
+ class SynastryAspectsModel(SubscriptableBaseModel):
303
+ """
304
+ Model representing all aspects between two astrological subjects.
305
+
306
+ Contains both all calculated synastry aspects and the filtered relevant aspects
307
+ between two charts.
308
+ """
309
+ first_subject: Union["AstrologicalSubjectModel", "CompositeSubjectModel", "PlanetReturnModel"] = Field(description="The first astrological subject.")
310
+ second_subject: Union["AstrologicalSubjectModel", "CompositeSubjectModel", "PlanetReturnModel"] = Field(description="The second astrological subject.")
311
+ all_aspects: List[AspectModel] = Field(description="Complete list of all calculated synastry aspects.")
312
+ relevant_aspects: List[AspectModel] = Field(description="Filtered list of relevant synastry aspects based on orb settings.")
313
+ active_points: List[AstrologicalPoint] = Field(description="List of active points used in the calculation.")
314
+ active_aspects: List["ActiveAspect"] = Field(description="List of active aspects with their orb settings.")
315
+
316
+
288
317
  class TransitsTimeRangeModel(SubscriptableBaseModel):
289
318
  """
290
319
  Model representing a collection of transit moments for an astrological subject.
@@ -5,7 +5,7 @@
5
5
 
6
6
 
7
7
  from pydantic import Field
8
- from typing import List, Optional, Union
8
+ from typing import Optional
9
9
  from kerykeion.kr_types.kr_models import SubscriptableBaseModel
10
10
 
11
11
 
@@ -163,6 +163,14 @@ class KerykeionLanguageModel(SubscriptableBaseModel):
163
163
  return_point: str = Field(title="Return Point", description="The return point label in the chart, in the language")
164
164
  natal: str = Field(title="Natal", description="The natal label in the chart, in the language")
165
165
  perspective_type: str = Field(title="Perspective Type", description="The perspective type label in the chart, in the language")
166
+ location: str = Field(title="Location", description="The location label in the chart, in the language")
167
+ day_of_week: str = Field(title="Day of Week", description="The day of week label in the chart, in the language")
168
+ elements: str = Field(title="Elements", description="The elements label in the chart, in the language")
169
+ qualities: str = Field(title="Qualities", description="The qualities label in the chart, in the language")
170
+ cardinal: str = Field(title="Cardinal", description="The cardinal quality label in the chart, in the language")
171
+ fixed: str = Field(title="Fixed", description="The fixed quality label in the chart, in the language")
172
+ mutable: str = Field(title="Mutable", description="The mutable quality label in the chart, in the language")
173
+ birth_chart: str = Field(title="Birth Chart", description="The birth chart label in the chart, in the language")
166
174
 
167
175
  # Settings Model
168
176
  class KerykeionSettingsModel(SubscriptableBaseModel):
@@ -10,7 +10,7 @@ import logging
10
10
  import swisseph as swe
11
11
 
12
12
  from datetime import datetime, timezone
13
- from typing import Optional, Union
13
+ from typing import Union
14
14
 
15
15
  from kerykeion.kr_types import KerykeionException
16
16
  from kerykeion.fetch_geonames import FetchGeonames
@@ -18,6 +18,7 @@ from kerykeion.utilities import julian_to_datetime, datetime_to_julian
18
18
  from kerykeion.astrological_subject_factory import (
19
19
  GEONAMES_DEFAULT_USERNAME_WARNING,
20
20
  DEFAULT_GEONAMES_CACHE_EXPIRE_AFTER_DAYS,
21
+ DEFAULT_GEONAMES_USERNAME,
21
22
  )
22
23
  from kerykeion.astrological_subject_factory import AstrologicalSubjectFactory
23
24
  from kerykeion.kr_types.kr_literals import ReturnType
@@ -118,10 +119,10 @@ class PlanetaryReturnFactory:
118
119
  self.city_data: dict[str, str] = geonames.get_serialized_data()
119
120
 
120
121
  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
122
+ "countryCode" not in self.city_data
123
+ or "timezonestr" not in self.city_data
124
+ or "lat" not in self.city_data
125
+ or "lng" not in self.city_data
125
126
  ):
126
127
  raise KerykeionException("No data found for this city, try again! Maybe check your connection?")
127
128
 
@@ -4,13 +4,21 @@
4
4
  """
5
5
 
6
6
  from kerykeion import AstrologicalSubjectFactory
7
- from kerykeion.aspects.synastry_aspects import SynastryAspects
7
+ from kerykeion.aspects.synastry_aspects_factory import SynastryAspectsFactory
8
8
  import logging
9
- from pathlib import Path
10
- from typing import Union
11
9
  from kerykeion.kr_types.kr_models import AstrologicalSubjectModel, RelationshipScoreAspectModel, RelationshipScoreModel
12
10
  from kerykeion.kr_types.kr_literals import RelationshipScoreDescription
13
11
 
12
+ # Scoring constants
13
+ DESTINY_SIGN_POINTS = 5
14
+ HIGH_PRECISION_ORBIT_THRESHOLD = 2
15
+ MAJOR_ASPECT_POINTS_HIGH_PRECISION = 11
16
+ MAJOR_ASPECT_POINTS_STANDARD = 8
17
+ MINOR_ASPECT_POINTS = 4
18
+ SUN_ASCENDANT_ASPECT_POINTS = 4
19
+ MOON_ASCENDANT_ASPECT_POINTS = 4
20
+ VENUS_MARS_ASPECT_POINTS = 4
21
+
14
22
 
15
23
  class RelationshipScoreFactory:
16
24
  """
@@ -56,16 +64,16 @@ class RelationshipScoreFactory:
56
64
  self.relationship_score_description: RelationshipScoreDescription = "Minimal"
57
65
  self.is_destiny_sign = True
58
66
  self.relationship_score_aspects: list[RelationshipScoreAspectModel] = []
59
- self._synastry_aspects = SynastryAspects(self.first_subject, self.second_subject).all_aspects
67
+ self._synastry_aspects = SynastryAspectsFactory.from_subjects(self.first_subject, self.second_subject).all_aspects
60
68
 
61
69
  def _evaluate_destiny_sign(self):
62
70
  """
63
71
  Evaluates if the subjects share the same sun sign quality and adds points if true.
64
72
  """
65
- if self.first_subject.sun["quality"] == self.second_subject.sun["quality"]:
73
+ if self.first_subject.sun["quality"] == self.second_subject.sun["quality"]: # type: ignore
66
74
  self.is_destiny_sign = True
67
- self.score_value += 5
68
- logging.debug(f"Destiny sign found, adding 5 points, total score: {self.score_value}")
75
+ self.score_value += DESTINY_SIGN_POINTS
76
+ logging.debug(f"Destiny sign found, adding {DESTINY_SIGN_POINTS} points, total score: {self.score_value}")
69
77
 
70
78
  def _evaluate_aspect(self, aspect, points):
71
79
  """
@@ -99,7 +107,7 @@ class RelationshipScoreFactory:
99
107
  aspect (dict): Aspect information.
100
108
  """
101
109
  if aspect["p1_name"] == "Sun" and aspect["p2_name"] == "Sun" and aspect["aspect"] in {"conjunction", "opposition", "square"}:
102
- points = 11 if aspect["orbit"] <= 2 else 8
110
+ points = MAJOR_ASPECT_POINTS_HIGH_PRECISION if aspect["orbit"] <= HIGH_PRECISION_ORBIT_THRESHOLD else MAJOR_ASPECT_POINTS_STANDARD
103
111
  self._evaluate_aspect(aspect, points)
104
112
 
105
113
  def _evaluate_sun_moon_conjunction(self, aspect):
@@ -112,7 +120,7 @@ class RelationshipScoreFactory:
112
120
  aspect (dict): Aspect information.
113
121
  """
114
122
  if {aspect["p1_name"], aspect["p2_name"]} == {"Moon", "Sun"} and aspect["aspect"] == "conjunction":
115
- points = 11 if aspect["orbit"] <= 2 else 8
123
+ points = MAJOR_ASPECT_POINTS_HIGH_PRECISION if aspect["orbit"] <= HIGH_PRECISION_ORBIT_THRESHOLD else MAJOR_ASPECT_POINTS_STANDARD
116
124
  self._evaluate_aspect(aspect, points)
117
125
 
118
126
  def _evaluate_sun_sun_other_aspects(self, aspect):
@@ -124,7 +132,7 @@ class RelationshipScoreFactory:
124
132
  aspect (dict): Aspect information.
125
133
  """
126
134
  if aspect["p1_name"] == "Sun" and aspect["p2_name"] == "Sun" and aspect["aspect"] not in {"conjunction", "opposition", "square"}:
127
- points = 4
135
+ points = MINOR_ASPECT_POINTS
128
136
  self._evaluate_aspect(aspect, points)
129
137
 
130
138
  def _evaluate_sun_moon_other_aspects(self, aspect):
@@ -136,7 +144,7 @@ class RelationshipScoreFactory:
136
144
  aspect (dict): Aspect information.
137
145
  """
138
146
  if {aspect["p1_name"], aspect["p2_name"]} == {"Moon", "Sun"} and aspect["aspect"] != "conjunction":
139
- points = 4
147
+ points = MINOR_ASPECT_POINTS
140
148
  self._evaluate_aspect(aspect, points)
141
149
 
142
150
  def _evaluate_sun_ascendant_aspect(self, aspect):
@@ -148,7 +156,7 @@ class RelationshipScoreFactory:
148
156
  aspect (dict): Aspect information.
149
157
  """
150
158
  if {aspect["p1_name"], aspect["p2_name"]} == {"Sun", "Ascendant"}:
151
- points = 4
159
+ points = SUN_ASCENDANT_ASPECT_POINTS
152
160
  self._evaluate_aspect(aspect, points)
153
161
 
154
162
  def _evaluate_moon_ascendant_aspect(self, aspect):
@@ -160,7 +168,7 @@ class RelationshipScoreFactory:
160
168
  aspect (dict): Aspect information.
161
169
  """
162
170
  if {aspect["p1_name"], aspect["p2_name"]} == {"Moon", "Ascendant"}:
163
- points = 4
171
+ points = MOON_ASCENDANT_ASPECT_POINTS
164
172
  self._evaluate_aspect(aspect, points)
165
173
 
166
174
  def _evaluate_venus_mars_aspect(self, aspect):
@@ -172,7 +180,7 @@ class RelationshipScoreFactory:
172
180
  aspect (dict): Aspect information.
173
181
  """
174
182
  if {aspect["p1_name"], aspect["p2_name"]} == {"Venus", "Mars"}:
175
- points = 4
183
+ points = VENUS_MARS_ASPECT_POINTS
176
184
  self._evaluate_aspect(aspect, points)
177
185
 
178
186
  def _evaluate_relationship_score_description(self):
@@ -218,10 +226,12 @@ if __name__ == "__main__":
218
226
 
219
227
  setup_logging(level="critical")
220
228
 
221
- john = AstrologicalSubjectFactory.from_birth_data("John Lennon", 1940, 10, 9, 18, 30, "Liverpool", "GB")
222
- yoko = AstrologicalSubjectFactory.from_birth_data("Yoko Ono", 1933, 2, 18, 18, 30, "Tokyo", "JP")
229
+ john = AstrologicalSubjectFactory.from_birth_data("John Lennon", 1940, 10, 9, 18, 30, "Liverpool", "GB", lng=53.416666, lat=-3, tz_str="Europe/London")
230
+ yoko = AstrologicalSubjectFactory.from_birth_data("Yoko Ono", 1933, 2, 18, 20, 30, "Tokyo", "JP", lng=35.68611, lat=139.7525, tz_str="Asia/Tokyo")
223
231
 
224
232
  factory = RelationshipScoreFactory(john, yoko)
225
233
  score = factory.get_relationship_score()
226
- print(score)
227
234
 
235
+ # Remove subjects key
236
+ score.subjects = []
237
+ print(score.model_dump_json(indent=4))