kerykeion 4.26.2__py3-none-any.whl → 5.0.0a2__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 (49) hide show
  1. kerykeion/__init__.py +9 -7
  2. kerykeion/aspects/aspects_utils.py +14 -8
  3. kerykeion/aspects/natal_aspects.py +26 -17
  4. kerykeion/aspects/synastry_aspects.py +32 -15
  5. kerykeion/aspects/transits_time_range.py +2 -2
  6. kerykeion/astrological_subject_factory.py +1132 -0
  7. kerykeion/charts/charts_utils.py +676 -146
  8. kerykeion/charts/draw_planets.py +9 -8
  9. kerykeion/charts/draw_planets_v2.py +639 -0
  10. kerykeion/charts/kerykeion_chart_svg.py +1334 -601
  11. kerykeion/charts/templates/chart.xml +184 -78
  12. kerykeion/charts/templates/wheel_only.xml +13 -12
  13. kerykeion/charts/themes/classic.css +91 -76
  14. kerykeion/charts/themes/dark-high-contrast.css +129 -107
  15. kerykeion/charts/themes/dark.css +130 -107
  16. kerykeion/charts/themes/light.css +130 -103
  17. kerykeion/charts/themes/strawberry.css +143 -0
  18. kerykeion/composite_subject_factory.py +26 -43
  19. kerykeion/ephemeris_data.py +6 -10
  20. kerykeion/house_comparison/__init__.py +3 -0
  21. kerykeion/house_comparison/house_comparison_factory.py +70 -0
  22. kerykeion/house_comparison/house_comparison_models.py +38 -0
  23. kerykeion/house_comparison/house_comparison_utils.py +98 -0
  24. kerykeion/kr_types/chart_types.py +13 -5
  25. kerykeion/kr_types/kr_literals.py +34 -6
  26. kerykeion/kr_types/kr_models.py +122 -160
  27. kerykeion/kr_types/settings_models.py +107 -143
  28. kerykeion/planetary_return_factory.py +299 -0
  29. kerykeion/{relationship_score/relationship_score_factory.py → relationship_score_factory.py} +10 -13
  30. kerykeion/report.py +4 -4
  31. kerykeion/settings/config_constants.py +35 -6
  32. kerykeion/settings/kerykeion_settings.py +1 -0
  33. kerykeion/settings/kr.config.json +1301 -1255
  34. kerykeion/settings/legacy/__init__.py +0 -0
  35. kerykeion/settings/legacy/legacy_celestial_points_settings.py +299 -0
  36. kerykeion/settings/legacy/legacy_chart_aspects_settings.py +71 -0
  37. kerykeion/settings/legacy/legacy_color_settings.py +42 -0
  38. kerykeion/transits_time_range.py +13 -9
  39. kerykeion/utilities.py +228 -31
  40. {kerykeion-4.26.2.dist-info → kerykeion-5.0.0a2.dist-info}/METADATA +119 -107
  41. kerykeion-5.0.0a2.dist-info/RECORD +54 -0
  42. {kerykeion-4.26.2.dist-info → kerykeion-5.0.0a2.dist-info}/WHEEL +1 -1
  43. kerykeion/astrological_subject.py +0 -841
  44. kerykeion/relationship_score/__init__.py +0 -2
  45. kerykeion/relationship_score/relationship_score.py +0 -175
  46. kerykeion-4.26.2.dist-info/LICENSE +0 -661
  47. kerykeion-4.26.2.dist-info/RECORD +0 -46
  48. /LICENSE → /kerykeion-5.0.0a2.dist-info/LICENSE +0 -0
  49. {kerykeion-4.26.2.dist-info → kerykeion-5.0.0a2.dist-info}/entry_points.txt +0 -0
kerykeion/utilities.py CHANGED
@@ -1,15 +1,24 @@
1
- from kerykeion.kr_types import KerykeionPointModel, KerykeionException, ZodiacSignModel, AstrologicalSubjectModel, LunarPhaseModel
2
- from kerykeion.kr_types.kr_literals import LunarPhaseEmoji, LunarPhaseName, PointType, Planet, Houses, AxialCusps
1
+ from kerykeion.kr_types import (
2
+ KerykeionPointModel,
3
+ KerykeionException,
4
+ ZodiacSignModel,
5
+ AstrologicalSubjectModel,
6
+ LunarPhaseModel,
7
+ CompositeSubjectModel,
8
+ PlanetReturnModel,
9
+ )
10
+ from kerykeion.kr_types.kr_literals import LunarPhaseEmoji, LunarPhaseName, PointType, AstrologicalPoint, Houses
3
11
  from typing import Union, get_args, TYPE_CHECKING
4
12
  import logging
5
13
  import math
6
14
  import re
15
+ from datetime import datetime
7
16
 
8
17
  if TYPE_CHECKING:
9
- from kerykeion import AstrologicalSubject
18
+ from kerykeion import AstrologicalSubjectFactory
10
19
 
11
20
 
12
- def get_number_from_name(name: Planet) -> int:
21
+ def get_number_from_name(name: AstrologicalPoint) -> int:
13
22
  """Utility function, gets planet id from the name."""
14
23
 
15
24
  if name == "Sun":
@@ -45,20 +54,20 @@ def get_number_from_name(name: Planet) -> int:
45
54
  return 15
46
55
  elif name == "Mean_Lilith":
47
56
  return 12
48
- elif name == "Ascendant": # TODO: Is this needed?
57
+ elif name == "Ascendant": # TODO: Is this needed?
49
58
  return 9900
50
- elif name == "Descendant": # TODO: Is this needed?
59
+ elif name == "Descendant": # TODO: Is this needed?
51
60
  return 9901
52
- elif name == "Medium_Coeli": # TODO: Is this needed?
61
+ elif name == "Medium_Coeli": # TODO: Is this needed?
53
62
  return 9902
54
- elif name == "Imum_Coeli": # TODO: Is this needed?
63
+ elif name == "Imum_Coeli": # TODO: Is this needed?
55
64
  return 9903
56
65
  else:
57
66
  raise KerykeionException(f"Error in getting number from name! Name: {name}")
58
67
 
59
68
 
60
69
  def get_kerykeion_point_from_degree(
61
- degree: Union[int, float], name: Union[Planet, Houses, AxialCusps], point_type: PointType
70
+ degree: Union[int, float], name: Union[AstrologicalPoint, Houses], point_type: PointType
62
71
  ) -> KerykeionPointModel:
63
72
  """
64
73
  Returns a KerykeionPointModel object based on the given degree.
@@ -74,8 +83,11 @@ def get_kerykeion_point_from_degree(
74
83
  Returns:
75
84
  KerykeionPointModel: The model representing the celestial point.
76
85
  """
86
+ # If - single degree is given, convert it to a positive degree
87
+ if degree < 0:
88
+ degree = degree % 360
77
89
 
78
- if degree < 0 or degree >= 360:
90
+ if degree >= 360:
79
91
  raise KerykeionException(f"Error in calculating positions! Degrees: {degree}")
80
92
 
81
93
  ZODIAC_SIGNS = {
@@ -109,6 +121,7 @@ def get_kerykeion_point_from_degree(
109
121
  point_type=point_type,
110
122
  )
111
123
 
124
+
112
125
  def setup_logging(level: str) -> None:
113
126
  """
114
127
  Setup logging for testing.
@@ -129,9 +142,7 @@ def setup_logging(level: str) -> None:
129
142
 
130
143
 
131
144
  def is_point_between(
132
- start_point: Union[int, float],
133
- end_point: Union[int, float],
134
- evaluated_point: Union[int, float]
145
+ start_point: Union[int, float], end_point: Union[int, float], evaluated_point: Union[int, float]
135
146
  ) -> bool:
136
147
  """
137
148
  Determines if a point is between two others on a circle, with additional rules:
@@ -159,7 +170,9 @@ def is_point_between(
159
170
  # Ensure the range is not greater than 180°. Otherwise, it is not truly defined what
160
171
  # being located in between two points on a circle actually means.
161
172
  if angular_difference > 180:
162
- raise KerykeionException(f"The angle between start and end point is not allowed to exceed 180°, yet is: {angular_difference}")
173
+ raise KerykeionException(
174
+ f"The angle between start and end point is not allowed to exceed 180°, yet is: {angular_difference}"
175
+ )
163
176
 
164
177
  # Handle explicitly when evaluated_point == start_point. Note: It may happen for mathematical
165
178
  # reasons that evaluated_point and start_point deviate very slightly from each other, but
@@ -178,7 +191,6 @@ def is_point_between(
178
191
  return (0 <= p1_p3) and (p1_p3 < angular_difference)
179
192
 
180
193
 
181
-
182
194
  def get_planet_house(planet_position_degree: Union[int, float], houses_degree_ut_list: list) -> Houses:
183
195
  """
184
196
  Determines the house in which a planet is located based on its position in degrees.
@@ -243,6 +255,7 @@ def get_moon_emoji_from_phase_int(phase: int) -> LunarPhaseEmoji:
243
255
 
244
256
  return result
245
257
 
258
+
246
259
  def get_moon_phase_name_from_phase_int(phase: int) -> LunarPhaseName:
247
260
  """
248
261
  Returns the name of the moon phase.
@@ -255,11 +268,10 @@ def get_moon_phase_name_from_phase_int(phase: int) -> LunarPhaseName:
255
268
  """
256
269
  lunar_phase_names = get_args(LunarPhaseName)
257
270
 
258
-
259
271
  if phase == 1:
260
272
  result = lunar_phase_names[0]
261
273
  elif phase < 7:
262
- result = lunar_phase_names[1]
274
+ result = lunar_phase_names[1]
263
275
  elif 7 <= phase <= 9:
264
276
  result = lunar_phase_names[2]
265
277
  elif phase < 14:
@@ -281,8 +293,8 @@ def get_moon_phase_name_from_phase_int(phase: int) -> LunarPhaseName:
281
293
 
282
294
  def check_and_adjust_polar_latitude(latitude: float) -> float:
283
295
  """
284
- Utility function to check if the location is in the polar circle.
285
- If it is, it sets the latitude to 66 or -66 degrees.
296
+ Utility function to check if the location is in the polar circle.
297
+ If it is, it sets the latitude to 66 or -66 degrees.
286
298
  """
287
299
  if latitude > 66.0:
288
300
  latitude = 66.0
@@ -295,28 +307,29 @@ def check_and_adjust_polar_latitude(latitude: float) -> float:
295
307
  return latitude
296
308
 
297
309
 
298
- def get_houses_list(subject: Union["AstrologicalSubject", AstrologicalSubjectModel]) -> list[KerykeionPointModel]:
310
+ def get_houses_list(
311
+ subject: Union[AstrologicalSubjectModel, CompositeSubjectModel, PlanetReturnModel]
312
+ ) -> list[KerykeionPointModel]:
299
313
  """
300
314
  Return the names of the houses in the order of the houses.
301
315
  """
302
316
  houses_absolute_position_list = []
303
317
  for house in subject.houses_names_list:
304
- houses_absolute_position_list.append(subject[house.lower()])
318
+ houses_absolute_position_list.append(subject[house.lower()])
305
319
 
306
320
  return houses_absolute_position_list
307
321
 
308
322
 
309
- def get_available_astrological_points_list(subject: Union["AstrologicalSubject", AstrologicalSubjectModel]) -> list[KerykeionPointModel]:
323
+ def get_available_astrological_points_list(
324
+ subject: AstrologicalSubjectModel
325
+ ) -> list[KerykeionPointModel]:
310
326
  """
311
327
  Return the names of the planets in the order of the planets.
312
328
  The names can be used to access the planets from the AstrologicalSubject object with the __getitem__ method or the [] operator.
313
329
  """
314
330
  planets_absolute_position_list = []
315
- for planet in subject.planets_names_list:
316
- planets_absolute_position_list.append(subject[planet.lower()])
317
-
318
- for axis in subject.axial_cusps_names_list:
319
- planets_absolute_position_list.append(subject[axis.lower()])
331
+ for planet in subject.active_points:
332
+ planets_absolute_position_list.append(subject[planet.lower()])
320
333
 
321
334
  return planets_absolute_position_list
322
335
 
@@ -387,7 +400,7 @@ def calculate_moon_phase(moon_abs_pos: float, sun_abs_pos: float) -> LunarPhaseM
387
400
  "moon_phase": moon_phase,
388
401
  "sun_phase": sun_phase,
389
402
  "moon_emoji": get_moon_emoji_from_phase_int(moon_phase),
390
- "moon_phase_name": get_moon_phase_name_from_phase_int(moon_phase)
403
+ "moon_phase_name": get_moon_phase_name_from_phase_int(moon_phase),
391
404
  }
392
405
 
393
406
  return LunarPhaseModel(**lunar_phase_dictionary)
@@ -490,8 +503,192 @@ def inline_css_variables_in_svg(svg_content: str) -> str:
490
503
  # This handles nested variables or variables that reference other variables
491
504
  processed_svg = svg_without_style_blocks
492
505
  while variable_usage_pattern.search(processed_svg):
493
- processed_svg = variable_usage_pattern.sub(
494
- lambda m: replace_css_variable_reference(m), processed_svg
495
- )
506
+ processed_svg = variable_usage_pattern.sub(lambda m: replace_css_variable_reference(m), processed_svg)
496
507
 
497
508
  return processed_svg
509
+
510
+
511
+ def datetime_to_julian(dt: datetime) -> float:
512
+ """
513
+ Converts a Python datetime object to Julian day.
514
+
515
+ Args:
516
+ dt: A datetime object
517
+
518
+ Returns:
519
+ float: The corresponding Julian day (JD)
520
+ """
521
+ # Extract year, month and day
522
+ year = dt.year
523
+ month = dt.month
524
+ day = dt.day
525
+
526
+ # Adjust month and year according to the conversion formula
527
+ if month <= 2:
528
+ year -= 1
529
+ month += 12
530
+
531
+ # Calculate century and year in century
532
+ a = year // 100
533
+ b = 2 - a + (a // 4)
534
+
535
+ # Calculate the Julian day
536
+ jd = int(365.25 * (year + 4716)) + int(30.6001 * (month + 1)) + day + b - 1524.5
537
+
538
+ # Add the time portion
539
+ hour = dt.hour
540
+ minute = dt.minute
541
+ second = dt.second
542
+ microsecond = dt.microsecond
543
+
544
+ jd += (hour + minute / 60 + second / 3600 + microsecond / 3600000000) / 24
545
+
546
+ return jd
547
+
548
+
549
+ def julian_to_datetime(jd):
550
+ """
551
+ Converts a Julian day to a Python datetime object.
552
+
553
+ Args:
554
+ jd: Julian day number (float)
555
+
556
+ Returns:
557
+ datetime: The corresponding datetime object
558
+ """
559
+ # Add 0.5 to the Julian day to adjust for noon-based Julian day
560
+ jd_plus = jd + 0.5
561
+
562
+ # Integer and fractional parts
563
+ Z = int(jd_plus)
564
+ F = jd_plus - Z
565
+
566
+ # Calculate alpha
567
+ if Z < 2299161:
568
+ A = Z # Julian calendar
569
+ else:
570
+ alpha = int((Z - 1867216.25) / 36524.25)
571
+ A = Z + 1 + alpha - int(alpha / 4) # Gregorian calendar
572
+
573
+ # Calculate B
574
+ B = A + 1524
575
+
576
+ # Calculate C
577
+ C = int((B - 122.1) / 365.25)
578
+
579
+ # Calculate D
580
+ D = int(365.25 * C)
581
+
582
+ # Calculate E
583
+ E = int((B - D) / 30.6001)
584
+
585
+ # Calculate day and month
586
+ day = B - D - int(30.6001 * E) + F
587
+
588
+ # Integer part of day
589
+ day_int = int(day)
590
+
591
+ # Fractional part converted to hours, minutes, seconds, microseconds
592
+ day_frac = day - day_int
593
+ hours = int(day_frac * 24)
594
+ minutes = int((day_frac * 24 - hours) * 60)
595
+ seconds = int((day_frac * 24 * 60 - hours * 60 - minutes) * 60)
596
+ microseconds = int(((day_frac * 24 * 60 - hours * 60 - minutes) * 60 - seconds) * 1000000)
597
+
598
+ # Calculate month
599
+ if E < 14:
600
+ month = E - 1
601
+ else:
602
+ month = E - 13
603
+
604
+ # Calculate year
605
+ if month > 2:
606
+ year = C - 4716
607
+ else:
608
+ year = C - 4715
609
+
610
+ # Create and return datetime object
611
+ return datetime(year, month, day_int, hours, minutes, seconds, microseconds)
612
+
613
+
614
+ def get_house_name(house_number: int) -> Houses:
615
+ """
616
+ Returns the name of the house based on its number.
617
+
618
+ Args:
619
+ house_number: House number (1-12)
620
+
621
+ Returns:
622
+ Name of the house
623
+ """
624
+ house_names: dict[int, Houses] = {
625
+ 1: "First_House",
626
+ 2: "Second_House",
627
+ 3: "Third_House",
628
+ 4: "Fourth_House",
629
+ 5: "Fifth_House",
630
+ 6: "Sixth_House",
631
+ 7: "Seventh_House",
632
+ 8: "Eighth_House",
633
+ 9: "Ninth_House",
634
+ 10: "Tenth_House",
635
+ 11: "Eleventh_House",
636
+ 12: "Twelfth_House",
637
+ }
638
+
639
+ name = house_names.get(house_number, None)
640
+ if name is None:
641
+ raise ValueError(f"Invalid house number: {house_number}")
642
+
643
+ return name
644
+
645
+
646
+ def get_house_number(house_name: Houses) -> int:
647
+ """
648
+ Returns the number of the house based on its name.
649
+
650
+ Args:
651
+ house_name: Name of the house
652
+
653
+ Returns:
654
+ House number (1-12)
655
+ """
656
+ house_numbers: dict[Houses, int] = {
657
+ "First_House": 1,
658
+ "Second_House": 2,
659
+ "Third_House": 3,
660
+ "Fourth_House": 4,
661
+ "Fifth_House": 5,
662
+ "Sixth_House": 6,
663
+ "Seventh_House": 7,
664
+ "Eighth_House": 8,
665
+ "Ninth_House": 9,
666
+ "Tenth_House": 10,
667
+ "Eleventh_House": 11,
668
+ "Twelfth_House": 12,
669
+ }
670
+
671
+ number = house_numbers.get(house_name, None)
672
+ if number is None:
673
+ raise ValueError(f"Invalid house name: {house_name}")
674
+
675
+ return number
676
+
677
+
678
+ def find_common_active_points(first_points: list[AstrologicalPoint], second_points: list[AstrologicalPoint]) -> list[AstrologicalPoint]:
679
+ """
680
+ Find the common astrological points between those available in the subject and those requested.
681
+
682
+ Args:
683
+ first_points: List of points available in the astrological subject
684
+ second_points: List of points requested by the user
685
+
686
+ Returns:
687
+ List containing only the requested points that are also available in the subject
688
+ """
689
+ common_points: list[AstrologicalPoint]
690
+ common_points = [point for point in second_points if point in first_points]
691
+
692
+ ignored_points = set(second_points) - set(common_points)
693
+
694
+ return common_points