kerykeion 5.0.0a10__py3-none-any.whl → 5.0.0a12__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 (33) hide show
  1. kerykeion/__init__.py +4 -9
  2. kerykeion/aspects/__init__.py +2 -4
  3. kerykeion/aspects/aspects_factory.py +514 -0
  4. kerykeion/astrological_subject_factory.py +685 -79
  5. kerykeion/charts/draw_planets.py +584 -343
  6. kerykeion/charts/kerykeion_chart_svg.py +10 -16
  7. kerykeion/charts/templates/wheel_only.xml +1 -1
  8. kerykeion/composite_subject_factory.py +228 -9
  9. kerykeion/ephemeris_data_factory.py +431 -0
  10. kerykeion/fetch_geonames.py +27 -8
  11. kerykeion/house_comparison/house_comparison_factory.py +48 -15
  12. kerykeion/house_comparison/house_comparison_models.py +51 -13
  13. kerykeion/house_comparison/house_comparison_utils.py +35 -5
  14. kerykeion/kr_types/kerykeion_exception.py +6 -0
  15. kerykeion/kr_types/kr_models.py +82 -12
  16. kerykeion/planetary_return_factory.py +532 -32
  17. kerykeion/relationship_score_factory.py +98 -44
  18. kerykeion/report.py +7 -0
  19. kerykeion/sweph/sefstars.txt +1602 -0
  20. kerykeion/transits_time_range_factory.py +293 -0
  21. kerykeion/utilities.py +129 -67
  22. {kerykeion-5.0.0a10.dist-info → kerykeion-5.0.0a12.dist-info}/METADATA +49 -22
  23. kerykeion-5.0.0a12.dist-info/RECORD +50 -0
  24. kerykeion/aspects/natal_aspects_factory.py +0 -236
  25. kerykeion/aspects/synastry_aspects_factory.py +0 -234
  26. kerykeion/charts/draw_planets_v2.py +0 -648
  27. kerykeion/charts/draw_planets_v3.py +0 -679
  28. kerykeion/enums.py +0 -57
  29. kerykeion/ephemeris_data.py +0 -238
  30. kerykeion/transits_time_range.py +0 -128
  31. kerykeion-5.0.0a10.dist-info/RECORD +0 -53
  32. {kerykeion-5.0.0a10.dist-info → kerykeion-5.0.0a12.dist-info}/WHEEL +0 -0
  33. {kerykeion-5.0.0a10.dist-info → kerykeion-5.0.0a12.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,431 @@
1
+ """
2
+ Ephemeris Data Factory Module
3
+
4
+ This module provides the EphemerisDataFactory class for generating time-series
5
+ astrological ephemeris data. It enables the creation of comprehensive astronomical
6
+ and astrological datasets across specified date ranges with flexible time intervals
7
+ and calculation parameters.
8
+
9
+ Key Features:
10
+ - Time-series ephemeris data generation
11
+ - Multiple time interval support (days, hours, minutes)
12
+ - Configurable astrological calculation systems
13
+ - Built-in performance safeguards and limits
14
+ - Multiple output formats (dictionaries or model instances)
15
+ - Complete AstrologicalSubject instance generation
16
+
17
+ The module supports both lightweight data extraction (via get_ephemeris_data)
18
+ and full-featured astrological analysis (via get_ephemeris_data_as_astrological_subjects),
19
+ making it suitable for various use cases from simple data collection to complex
20
+ astrological research and analysis applications.
21
+
22
+ Classes:
23
+ EphemerisDataFactory: Main factory class for generating ephemeris data
24
+
25
+ Dependencies:
26
+ - kerykeion.AstrologicalSubjectFactory: For creating astrological subjects
27
+ - kerykeion.utilities: For house and planetary data extraction
28
+ - kerykeion.kr_types: For type definitions and model structures
29
+ - datetime: For date/time handling
30
+ - logging: For performance warnings
31
+
32
+ Example:
33
+ Basic usage for daily ephemeris data:
34
+
35
+ >>> from datetime import datetime
36
+ >>> from kerykeion.ephemeris_data_factory import EphemerisDataFactory
37
+ >>>
38
+ >>> start = datetime(2024, 1, 1)
39
+ >>> end = datetime(2024, 1, 31)
40
+ >>> factory = EphemerisDataFactory(start, end)
41
+ >>> data = factory.get_ephemeris_data()
42
+ >>> print(f"Generated {len(data)} data points")
43
+
44
+ Author: Giacomo Battaglia
45
+ Copyright: (C) 2025 Kerykeion Project
46
+ License: AGPL-3.0
47
+ """
48
+
49
+ from kerykeion import AstrologicalSubjectFactory
50
+ from kerykeion.kr_types.kr_models import AstrologicalSubjectModel
51
+ from kerykeion.utilities import get_houses_list, get_available_astrological_points_list
52
+ from kerykeion.astrological_subject_factory import DEFAULT_HOUSES_SYSTEM_IDENTIFIER, DEFAULT_PERSPECTIVE_TYPE, DEFAULT_ZODIAC_TYPE
53
+ from kerykeion.kr_types import EphemerisDictModel
54
+ from kerykeion.kr_types import SiderealMode, HousesSystemIdentifier, PerspectiveType, ZodiacType
55
+ from datetime import datetime, timedelta
56
+ from typing import Literal, Union, List
57
+ import logging
58
+
59
+
60
+ class EphemerisDataFactory:
61
+ """
62
+ A factory class for generating ephemeris data over a specified date range.
63
+
64
+ This class calculates astrological ephemeris data (planetary positions and house cusps)
65
+ for a sequence of dates, allowing for detailed astronomical calculations across time periods.
66
+ It supports different time intervals (days, hours, or minutes) and various astrological
67
+ calculation systems.
68
+
69
+ The factory creates data points at regular intervals between start and end dates,
70
+ with built-in safeguards to prevent excessive computational loads through configurable
71
+ maximum limits.
72
+
73
+ Args:
74
+ start_datetime (datetime): The starting date and time for ephemeris calculations.
75
+ end_datetime (datetime): The ending date and time for ephemeris calculations.
76
+ step_type (Literal["days", "hours", "minutes"], optional): The time interval unit
77
+ for data points. Defaults to "days".
78
+ step (int, optional): The number of units to advance for each data point.
79
+ For example, step=2 with step_type="days" creates data points every 2 days.
80
+ Defaults to 1.
81
+ lat (float, optional): Geographic latitude in decimal degrees for calculations.
82
+ Positive values for North, negative for South. Defaults to 51.4769 (Greenwich).
83
+ lng (float, optional): Geographic longitude in decimal degrees for calculations.
84
+ Positive values for East, negative for West. Defaults to 0.0005 (Greenwich).
85
+ tz_str (str, optional): Timezone identifier (e.g., "Europe/London", "America/New_York").
86
+ Defaults to "Etc/UTC".
87
+ is_dst (bool, optional): Whether daylight saving time is active for the location.
88
+ Only relevant for certain timezone calculations. Defaults to False.
89
+ zodiac_type (ZodiacType, optional): The zodiac system to use (tropical or sidereal).
90
+ Defaults to DEFAULT_ZODIAC_TYPE.
91
+ sidereal_mode (Union[SiderealMode, None], optional): The sidereal calculation mode
92
+ if using sidereal zodiac. Only applies when zodiac_type is sidereal.
93
+ Defaults to None.
94
+ houses_system_identifier (HousesSystemIdentifier, optional): The house system
95
+ for astrological house calculations (e.g., Placidus, Koch, Equal).
96
+ Defaults to DEFAULT_HOUSES_SYSTEM_IDENTIFIER.
97
+ perspective_type (PerspectiveType, optional): The calculation perspective
98
+ (geocentric, heliocentric, etc.). Defaults to DEFAULT_PERSPECTIVE_TYPE.
99
+ max_days (Union[int, None], optional): Maximum number of daily data points allowed.
100
+ Set to None to disable this safety check. Defaults to 730 (2 years).
101
+ max_hours (Union[int, None], optional): Maximum number of hourly data points allowed.
102
+ Set to None to disable this safety check. Defaults to 8760 (1 year).
103
+ max_minutes (Union[int, None], optional): Maximum number of minute-interval data points.
104
+ Set to None to disable this safety check. Defaults to 525600 (1 year).
105
+
106
+ Raises:
107
+ ValueError: If step_type is not one of "days", "hours", or "minutes".
108
+ ValueError: If the calculated number of data points exceeds the respective maximum limit.
109
+ ValueError: If no valid dates are generated from the input parameters.
110
+
111
+ Examples:
112
+ Create daily ephemeris data for a month:
113
+
114
+ >>> from datetime import datetime
115
+ >>> start = datetime(2024, 1, 1)
116
+ >>> end = datetime(2024, 1, 31)
117
+ >>> factory = EphemerisDataFactory(start, end)
118
+ >>> data = factory.get_ephemeris_data()
119
+
120
+ Create hourly data for a specific location:
121
+
122
+ >>> factory = EphemerisDataFactory(
123
+ ... start, end,
124
+ ... step_type="hours",
125
+ ... lat=40.7128, # New York
126
+ ... lng=-74.0060,
127
+ ... tz_str="America/New_York"
128
+ ... )
129
+ >>> subjects = factory.get_ephemeris_data_as_astrological_subjects()
130
+
131
+ Note:
132
+ Large date ranges with small step intervals can generate thousands of data points,
133
+ which may require significant computation time and memory. The factory includes
134
+ warnings for calculations exceeding 1000 data points and enforces maximum limits
135
+ to prevent system overload.
136
+ """
137
+
138
+ def __init__(
139
+ self,
140
+ start_datetime: datetime,
141
+ end_datetime: datetime,
142
+ step_type: Literal["days", "hours", "minutes"] = "days",
143
+ step: int = 1,
144
+ lat: float = 51.4769,
145
+ lng: float = 0.0005,
146
+ tz_str: str = "Etc/UTC",
147
+ is_dst: bool = False,
148
+ zodiac_type: ZodiacType = DEFAULT_ZODIAC_TYPE,
149
+ sidereal_mode: Union[SiderealMode, None] = None,
150
+ houses_system_identifier: HousesSystemIdentifier = DEFAULT_HOUSES_SYSTEM_IDENTIFIER,
151
+ perspective_type: PerspectiveType = DEFAULT_PERSPECTIVE_TYPE,
152
+ max_days: Union[int, None] = 730,
153
+ max_hours: Union[int, None] = 8760,
154
+ max_minutes: Union[int, None] = 525600,
155
+ ):
156
+ self.start_datetime = start_datetime
157
+ self.end_datetime = end_datetime
158
+ self.step_type = step_type
159
+ self.step = step
160
+ self.lat = lat
161
+ self.lng = lng
162
+ self.tz_str = tz_str
163
+ self.is_dst = is_dst
164
+ self.zodiac_type = zodiac_type
165
+ self.sidereal_mode = sidereal_mode
166
+ self.houses_system_identifier = houses_system_identifier
167
+ self.perspective_type = perspective_type
168
+ self.max_days = max_days
169
+ self.max_hours = max_hours
170
+ self.max_minutes = max_minutes
171
+
172
+ self.dates_list = []
173
+ if self.step_type == "days":
174
+ self.dates_list = [self.start_datetime + timedelta(days=i * self.step) for i in range((self.end_datetime - self.start_datetime).days // self.step + 1)]
175
+ if max_days and (len(self.dates_list) > max_days):
176
+ raise ValueError(f"Too many days: {len(self.dates_list)} > {self.max_days}. To prevent this error, set max_days to a higher value or reduce the date range.")
177
+
178
+ elif self.step_type == "hours":
179
+ hours_diff = (self.end_datetime - self.start_datetime).total_seconds() / 3600
180
+ self.dates_list = [self.start_datetime + timedelta(hours=i * self.step) for i in range(int(hours_diff) // self.step + 1)]
181
+ if max_hours and (len(self.dates_list) > max_hours):
182
+ raise ValueError(f"Too many hours: {len(self.dates_list)} > {self.max_hours}. To prevent this error, set max_hours to a higher value or reduce the date range.")
183
+
184
+ elif self.step_type == "minutes":
185
+ minutes_diff = (self.end_datetime - self.start_datetime).total_seconds() / 60
186
+ self.dates_list = [self.start_datetime + timedelta(minutes=i * self.step) for i in range(int(minutes_diff) // self.step + 1)]
187
+ if max_minutes and (len(self.dates_list) > max_minutes):
188
+ raise ValueError(f"Too many minutes: {len(self.dates_list)} > {self.max_minutes}. To prevent this error, set max_minutes to a higher value or reduce the date range.")
189
+
190
+ else:
191
+ raise ValueError(f"Invalid step type: {self.step_type}")
192
+
193
+ if not self.dates_list:
194
+ raise ValueError("No dates found. Check the date range and step values.")
195
+
196
+ if len(self.dates_list) > 1000:
197
+ logging.warning(f"Large number of dates: {len(self.dates_list)}. The calculation may take a while.")
198
+
199
+ def get_ephemeris_data(self, as_model: bool = False) -> list:
200
+ """
201
+ Generate ephemeris data for the specified date range.
202
+
203
+ This method creates a comprehensive dataset containing planetary positions and
204
+ astrological house cusps for each date in the configured time series. The data
205
+ is structured for easy consumption by astrological applications and analysis tools.
206
+
207
+ The returned data includes all available astrological points (planets, asteroids,
208
+ lunar nodes, etc.) as configured by the perspective type, along with complete
209
+ house cusp information for each calculated moment.
210
+
211
+ Args:
212
+ as_model (bool, optional): If True, returns data as validated model instances
213
+ (EphemerisDictModel objects) which provide type safety and validation.
214
+ If False, returns raw dictionary data for maximum flexibility.
215
+ Defaults to False.
216
+
217
+ Returns:
218
+ list: A list of ephemeris data points, where each element represents one
219
+ calculated moment in time. The structure depends on the as_model parameter:
220
+
221
+ If as_model=False (default):
222
+ List of dictionaries with keys:
223
+ - "date" (str): ISO format datetime string (e.g., "2020-01-01T00:00:00")
224
+ - "planets" (list): List of dictionaries, each containing planetary data
225
+ with keys like 'name', 'abs_pos', 'lon', 'lat', 'dist', 'speed', etc.
226
+ - "houses" (list): List of dictionaries containing house cusp data
227
+ with keys like 'name', 'abs_pos', 'lon', etc.
228
+
229
+ If as_model=True:
230
+ List of EphemerisDictModel instances providing the same data
231
+ with type validation and structured access.
232
+
233
+ Examples:
234
+ Basic usage with dictionary output:
235
+
236
+ >>> factory = EphemerisDataFactory(start_date, end_date)
237
+ >>> data = factory.get_ephemeris_data()
238
+ >>> print(f"Sun position: {data[0]['planets'][0]['abs_pos']}")
239
+ >>> print(f"First house cusp: {data[0]['houses'][0]['abs_pos']}")
240
+
241
+ Using model instances for type safety:
242
+
243
+ >>> data_models = factory.get_ephemeris_data(as_model=True)
244
+ >>> first_point = data_models[0]
245
+ >>> print(f"Date: {first_point.date}")
246
+ >>> print(f"Number of planets: {len(first_point.planets)}")
247
+
248
+ Note:
249
+ - The calculation time is proportional to the number of data points
250
+ - For large datasets (>1000 points), consider using the method in batches
251
+ - Planet order and availability depend on the configured perspective type
252
+ - House system affects the house cusp calculations
253
+ - All positions are in the configured zodiac system (tropical/sidereal)
254
+ """
255
+ ephemeris_data_list = []
256
+ for date in self.dates_list:
257
+ subject = AstrologicalSubjectFactory.from_birth_data(
258
+ year=date.year,
259
+ month=date.month,
260
+ day=date.day,
261
+ hour=date.hour,
262
+ minute=date.minute,
263
+ lng=self.lng,
264
+ lat=self.lat,
265
+ tz_str=self.tz_str,
266
+ city="Placeholder",
267
+ nation="Placeholder",
268
+ online=False,
269
+ zodiac_type=self.zodiac_type,
270
+ sidereal_mode=self.sidereal_mode,
271
+ houses_system_identifier=self.houses_system_identifier,
272
+ perspective_type=self.perspective_type,
273
+ is_dst=self.is_dst,
274
+ )
275
+
276
+ houses_list = get_houses_list(subject)
277
+ available_planets = get_available_astrological_points_list(subject)
278
+
279
+ ephemeris_data_list.append({"date": date.isoformat(), "planets": available_planets, "houses": houses_list})
280
+
281
+ if as_model:
282
+ return [EphemerisDictModel(**data) for data in ephemeris_data_list]
283
+
284
+ return ephemeris_data_list
285
+
286
+ def get_ephemeris_data_as_astrological_subjects(self, as_model: bool = False) -> List[AstrologicalSubjectModel]:
287
+ """
288
+ Generate ephemeris data as complete AstrologicalSubject instances.
289
+
290
+ This method creates fully-featured AstrologicalSubject objects for each date in the
291
+ configured time series, providing access to all astrological calculation methods
292
+ and properties. Unlike the dictionary-based approach of get_ephemeris_data(),
293
+ this method returns objects with the complete Kerykeion API available.
294
+
295
+ Each AstrologicalSubject instance represents a complete astrological chart for
296
+ the specified moment, location, and calculation settings. This allows direct
297
+ access to methods like get_sun(), get_all_points(), draw_chart(), calculate
298
+ aspects, and all other astrological analysis features.
299
+
300
+ Args:
301
+ as_model (bool, optional): If True, returns AstrologicalSubjectModel instances
302
+ (Pydantic model versions) which provide serialization and validation features.
303
+ If False, returns raw AstrologicalSubject instances with full method access.
304
+ Defaults to False.
305
+
306
+ Returns:
307
+ List[AstrologicalSubjectModel]: A list of AstrologicalSubject or
308
+ AstrologicalSubjectModel instances (depending on as_model parameter).
309
+ Each element represents one calculated moment in time with full
310
+ astrological chart data and methods available.
311
+
312
+ Each subject contains:
313
+ - All planetary and astrological point positions
314
+ - Complete house system calculations
315
+ - Chart drawing capabilities
316
+ - Aspect calculation methods
317
+ - Access to all Kerykeion astrological features
318
+
319
+ Examples:
320
+ Basic usage for accessing individual chart features:
321
+
322
+ >>> factory = EphemerisDataFactory(start_date, end_date)
323
+ >>> subjects = factory.get_ephemeris_data_as_astrological_subjects()
324
+ >>>
325
+ >>> # Access specific planetary data
326
+ >>> sun_data = subjects[0].get_sun()
327
+ >>> moon_data = subjects[0].get_moon()
328
+ >>>
329
+ >>> # Get all astrological points
330
+ >>> all_points = subjects[0].get_all_points()
331
+ >>>
332
+ >>> # Generate chart visualization
333
+ >>> chart_svg = subjects[0].draw_chart()
334
+
335
+ Using model instances for serialization:
336
+
337
+ >>> subjects_models = factory.get_ephemeris_data_as_astrological_subjects(as_model=True)
338
+ >>> # Model instances can be easily serialized to JSON
339
+ >>> json_data = subjects_models[0].model_dump_json()
340
+
341
+ Batch processing for analysis:
342
+
343
+ >>> subjects = factory.get_ephemeris_data_as_astrological_subjects()
344
+ >>> sun_positions = [subj.sun['abs_pos'] for subj in subjects if subj.sun]
345
+ >>> # Analyze sun position changes over time
346
+
347
+ Use Cases:
348
+ - Time-series astrological analysis
349
+ - Planetary motion tracking
350
+ - Aspect pattern analysis over time
351
+ - Chart animation data generation
352
+ - Astrological research and statistics
353
+ - Progressive chart calculations
354
+
355
+ Performance Notes:
356
+ - More computationally intensive than get_ephemeris_data()
357
+ - Each subject performs full astrological calculations
358
+ - Memory usage scales with the number of data points
359
+ - Consider processing in batches for very large date ranges
360
+ - Ideal for comprehensive analysis requiring full chart features
361
+
362
+ See Also:
363
+ get_ephemeris_data(): For lightweight dictionary-based ephemeris data
364
+ AstrologicalSubject: For details on available methods and properties
365
+ """
366
+ subjects_list = []
367
+ for date in self.dates_list:
368
+ subject = AstrologicalSubjectFactory.from_birth_data(
369
+ year=date.year,
370
+ month=date.month,
371
+ day=date.day,
372
+ hour=date.hour,
373
+ minute=date.minute,
374
+ lng=self.lng,
375
+ lat=self.lat,
376
+ tz_str=self.tz_str,
377
+ city="Placeholder",
378
+ nation="Placeholder",
379
+ online=False,
380
+ zodiac_type=self.zodiac_type,
381
+ sidereal_mode=self.sidereal_mode,
382
+ houses_system_identifier=self.houses_system_identifier,
383
+ perspective_type=self.perspective_type,
384
+ is_dst=self.is_dst,
385
+ )
386
+
387
+ if as_model:
388
+ subjects_list.append(subject.model())
389
+ else:
390
+ subjects_list.append(subject)
391
+
392
+ return subjects_list
393
+
394
+
395
+ if "__main__" == __name__:
396
+ start_date = datetime.fromisoformat("2020-01-01")
397
+ end_date = datetime.fromisoformat("2020-01-03")
398
+
399
+ factory = EphemerisDataFactory(
400
+ start_datetime=start_date,
401
+ end_datetime=end_date,
402
+ step_type="minutes",
403
+ step=60, # One hour intervals to make the example more manageable
404
+ lat=37.9838,
405
+ lng=23.7275,
406
+ tz_str="Europe/Athens",
407
+ is_dst=False,
408
+ max_hours=None,
409
+ max_minutes=None,
410
+ max_days=None,
411
+ )
412
+
413
+ # Test original method
414
+ ephemeris_data = factory.get_ephemeris_data(as_model=True)
415
+ print(f"Number of ephemeris data points: {len(ephemeris_data)}")
416
+ print(f"First data point date: {ephemeris_data[0].date}")
417
+
418
+ # Test new method
419
+ subjects = factory.get_ephemeris_data_as_astrological_subjects()
420
+ print(f"Number of astrological subjects: {len(subjects)}")
421
+ print(f"First subject sun position: {subjects[0].sun}")
422
+
423
+ # Example of accessing more data from the first subject
424
+ first_subject = subjects[0]
425
+ print(f"Sun sign: {first_subject.sun['sign']}")
426
+
427
+ # Compare sun positions from both methods
428
+ for i in range(min(3, len(subjects))):
429
+ print(f"Date: {ephemeris_data[i].date}")
430
+ print(f"Sun position from dict: {ephemeris_data[i].planets[0]['abs_pos']}")
431
+ print("---")
@@ -1,6 +1,8 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  """
3
- This is part of Kerykeion (C) 2025 Giacomo Battaglia
3
+ Author: Giacomo Battaglia
4
+ Copyright: (C) 2025 Kerykeion Project
5
+ License: AGPL-3.0
4
6
  """
5
7
 
6
8
 
@@ -13,13 +15,16 @@ from typing import Union
13
15
 
14
16
  class FetchGeonames:
15
17
  """
16
- Class to handle requests to the GeoNames API
18
+ Class to handle requests to the GeoNames API for location data and timezone information.
19
+
20
+ This class provides cached access to the GeoNames API to retrieve location coordinates,
21
+ timezone information, and other geographical data for astrological calculations.
17
22
 
18
23
  Args:
19
- - city_name (str): Name of the city
20
- - country_code (str): Two letters country code
21
- - username (str, optional): GeoNames username, defaults to "century.boy".
22
- - cache_expire_after_days (int, optional): Cache expiration time in days, defaults to 30.
24
+ city_name: Name of the city to search for.
25
+ country_code: Two-letter country code (ISO 3166-1 alpha-2).
26
+ username: GeoNames username for API access, defaults to "century.boy".
27
+ cache_expire_after_days: Number of days to cache responses, defaults to 30.
23
28
  """
24
29
 
25
30
  def __init__(
@@ -43,7 +48,14 @@ class FetchGeonames:
43
48
 
44
49
  def __get_timezone(self, lat: Union[str, float, int], lon: Union[str, float, int]) -> dict[str, str]:
45
50
  """
46
- Get the timezone for a given latitude and longitude
51
+ Get timezone information for a given latitude and longitude.
52
+
53
+ Args:
54
+ lat: Latitude coordinate.
55
+ lon: Longitude coordinate.
56
+
57
+ Returns:
58
+ dict: Timezone data including timezone string and cache status.
47
59
  """
48
60
  # Dictionary that will be returned:
49
61
  timezone_data = {}
@@ -75,7 +87,14 @@ class FetchGeonames:
75
87
 
76
88
  def __get_contry_data(self, city_name: str, country_code: str) -> dict[str, str]:
77
89
  """
78
- Get the city data *whitout timezone* for a given city and country name
90
+ Get city location data without timezone for a given city and country.
91
+
92
+ Args:
93
+ city_name: Name of the city to search for.
94
+ country_code: Two-letter country code.
95
+
96
+ Returns:
97
+ dict: City location data excluding timezone information.
79
98
  """
80
99
  # Dictionary that will be returned:
81
100
  city_data_whitout_tz = {}
@@ -1,3 +1,14 @@
1
+ """
2
+ House Comparison Factory Module
3
+
4
+ Provides factory class for house comparison analysis between astrological subjects.
5
+ Enables bidirectional analysis of astrological point placements in house systems.
6
+
7
+ Author: Giacomo Battaglia
8
+ Copyright: (C) 2025 Kerykeion Project
9
+ License: AGPL-3.0
10
+ """
11
+
1
12
  from kerykeion.house_comparison.house_comparison_utils import calculate_points_in_reciprocal_houses
2
13
  from typing import Union
3
14
  from kerykeion.settings.config_constants import DEFAULT_ACTIVE_POINTS
@@ -9,22 +20,26 @@ from kerykeion.kr_types.kr_literals import AstrologicalPoint
9
20
 
10
21
  class HouseComparisonFactory:
11
22
  """
12
- Factory class for creating house comparison analyses between two astrological charts.
23
+ Factory for creating house comparison analyses between two astrological subjects.
13
24
 
14
- This class handles the generation of house comparison data, calculating how planets
15
- from one chart interact with the house system of another chart (and vice versa).
16
- This is useful for synastry analysis and other forms of relationship astrology.
25
+ Analyzes placement of astrological points from one subject within the house system
26
+ of another subject, performing bidirectional analysis for synastry studies and
27
+ subject comparisons. Supports both natal subjects and planetary return subjects.
17
28
 
18
29
  Attributes:
19
- first_subject (AstrologicalSubject): The first person's astrological chart
20
- second_subject (AstrologicalSubject): The second person's astrological chart
30
+ first_subject: First astrological subject (natal or return subject)
31
+ second_subject: Second astrological subject (natal or return subject)
32
+ active_points: List of astrological points to include in analysis
21
33
 
22
34
  Example:
23
- >>> natal_chart = AstrologicalSubjectFactory.from_birth_data("Person A", 1990, 5, 15, 10, 30, "Rome", "IT")
24
- >>> partner_chart = AstrologicalSubjectFactory.from_birth_data("Person B", 1992, 8, 23, 14, 45, "Milan", "IT")
35
+ >>> natal_chart = AstrologicalSubjectFactory.from_birth_data(
36
+ ... "Person A", 1990, 5, 15, 10, 30, "Rome", "IT"
37
+ ... )
38
+ >>> partner_chart = AstrologicalSubjectFactory.from_birth_data(
39
+ ... "Person B", 1992, 8, 23, 14, 45, "Milan", "IT"
40
+ ... )
25
41
  >>> factory = HouseComparisonFactory(natal_chart, partner_chart)
26
42
  >>> comparison = factory.get_house_comparison()
27
- >>> print(comparison.model_dump_json(indent=4))
28
43
 
29
44
  """
30
45
  def __init__(self,
@@ -33,21 +48,39 @@ class HouseComparisonFactory:
33
48
  active_points: list[AstrologicalPoint] = DEFAULT_ACTIVE_POINTS,
34
49
 
35
50
  ):
51
+ """
52
+ Initialize the house comparison factory.
53
+
54
+ Args:
55
+ first_subject: First astrological subject for comparison
56
+ second_subject: Second astrological subject for comparison
57
+ active_points: List of astrological points to include in analysis.
58
+ Defaults to standard active points.
59
+
60
+ Note:
61
+ Both subjects must have valid house system data for accurate analysis.
62
+ """
36
63
  self.first_subject = first_subject
37
64
  self.second_subject = second_subject
38
65
  self.active_points = active_points
39
66
 
40
67
  def get_house_comparison(self) -> "HouseComparisonModel":
41
68
  """
42
- Creates a house comparison model for two astrological subjects.
69
+ Generate bidirectional house comparison analysis between the two subjects.
43
70
 
44
- Args:
45
- chart1: First astrological subject
46
- chart2: Second astrological subject
47
- description: Description of the comparison
71
+ Calculates where each active astrological point from one subject falls within
72
+ the house system of the other subject, and vice versa.
48
73
 
49
74
  Returns:
50
- "HouseComparisonModel": Model containing the comparison data.
75
+ HouseComparisonModel: Model containing:
76
+ - first_subject_name: Name of the first subject
77
+ - second_subject_name: Name of the second subject
78
+ - first_points_in_second_houses: First subject's points in second subject's houses
79
+ - second_points_in_first_houses: Second subject's points in first subject's houses
80
+
81
+ Note:
82
+ Analysis scope is determined by the active_points list. Only specified
83
+ points will be included in the results.
51
84
  """
52
85
  first_points_in_second_houses = calculate_points_in_reciprocal_houses(self.first_subject, self.second_subject, self.active_points)
53
86
  second_points_in_first_houses = calculate_points_in_reciprocal_houses(self.second_subject, self.first_subject, self.active_points)