kerykeion.relationship_score.relationship_score

This is part of Kerykeion (C) 2025 Giacomo Battaglia

  1# -*- coding: utf-8 -*-
  2"""
  3    This is part of Kerykeion (C) 2025 Giacomo Battaglia
  4"""
  5
  6from kerykeion import AstrologicalSubject
  7from kerykeion.aspects.synastry_aspects import SynastryAspects
  8import logging
  9from pathlib import Path
 10from typing import Union
 11from kerykeion.kr_types.kr_models import AstrologicalSubjectModel
 12import warnings
 13
 14
 15class RelationshipScore:
 16    """
 17    Calculates the relevance of the relationship between two subjects using the Ciro Discepolo method.
 18
 19    Results:
 20        - 0 to 5: Minimal relationship
 21        - 5 to 10: Medium relationship
 22        - 10 to 15: Important relationship
 23        - 15 to 20: Very important relationship
 24        - 20 and 35: Exceptional relationship
 25        - 35 and above: Rare Exceptional relationship
 26
 27    Documentation: http://www.cirodiscepolo.it/Articoli/Discepoloele.htm
 28
 29    Args:
 30        first_subject (AstrologicalSubject): First subject instance
 31        second_subject (AstrologicalSubject): Second subject instance
 32    """
 33
 34    def __init__(
 35        self,
 36        first_subject: Union[AstrologicalSubject, AstrologicalSubjectModel],
 37        second_subject: Union[AstrologicalSubject, AstrologicalSubjectModel],
 38        new_settings_file: Union[Path, None] = None,
 39    ):
 40        warnings.warn(
 41            "The RelationshipScore class is deprecated and will be removed in a future version. Use RelationshipScoreFactory instead.",
 42            DeprecationWarning,
 43            stacklevel=2
 44        )
 45        self.first_subject = first_subject
 46        self.second_subject = second_subject
 47        self.score = 0
 48        self.is_destiny_sign = False
 49        self.relevant_aspects: list = []
 50        self.relevant_default_aspects: list = []
 51        self.__all_synastry_aspects = SynastryAspects(first_subject, second_subject, new_settings_file=new_settings_file).all_aspects
 52
 53        # Calculate all aspects at initialization
 54        self._calculate_all()
 55
 56    def __str__(self) -> str:
 57        return f"CoupleScoreInstance: {self.first_subject.name} and {self.second_subject.name}, score: {self.score}"
 58
 59    def __dict__(self) -> dict: # type: ignore
 60        return {
 61            "first_subject_name": self.first_subject.name,
 62            "second_subject_name": self.second_subject.name,
 63            "score": self.score,
 64            "relevant_aspects": self.relevant_aspects,
 65            "relevant_default_aspects": self.relevant_default_aspects,
 66            "is_destiny_sign": self.is_destiny_sign,
 67        }
 68
 69    def _log_aspect(self, aspect: dict, points: int) -> None:
 70        logging.debug(f"{points} Points: {aspect['p1_name']} {aspect['aspect']} {aspect['p2_name']}, rounded orbit: {int(aspect['orbit'])}")
 71
 72    def _evaluate_destiny_sign(self) -> int:
 73        """
 74        Adds 5 points if the subjects share the same sun sign quality.
 75        """
 76        if self.first_subject.sun["quality"] == self.second_subject.sun["quality"]:
 77            logging.debug(f'5 points: Destiny sign, {self.first_subject.sun["sign"]} and {self.second_subject.sun["sign"]}')
 78            self.is_destiny_sign = True
 79            return 5
 80        return 0
 81
 82    def _check_if_sun_sun_aspect(self, aspect: dict) -> int:
 83        """
 84        Adds points for Sun-Sun aspects:
 85        - 8 points for conjunction/opposition/square
 86        - 11 points if the aspect's orbit is <= 2 degrees
 87        """
 88        aspect_types = ["conjunction", "opposition", "square"]
 89
 90        if aspect["p1_name"] == "Sun" and aspect["p2_name"] == "Sun" and aspect["aspect"] in aspect_types:
 91            self.relevant_default_aspects.append(aspect)
 92            score = 11 if aspect["orbit"] <= 2 else 8
 93
 94            self._log_aspect(aspect, score)
 95            self.relevant_aspects.append(self._create_aspects_dictionary(aspect, score))
 96
 97            return score
 98        return 0
 99
100    def _check_if_sun_moon_conjunction(self, aspect: dict) -> int:
101        """
102        Adds points for Sun-Moon conjunction:
103        - 8 points for conjunction
104        - 11 points if the aspect's orbit is <= 2 degrees
105        """
106        planets = {"Moon", "Sun"}
107
108        if {aspect["p1_name"], aspect["p2_name"]} == planets and aspect["aspect"] == "conjunction":
109            self.relevant_default_aspects.append(aspect)
110            score = 11 if aspect["orbit"] <= 2 else 8
111
112            self._log_aspect(aspect, score)
113            self.relevant_aspects.append(self._create_aspects_dictionary(aspect, score))
114
115            return score
116        return 0
117
118    def _check_if_sun_moon_asc_aspect(self, aspect: dict) -> int:
119        """
120        Adds 4 points for aspects involving Sun, Moon, and Ascendant.
121        """
122        planets = ["Sun", "Moon", "First_House"]
123
124        if self._check_if_sun_sun_aspect(aspect) or self._check_if_sun_moon_conjunction(aspect):
125            return 0
126
127        if aspect["p1_name"] in planets and aspect["p2_name"] in planets:
128            self.relevant_default_aspects.append(aspect)
129            score = 4
130
131            self._log_aspect(aspect, score)
132            self.relevant_aspects.append(self._create_aspects_dictionary(aspect, score))
133
134            return score
135        return 0
136
137    def _check_if_venus_mars_aspect(self, aspect: dict) -> int:
138        """
139        Adds 4 points for Venus-Mars aspects.
140        """
141        planets = {"Venus", "Mars"}
142
143        if {aspect["p1_name"], aspect["p2_name"]} == planets:
144            score = 4
145            self.relevant_default_aspects.append(aspect)
146
147            self._log_aspect(aspect, score)
148            self.relevant_aspects.append(self._create_aspects_dictionary(aspect, score))
149
150            return score
151        return 0
152
153    def _create_aspects_dictionary(self, aspect: dict, score: int) -> dict:
154        """
155        Creates a dictionary representation of an aspect with its score.
156        """
157        return {
158            "points": score,
159            "p1_name": aspect["p1_name"],
160            "p2_name": aspect["p2_name"],
161            "aspect": aspect["aspect"],
162            "orbit": aspect["orbit"],
163        }
164
165    def _calculate_all(self) -> None:
166        """
167        Calculates the total score based on all relevant aspects.
168        """
169        self.score += self._evaluate_destiny_sign()
170
171        for aspect in self.__all_synastry_aspects:
172            self.score += self._check_if_sun_sun_aspect(aspect)
173            self.score += self._check_if_sun_moon_conjunction(aspect)
174            self.score += self._check_if_sun_moon_asc_aspect(aspect)
175            self.score += self._check_if_venus_mars_aspect(aspect)
class RelationshipScore:
 16class RelationshipScore:
 17    """
 18    Calculates the relevance of the relationship between two subjects using the Ciro Discepolo method.
 19
 20    Results:
 21        - 0 to 5: Minimal relationship
 22        - 5 to 10: Medium relationship
 23        - 10 to 15: Important relationship
 24        - 15 to 20: Very important relationship
 25        - 20 and 35: Exceptional relationship
 26        - 35 and above: Rare Exceptional relationship
 27
 28    Documentation: http://www.cirodiscepolo.it/Articoli/Discepoloele.htm
 29
 30    Args:
 31        first_subject (AstrologicalSubject): First subject instance
 32        second_subject (AstrologicalSubject): Second subject instance
 33    """
 34
 35    def __init__(
 36        self,
 37        first_subject: Union[AstrologicalSubject, AstrologicalSubjectModel],
 38        second_subject: Union[AstrologicalSubject, AstrologicalSubjectModel],
 39        new_settings_file: Union[Path, None] = None,
 40    ):
 41        warnings.warn(
 42            "The RelationshipScore class is deprecated and will be removed in a future version. Use RelationshipScoreFactory instead.",
 43            DeprecationWarning,
 44            stacklevel=2
 45        )
 46        self.first_subject = first_subject
 47        self.second_subject = second_subject
 48        self.score = 0
 49        self.is_destiny_sign = False
 50        self.relevant_aspects: list = []
 51        self.relevant_default_aspects: list = []
 52        self.__all_synastry_aspects = SynastryAspects(first_subject, second_subject, new_settings_file=new_settings_file).all_aspects
 53
 54        # Calculate all aspects at initialization
 55        self._calculate_all()
 56
 57    def __str__(self) -> str:
 58        return f"CoupleScoreInstance: {self.first_subject.name} and {self.second_subject.name}, score: {self.score}"
 59
 60    def __dict__(self) -> dict: # type: ignore
 61        return {
 62            "first_subject_name": self.first_subject.name,
 63            "second_subject_name": self.second_subject.name,
 64            "score": self.score,
 65            "relevant_aspects": self.relevant_aspects,
 66            "relevant_default_aspects": self.relevant_default_aspects,
 67            "is_destiny_sign": self.is_destiny_sign,
 68        }
 69
 70    def _log_aspect(self, aspect: dict, points: int) -> None:
 71        logging.debug(f"{points} Points: {aspect['p1_name']} {aspect['aspect']} {aspect['p2_name']}, rounded orbit: {int(aspect['orbit'])}")
 72
 73    def _evaluate_destiny_sign(self) -> int:
 74        """
 75        Adds 5 points if the subjects share the same sun sign quality.
 76        """
 77        if self.first_subject.sun["quality"] == self.second_subject.sun["quality"]:
 78            logging.debug(f'5 points: Destiny sign, {self.first_subject.sun["sign"]} and {self.second_subject.sun["sign"]}')
 79            self.is_destiny_sign = True
 80            return 5
 81        return 0
 82
 83    def _check_if_sun_sun_aspect(self, aspect: dict) -> int:
 84        """
 85        Adds points for Sun-Sun aspects:
 86        - 8 points for conjunction/opposition/square
 87        - 11 points if the aspect's orbit is <= 2 degrees
 88        """
 89        aspect_types = ["conjunction", "opposition", "square"]
 90
 91        if aspect["p1_name"] == "Sun" and aspect["p2_name"] == "Sun" and aspect["aspect"] in aspect_types:
 92            self.relevant_default_aspects.append(aspect)
 93            score = 11 if aspect["orbit"] <= 2 else 8
 94
 95            self._log_aspect(aspect, score)
 96            self.relevant_aspects.append(self._create_aspects_dictionary(aspect, score))
 97
 98            return score
 99        return 0
100
101    def _check_if_sun_moon_conjunction(self, aspect: dict) -> int:
102        """
103        Adds points for Sun-Moon conjunction:
104        - 8 points for conjunction
105        - 11 points if the aspect's orbit is <= 2 degrees
106        """
107        planets = {"Moon", "Sun"}
108
109        if {aspect["p1_name"], aspect["p2_name"]} == planets and aspect["aspect"] == "conjunction":
110            self.relevant_default_aspects.append(aspect)
111            score = 11 if aspect["orbit"] <= 2 else 8
112
113            self._log_aspect(aspect, score)
114            self.relevant_aspects.append(self._create_aspects_dictionary(aspect, score))
115
116            return score
117        return 0
118
119    def _check_if_sun_moon_asc_aspect(self, aspect: dict) -> int:
120        """
121        Adds 4 points for aspects involving Sun, Moon, and Ascendant.
122        """
123        planets = ["Sun", "Moon", "First_House"]
124
125        if self._check_if_sun_sun_aspect(aspect) or self._check_if_sun_moon_conjunction(aspect):
126            return 0
127
128        if aspect["p1_name"] in planets and aspect["p2_name"] in planets:
129            self.relevant_default_aspects.append(aspect)
130            score = 4
131
132            self._log_aspect(aspect, score)
133            self.relevant_aspects.append(self._create_aspects_dictionary(aspect, score))
134
135            return score
136        return 0
137
138    def _check_if_venus_mars_aspect(self, aspect: dict) -> int:
139        """
140        Adds 4 points for Venus-Mars aspects.
141        """
142        planets = {"Venus", "Mars"}
143
144        if {aspect["p1_name"], aspect["p2_name"]} == planets:
145            score = 4
146            self.relevant_default_aspects.append(aspect)
147
148            self._log_aspect(aspect, score)
149            self.relevant_aspects.append(self._create_aspects_dictionary(aspect, score))
150
151            return score
152        return 0
153
154    def _create_aspects_dictionary(self, aspect: dict, score: int) -> dict:
155        """
156        Creates a dictionary representation of an aspect with its score.
157        """
158        return {
159            "points": score,
160            "p1_name": aspect["p1_name"],
161            "p2_name": aspect["p2_name"],
162            "aspect": aspect["aspect"],
163            "orbit": aspect["orbit"],
164        }
165
166    def _calculate_all(self) -> None:
167        """
168        Calculates the total score based on all relevant aspects.
169        """
170        self.score += self._evaluate_destiny_sign()
171
172        for aspect in self.__all_synastry_aspects:
173            self.score += self._check_if_sun_sun_aspect(aspect)
174            self.score += self._check_if_sun_moon_conjunction(aspect)
175            self.score += self._check_if_sun_moon_asc_aspect(aspect)
176            self.score += self._check_if_venus_mars_aspect(aspect)

Calculates the relevance of the relationship between two subjects using the Ciro Discepolo method.

Results: - 0 to 5: Minimal relationship - 5 to 10: Medium relationship - 10 to 15: Important relationship - 15 to 20: Very important relationship - 20 and 35: Exceptional relationship - 35 and above: Rare Exceptional relationship

Documentation: http://www.cirodiscepolo.it/Articoli/Discepoloele.htm

Args: first_subject (AstrologicalSubject): First subject instance second_subject (AstrologicalSubject): Second subject instance

35    def __init__(
36        self,
37        first_subject: Union[AstrologicalSubject, AstrologicalSubjectModel],
38        second_subject: Union[AstrologicalSubject, AstrologicalSubjectModel],
39        new_settings_file: Union[Path, None] = None,
40    ):
41        warnings.warn(
42            "The RelationshipScore class is deprecated and will be removed in a future version. Use RelationshipScoreFactory instead.",
43            DeprecationWarning,
44            stacklevel=2
45        )
46        self.first_subject = first_subject
47        self.second_subject = second_subject
48        self.score = 0
49        self.is_destiny_sign = False
50        self.relevant_aspects: list = []
51        self.relevant_default_aspects: list = []
52        self.__all_synastry_aspects = SynastryAspects(first_subject, second_subject, new_settings_file=new_settings_file).all_aspects
53
54        # Calculate all aspects at initialization
55        self._calculate_all()
first_subject
second_subject
score
is_destiny_sign
relevant_aspects: list
relevant_default_aspects: list