From ad96b8bebf9676f1d450bd6f337367664a9616ea Mon Sep 17 00:00:00 2001 From: LiamNg Date: Sun, 20 Jun 2021 19:45:10 +0700 Subject: [PATCH] feat(event): add support for Earth seasons (#24) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore: fix files to commit on tag * fix(ephemerides): fix a bug that made the ephemerides calculations impossible for the Poles (#21) * chore: fix the target branch on Dependabot's config * ci: remove Commitlint workflow (replaced by semantic-pull-request) (#25) * ci: remove Commitlint workflow (replaced by semantic-pull-request) * ci: fix doctests not running correctly on some OS and Python versions * Add season change event in events.py and enum.py * Add minor change in season change event * Add documentation, change enum constants, change output object of _search_earth_season_change * Minor changes to _search_earth_season_change * Update EventType enum, update class of details * Fix minor bugs * docs: Update docs for _search_earth_season_change and _search_conjunction.chore: make minor changes to _search_earth_season_change * Update: minor changes to match Python coding style. * Update: minor changes to match Python coding style. Docs: update docstring of _search_earth_season_change and _search_conjunction * test:update legacy tests for events.py. update: update enum.py and events.py to match black coding style. * Fix some minor issues with Black and Event.details field Co-authored-by: Jérôme Deuchnord Co-authored-by: Jérôme Deuchnord BREAKING CHANGE: the `Event.details` field is now a dictionary (was previously a string). --- kosmorrolib/enum.py | 10 +++++- kosmorrolib/events.py | 74 ++++++++++++++++++++++++++++++++++++++----- kosmorrolib/model.py | 2 +- tests/events.py | 6 ++-- 4 files changed, 79 insertions(+), 13 deletions(-) diff --git a/kosmorrolib/enum.py b/kosmorrolib/enum.py index e9420cd..b4438e9 100644 --- a/kosmorrolib/enum.py +++ b/kosmorrolib/enum.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -from enum import Enum, auto +from enum import Enum class MoonPhaseType(Enum): @@ -16,6 +16,13 @@ class MoonPhaseType(Enum): WANING_CRESCENT = 8 +class SeasonType(Enum): + MARCH_EQUINOX = 0 + JUNE_SOLSTICE = 1 + SEPTEMBER_EQUINOX = 2 + DECEMBER_SOLSTICE = 3 + + class EventType(Enum): """An enumeration for the supported event types.""" @@ -25,6 +32,7 @@ class EventType(Enum): MAXIMAL_ELONGATION = 4 MOON_PERIGEE = 5 MOON_APOGEE = 6 + SEASON_CHANGE = 7 class ObjectType(Enum): diff --git a/kosmorrolib/events.py b/kosmorrolib/events.py index 20d5d03..a0bcee8 100644 --- a/kosmorrolib/events.py +++ b/kosmorrolib/events.py @@ -5,16 +5,34 @@ from datetime import date from skyfield.errors import EphemerisRangeError from skyfield.timelib import Time from skyfield.searchlib import find_discrete, find_maxima, find_minima +from skyfield import almanac from numpy import pi -from .model import Event, Star, Planet, ASTERS -from .dateutil import translate_to_timezone -from .enum import EventType, ObjectIdentifier -from .exceptions import OutOfRangeDateError -from .core import get_timescale, get_skf_objects, flatten_list +from kosmorrolib.model import Event, Star, Planet, ASTERS +from kosmorrolib.dateutil import translate_to_timezone +from kosmorrolib.enum import EventType, ObjectIdentifier, SeasonType +from kosmorrolib.exceptions import OutOfRangeDateError +from kosmorrolib.core import get_timescale, get_skf_objects, flatten_list def _search_conjunction(start_time: Time, end_time: Time, timezone: int) -> [Event]: + """Function to search conjunction. + + **Warning:** this is an internal function, not intended for use by end-developers. + + Will return MOON and VENUS opposition on 2021-06-12: + + >>> conjunction = _search_conjunction(get_timescale().utc(2021,6,12),get_timescale().utc(2021,6,13),0) + >>> len(conjunction) + 1 + >>> conjunction[0].objects + [, ] + + Will return nothing if no conjunction happens: + + >>> _search_conjunction(get_timescale().utc(2021,6,17),get_timescale().utc(2021,6,18),0) + [] + """ earth = get_skf_objects()["earth"] aster1 = None aster2 = None @@ -177,13 +195,13 @@ def _search_maximal_elongations( ) for i, time in enumerate(times): - elongation = elongations[i] + elongation = round(elongations[i], 1) events.append( Event( EventType.MAXIMAL_ELONGATION, [aster], translate_to_timezone(time.utc_datetime(), timezone), - details="{:.3n}°".format(elongation), + details={"deg": elongation}, ) ) @@ -245,6 +263,45 @@ def _search_moon_perigee(start_time: Time, end_time: Time, timezone: int) -> [Ev return events +def _search_earth_season_change( + start_time: Time, end_time: Time, timezone: int +) -> [Event]: + """Function to find earth season change event. + + **Warning:** this is an internal function, not intended for use by end-developers. + + Will return JUNE SOLSTICE on 2020/06/20: + + >>> season_change = _search_earth_season_change(get_timescale().utc(2020, 6, 20), get_timescale().utc(2020, 6, 21), 0) + >>> len(season_change) + 1 + >>> season_change[0].event_type + + >>> season_change[0].details + {'season': } + + Will return nothing if there is no season change event in the period of time being calculated: + + >>> _search_earth_season_change(get_timescale().utc(2021, 6, 17), get_timescale().utc(2021, 6, 18), 0) + [] + """ + events = [] + event_time, event_id = almanac.find_discrete( + start_time, end_time, almanac.seasons(get_skf_objects()) + ) + if len(event_time) == 0: + return [] + events.append( + Event( + EventType.SEASON_CHANGE, + [], + translate_to_timezone(event_time.utc_datetime()[0], timezone), + details={"season": SeasonType(event_id[0])}, + ) + ) + return events + + def get_events(for_date: date = date.today(), timezone: int = 0) -> [Event]: """Calculate and return a list of events for the given date, adjusted to the given timezone if any. @@ -265,7 +322,7 @@ def get_events(for_date: date = date.today(), timezone: int = 0) -> [Event]: If there is no events for the given date, then an empty list is returned: - >>> get_events(date(2021, 3, 20)) + >>> get_events(date(2021, 4, 20)) [] :param for_date: the date for which the events must be calculated @@ -289,6 +346,7 @@ def get_events(for_date: date = date.today(), timezone: int = 0) -> [Event]: _search_maximal_elongations, _search_moon_apogee, _search_moon_perigee, + _search_earth_season_change, ]: found_events.append(fun(start_time, end_time, timezone)) diff --git a/kosmorrolib/model.py b/kosmorrolib/model.py index 1f4a362..4a69147 100644 --- a/kosmorrolib/model.py +++ b/kosmorrolib/model.py @@ -154,7 +154,7 @@ class Event(Serializable): objects: [Object], start_time: datetime, end_time: Union[datetime, None] = None, - details: str = None, + details: {str: any} = None, ): self.event_type = event_type self.objects = objects diff --git a/tests/events.py b/tests/events.py index 70f12a7..0132692 100644 --- a/tests/events.py +++ b/tests/events.py @@ -74,7 +74,7 @@ EXPECTED_EVENTS = [ EventType.MAXIMAL_ELONGATION, [ASTERS[2]], datetime(2020, 2, 10, 13, 46), - details="18.2°", + details={"deg": 18.2}, ), Event(EventType.MOON_PERIGEE, [ASTERS[1]], datetime(2020, 2, 10, 20, 34)), ], @@ -86,14 +86,14 @@ EXPECTED_EVENTS = [ EventType.MAXIMAL_ELONGATION, [ASTERS[2]], datetime(2020, 3, 24, 1, 56), - details="27.8°", + details={"deg": 27.8}, ), Event(EventType.MOON_APOGEE, [ASTERS[1]], datetime(2020, 3, 24, 15, 39)), Event( EventType.MAXIMAL_ELONGATION, [ASTERS[3]], datetime(2020, 3, 24, 21, 58), - details="46.1°", + details={"deg": 46.1}, ), ], ),