# kerykeion.relationship_score.relationship_score

This is part of Kerykeion (C) 2025 Giacomo Battaglia

View Source

      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: View Source

     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

RelationshipScore( first_subject: Union[kerykeion.astrological_subject.AstrologicalSubject, kerykeion.kr_types.kr_models.AstrologicalSubjectModel], second_subject: Union[kerykeion.astrological_subject.AstrologicalSubject, kerykeion.kr_types.kr_models.AstrologicalSubjectModel], new_settings_file: Optional[pathlib._local.Path] = None) View Source

    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