kerykeion 4.12.8__tar.gz → 4.13.0__tar.gz

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 (31) hide show
  1. {kerykeion-4.12.8 → kerykeion-4.13.0}/PKG-INFO +1 -1
  2. {kerykeion-4.12.8 → kerykeion-4.13.0}/kerykeion/__init__.py +1 -0
  3. {kerykeion-4.12.8 → kerykeion-4.13.0}/kerykeion/astrological_subject.py +21 -11
  4. kerykeion-4.13.0/kerykeion/ephemeris_data.py +174 -0
  5. {kerykeion-4.12.8 → kerykeion-4.13.0}/kerykeion/kr_types/kr_models.py +5 -0
  6. {kerykeion-4.12.8 → kerykeion-4.13.0}/pyproject.toml +1 -1
  7. {kerykeion-4.12.8 → kerykeion-4.13.0}/LICENSE +0 -0
  8. {kerykeion-4.12.8 → kerykeion-4.13.0}/README.md +0 -0
  9. {kerykeion-4.12.8 → kerykeion-4.13.0}/kerykeion/aspects/__init__.py +0 -0
  10. {kerykeion-4.12.8 → kerykeion-4.13.0}/kerykeion/aspects/aspects_utils.py +0 -0
  11. {kerykeion-4.12.8 → kerykeion-4.13.0}/kerykeion/aspects/natal_aspects.py +0 -0
  12. {kerykeion-4.12.8 → kerykeion-4.13.0}/kerykeion/aspects/synastry_aspects.py +0 -0
  13. {kerykeion-4.12.8 → kerykeion-4.13.0}/kerykeion/charts/__init__.py +0 -0
  14. {kerykeion-4.12.8 → kerykeion-4.13.0}/kerykeion/charts/charts_utils.py +0 -0
  15. {kerykeion-4.12.8 → kerykeion-4.13.0}/kerykeion/charts/kerykeion_chart_svg.py +0 -0
  16. {kerykeion-4.12.8 → kerykeion-4.13.0}/kerykeion/charts/templates/chart.xml +0 -0
  17. {kerykeion-4.12.8 → kerykeion-4.13.0}/kerykeion/enums.py +0 -0
  18. {kerykeion-4.12.8 → kerykeion-4.13.0}/kerykeion/fetch_geonames.py +0 -0
  19. {kerykeion-4.12.8 → kerykeion-4.13.0}/kerykeion/kr_types/__init__.py +0 -0
  20. {kerykeion-4.12.8 → kerykeion-4.13.0}/kerykeion/kr_types/chart_types.py +0 -0
  21. {kerykeion-4.12.8 → kerykeion-4.13.0}/kerykeion/kr_types/kerykeion_exception.py +0 -0
  22. {kerykeion-4.12.8 → kerykeion-4.13.0}/kerykeion/kr_types/kr_literals.py +0 -0
  23. {kerykeion-4.12.8 → kerykeion-4.13.0}/kerykeion/kr_types/settings_models.py +0 -0
  24. {kerykeion-4.12.8 → kerykeion-4.13.0}/kerykeion/relationship_score.py +0 -0
  25. {kerykeion-4.12.8 → kerykeion-4.13.0}/kerykeion/report.py +0 -0
  26. {kerykeion-4.12.8 → kerykeion-4.13.0}/kerykeion/settings/__init__.py +0 -0
  27. {kerykeion-4.12.8 → kerykeion-4.13.0}/kerykeion/settings/kerykeion_settings.py +0 -0
  28. {kerykeion-4.12.8 → kerykeion-4.13.0}/kerykeion/settings/kr.config.json +0 -0
  29. {kerykeion-4.12.8 → kerykeion-4.13.0}/kerykeion/sweph/README.md +0 -0
  30. {kerykeion-4.12.8 → kerykeion-4.13.0}/kerykeion/sweph/seas_18.se1 +0 -0
  31. {kerykeion-4.12.8 → kerykeion-4.13.0}/kerykeion/utilities.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: kerykeion
3
- Version: 4.12.8
3
+ Version: 4.13.0
4
4
  Summary: A python library for astrology.
5
5
  Home-page: https://www.kerykeion.net/
6
6
  License: AGPL-3.0
@@ -14,3 +14,4 @@ from .aspects import SynastryAspects, NatalAspects
14
14
  from .report import Report
15
15
  from .settings import KerykeionSettingsModel, get_settings
16
16
  from .enums import Planets, Aspects, Signs
17
+ from .ephemeris_data import EphemerisDataFactory
@@ -87,6 +87,9 @@ class AstrologicalSubject:
87
87
  - perspective_type (PerspectiveType, optional): The perspective to use for the calculation of the chart.
88
88
  Defaults to "Apparent Geocentric".
89
89
  Available perspectives are visible in the PerspectiveType Literal.
90
+ - is_dst (Union[None, bool], optional): Specify if the time is in DST. Defaults to None.
91
+ By default (None), the library will try to guess if the time is in DST or not and raise an AmbiguousTimeError
92
+ if it can't guess. If you know the time is in DST, set this to True, if you know it's not, set it to False.
90
93
  """
91
94
 
92
95
  # Defined by the user
@@ -108,6 +111,7 @@ class AstrologicalSubject:
108
111
  houses_system_identifier: HousesSystemIdentifier
109
112
  houses_system_name: str
110
113
  perspective_type: PerspectiveType
114
+ is_dst: Union[None, bool]
111
115
 
112
116
  # Generated internally
113
117
  city_data: dict[str, str]
@@ -170,7 +174,8 @@ class AstrologicalSubject:
170
174
  disable_chiron: bool = False,
171
175
  sidereal_mode: Union[SiderealMode, None] = None,
172
176
  houses_system_identifier: HousesSystemIdentifier = DEFAULT_HOUSES_SYSTEM_IDENTIFIER,
173
- perspective_type: PerspectiveType = DEFAULT_PERSPECTIVE_TYPE
177
+ perspective_type: PerspectiveType = DEFAULT_PERSPECTIVE_TYPE,
178
+ is_dst: Union[None, bool] = None
174
179
  ) -> None:
175
180
  logging.debug("Starting Kerykeion")
176
181
 
@@ -193,6 +198,7 @@ class AstrologicalSubject:
193
198
  self.sidereal_mode = sidereal_mode
194
199
  self.houses_system_identifier = houses_system_identifier
195
200
  self.perspective_type = perspective_type
201
+ self.is_dst = is_dst
196
202
 
197
203
  #---------------#
198
204
  # General setup #
@@ -212,11 +218,11 @@ class AstrologicalSubject:
212
218
  self.nation = "GB"
213
219
  logging.info("No nation specified, using GB as default")
214
220
 
215
- if not self.lat:
221
+ if not self.lat and not self.online:
216
222
  self.lat = 51.5074
217
223
  logging.info("No latitude specified, using London as default")
218
224
 
219
- if not self.lng:
225
+ if not self.lng and not self.online:
220
226
  self.lng = 0
221
227
  logging.info("No longitude specified, using London as default")
222
228
 
@@ -244,6 +250,7 @@ class AstrologicalSubject:
244
250
  elif self.perspective_type == "Topocentric":
245
251
  self._iflag += swe.FLG_TOPOCTR
246
252
  # geopos_is_set, for topocentric
253
+ self._fetch_and_set_tz_and_coordinates_from_geonames()
247
254
  swe.set_topo(self.lng, self.lat, 0)
248
255
  # <--- Chart Perspective check and setup
249
256
 
@@ -279,17 +286,22 @@ class AstrologicalSubject:
279
286
  #------------------------#
280
287
  # Start the calculations #
281
288
  #------------------------#
282
-
283
- self.lat = check_and_adjust_polar_latitude(self.lat)
284
289
 
285
290
  # UTC, julian day and local time setup --->
286
- if (self.online) and (not self.tz_str):
287
- self._fetch_tz_from_geonames()
291
+ if (self.online) and (not self.tz_str) and (not self.lat) and (not self.lng):
292
+ self._fetch_and_set_tz_and_coordinates_from_geonames()
293
+
294
+ self.lat = check_and_adjust_polar_latitude(self.lat)
288
295
 
289
296
  # Local time to UTC
290
297
  local_time = pytz.timezone(self.tz_str)
291
298
  naive_datetime = datetime(self.year, self.month, self.day, self.hour, self.minute, 0)
292
- local_datetime = local_time.localize(naive_datetime, is_dst=None)
299
+
300
+ try:
301
+ local_datetime = local_time.localize(naive_datetime, is_dst=self.is_dst)
302
+ except pytz.exceptions.AmbiguousTimeError:
303
+ raise KerykeionException("Ambiguous time! Please specify if the time is in DST or not with the is_dst argument.")
304
+
293
305
  utc_object = local_datetime.astimezone(pytz.utc)
294
306
  self.iso_formatted_utc_datetime = utc_object.isoformat()
295
307
 
@@ -323,7 +335,7 @@ class AstrologicalSubject:
323
335
  def get(self, item, default=None):
324
336
  return getattr(self, item, default)
325
337
 
326
- def _fetch_tz_from_geonames(self) -> None:
338
+ def _fetch_and_set_tz_and_coordinates_from_geonames(self) -> None:
327
339
  """Gets the nearest time zone for the calculation"""
328
340
  logging.info("Fetching timezone/coordinates from geonames")
329
341
 
@@ -347,8 +359,6 @@ class AstrologicalSubject:
347
359
  self.lat = float(self.city_data["lat"])
348
360
  self.tz_str = self.city_data["timezonestr"]
349
361
 
350
- self.lat = check_and_adjust_polar_latitude(self.lat)
351
-
352
362
  def _houses(self) -> None:
353
363
  """
354
364
  Calculate positions and store them in dictionaries
@@ -0,0 +1,174 @@
1
+ from kerykeion import AstrologicalSubject
2
+ from kerykeion.astrological_subject import DEFAULT_HOUSES_SYSTEM_IDENTIFIER, DEFAULT_PERSPECTIVE_TYPE, DEFAULT_ZODIAC_TYPE
3
+ from kerykeion.kr_types import EphemerisDictModel
4
+ from kerykeion.kr_types import SiderealMode, HousesSystemIdentifier, PerspectiveType, ZodiacType
5
+ from datetime import datetime, timedelta
6
+ from typing import Literal
7
+ import logging
8
+
9
+
10
+ class EphemerisDataFactory:
11
+ """
12
+ This class is used to generate ephemeris data for a given date range.
13
+
14
+ Parameters:
15
+ - start_datetime: datetime object representing the start date and time.
16
+ - end_datetime: datetime object representing the end date and time.
17
+ - step_type: string representing the step type. It can be "days", "hours", or "minutes". Default is "days".
18
+ - step: integer representing the step value. Default is 1.
19
+ - lat: float representing the latitude. Default is 51.4769 (Greenwich).
20
+ - lng: float representing the longitude. Default is 0.0005 (Greenwich).
21
+ - tz_str: string representing the timezone. Default is "Etc/UTC".
22
+ - is_dst: boolean representing if daylight saving time is active. Default is False.
23
+ - disable_chiron: boolean representing if Chiron should be disabled. Default is False.
24
+ - zodiac_type: ZodiacType object representing the zodiac type. Default is DEFAULT_ZODIAC_TYPE.
25
+ - sidereal_mode: SiderealMode object representing the sidereal mode. Default is None.
26
+ - houses_system_identifier: HousesSystemIdentifier object representing the houses system identifier. Default is DEFAULT_HOUSES_SYSTEM_IDENTIFIER.
27
+ - perspective_type: PerspectiveType object representing the perspective type. Default is DEFAULT_PERSPECTIVE_TYPE.
28
+ - max_days: integer representing the maximum number of days.
29
+ Set it to None to disable the check. Default is 730.
30
+ - max_hours: integer representing the maximum number of hours.
31
+ Set it to None to disable the check. Default is 8760.
32
+ - max_minutes: integer representing the maximum number of minutes.
33
+ Set it to None to disable the check. Default is 525600.
34
+
35
+ Raises:
36
+ - ValueError: if the step type is invalid.
37
+ - ValueError: if the number of days, hours, or minutes is greater than the maximum allowed.
38
+ """
39
+
40
+ def __init__(
41
+ self,
42
+ start_datetime: datetime,
43
+ end_datetime: datetime,
44
+ step_type: Literal["days", "hours", "minutes"] = "days",
45
+ step: int = 1,
46
+ lat: float = 51.4769,
47
+ lng: float = 0.0005,
48
+ tz_str: str = "Etc/UTC",
49
+ is_dst: bool = False,
50
+ disable_chiron: bool = False,
51
+ zodiac_type: ZodiacType = DEFAULT_ZODIAC_TYPE,
52
+ sidereal_mode: SiderealMode | None = None,
53
+ houses_system_identifier: HousesSystemIdentifier = DEFAULT_HOUSES_SYSTEM_IDENTIFIER,
54
+ perspective_type: PerspectiveType = DEFAULT_PERSPECTIVE_TYPE,
55
+ max_days: int = 730,
56
+ max_hours: int = 8760,
57
+ max_minutes: int = 525600,
58
+ ):
59
+ self.start_datetime = start_datetime
60
+ self.end_datetime = end_datetime
61
+ self.step_type = step_type
62
+ self.step = step
63
+ self.lat = lat
64
+ self.lng = lng
65
+ self.tz_str = tz_str
66
+ self.is_dst = is_dst
67
+ self.disable_chiron = disable_chiron
68
+ self.zodiac_type = zodiac_type
69
+ self.sidereal_mode = sidereal_mode
70
+ self.houses_system_identifier = houses_system_identifier
71
+ self.perspective_type = perspective_type
72
+ self.max_days = max_days
73
+ self.max_hours = max_hours
74
+ self.max_minutes = max_minutes
75
+
76
+ self.dates_list = []
77
+ if self.step_type == "days":
78
+ self.dates_list = [self.start_datetime + timedelta(days=i) for i in range((self.end_datetime - self.start_datetime).days)]
79
+ if max_days and (len(self.dates_list) > max_days):
80
+ 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.")
81
+
82
+ elif self.step_type == "hours":
83
+ self.dates_list = [self.start_datetime + timedelta(hours=i) for i in range((self.end_datetime - self.start_datetime).days * 24)]
84
+ if max_hours and (len(self.dates_list) > max_hours):
85
+ 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.")
86
+
87
+ elif self.step_type == "minutes":
88
+ self.dates_list = [self.start_datetime + timedelta(minutes=i) for i in range((self.end_datetime - self.start_datetime).days * 24 * 60)]
89
+ if max_minutes and (len(self.dates_list) > max_minutes):
90
+ 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.")
91
+
92
+ else:
93
+ raise ValueError(f"Invalid step type: {self.step_type}")
94
+
95
+ if not self.dates_list:
96
+ raise ValueError("No dates found. Check the date range and step values.")
97
+
98
+ if len(self.dates_list) > 1000:
99
+ logging.warning(f"Large number of dates: {len(self.dates_list)}. The calculation may take a while.")
100
+
101
+ def get_ephemeris_data(self) -> list:
102
+ """
103
+ Generate ephemeris data for the specified date range.
104
+ The data is structured as a list of dictionaries, where each dictionary contains the date, planets, and houses data.
105
+ Eg. [{"date": "2020-01-01T00:00:00", "planets": [{...}, {...}, ...], "houses": [{...}, {...}, ...]}, ...]
106
+
107
+ Args:
108
+ - as_model: boolean representing if the ephemeris data should be returned as model instances. Default is False.
109
+ - as_dict: boolean representing if the ephemeris data should be returned as dictionaries. Default is False.
110
+
111
+ Returns:
112
+ - list of dictionaries representing the ephemeris data.
113
+ """
114
+ ephemeris_data_list = []
115
+ for date in self.dates_list:
116
+ subject = AstrologicalSubject(
117
+ year=date.year,
118
+ month=date.month,
119
+ day=date.day,
120
+ hour=date.hour,
121
+ minute=date.minute,
122
+ lng=self.lng,
123
+ lat=self.lat,
124
+ tz_str=self.tz_str,
125
+ city="Placeholder",
126
+ nation="Placeholder",
127
+ online=False,
128
+ disable_chiron=self.disable_chiron,
129
+ zodiac_type=self.zodiac_type,
130
+ sidereal_mode=self.sidereal_mode,
131
+ houses_system_identifier=self.houses_system_identifier,
132
+ perspective_type=self.perspective_type,
133
+ is_dst=self.is_dst,
134
+ )
135
+
136
+ ephemeris_data_list.append({"date": date.isoformat(), "planets": subject.planets_list, "houses": subject.houses_list})
137
+
138
+ return ephemeris_data_list
139
+
140
+ def get_ephemeris_data_as_model(self) -> list[EphemerisDictModel]:
141
+ """
142
+ Generate ephemeris data as model instances for the specified date range.
143
+ The data is structured as a list of EphemerisDictModel instances.
144
+
145
+ Returns:
146
+ - list of EphemerisDictModel instances representing the ephemeris data.
147
+ """
148
+ return [EphemerisDictModel(**data) for data in self.get_ephemeris_data()]
149
+
150
+
151
+ if "__main__" == __name__:
152
+ start_date = datetime.fromisoformat("2020-01-01")
153
+ end_date = datetime.fromisoformat("2020-01-03")
154
+
155
+ factory = EphemerisDataFactory(
156
+ start_datetime=start_date,
157
+ end_datetime=end_date,
158
+ step_type="minutes",
159
+ step=1,
160
+ lat=37.9838,
161
+ lng=23.7275,
162
+ tz_str="Europe/Athens",
163
+ is_dst=False,
164
+ max_hours=None,
165
+ max_minutes=None,
166
+ max_days=None,
167
+ )
168
+
169
+ ephemeris_data = factory.get_ephemeris_data_as_model()
170
+ print(ephemeris_data[0])
171
+ print(len(ephemeris_data))
172
+
173
+ for ephe in ephemeris_data:
174
+ print(ephe.planets[0]["abs_pos"])
@@ -120,6 +120,11 @@ class AstrologicalSubjectModel(SubscriptableBaseModel):
120
120
  # planets_degrees_ut: list[float]
121
121
  # houses_degree_ut: list[float]
122
122
 
123
+ class EphemerisDictModel(BaseModel):
124
+ date: str
125
+ planets: list[KerykeionPointModel]
126
+ houses: list[KerykeionPointModel]
127
+
123
128
  if __name__ == "__main__":
124
129
  from kerykeion.utilities import setup_logging
125
130
 
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "kerykeion"
3
- version = "4.12.8"
3
+ version = "4.13.0"
4
4
  authors = ["Giacomo Battaglia <battaglia.giacomo@yahoo.it>"]
5
5
  description = "A python library for astrology."
6
6
  license = "AGPL-3.0"
File without changes
File without changes