kerykeion.relationship_score.relationship_score_factory
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, RelationshipScoreAspectModel, RelationshipScoreModel 12from kerykeion.kr_types.kr_literals import RelationshipScoreDescription 13 14 15class RelationshipScoreFactory: 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 to 35: Exceptional relationship 25 - 30 and above: Rare Exceptional relationship 26 27 Documentation: http://www.cirodiscepolo.it/Articoli/Discepoloele.htm 28 29 Args: 30 first_subject (Union[AstrologicalSubject, AstrologicalSubjectModel]): First subject instance 31 second_subject (Union[AstrologicalSubject, AstrologicalSubjectModel]): Second subject instance 32 """ 33 34 SCORE_MAPPING = [ 35 ("Minimal", 5), 36 ("Medium", 10), 37 ("Important", 15), 38 ("Very Important", 20), 39 ("Exceptional", 30), 40 ("Rare Exceptional", float("inf")), 41 ] 42 43 MAJOR_ASPECTS = {"conjunction", "opposition", "square", "trine", "sextile"} 44 45 def __init__( 46 self, 47 first_subject: Union[AstrologicalSubject, AstrologicalSubjectModel], 48 second_subject: Union[AstrologicalSubject, AstrologicalSubjectModel], 49 use_only_major_aspects: bool = True, 50 ): 51 if isinstance(first_subject, AstrologicalSubject): 52 self.first_subject = first_subject.model() 53 if isinstance(second_subject, AstrologicalSubject): 54 self.second_subject = second_subject.model() 55 56 self.use_only_major_aspects = use_only_major_aspects 57 58 self.score_value = 0 59 self.relationship_score_description: RelationshipScoreDescription = "Minimal" 60 self.is_destiny_sign = True 61 self.relationship_score_aspects: list[RelationshipScoreAspectModel] = [] 62 self._synastry_aspects = SynastryAspects(self.first_subject, self.second_subject).all_aspects 63 64 def _evaluate_destiny_sign(self): 65 """ 66 Evaluates if the subjects share the same sun sign quality and adds points if true. 67 """ 68 if self.first_subject.sun["quality"] == self.second_subject.sun["quality"]: 69 self.is_destiny_sign = True 70 self.score_value += 5 71 logging.debug(f"Destiny sign found, adding 5 points, total score: {self.score_value}") 72 73 def _evaluate_aspect(self, aspect, points): 74 """ 75 Evaluates an aspect and adds points to the score. 76 77 Args: 78 aspect (dict): Aspect information. 79 points (int): Points to add. 80 """ 81 if self.use_only_major_aspects and aspect["aspect"] not in self.MAJOR_ASPECTS: 82 return 83 84 self.score_value += points 85 self.relationship_score_aspects.append( 86 RelationshipScoreAspectModel( 87 p1_name=aspect["p1_name"], 88 p2_name=aspect["p2_name"], 89 aspect=aspect["aspect"], 90 orbit=aspect["orbit"], 91 ) 92 ) 93 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)}") 94 95 def _evaluate_sun_sun_main_aspect(self, aspect): 96 """ 97 Evaluates Sun-Sun main aspects and adds points accordingly: 98 - 8 points for conjunction/opposition/square 99 - 11 points if the aspect's orbit is <= 2 degrees 100 101 Args: 102 aspect (dict): Aspect information. 103 """ 104 if aspect["p1_name"] == "Sun" and aspect["p2_name"] == "Sun" and aspect["aspect"] in {"conjunction", "opposition", "square"}: 105 points = 11 if aspect["orbit"] <= 2 else 8 106 self._evaluate_aspect(aspect, points) 107 108 def _evaluate_sun_moon_conjunction(self, aspect): 109 """ 110 Evaluates Sun-Moon conjunctions and adds points accordingly: 111 - 8 points for conjunction 112 - 11 points if the aspect's orbit is <= 2 degrees 113 114 Args: 115 aspect (dict): Aspect information. 116 """ 117 if {aspect["p1_name"], aspect["p2_name"]} == {"Moon", "Sun"} and aspect["aspect"] == "conjunction": 118 points = 11 if aspect["orbit"] <= 2 else 8 119 self._evaluate_aspect(aspect, points) 120 121 def _evaluate_sun_sun_other_aspects(self, aspect): 122 """ 123 Evaluates Sun-Sun aspects that are not conjunctions and adds points accordingly: 124 - 4 points for other aspects 125 126 Args: 127 aspect (dict): Aspect information. 128 """ 129 if aspect["p1_name"] == "Sun" and aspect["p2_name"] == "Sun" and aspect["aspect"] not in {"conjunction", "opposition", "square"}: 130 points = 4 131 self._evaluate_aspect(aspect, points) 132 133 def _evaluate_sun_moon_other_aspects(self, aspect): 134 """ 135 Evaluates Sun-Moon aspects that are not conjunctions and adds points accordingly: 136 - 4 points for other aspects 137 138 Args: 139 aspect (dict): Aspect information. 140 """ 141 if {aspect["p1_name"], aspect["p2_name"]} == {"Moon", "Sun"} and aspect["aspect"] != "conjunction": 142 points = 4 143 self._evaluate_aspect(aspect, points) 144 145 def _evaluate_sun_ascendant_aspect(self, aspect): 146 """ 147 Evaluates Sun-Ascendant aspects and adds points accordingly: 148 - 4 points for any aspect 149 150 Args: 151 aspect (dict): Aspect information. 152 """ 153 if {aspect["p1_name"], aspect["p2_name"]} == {"Sun", "Ascendant"}: 154 points = 4 155 self._evaluate_aspect(aspect, points) 156 157 def _evaluate_moon_ascendant_aspect(self, aspect): 158 """ 159 Evaluates Moon-Ascendant aspects and adds points accordingly: 160 - 4 points for any aspect 161 162 Args: 163 aspect (dict): Aspect information. 164 """ 165 if {aspect["p1_name"], aspect["p2_name"]} == {"Moon", "Ascendant"}: 166 points = 4 167 self._evaluate_aspect(aspect, points) 168 169 def _evaluate_venus_mars_aspect(self, aspect): 170 """ 171 Evaluates Venus-Mars aspects and adds points accordingly: 172 - 4 points for any aspect 173 174 Args: 175 aspect (dict): Aspect information. 176 """ 177 if {aspect["p1_name"], aspect["p2_name"]} == {"Venus", "Mars"}: 178 points = 4 179 self._evaluate_aspect(aspect, points) 180 181 def _evaluate_relationship_score_description(self): 182 """ 183 Evaluates the relationship score description based on the total score. 184 """ 185 for description, threshold in self.SCORE_MAPPING: 186 if self.score_value < threshold: 187 self.relationship_score_description = description 188 break 189 190 def get_relationship_score(self): 191 """ 192 Calculates the relationship score based on synastry aspects. 193 194 Returns: 195 RelationshipScoreModel: The calculated relationship score. 196 """ 197 self._evaluate_destiny_sign() 198 199 for aspect in self._synastry_aspects: 200 self._evaluate_sun_sun_main_aspect(aspect) 201 self._evaluate_sun_moon_conjunction(aspect) 202 self._evaluate_sun_moon_other_aspects(aspect) 203 self._evaluate_sun_sun_other_aspects(aspect) 204 self._evaluate_sun_ascendant_aspect(aspect) 205 self._evaluate_moon_ascendant_aspect(aspect) 206 self._evaluate_venus_mars_aspect(aspect) 207 208 self._evaluate_relationship_score_description() 209 210 return RelationshipScoreModel( 211 score_value=self.score_value, 212 score_description=self.relationship_score_description, 213 is_destiny_sign=self.is_destiny_sign, 214 aspects=self.relationship_score_aspects, 215 subjects=[self.first_subject, self.second_subject], 216 ) 217 218 219if __name__ == "__main__": 220 from kerykeion.utilities import setup_logging 221 222 setup_logging(level="critical") 223 224 giacomo = AstrologicalSubject("Giacomo", 1993, 6, 10, 12, 15, "Montichiari", "IT") 225 yoko = AstrologicalSubject("Susie Cox", 1949, 6, 17, 9, 40, "Tucson", "US") 226 227 factory = RelationshipScoreFactory(giacomo, yoko) 228 score = factory.get_relationship_score() 229 print(score)
16class RelationshipScoreFactory: 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 to 35: Exceptional relationship 26 - 30 and above: Rare Exceptional relationship 27 28 Documentation: http://www.cirodiscepolo.it/Articoli/Discepoloele.htm 29 30 Args: 31 first_subject (Union[AstrologicalSubject, AstrologicalSubjectModel]): First subject instance 32 second_subject (Union[AstrologicalSubject, AstrologicalSubjectModel]): Second subject instance 33 """ 34 35 SCORE_MAPPING = [ 36 ("Minimal", 5), 37 ("Medium", 10), 38 ("Important", 15), 39 ("Very Important", 20), 40 ("Exceptional", 30), 41 ("Rare Exceptional", float("inf")), 42 ] 43 44 MAJOR_ASPECTS = {"conjunction", "opposition", "square", "trine", "sextile"} 45 46 def __init__( 47 self, 48 first_subject: Union[AstrologicalSubject, AstrologicalSubjectModel], 49 second_subject: Union[AstrologicalSubject, AstrologicalSubjectModel], 50 use_only_major_aspects: bool = True, 51 ): 52 if isinstance(first_subject, AstrologicalSubject): 53 self.first_subject = first_subject.model() 54 if isinstance(second_subject, AstrologicalSubject): 55 self.second_subject = second_subject.model() 56 57 self.use_only_major_aspects = use_only_major_aspects 58 59 self.score_value = 0 60 self.relationship_score_description: RelationshipScoreDescription = "Minimal" 61 self.is_destiny_sign = True 62 self.relationship_score_aspects: list[RelationshipScoreAspectModel] = [] 63 self._synastry_aspects = SynastryAspects(self.first_subject, self.second_subject).all_aspects 64 65 def _evaluate_destiny_sign(self): 66 """ 67 Evaluates if the subjects share the same sun sign quality and adds points if true. 68 """ 69 if self.first_subject.sun["quality"] == self.second_subject.sun["quality"]: 70 self.is_destiny_sign = True 71 self.score_value += 5 72 logging.debug(f"Destiny sign found, adding 5 points, total score: {self.score_value}") 73 74 def _evaluate_aspect(self, aspect, points): 75 """ 76 Evaluates an aspect and adds points to the score. 77 78 Args: 79 aspect (dict): Aspect information. 80 points (int): Points to add. 81 """ 82 if self.use_only_major_aspects and aspect["aspect"] not in self.MAJOR_ASPECTS: 83 return 84 85 self.score_value += points 86 self.relationship_score_aspects.append( 87 RelationshipScoreAspectModel( 88 p1_name=aspect["p1_name"], 89 p2_name=aspect["p2_name"], 90 aspect=aspect["aspect"], 91 orbit=aspect["orbit"], 92 ) 93 ) 94 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)}") 95 96 def _evaluate_sun_sun_main_aspect(self, aspect): 97 """ 98 Evaluates Sun-Sun main aspects and adds points accordingly: 99 - 8 points for conjunction/opposition/square 100 - 11 points if the aspect's orbit is <= 2 degrees 101 102 Args: 103 aspect (dict): Aspect information. 104 """ 105 if aspect["p1_name"] == "Sun" and aspect["p2_name"] == "Sun" and aspect["aspect"] in {"conjunction", "opposition", "square"}: 106 points = 11 if aspect["orbit"] <= 2 else 8 107 self._evaluate_aspect(aspect, points) 108 109 def _evaluate_sun_moon_conjunction(self, aspect): 110 """ 111 Evaluates Sun-Moon conjunctions and adds points accordingly: 112 - 8 points for conjunction 113 - 11 points if the aspect's orbit is <= 2 degrees 114 115 Args: 116 aspect (dict): Aspect information. 117 """ 118 if {aspect["p1_name"], aspect["p2_name"]} == {"Moon", "Sun"} and aspect["aspect"] == "conjunction": 119 points = 11 if aspect["orbit"] <= 2 else 8 120 self._evaluate_aspect(aspect, points) 121 122 def _evaluate_sun_sun_other_aspects(self, aspect): 123 """ 124 Evaluates Sun-Sun aspects that are not conjunctions and adds points accordingly: 125 - 4 points for other aspects 126 127 Args: 128 aspect (dict): Aspect information. 129 """ 130 if aspect["p1_name"] == "Sun" and aspect["p2_name"] == "Sun" and aspect["aspect"] not in {"conjunction", "opposition", "square"}: 131 points = 4 132 self._evaluate_aspect(aspect, points) 133 134 def _evaluate_sun_moon_other_aspects(self, aspect): 135 """ 136 Evaluates Sun-Moon aspects that are not conjunctions and adds points accordingly: 137 - 4 points for other aspects 138 139 Args: 140 aspect (dict): Aspect information. 141 """ 142 if {aspect["p1_name"], aspect["p2_name"]} == {"Moon", "Sun"} and aspect["aspect"] != "conjunction": 143 points = 4 144 self._evaluate_aspect(aspect, points) 145 146 def _evaluate_sun_ascendant_aspect(self, aspect): 147 """ 148 Evaluates Sun-Ascendant aspects and adds points accordingly: 149 - 4 points for any aspect 150 151 Args: 152 aspect (dict): Aspect information. 153 """ 154 if {aspect["p1_name"], aspect["p2_name"]} == {"Sun", "Ascendant"}: 155 points = 4 156 self._evaluate_aspect(aspect, points) 157 158 def _evaluate_moon_ascendant_aspect(self, aspect): 159 """ 160 Evaluates Moon-Ascendant aspects and adds points accordingly: 161 - 4 points for any aspect 162 163 Args: 164 aspect (dict): Aspect information. 165 """ 166 if {aspect["p1_name"], aspect["p2_name"]} == {"Moon", "Ascendant"}: 167 points = 4 168 self._evaluate_aspect(aspect, points) 169 170 def _evaluate_venus_mars_aspect(self, aspect): 171 """ 172 Evaluates Venus-Mars aspects and adds points accordingly: 173 - 4 points for any aspect 174 175 Args: 176 aspect (dict): Aspect information. 177 """ 178 if {aspect["p1_name"], aspect["p2_name"]} == {"Venus", "Mars"}: 179 points = 4 180 self._evaluate_aspect(aspect, points) 181 182 def _evaluate_relationship_score_description(self): 183 """ 184 Evaluates the relationship score description based on the total score. 185 """ 186 for description, threshold in self.SCORE_MAPPING: 187 if self.score_value < threshold: 188 self.relationship_score_description = description 189 break 190 191 def get_relationship_score(self): 192 """ 193 Calculates the relationship score based on synastry aspects. 194 195 Returns: 196 RelationshipScoreModel: The calculated relationship score. 197 """ 198 self._evaluate_destiny_sign() 199 200 for aspect in self._synastry_aspects: 201 self._evaluate_sun_sun_main_aspect(aspect) 202 self._evaluate_sun_moon_conjunction(aspect) 203 self._evaluate_sun_moon_other_aspects(aspect) 204 self._evaluate_sun_sun_other_aspects(aspect) 205 self._evaluate_sun_ascendant_aspect(aspect) 206 self._evaluate_moon_ascendant_aspect(aspect) 207 self._evaluate_venus_mars_aspect(aspect) 208 209 self._evaluate_relationship_score_description() 210 211 return RelationshipScoreModel( 212 score_value=self.score_value, 213 score_description=self.relationship_score_description, 214 is_destiny_sign=self.is_destiny_sign, 215 aspects=self.relationship_score_aspects, 216 subjects=[self.first_subject, self.second_subject], 217 )
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 to 35: Exceptional relationship - 30 and above: Rare Exceptional relationship
Documentation: http://www.cirodiscepolo.it/Articoli/Discepoloele.htm
Args: first_subject (Union[AstrologicalSubject, AstrologicalSubjectModel]): First subject instance second_subject (Union[AstrologicalSubject, AstrologicalSubjectModel]): Second subject instance
46 def __init__( 47 self, 48 first_subject: Union[AstrologicalSubject, AstrologicalSubjectModel], 49 second_subject: Union[AstrologicalSubject, AstrologicalSubjectModel], 50 use_only_major_aspects: bool = True, 51 ): 52 if isinstance(first_subject, AstrologicalSubject): 53 self.first_subject = first_subject.model() 54 if isinstance(second_subject, AstrologicalSubject): 55 self.second_subject = second_subject.model() 56 57 self.use_only_major_aspects = use_only_major_aspects 58 59 self.score_value = 0 60 self.relationship_score_description: RelationshipScoreDescription = "Minimal" 61 self.is_destiny_sign = True 62 self.relationship_score_aspects: list[RelationshipScoreAspectModel] = [] 63 self._synastry_aspects = SynastryAspects(self.first_subject, self.second_subject).all_aspects
191 def get_relationship_score(self): 192 """ 193 Calculates the relationship score based on synastry aspects. 194 195 Returns: 196 RelationshipScoreModel: The calculated relationship score. 197 """ 198 self._evaluate_destiny_sign() 199 200 for aspect in self._synastry_aspects: 201 self._evaluate_sun_sun_main_aspect(aspect) 202 self._evaluate_sun_moon_conjunction(aspect) 203 self._evaluate_sun_moon_other_aspects(aspect) 204 self._evaluate_sun_sun_other_aspects(aspect) 205 self._evaluate_sun_ascendant_aspect(aspect) 206 self._evaluate_moon_ascendant_aspect(aspect) 207 self._evaluate_venus_mars_aspect(aspect) 208 209 self._evaluate_relationship_score_description() 210 211 return RelationshipScoreModel( 212 score_value=self.score_value, 213 score_description=self.relationship_score_description, 214 is_destiny_sign=self.is_destiny_sign, 215 aspects=self.relationship_score_aspects, 216 subjects=[self.first_subject, self.second_subject], 217 )
Calculates the relationship score based on synastry aspects.
Returns: RelationshipScoreModel: The calculated relationship score.