kerykeion 4.12.7__py3-none-any.whl → 4.13.0__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.

kerykeion/__init__.py CHANGED
@@ -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"])
@@ -72,6 +72,11 @@ class AstrologicalSubjectModel(SubscriptableBaseModel):
72
72
  iso_formatted_local_datetime: str
73
73
  iso_formatted_utc_datetime: str
74
74
  julian_day: float
75
+
76
+ # Deprecated properties -->
77
+ utc_time: float
78
+ local_time: float
79
+ # <-- Deprecated properties
75
80
 
76
81
  # Planets
77
82
  sun: KerykeionPointModel
@@ -109,16 +114,17 @@ class AstrologicalSubjectModel(SubscriptableBaseModel):
109
114
  # Lunar Phase
110
115
  lunar_phase: LunarPhaseModel
111
116
 
112
- # Deprecated properties
113
- utc_time: float
114
- local_time: float
115
-
116
117
  # Lists
117
118
  # houses_list: list[KerykeionPointModel]
118
119
  # planets_list: list[KerykeionPointModel]
119
120
  # planets_degrees_ut: list[float]
120
121
  # houses_degree_ut: list[float]
121
122
 
123
+ class EphemerisDictModel(BaseModel):
124
+ date: str
125
+ planets: list[KerykeionPointModel]
126
+ houses: list[KerykeionPointModel]
127
+
122
128
  if __name__ == "__main__":
123
129
  from kerykeion.utilities import setup_logging
124
130
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: kerykeion
3
- Version: 4.12.7
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
@@ -1,21 +1,22 @@
1
1
  LICENSE,sha256=UTLH8EdbAsgQei4PA2PnBCPGLSZkq5J-dhkyJuXgWQU,34273
2
- kerykeion/__init__.py,sha256=AOTx2pvk1-SByVSen5VW4QHsN5LTIjNxpHOIjlm8XA4,487
2
+ kerykeion/__init__.py,sha256=f4_J_hAh_OSLAUrabqVhrc1rH59I6X1LS9vuAEzE9PY,536
3
3
  kerykeion/aspects/__init__.py,sha256=9FlDVI1ndCJga0-chNIhcLitjU_x3kbtAFfFqVp2ejc,293
4
4
  kerykeion/aspects/aspects_utils.py,sha256=ZCOnhgW6CZQrCruAGaf8vkUlBtjubbfKOqXy6qyQupE,5321
5
5
  kerykeion/aspects/natal_aspects.py,sha256=R47UToYKqbVrRmGzmY4pgsikcoXNmJvs5KhSmz7HZtM,4460
6
6
  kerykeion/aspects/synastry_aspects.py,sha256=BqG0E4tDZJjPQm3NTX3G6LkTuijP0AsKtI9pwH0F_Mc,3946
7
- kerykeion/astrological_subject.py,sha256=_y1676b9ZLbuQp6fTw-fcyIyHlBom4I7N_Xk6y5m1KM,32803
7
+ kerykeion/astrological_subject.py,sha256=xUuhiSbWbFWPKREdRUl9VCoxDyzs_iu8tRTCVkNx76I,33565
8
8
  kerykeion/charts/__init__.py,sha256=Juxkduy2TaagWblh_7CE8Acrg3dHL27-WEddJhau_eQ,127
9
9
  kerykeion/charts/charts_utils.py,sha256=Pfy-SkPnABIy5ZkKgypnGqxXOAKhTRIeQX3fQKO8DgQ,15065
10
10
  kerykeion/charts/kerykeion_chart_svg.py,sha256=P3xRWedqdbYjeI1xX9saL729IXBfJ_8TWLz1AzUF5vI,64864
11
11
  kerykeion/charts/templates/chart.xml,sha256=aP_dVC2w2sULBtGrjrOUi46e5m59zKaMizMnTq1VfNM,67714
12
12
  kerykeion/enums.py,sha256=Ben9GLYkPucpYY2ZDpURzUbNCc9jzK2MuaffkgiXFdQ,965
13
+ kerykeion/ephemeris_data.py,sha256=1eky2iglWU4fTSPb2b63f6PnwQEL28MGtzJi3GNAnjI,7995
13
14
  kerykeion/fetch_geonames.py,sha256=NmyTErvKISjJCAxvRB1H35aVZI8_-_U-Cqb_rmqRseA,4563
14
15
  kerykeion/kr_types/__init__.py,sha256=-qhGQikurdoHnGtuT1bsaEeZ-IwmZtIHMjGOPC9_oqQ,295
15
16
  kerykeion/kr_types/chart_types.py,sha256=qaZjm1rMpDDnUZlLjEihd9KPyv3PvrV8vkRmNBuRZzE,2201
16
17
  kerykeion/kr_types/kerykeion_exception.py,sha256=G-7VFta78qBt10l54JZWvwH-3lUNKmDwuILXaVGVu9A,314
17
18
  kerykeion/kr_types/kr_literals.py,sha256=Urk4wbRXeCOMhJEIkfvwTWrbU-Z5csDnh03hVwxpMbc,3140
18
- kerykeion/kr_types/kr_models.py,sha256=AoPxoy3J472Z5yUkTpKg93AZfga8wDuM7ZRTLp07aDQ,3526
19
+ kerykeion/kr_types/kr_models.py,sha256=LuImP9hoTHu2L41mBPtjwe29PjZvYdGzB81pp3vkQvU,3695
19
20
  kerykeion/kr_types/settings_models.py,sha256=Gh467QjvlGmheD6eJI1IHpuK4cz_hbtjGTJT_1NMoAE,12376
20
21
  kerykeion/relationship_score.py,sha256=R9JugfK5_gJgr5ND-EghkqpqZcutzzKlJ-2JnYUMVv4,6794
21
22
  kerykeion/report.py,sha256=kS5avIN119pJVapYjZOvabg77nEcA8sSrOuXbRifABk,2565
@@ -25,8 +26,8 @@ kerykeion/settings/kr.config.json,sha256=1Yhv9RGHom5U9e-JZZRWVfT2Ubllz2WrckdwadD
25
26
  kerykeion/sweph/README.md,sha256=L7FtNAJTWtrZNGKa8MX87SjduFYPYxwWhaI5fmtzNZo,73
26
27
  kerykeion/sweph/seas_18.se1,sha256=X9nCqhZU43wJpq61WAdueVQJt9xL2UjrwPqn1Kdoa1s,223002
27
28
  kerykeion/utilities.py,sha256=O2VNTu5iozaL_aGsYHJRBo3ckQxsVckxDYfZME8z-ZI,11397
28
- kerykeion-4.12.7.dist-info/LICENSE,sha256=UTLH8EdbAsgQei4PA2PnBCPGLSZkq5J-dhkyJuXgWQU,34273
29
- kerykeion-4.12.7.dist-info/METADATA,sha256=AKSJMwvWXR3a2Fu_BiWjaNXeO4EmBlNuduORRIMcoMs,14410
30
- kerykeion-4.12.7.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
31
- kerykeion-4.12.7.dist-info/entry_points.txt,sha256=5SmANYscFDDTdeovHvGQ-cnj0hdFvGoxPaWLCpyDFnQ,49
32
- kerykeion-4.12.7.dist-info/RECORD,,
29
+ kerykeion-4.13.0.dist-info/LICENSE,sha256=UTLH8EdbAsgQei4PA2PnBCPGLSZkq5J-dhkyJuXgWQU,34273
30
+ kerykeion-4.13.0.dist-info/METADATA,sha256=lmVZSmrQsVXAEy9M2ycNOAKzp-iPx5GrVXj3ZYjoLV8,14410
31
+ kerykeion-4.13.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
32
+ kerykeion-4.13.0.dist-info/entry_points.txt,sha256=5SmANYscFDDTdeovHvGQ-cnj0hdFvGoxPaWLCpyDFnQ,49
33
+ kerykeion-4.13.0.dist-info/RECORD,,