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,301 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Relationship Score Factory Module
4
+
5
+ This module calculates relationship scores between two astrological subjects using the
6
+ Ciro Discepolo method. It analyzes synastry aspects to generate numerical compatibility
7
+ scores with descriptive categories.
8
+
9
+ Key Features:
10
+ - Point-based scoring system using synastry aspects
11
+ - Configurable major/minor aspect filtering
12
+ - Orbital precision weighting
13
+ - Categorical score descriptions
14
+
15
+ Score Categories:
16
+ - 0-5: Minimal relationship
17
+ - 5-10: Medium relationship
18
+ - 10-15: Important relationship
19
+ - 15-20: Very important relationship
20
+ - 20-30: Exceptional relationship
21
+ - 30+: Rare exceptional relationship
22
+
23
+ Classes:
24
+ RelationshipScoreFactory: Main factory for calculating relationship scores
25
+
26
+ Example:
27
+ >>> from kerykeion import AstrologicalSubjectFactory
28
+ >>> from kerykeion.relationship_score_factory import RelationshipScoreFactory
29
+ >>>
30
+ >>> person1 = AstrologicalSubjectFactory.from_birth_data("John", 1990, 5, 15, 12, 0, "New York", "US")
31
+ >>> person2 = AstrologicalSubjectFactory.from_birth_data("Jane", 1988, 8, 22, 14, 30, "London", "GB")
32
+ >>> factory = RelationshipScoreFactory(person1, person2)
33
+ >>> score = factory.get_relationship_score()
34
+ >>> print(f"Score: {score.score_value} ({score.score_description})")
35
+
36
+ Reference:
37
+ Ciro Discepolo Method: http://www.cirodiscepolo.it/Articoli/Discepoloele.htm
38
+
39
+ Author: Giacomo Battaglia
40
+ Copyright: (C) 2025 Kerykeion Project
41
+ License: AGPL-3.0
42
+ """
43
+
44
+ import logging
45
+ from typing import Optional
46
+
47
+ from kerykeion import AstrologicalSubjectFactory
48
+ from kerykeion.aspects import AspectsFactory
49
+ from kerykeion.schemas.kr_models import AstrologicalSubjectModel, RelationshipScoreAspectModel, RelationshipScoreModel
50
+ from kerykeion.schemas.kr_literals import RelationshipScoreDescription
51
+
52
+ # Scoring constants
53
+ DESTINY_SIGN_POINTS = 5
54
+ HIGH_PRECISION_ORBIT_THRESHOLD = 2
55
+ MAJOR_ASPECT_POINTS_HIGH_PRECISION = 11
56
+ MAJOR_ASPECT_POINTS_STANDARD = 8
57
+ MINOR_ASPECT_POINTS = 4
58
+ SUN_ASCENDANT_ASPECT_POINTS = 4
59
+ MOON_ASCENDANT_ASPECT_POINTS = 4
60
+ VENUS_MARS_ASPECT_POINTS = 4
61
+
62
+
63
+ class RelationshipScoreFactory:
64
+ """
65
+ Calculates relationship scores between two subjects using the Ciro Discepolo method.
66
+
67
+ The scoring system evaluates synastry aspects between planetary positions to generate
68
+ numerical compatibility scores with categorical descriptions.
69
+
70
+ Score Ranges:
71
+ - 0-5: Minimal relationship
72
+ - 5-10: Medium relationship
73
+ - 10-15: Important relationship
74
+ - 15-20: Very important relationship
75
+ - 20-30: Exceptional relationship
76
+ - 30+: Rare exceptional relationship
77
+
78
+ Args:
79
+ first_subject (AstrologicalSubjectModel): First astrological subject
80
+ second_subject (AstrologicalSubjectModel): Second astrological subject
81
+ use_only_major_aspects (bool, optional): Filter to major aspects only. Defaults to True.
82
+ axis_orb_limit (float | None, optional): Optional orb threshold for chart axes
83
+ filtering during aspect calculation.
84
+
85
+ Reference:
86
+ http://www.cirodiscepolo.it/Articoli/Discepoloele.htm
87
+ """
88
+
89
+ SCORE_MAPPING = [
90
+ ("Minimal", 5),
91
+ ("Medium", 10),
92
+ ("Important", 15),
93
+ ("Very Important", 20),
94
+ ("Exceptional", 30),
95
+ ("Rare Exceptional", float("inf")),
96
+ ]
97
+
98
+ MAJOR_ASPECTS = {"conjunction", "opposition", "square", "trine", "sextile"}
99
+
100
+ def __init__(
101
+ self,
102
+ first_subject: AstrologicalSubjectModel,
103
+ second_subject: AstrologicalSubjectModel,
104
+ use_only_major_aspects: bool = True,
105
+ *,
106
+ axis_orb_limit: Optional[float] = None,
107
+ ):
108
+ self.use_only_major_aspects = use_only_major_aspects
109
+ self.first_subject: AstrologicalSubjectModel = first_subject
110
+ self.second_subject: AstrologicalSubjectModel = second_subject
111
+
112
+ self.score_value = 0
113
+ self.relationship_score_description: RelationshipScoreDescription = "Minimal"
114
+ self.is_destiny_sign = False
115
+ self.relationship_score_aspects: list[RelationshipScoreAspectModel] = []
116
+ self._synastry_aspects = AspectsFactory.dual_chart_aspects(
117
+ self.first_subject,
118
+ self.second_subject,
119
+ axis_orb_limit=axis_orb_limit,
120
+ ).aspects
121
+
122
+ def _evaluate_destiny_sign(self):
123
+ """
124
+ Checks if subjects share the same sun sign quality and adds points.
125
+
126
+ Adds 5 points if both subjects have sun signs with matching quality
127
+ (cardinal, fixed, or mutable).
128
+ """
129
+ if self.first_subject.sun["quality"] == self.second_subject.sun["quality"]: # type: ignore
130
+ self.is_destiny_sign = True
131
+ self.score_value += DESTINY_SIGN_POINTS
132
+ logging.debug(f"Destiny sign found, adding {DESTINY_SIGN_POINTS} points, total score: {self.score_value}")
133
+
134
+ def _evaluate_aspect(self, aspect, points):
135
+ """
136
+ Processes an aspect and adds points to the total score.
137
+
138
+ Args:
139
+ aspect (dict): Aspect data containing planetary positions and geometry
140
+ points (int): Points to add to the total score
141
+ """
142
+ if self.use_only_major_aspects and aspect["aspect"] not in self.MAJOR_ASPECTS:
143
+ return
144
+
145
+ self.score_value += points
146
+ self.relationship_score_aspects.append(
147
+ RelationshipScoreAspectModel(
148
+ p1_name=aspect["p1_name"],
149
+ p2_name=aspect["p2_name"],
150
+ aspect=aspect["aspect"],
151
+ orbit=aspect["orbit"],
152
+ )
153
+ )
154
+ logging.debug(f"{aspect['p1_name']}-{aspect['p2_name']} aspect: {aspect['aspect']} with orbit {aspect['orbit']} degrees, adding {points} points, total score: {self.score_value}, total aspects: {len(self.relationship_score_aspects)}")
155
+
156
+ def _evaluate_sun_sun_main_aspect(self, aspect):
157
+ """
158
+ Evaluates Sun-Sun conjunction, opposition, or square aspects.
159
+
160
+ Adds 8 points for standard orbs, 11 points for tight orbs (≤2°).
161
+
162
+ Args:
163
+ aspect (dict): Aspect data
164
+ """
165
+ if aspect["p1_name"] == "Sun" and aspect["p2_name"] == "Sun" and aspect["aspect"] in {"conjunction", "opposition", "square"}:
166
+ points = MAJOR_ASPECT_POINTS_HIGH_PRECISION if aspect["orbit"] <= HIGH_PRECISION_ORBIT_THRESHOLD else MAJOR_ASPECT_POINTS_STANDARD
167
+ self._evaluate_aspect(aspect, points)
168
+
169
+ def _evaluate_sun_moon_conjunction(self, aspect):
170
+ """
171
+ Evaluates Sun-Moon conjunction aspects.
172
+
173
+ Adds 8 points for standard orbs, 11 points for tight orbs (≤2°).
174
+
175
+ Args:
176
+ aspect (dict): Aspect data
177
+ """
178
+ if {aspect["p1_name"], aspect["p2_name"]} == {"Moon", "Sun"} and aspect["aspect"] == "conjunction":
179
+ points = MAJOR_ASPECT_POINTS_HIGH_PRECISION if aspect["orbit"] <= HIGH_PRECISION_ORBIT_THRESHOLD else MAJOR_ASPECT_POINTS_STANDARD
180
+ self._evaluate_aspect(aspect, points)
181
+
182
+ def _evaluate_sun_sun_other_aspects(self, aspect):
183
+ """
184
+ Evaluates Sun-Sun aspects other than conjunction, opposition, or square.
185
+
186
+ Adds 4 points for any qualifying aspect.
187
+
188
+ Args:
189
+ aspect (dict): Aspect data
190
+ """
191
+ if aspect["p1_name"] == "Sun" and aspect["p2_name"] == "Sun" and aspect["aspect"] not in {"conjunction", "opposition", "square"}:
192
+ points = MINOR_ASPECT_POINTS
193
+ self._evaluate_aspect(aspect, points)
194
+
195
+ def _evaluate_sun_moon_other_aspects(self, aspect):
196
+ """
197
+ Evaluates Sun-Moon aspects other than conjunctions.
198
+
199
+ Adds 4 points for any qualifying aspect.
200
+
201
+ Args:
202
+ aspect (dict): Aspect data
203
+ """
204
+ if {aspect["p1_name"], aspect["p2_name"]} == {"Moon", "Sun"} and aspect["aspect"] != "conjunction":
205
+ points = MINOR_ASPECT_POINTS
206
+ self._evaluate_aspect(aspect, points)
207
+
208
+ def _evaluate_sun_ascendant_aspect(self, aspect):
209
+ """
210
+ Evaluates Sun-Ascendant aspects.
211
+
212
+ Adds 4 points for any aspect between Sun and Ascendant.
213
+
214
+ Args:
215
+ aspect (dict): Aspect data
216
+ """
217
+ if {aspect["p1_name"], aspect["p2_name"]} == {"Sun", "Ascendant"}:
218
+ points = SUN_ASCENDANT_ASPECT_POINTS
219
+ self._evaluate_aspect(aspect, points)
220
+
221
+ def _evaluate_moon_ascendant_aspect(self, aspect):
222
+ """
223
+ Evaluates Moon-Ascendant aspects.
224
+
225
+ Adds 4 points for any aspect between Moon and Ascendant.
226
+
227
+ Args:
228
+ aspect (dict): Aspect data
229
+ """
230
+ if {aspect["p1_name"], aspect["p2_name"]} == {"Moon", "Ascendant"}:
231
+ points = MOON_ASCENDANT_ASPECT_POINTS
232
+ self._evaluate_aspect(aspect, points)
233
+
234
+ def _evaluate_venus_mars_aspect(self, aspect):
235
+ """
236
+ Evaluates Venus-Mars aspects.
237
+
238
+ Adds 4 points for any aspect between Venus and Mars.
239
+
240
+ Args:
241
+ aspect (dict): Aspect data
242
+ """
243
+ if {aspect["p1_name"], aspect["p2_name"]} == {"Venus", "Mars"}:
244
+ points = VENUS_MARS_ASPECT_POINTS
245
+ self._evaluate_aspect(aspect, points)
246
+
247
+ def _evaluate_relationship_score_description(self):
248
+ """
249
+ Determines the categorical description based on the numerical score.
250
+
251
+ Maps the total score to predefined description ranges.
252
+ """
253
+ for description, threshold in self.SCORE_MAPPING:
254
+ if self.score_value < threshold:
255
+ self.relationship_score_description = description
256
+ break
257
+
258
+ def get_relationship_score(self):
259
+ """
260
+ Calculates the complete relationship score using all evaluation methods.
261
+
262
+ Returns:
263
+ RelationshipScoreModel: Score object containing numerical value, description,
264
+ destiny sign status, contributing aspects, and subject data.
265
+ """
266
+ self._evaluate_destiny_sign()
267
+
268
+ for aspect in self._synastry_aspects:
269
+ self._evaluate_sun_sun_main_aspect(aspect)
270
+ self._evaluate_sun_moon_conjunction(aspect)
271
+ self._evaluate_sun_moon_other_aspects(aspect)
272
+ self._evaluate_sun_sun_other_aspects(aspect)
273
+ self._evaluate_sun_ascendant_aspect(aspect)
274
+ self._evaluate_moon_ascendant_aspect(aspect)
275
+ self._evaluate_venus_mars_aspect(aspect)
276
+
277
+ self._evaluate_relationship_score_description()
278
+
279
+ return RelationshipScoreModel(
280
+ score_value=self.score_value,
281
+ score_description=self.relationship_score_description,
282
+ is_destiny_sign=self.is_destiny_sign,
283
+ aspects=self.relationship_score_aspects,
284
+ subjects=[self.first_subject, self.second_subject],
285
+ )
286
+
287
+
288
+ if __name__ == "__main__":
289
+ from kerykeion.utilities import setup_logging
290
+
291
+ setup_logging(level="critical")
292
+
293
+ john = AstrologicalSubjectFactory.from_birth_data("John Lennon", 1940, 10, 9, 18, 30, "Liverpool", "GB", lng=53.416666, lat=-3, tz_str="Europe/London")
294
+ yoko = AstrologicalSubjectFactory.from_birth_data("Yoko Ono", 1933, 2, 18, 20, 30, "Tokyo", "JP", lng=35.68611, lat=139.7525, tz_str="Asia/Tokyo")
295
+
296
+ factory = RelationshipScoreFactory(john, yoko)
297
+ score = factory.get_relationship_score()
298
+
299
+ # Remove subjects key
300
+ score.subjects = []
301
+ print(score.model_dump_json(indent=4))