kerykeion.utilities

  1from kerykeion.kr_types import KerykeionPointModel, KerykeionException, KerykeionSettingsModel, AstrologicalSubjectModel
  2from kerykeion.kr_types.kr_literals import LunarPhaseEmoji, LunarPhaseName, PointType, Planet
  3from typing import Union
  4import logging
  5import math
  6
  7
  8
  9def get_number_from_name(name: Planet) -> int:
 10    """Utility function, gets planet id from the name."""
 11
 12    if name == "Sun":
 13        return 0
 14    elif name == "Moon":
 15        return 1
 16    elif name == "Mercury":
 17        return 2
 18    elif name == "Venus":
 19        return 3
 20    elif name == "Mars":
 21        return 4
 22    elif name == "Jupiter":
 23        return 5
 24    elif name == "Saturn":
 25        return 6
 26    elif name == "Uranus":
 27        return 7
 28    elif name == "Neptune":
 29        return 8
 30    elif name == "Pluto":
 31        return 9
 32    elif name == "Mean_Node":
 33        return 10
 34    elif name == "True_Node":
 35        return 11
 36    elif name == "Chiron":
 37        return 15
 38    else:
 39        raise KerykeionException(f"Error in getting number from name! Name: {name}")
 40
 41
 42def calculate_position(
 43    degree: Union[int, float], number_name: str, point_type: PointType
 44) -> KerykeionPointModel:
 45    """Utility function to create a dictionary dividing the houses or the planets list."""
 46
 47    if degree < 30:
 48        dictionary = {
 49            "name": number_name,
 50            "quality": "Cardinal",
 51            "element": "Fire",
 52            "sign": "Ari",
 53            "sign_num": 0,
 54            "position": degree,
 55            "abs_pos": degree,
 56            "emoji": "♈️",
 57            "point_type": point_type,
 58        }
 59
 60    elif degree < 60:
 61        result = degree - 30
 62        dictionary = {
 63            "name": number_name,
 64            "quality": "Fixed",
 65            "element": "Earth",
 66            "sign": "Tau",
 67            "sign_num": 1,
 68            "position": result,
 69            "abs_pos": degree,
 70            "emoji": "♉️",
 71            "point_type": point_type,
 72        }
 73    elif degree < 90:
 74        result = degree - 60
 75        dictionary = {
 76            "name": number_name,
 77            "quality": "Mutable",
 78            "element": "Air",
 79            "sign": "Gem",
 80            "sign_num": 2,
 81            "position": result,
 82            "abs_pos": degree,
 83            "emoji": "♊️",
 84            "point_type": point_type,
 85        }
 86    elif degree < 120:
 87        result = degree - 90
 88        dictionary = {
 89            "name": number_name,
 90            "quality": "Cardinal",
 91            "element": "Water",
 92            "sign": "Can",
 93            "sign_num": 3,
 94            "position": result,
 95            "abs_pos": degree,
 96            "emoji": "♋️",
 97            "point_type": point_type,
 98        }
 99    elif degree < 150:
100        result = degree - 120
101        dictionary = {
102            "name": number_name,
103            "quality": "Fixed",
104            "element": "Fire",
105            "sign": "Leo",
106            "sign_num": 4,
107            "position": result,
108            "abs_pos": degree,
109            "emoji": "♌️",
110            "point_type": point_type,
111        }
112    elif degree < 180:
113        result = degree - 150
114        dictionary = {
115            "name": number_name,
116            "quality": "Mutable",
117            "element": "Earth",
118            "sign": "Vir",
119            "sign_num": 5,
120            "position": result,
121            "abs_pos": degree,
122            "emoji": "♍️",
123            "point_type": point_type,
124        }
125    elif degree < 210:
126        result = degree - 180
127        dictionary = {
128            "name": number_name,
129            "quality": "Cardinal",
130            "element": "Air",
131            "sign": "Lib",
132            "sign_num": 6,
133            "position": result,
134            "abs_pos": degree,
135            "emoji": "♎️",
136            "point_type": point_type,
137        }
138    elif degree < 240:
139        result = degree - 210
140        dictionary = {
141            "name": number_name,
142            "quality": "Fixed",
143            "element": "Water",
144            "sign": "Sco",
145            "sign_num": 7,
146            "position": result,
147            "abs_pos": degree,
148            "emoji": "♏️",
149            "point_type": point_type,
150        }
151    elif degree < 270:
152        result = degree - 240
153        dictionary = {
154            "name": number_name,
155            "quality": "Mutable",
156            "element": "Fire",
157            "sign": "Sag",
158            "sign_num": 8,
159            "position": result,
160            "abs_pos": degree,
161            "emoji": "♐️",
162            "point_type": point_type,
163        }
164    elif degree < 300:
165        result = degree - 270
166        dictionary = {
167            "name": number_name,
168            "quality": "Cardinal",
169            "element": "Earth",
170            "sign": "Cap",
171            "sign_num": 9,
172            "position": result,
173            "abs_pos": degree,
174            "emoji": "♑️",
175            "point_type": point_type,
176        }
177    elif degree < 330:
178        result = degree - 300
179        dictionary = {
180            "name": number_name,
181            "quality": "Fixed",
182            "element": "Air",
183            "sign": "Aqu",
184            "sign_num": 10,
185            "position": result,
186            "abs_pos": degree,
187            "emoji": "♒️",
188            "point_type": point_type,
189        }
190    elif degree < 360:
191        result = degree - 330
192        dictionary = {
193            "name": number_name,
194            "quality": "Mutable",
195            "element": "Water",
196            "sign": "Pis",
197            "sign_num": 11,
198            "position": result,
199            "abs_pos": degree,
200            "emoji": "♓️",
201            "point_type": point_type,
202        }
203    else:
204        raise KerykeionException(f"Error in calculating positions! Degrees: {degree}")
205
206    return KerykeionPointModel(**dictionary)
207
208
209def setup_logging(level: str) -> None:
210    """
211    Setup logging for testing.
212
213    Args:
214        level: Log level as a string, options: debug, info, warning, error
215    """
216    logging_options: dict[str, int] = {
217        "debug": logging.DEBUG,
218        "info": logging.INFO,
219        "warning": logging.WARNING,
220        "error": logging.ERROR,
221    }
222    format: str = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
223    loglevel: int = logging_options.get(level, logging.INFO)
224    logging.basicConfig(format=format, level=loglevel)
225
226def check_if_point_between(
227    start_point: Union[int, float], end_point: Union[int, float], evaluated_point: Union[int, float]
228) -> bool:
229    """
230    Finds if a point is between two other in a circle.
231
232    Args:
233        - start_point: The first point
234        - end_point: The second point
235        - point: The point to check if it is between start_point and end_point
236
237    Returns:
238        - True if point is between start_point and end_point, False otherwise
239    """
240
241    p1_p2 = math.fmod(end_point - start_point + 360, 360)
242    p1_p3 = math.fmod(evaluated_point - start_point + 360, 360)
243
244    if (p1_p2 <= 180) != (p1_p3 > p1_p2):
245        return True
246    else:
247        return False
248
249
250def get_planet_house(planet_position_degree: Union[int, float], houses_degree_ut_list: list) -> str:
251    """
252    Returns the house in which a planet is located.
253
254    Args:
255        - planet_position_degree: The position of the planet in degrees
256        - houses_degree_ut_list: A list of the houses in degrees (0-360)
257
258    Returns:
259        - The house in which the planet is located
260    """
261
262    house = None
263    if check_if_point_between(houses_degree_ut_list[0], houses_degree_ut_list[1], planet_position_degree) == True:
264        house = "First_House"
265    elif check_if_point_between(houses_degree_ut_list[1], houses_degree_ut_list[2], planet_position_degree) == True:
266        house = "Second_House"
267    elif check_if_point_between(houses_degree_ut_list[2], houses_degree_ut_list[3], planet_position_degree) == True:
268        house = "Third_House"
269    elif check_if_point_between(houses_degree_ut_list[3], houses_degree_ut_list[4], planet_position_degree) == True:
270        house = "Fourth_House"
271    elif check_if_point_between(houses_degree_ut_list[4], houses_degree_ut_list[5], planet_position_degree) == True:
272        house = "Fifth_House"
273    elif check_if_point_between(houses_degree_ut_list[5], houses_degree_ut_list[6], planet_position_degree) == True:
274        house = "Sixth_House"
275    elif check_if_point_between(houses_degree_ut_list[6], houses_degree_ut_list[7], planet_position_degree) == True:
276        house = "Seventh_House"
277    elif check_if_point_between(houses_degree_ut_list[7], houses_degree_ut_list[8], planet_position_degree) == True:
278        house = "Eighth_House"
279    elif check_if_point_between(houses_degree_ut_list[8], houses_degree_ut_list[9], planet_position_degree) == True:
280        house = "Ninth_House"
281    elif check_if_point_between(houses_degree_ut_list[9], houses_degree_ut_list[10], planet_position_degree) == True:
282        house = "Tenth_House"
283    elif check_if_point_between(houses_degree_ut_list[10], houses_degree_ut_list[11], planet_position_degree) == True:
284        house = "Eleventh_House"
285    elif check_if_point_between(houses_degree_ut_list[11], houses_degree_ut_list[0], planet_position_degree) == True:
286        house = "Twelfth_House"
287    else:
288        raise ValueError("Error in house calculation, planet: ", planet_position_degree, "houses: ", houses_degree_ut_list)
289
290    return house
291
292def get_moon_emoji_from_phase_int(phase: int) -> LunarPhaseEmoji:
293    """
294    Returns the emoji of the moon phase.
295    
296    Args:
297        - phase: The phase of the moon (0-28)
298    
299    Returns:
300        - The emoji of the moon phase
301    """
302    
303    if phase == 1:
304        result = "🌑"
305    elif phase < 7:
306        result = "🌒"
307    elif 7 <= phase <= 9:
308        result = "🌓"
309    elif phase < 14:
310        result = "🌔"
311    elif phase == 14:
312        result = "🌕"
313    elif phase < 20:
314        result = "🌖"
315    elif 20 <= phase <= 22:
316        result = "🌗"
317    elif phase <= 28:
318        result = "🌘"
319
320    else:
321        raise KerykeionException(f"Error in moon emoji calculation! Phase: {phase}")
322
323    return result
324
325def get_moon_phase_name_from_phase_int(phase: int) -> LunarPhaseName:
326    """
327    Returns the name of the moon phase.
328    
329    Args:
330        - phase: The phase of the moon (0-28)
331    
332    Returns:
333        - The name of the moon phase
334    """
335    
336    if phase == 1:
337        result = "New Moon"
338    elif phase < 7:
339        result = "Waxing Crescent"
340    elif 7 <= phase <= 9:
341        result = "First Quarter"
342    elif phase < 14:
343        result = "Waxing Gibbous"
344    elif phase == 14:
345        result = "Full Moon"
346    elif phase < 20:
347        result = "Waning Gibbous"
348    elif 20 <= phase <= 22:
349        result = "Last Quarter"
350    elif phase <= 28:
351        result = "Waning Crescent"
352
353    else:
354        raise KerykeionException(f"Error in moon name calculation! Phase: {phase}")
355    
356    return result
357
358
359def check_and_adjust_polar_latitude(latitude: float, longitude: float) -> bool:
360    """
361        Utility function to check if the location is in the polar circle.
362        If it is, it sets the latitude to 66 or -66 degrees.
363    """
364    if latitude > 66.0:
365        latitude = 66.0
366        logging.info("Polar circle override for houses, using 66 degrees")
367
368    elif latitude < -66.0:
369        latitude = -66.0
370        logging.info("Polar circle override for houses, using -66 degrees")
def get_number_from_name( name: Literal['Sun', 'Moon', 'Mercury', 'Venus', 'Mars', 'Jupiter', 'Saturn', 'Uranus', 'Neptune', 'Pluto', 'Mean_Node', 'True_Node', 'Chiron']) -> int:
10def get_number_from_name(name: Planet) -> int:
11    """Utility function, gets planet id from the name."""
12
13    if name == "Sun":
14        return 0
15    elif name == "Moon":
16        return 1
17    elif name == "Mercury":
18        return 2
19    elif name == "Venus":
20        return 3
21    elif name == "Mars":
22        return 4
23    elif name == "Jupiter":
24        return 5
25    elif name == "Saturn":
26        return 6
27    elif name == "Uranus":
28        return 7
29    elif name == "Neptune":
30        return 8
31    elif name == "Pluto":
32        return 9
33    elif name == "Mean_Node":
34        return 10
35    elif name == "True_Node":
36        return 11
37    elif name == "Chiron":
38        return 15
39    else:
40        raise KerykeionException(f"Error in getting number from name! Name: {name}")

Utility function, gets planet id from the name.

def calculate_position( degree: Union[int, float], number_name: str, point_type: Literal['Planet', 'House']) -> kerykeion.kr_types.kr_models.KerykeionPointModel:
 43def calculate_position(
 44    degree: Union[int, float], number_name: str, point_type: PointType
 45) -> KerykeionPointModel:
 46    """Utility function to create a dictionary dividing the houses or the planets list."""
 47
 48    if degree < 30:
 49        dictionary = {
 50            "name": number_name,
 51            "quality": "Cardinal",
 52            "element": "Fire",
 53            "sign": "Ari",
 54            "sign_num": 0,
 55            "position": degree,
 56            "abs_pos": degree,
 57            "emoji": "♈️",
 58            "point_type": point_type,
 59        }
 60
 61    elif degree < 60:
 62        result = degree - 30
 63        dictionary = {
 64            "name": number_name,
 65            "quality": "Fixed",
 66            "element": "Earth",
 67            "sign": "Tau",
 68            "sign_num": 1,
 69            "position": result,
 70            "abs_pos": degree,
 71            "emoji": "♉️",
 72            "point_type": point_type,
 73        }
 74    elif degree < 90:
 75        result = degree - 60
 76        dictionary = {
 77            "name": number_name,
 78            "quality": "Mutable",
 79            "element": "Air",
 80            "sign": "Gem",
 81            "sign_num": 2,
 82            "position": result,
 83            "abs_pos": degree,
 84            "emoji": "♊️",
 85            "point_type": point_type,
 86        }
 87    elif degree < 120:
 88        result = degree - 90
 89        dictionary = {
 90            "name": number_name,
 91            "quality": "Cardinal",
 92            "element": "Water",
 93            "sign": "Can",
 94            "sign_num": 3,
 95            "position": result,
 96            "abs_pos": degree,
 97            "emoji": "♋️",
 98            "point_type": point_type,
 99        }
100    elif degree < 150:
101        result = degree - 120
102        dictionary = {
103            "name": number_name,
104            "quality": "Fixed",
105            "element": "Fire",
106            "sign": "Leo",
107            "sign_num": 4,
108            "position": result,
109            "abs_pos": degree,
110            "emoji": "♌️",
111            "point_type": point_type,
112        }
113    elif degree < 180:
114        result = degree - 150
115        dictionary = {
116            "name": number_name,
117            "quality": "Mutable",
118            "element": "Earth",
119            "sign": "Vir",
120            "sign_num": 5,
121            "position": result,
122            "abs_pos": degree,
123            "emoji": "♍️",
124            "point_type": point_type,
125        }
126    elif degree < 210:
127        result = degree - 180
128        dictionary = {
129            "name": number_name,
130            "quality": "Cardinal",
131            "element": "Air",
132            "sign": "Lib",
133            "sign_num": 6,
134            "position": result,
135            "abs_pos": degree,
136            "emoji": "♎️",
137            "point_type": point_type,
138        }
139    elif degree < 240:
140        result = degree - 210
141        dictionary = {
142            "name": number_name,
143            "quality": "Fixed",
144            "element": "Water",
145            "sign": "Sco",
146            "sign_num": 7,
147            "position": result,
148            "abs_pos": degree,
149            "emoji": "♏️",
150            "point_type": point_type,
151        }
152    elif degree < 270:
153        result = degree - 240
154        dictionary = {
155            "name": number_name,
156            "quality": "Mutable",
157            "element": "Fire",
158            "sign": "Sag",
159            "sign_num": 8,
160            "position": result,
161            "abs_pos": degree,
162            "emoji": "♐️",
163            "point_type": point_type,
164        }
165    elif degree < 300:
166        result = degree - 270
167        dictionary = {
168            "name": number_name,
169            "quality": "Cardinal",
170            "element": "Earth",
171            "sign": "Cap",
172            "sign_num": 9,
173            "position": result,
174            "abs_pos": degree,
175            "emoji": "♑️",
176            "point_type": point_type,
177        }
178    elif degree < 330:
179        result = degree - 300
180        dictionary = {
181            "name": number_name,
182            "quality": "Fixed",
183            "element": "Air",
184            "sign": "Aqu",
185            "sign_num": 10,
186            "position": result,
187            "abs_pos": degree,
188            "emoji": "♒️",
189            "point_type": point_type,
190        }
191    elif degree < 360:
192        result = degree - 330
193        dictionary = {
194            "name": number_name,
195            "quality": "Mutable",
196            "element": "Water",
197            "sign": "Pis",
198            "sign_num": 11,
199            "position": result,
200            "abs_pos": degree,
201            "emoji": "♓️",
202            "point_type": point_type,
203        }
204    else:
205        raise KerykeionException(f"Error in calculating positions! Degrees: {degree}")
206
207    return KerykeionPointModel(**dictionary)

Utility function to create a dictionary dividing the houses or the planets list.

def setup_logging(level: str) -> None:
210def setup_logging(level: str) -> None:
211    """
212    Setup logging for testing.
213
214    Args:
215        level: Log level as a string, options: debug, info, warning, error
216    """
217    logging_options: dict[str, int] = {
218        "debug": logging.DEBUG,
219        "info": logging.INFO,
220        "warning": logging.WARNING,
221        "error": logging.ERROR,
222    }
223    format: str = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
224    loglevel: int = logging_options.get(level, logging.INFO)
225    logging.basicConfig(format=format, level=loglevel)

Setup logging for testing.

Args: level: Log level as a string, options: debug, info, warning, error

def check_if_point_between( start_point: Union[int, float], end_point: Union[int, float], evaluated_point: Union[int, float]) -> bool:
227def check_if_point_between(
228    start_point: Union[int, float], end_point: Union[int, float], evaluated_point: Union[int, float]
229) -> bool:
230    """
231    Finds if a point is between two other in a circle.
232
233    Args:
234        - start_point: The first point
235        - end_point: The second point
236        - point: The point to check if it is between start_point and end_point
237
238    Returns:
239        - True if point is between start_point and end_point, False otherwise
240    """
241
242    p1_p2 = math.fmod(end_point - start_point + 360, 360)
243    p1_p3 = math.fmod(evaluated_point - start_point + 360, 360)
244
245    if (p1_p2 <= 180) != (p1_p3 > p1_p2):
246        return True
247    else:
248        return False

Finds if a point is between two other in a circle.

Args: - start_point: The first point - end_point: The second point - point: The point to check if it is between start_point and end_point

Returns: - True if point is between start_point and end_point, False otherwise

def get_planet_house( planet_position_degree: Union[int, float], houses_degree_ut_list: list) -> str:
251def get_planet_house(planet_position_degree: Union[int, float], houses_degree_ut_list: list) -> str:
252    """
253    Returns the house in which a planet is located.
254
255    Args:
256        - planet_position_degree: The position of the planet in degrees
257        - houses_degree_ut_list: A list of the houses in degrees (0-360)
258
259    Returns:
260        - The house in which the planet is located
261    """
262
263    house = None
264    if check_if_point_between(houses_degree_ut_list[0], houses_degree_ut_list[1], planet_position_degree) == True:
265        house = "First_House"
266    elif check_if_point_between(houses_degree_ut_list[1], houses_degree_ut_list[2], planet_position_degree) == True:
267        house = "Second_House"
268    elif check_if_point_between(houses_degree_ut_list[2], houses_degree_ut_list[3], planet_position_degree) == True:
269        house = "Third_House"
270    elif check_if_point_between(houses_degree_ut_list[3], houses_degree_ut_list[4], planet_position_degree) == True:
271        house = "Fourth_House"
272    elif check_if_point_between(houses_degree_ut_list[4], houses_degree_ut_list[5], planet_position_degree) == True:
273        house = "Fifth_House"
274    elif check_if_point_between(houses_degree_ut_list[5], houses_degree_ut_list[6], planet_position_degree) == True:
275        house = "Sixth_House"
276    elif check_if_point_between(houses_degree_ut_list[6], houses_degree_ut_list[7], planet_position_degree) == True:
277        house = "Seventh_House"
278    elif check_if_point_between(houses_degree_ut_list[7], houses_degree_ut_list[8], planet_position_degree) == True:
279        house = "Eighth_House"
280    elif check_if_point_between(houses_degree_ut_list[8], houses_degree_ut_list[9], planet_position_degree) == True:
281        house = "Ninth_House"
282    elif check_if_point_between(houses_degree_ut_list[9], houses_degree_ut_list[10], planet_position_degree) == True:
283        house = "Tenth_House"
284    elif check_if_point_between(houses_degree_ut_list[10], houses_degree_ut_list[11], planet_position_degree) == True:
285        house = "Eleventh_House"
286    elif check_if_point_between(houses_degree_ut_list[11], houses_degree_ut_list[0], planet_position_degree) == True:
287        house = "Twelfth_House"
288    else:
289        raise ValueError("Error in house calculation, planet: ", planet_position_degree, "houses: ", houses_degree_ut_list)
290
291    return house

Returns the house in which a planet is located.

Args: - planet_position_degree: The position of the planet in degrees - houses_degree_ut_list: A list of the houses in degrees (0-360)

Returns: - The house in which the planet is located

def get_moon_emoji_from_phase_int(phase: int) -> Literal['🌑', '🌒', '🌓', '🌔', '🌕', '🌖', '🌗', '🌘']:
293def get_moon_emoji_from_phase_int(phase: int) -> LunarPhaseEmoji:
294    """
295    Returns the emoji of the moon phase.
296    
297    Args:
298        - phase: The phase of the moon (0-28)
299    
300    Returns:
301        - The emoji of the moon phase
302    """
303    
304    if phase == 1:
305        result = "🌑"
306    elif phase < 7:
307        result = "🌒"
308    elif 7 <= phase <= 9:
309        result = "🌓"
310    elif phase < 14:
311        result = "🌔"
312    elif phase == 14:
313        result = "🌕"
314    elif phase < 20:
315        result = "🌖"
316    elif 20 <= phase <= 22:
317        result = "🌗"
318    elif phase <= 28:
319        result = "🌘"
320
321    else:
322        raise KerykeionException(f"Error in moon emoji calculation! Phase: {phase}")
323
324    return result

Returns the emoji of the moon phase.

Args: - phase: The phase of the moon (0-28)

Returns: - The emoji of the moon phase

def get_moon_phase_name_from_phase_int( phase: int) -> Literal['New Moon', 'Waxing Crescent', 'First Quarter', 'Waxing Gibbous', 'Full Moon', 'Waning Gibbous', 'Last Quarter', 'Waning Crescent']:
326def get_moon_phase_name_from_phase_int(phase: int) -> LunarPhaseName:
327    """
328    Returns the name of the moon phase.
329    
330    Args:
331        - phase: The phase of the moon (0-28)
332    
333    Returns:
334        - The name of the moon phase
335    """
336    
337    if phase == 1:
338        result = "New Moon"
339    elif phase < 7:
340        result = "Waxing Crescent"
341    elif 7 <= phase <= 9:
342        result = "First Quarter"
343    elif phase < 14:
344        result = "Waxing Gibbous"
345    elif phase == 14:
346        result = "Full Moon"
347    elif phase < 20:
348        result = "Waning Gibbous"
349    elif 20 <= phase <= 22:
350        result = "Last Quarter"
351    elif phase <= 28:
352        result = "Waning Crescent"
353
354    else:
355        raise KerykeionException(f"Error in moon name calculation! Phase: {phase}")
356    
357    return result

Returns the name of the moon phase.

Args: - phase: The phase of the moon (0-28)

Returns: - The name of the moon phase

def check_and_adjust_polar_latitude(latitude: float, longitude: float) -> bool:
360def check_and_adjust_polar_latitude(latitude: float, longitude: float) -> bool:
361    """
362        Utility function to check if the location is in the polar circle.
363        If it is, it sets the latitude to 66 or -66 degrees.
364    """
365    if latitude > 66.0:
366        latitude = 66.0
367        logging.info("Polar circle override for houses, using 66 degrees")
368
369    elif latitude < -66.0:
370        latitude = -66.0
371        logging.info("Polar circle override for houses, using -66 degrees")

Utility function to check if the location is in the polar circle. If it is, it sets the latitude to 66 or -66 degrees.