From 8b723bfd221dbcddae1717c5e1211a99d828b67e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Deuchnord?= Date: Fri, 11 Dec 2020 10:17:53 +0100 Subject: [PATCH] refactor: use `enum` instead of `dict`s for the events and moon phase (#129) BREAKING CHANGE: some methodes in Event and MoonPhase have been dropped in favor of `enum.Enum`'s `name` and `value` properties. --- .github/workflows/i18n.yml | 17 ++- Makefile | 8 +- kosmorrolib/data.py | 111 +++---------------- kosmorrolib/dumper.py | 12 +- kosmorrolib/enum.py | 40 +++++++ kosmorrolib/ephemerides.py | 43 +++++++- kosmorrolib/events.py | 15 ++- kosmorrolib/locales/messages.pot | 184 +++++++++++++++---------------- test/dumper.py | 7 +- test/ephemerides.py | 65 +++++------ test/events.py | 47 ++++---- 11 files changed, 282 insertions(+), 267 deletions(-) create mode 100644 kosmorrolib/enum.py diff --git a/.github/workflows/i18n.yml b/.github/workflows/i18n.yml index 086378a..c4a2f41 100644 --- a/.github/workflows/i18n.yml +++ b/.github/workflows/i18n.yml @@ -19,13 +19,26 @@ jobs: - name: Check i18n run: | pipenv run python setup.py extract_messages --output-file=/tmp/kosmorro-messages.pot > /dev/null - n=$(diff -y --suppress-common-lines kosmorrolib/locales/messages.pot /tmp/kosmorro-messages.pot | grep -v -E '^"POT-Creation-Date: ' | wc -l) + diff=$(diff kosmorrolib/locales/messages.pot /tmp/kosmorro-messages.pot | grep '^>') + n=$(echo "$diff" | grep -v '> "POT-Creation-Date: ' | wc -l) + + if [ "$(echo "$diff" | grep -E '^"Generated-By: Babel' | wc -l)" -eq "1" ]; then + echo "❌ You dependencies may be out of date!" + echo " Please run the following command to fix this:" + echo + echo " pipenv sync --dev" + echo + echo " Then update the messages file:" + echo + echo " make messages" + exit 2 + fi if [ "$n" -ne "0" ]; then echo "❌ The messages file is not up-to-date!" echo " Please run the following command to fix this:" echo - echo " pipenv run python setup.py extract_messages --output-file=kosmorrolib/locales/messages.pot" + echo " make messages" exit 1 fi diff --git a/Makefile b/Makefile index 776b6db..f484f00 100644 --- a/Makefile +++ b/Makefile @@ -6,13 +6,17 @@ test: unset KOSMORRO_TIMEZONE; \ LANG=C pipenv run python3 -m coverage run -m unittest test -build: i18n +build: i18n manpages python3 setup.py sdist bdist_wheel -i18n: +messages: + pipenv run python setup.py extract_messages --output-file=kosmorrolib/locales/messages.pot + +manpages: ronn --roff manpage/kosmorro.1.md ronn --roff manpage/kosmorro.7.md +i18n: if [ "$$POEDITOR_API_ACCESS" != "" ]; then \ python3 .scripts/build/getlangs.py; \ python3 setup.py compile_catalog; \ diff --git a/kosmorrolib/data.py b/kosmorrolib/data.py index 655d075..ea30eb4 100644 --- a/kosmorrolib/data.py +++ b/kosmorrolib/data.py @@ -25,30 +25,10 @@ from numpy import pi, arcsin from skyfield.api import Topos, Time from skyfield.vectorlib import VectorSum as SkfPlanet -from .core import get_skf_objects, get_timescale +from .core import get_skf_objects +from .enum import MoonPhaseType, EventType from .i18n import _ -MOON_PHASES = { - 'NEW_MOON': _('New Moon'), - 'WAXING_CRESCENT': _('Waxing crescent'), - 'FIRST_QUARTER': _('First Quarter'), - 'WAXING_GIBBOUS': _('Waxing gibbous'), - 'FULL_MOON': _('Full Moon'), - 'WANING_GIBBOUS': _('Waning gibbous'), - 'LAST_QUARTER': _('Last Quarter'), - 'WANING_CRESCENT': _('Waning crescent'), - 'UNKNOWN': _('Unavailable') -} - -EVENTS = { - 'OPPOSITION': {'message': _('%s is in opposition')}, - 'CONJUNCTION': {'message': _('%s and %s are in conjunction')}, - 'OCCULTATION': {'message': _('%s occults %s')}, - 'MAXIMAL_ELONGATION': {'message': _("%s's largest elongation")}, - 'MOON_PERIGEE': {'message': _("%s is at its perigee")}, - 'MOON_APOGEE': {'message': _("%s is at its apogee")}, -} - class Serializable(ABC): @abstractmethod @@ -57,40 +37,27 @@ class Serializable(ABC): class MoonPhase(Serializable): - def __init__(self, identifier: str, time: datetime = None, next_phase_date: datetime = None): - if identifier not in MOON_PHASES.keys(): - raise ValueError('identifier parameter must be one of %s (got %s)' % (', '.join(MOON_PHASES.keys()), - identifier)) - - self.identifier = identifier + def __init__(self, phase_type: MoonPhaseType, time: datetime = None, next_phase_date: datetime = None): + self.phase_type = phase_type self.time = time self.next_phase_date = next_phase_date - def get_phase(self): - return MOON_PHASES[self.identifier] - - def get_next_phase_name(self): - next_identifier = self.get_next_phase() - - return MOON_PHASES[next_identifier] - def get_next_phase(self): - if self.identifier == 'NEW_MOON' or self.identifier == 'WAXING_CRESCENT': - next_identifier = 'FIRST_QUARTER' - elif self.identifier == 'FIRST_QUARTER' or self.identifier == 'WAXING_GIBBOUS': - next_identifier = 'FULL_MOON' - elif self.identifier == 'FULL_MOON' or self.identifier == 'WANING_GIBBOUS': - next_identifier = 'LAST_QUARTER' - else: - next_identifier = 'NEW_MOON' - return next_identifier + if self.phase_type in [MoonPhaseType.NEW_MOON, MoonPhaseType.WAXING_CRESCENT]: + return MoonPhaseType.FIRST_QUARTER + if self.phase_type in [MoonPhaseType.FIRST_QUARTER, MoonPhaseType.WAXING_GIBBOUS]: + return MoonPhaseType.FULL_MOON + if self.phase_type in [MoonPhaseType.FULL_MOON, MoonPhaseType.WANING_GIBBOUS]: + return MoonPhaseType.LAST_QUARTER + + return MoonPhaseType.NEW_MOON def serialize(self) -> dict: return { - 'phase': self.identifier, + 'phase': self.phase_type.name, 'time': self.time.isoformat() if self.time is not None else None, 'next': { - 'phase': self.get_next_phase(), + 'phase': self.get_next_phase().name, 'time': self.next_phase_date.isoformat() } } @@ -168,13 +135,8 @@ class Satellite(Object): class Event(Serializable): - def __init__(self, event_type: str, objects: [Object], start_time: datetime, + def __init__(self, event_type: EventType, objects: [Object], start_time: datetime, end_time: Union[datetime, None] = None, details: str = None): - if event_type not in EVENTS.keys(): - accepted_types = ', '.join(EVENTS.keys()) - raise ValueError('event_type parameter must be one of the following: %s (got %s)' % (accepted_types, - event_type)) - self.event_type = event_type self.objects = objects self.start_time = start_time @@ -189,7 +151,7 @@ class Event(Serializable): self.details) def get_description(self, show_details: bool = True) -> str: - description = EVENTS[self.event_type]['message'] % self._get_objects_name() + description = self.event_type.value % self._get_objects_name() if show_details and self.details is not None: description += ' ({:s})'.format(self.details) return description @@ -203,50 +165,13 @@ class Event(Serializable): def serialize(self) -> dict: return { 'objects': [object.serialize() for object in self.objects], - 'event': self.event_type, + 'event': self.event_type.name, 'starts_at': self.start_time.isoformat(), 'ends_at': self.end_time.isoformat() if self.end_time is not None else None, 'details': self.details } -def skyfield_to_moon_phase(times: [Time], vals: [int], now: Time) -> Union[MoonPhase, None]: - tomorrow = get_timescale().utc(now.utc_datetime().year, now.utc_datetime().month, now.utc_datetime().day + 1) - - phases = list(MOON_PHASES.keys()) - current_phase = None - current_phase_time = None - next_phase_time = None - i = 0 - - if len(times) == 0: - return None - - for i, time in enumerate(times): - if now.utc_iso() <= time.utc_iso(): - if vals[i] in [0, 2, 4, 6]: - if time.utc_datetime() < tomorrow.utc_datetime(): - current_phase_time = time - current_phase = phases[vals[i]] - else: - i -= 1 - current_phase_time = None - current_phase = phases[vals[i]] - else: - current_phase = phases[vals[i]] - - break - - for j in range(i + 1, len(times)): - if vals[j] in [0, 2, 4, 6]: - next_phase_time = times[j] - break - - return MoonPhase(current_phase, - current_phase_time.utc_datetime() if current_phase_time is not None else None, - next_phase_time.utc_datetime() if next_phase_time is not None else None) - - class AsterEphemerides(Serializable): def __init__(self, rise_time: Union[datetime, None], @@ -267,8 +192,6 @@ class AsterEphemerides(Serializable): } -MONTHS = ['JAN', 'FEB', 'MAR', 'APR', 'MAY', 'JUN', 'JUL', 'AUG', 'SEP', 'OCT', 'NOV', 'DEC'] - EARTH = Planet('Earth', 'EARTH') ASTERS = [Star(_('Sun'), 'SUN', radius=696342), diff --git a/kosmorrolib/dumper.py b/kosmorrolib/dumper.py index 9a81f17..e64822d 100644 --- a/kosmorrolib/dumper.py +++ b/kosmorrolib/dumper.py @@ -23,6 +23,7 @@ import os from pathlib import Path from tabulate import tabulate from termcolor import colored + from .data import ASTERS, AsterEphemerides, MoonPhase, Event from .i18n import _, FULL_DATE_FORMAT, SHORT_DATETIME_FORMAT, TIME_FORMAT from .version import VERSION @@ -159,9 +160,9 @@ class TextDumper(Dumper): if moon_phase is None: return _('Moon phase is unavailable for this date.') - current_moon_phase = ' '.join([self.style(_('Moon phase:'), 'strong'), moon_phase.get_phase()]) + current_moon_phase = ' '.join([self.style(_('Moon phase:'), 'strong'), moon_phase.phase_type.value]) new_moon_phase = _('{next_moon_phase} on {next_moon_phase_date} at {next_moon_phase_time}').format( - next_moon_phase=moon_phase.get_next_phase_name(), + next_moon_phase=moon_phase.get_next_phase().value, next_moon_phase_date=moon_phase.next_phase_date.strftime(FULL_DATE_FORMAT), next_moon_phase_time=moon_phase.next_phase_date.strftime(TIME_FORMAT) ) @@ -183,12 +184,9 @@ class _LatexDumper(Dumper): kosmorro_logo_path = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'assets', 'png', 'kosmorro-logo.png') - if self.moon_phase is None: - self.moon_phase = MoonPhase('UNKNOWN') - moon_phase_graphics = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'assets', 'moonphases', 'png', - '.'.join([self.moon_phase.identifier.lower().replace('_', '-'), + '.'.join([self.moon_phase.phase_type.name.lower().replace('_', '-'), 'png'])) document = template @@ -234,7 +232,7 @@ class _LatexDumper(Dumper): .replace('+++GRAPH_LABEL_HOURS+++', _('hours')) \ .replace('+++MOON-PHASE-GRAPHICS+++', moon_phase_graphics) \ .replace('+++CURRENT-MOON-PHASE-TITLE+++', _('Moon phase:')) \ - .replace('+++CURRENT-MOON-PHASE+++', self.moon_phase.get_phase()) \ + .replace('+++CURRENT-MOON-PHASE+++', self.moon_phase.phase_type.value) \ .replace('+++SECTION-EVENTS+++', _('Expected events')) \ .replace('+++EVENTS+++', self._make_events()) diff --git a/kosmorrolib/enum.py b/kosmorrolib/enum.py new file mode 100644 index 0000000..718e8cf --- /dev/null +++ b/kosmorrolib/enum.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python3 + +# Kosmorro - Compute The Next Ephemerides +# Copyright (C) 2019 Jérôme Deuchnord +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +from enum import Enum +from .i18n import _ + + +class MoonPhaseType(Enum): + NEW_MOON = _('New Moon') + WAXING_CRESCENT = _('Waxing crescent') + FIRST_QUARTER = _('First Quarter') + WAXING_GIBBOUS = _('Waxing gibbous') + FULL_MOON = _('Full Moon') + WANING_GIBBOUS = _('Waning gibbous') + LAST_QUARTER = _('Last Quarter') + WANING_CRESCENT = _('Waning crescent') + + +class EventType(Enum): + OPPOSITION = _('%s is in opposition') + CONJUNCTION = _('%s and %s are in conjunction') + OCCULTATION = _('%s occults %s') + MAXIMAL_ELONGATION = _("%s's largest elongation") + MOON_PERIGEE = _("%s is at its perigee") + MOON_APOGEE = _("%s is at its apogee") diff --git a/kosmorrolib/ephemerides.py b/kosmorrolib/ephemerides.py index 33642a7..b8dd213 100644 --- a/kosmorrolib/ephemerides.py +++ b/kosmorrolib/ephemerides.py @@ -17,20 +17,59 @@ # along with this program. If not, see . import datetime +from typing import Union from skyfield.searchlib import find_discrete, find_maxima from skyfield.timelib import Time from skyfield.constants import tau from skyfield.errors import EphemerisRangeError -from .data import Position, AsterEphemerides, MoonPhase, Object, ASTERS, skyfield_to_moon_phase +from .data import Position, AsterEphemerides, MoonPhase, Object, ASTERS from .dateutil import translate_to_timezone from .core import get_skf_objects, get_timescale, get_iau2000b +from .enum import MoonPhaseType from .exceptions import OutOfRangeDateError RISEN_ANGLE = -0.8333 +def _get_skyfield_to_moon_phase(times: [Time], vals: [int], now: Time) -> Union[MoonPhase, None]: + tomorrow = get_timescale().utc(now.utc_datetime().year, now.utc_datetime().month, now.utc_datetime().day + 1) + + phases = list(MoonPhaseType) + current_phase = None + current_phase_time = None + next_phase_time = None + i = 0 + + if len(times) == 0: + return None + + for i, time in enumerate(times): + if now.utc_iso() <= time.utc_iso(): + if vals[i] in [0, 2, 4, 6]: + if time.utc_datetime() < tomorrow.utc_datetime(): + current_phase_time = time + current_phase = phases[vals[i]] + else: + i -= 1 + current_phase_time = None + current_phase = phases[vals[i]] + else: + current_phase = phases[vals[i]] + + break + + for j in range(i + 1, len(times)): + if vals[j] in [0, 2, 4, 6]: + next_phase_time = times[j] + break + + return MoonPhase(current_phase, + current_phase_time.utc_datetime() if current_phase_time is not None else None, + next_phase_time.utc_datetime() if next_phase_time is not None else None) + + def get_moon_phase(compute_date: datetime.date, timezone: int = 0) -> MoonPhase: earth = get_skf_objects()['earth'] moon = get_skf_objects()['moon'] @@ -60,7 +99,7 @@ def get_moon_phase(compute_date: datetime.date, timezone: int = 0) -> MoonPhase: raise OutOfRangeDateError(start, end) - return skyfield_to_moon_phase(times, phase, today) + return _get_skyfield_to_moon_phase(times, phase, today) def get_ephemerides(date: datetime.date, position: Position, timezone: int = 0) -> [AsterEphemerides]: diff --git a/kosmorrolib/events.py b/kosmorrolib/events.py index 8e7f19b..9bbd23f 100644 --- a/kosmorrolib/events.py +++ b/kosmorrolib/events.py @@ -25,6 +25,7 @@ from numpy import pi from .data import Event, Star, Planet, ASTERS from .dateutil import translate_to_timezone +from .enum import EventType from .exceptions import OutOfRangeDateError from .core import get_timescale, get_skf_objects, flatten_list @@ -68,10 +69,10 @@ def _search_conjunction(start_time: Time, end_time: Time, timezone: int) -> [Eve aster2] if aster1_pos.distance().km < aster2_pos.distance().km else [aster2, aster1] - conjunctions.append(Event('OCCULTATION', occulting_aster, + conjunctions.append(Event(EventType.OCCULTATION, occulting_aster, translate_to_timezone(time.utc_datetime(), timezone))) else: - conjunctions.append(Event('CONJUNCTION', [aster1, aster2], + conjunctions.append(Event(EventType.CONJUNCTION, [aster1, aster2], translate_to_timezone(time.utc_datetime(), timezone))) computed.append(aster1) @@ -101,7 +102,7 @@ def _search_oppositions(start_time: Time, end_time: Time, timezone: int) -> [Eve times, _ = find_discrete(start_time, end_time, is_oppositing) for time in times: - events.append(Event('OPPOSITION', [aster], translate_to_timezone(time.utc_datetime(), timezone))) + events.append(Event(EventType.OPPOSITION, [aster], translate_to_timezone(time.utc_datetime(), timezone))) return events @@ -129,7 +130,9 @@ def _search_maximal_elongations(start_time: Time, end_time: Time, timezone: int) for i, time in enumerate(times): elongation = elongations[i] - events.append(Event('MAXIMAL_ELONGATION', [aster], translate_to_timezone(time.utc_datetime(), timezone), + events.append(Event(EventType.MAXIMAL_ELONGATION, + [aster], + translate_to_timezone(time.utc_datetime(), timezone), details='{:.3n}°'.format(elongation))) return events @@ -157,7 +160,7 @@ def _search_moon_apogee(start_time: Time, end_time: Time, timezone: int) -> [Eve times, _ = find_maxima(start_time, end_time, f=_get_moon_distance(), epsilon=1./24/60) for time in times: - events.append(Event('MOON_APOGEE', [moon], translate_to_timezone(time.utc_datetime(), timezone))) + events.append(Event(EventType.MOON_APOGEE, [moon], translate_to_timezone(time.utc_datetime(), timezone))) return events @@ -169,7 +172,7 @@ def _search_moon_perigee(start_time: Time, end_time: Time, timezone: int) -> [Ev times, _ = find_minima(start_time, end_time, f=_get_moon_distance(), epsilon=1./24/60) for time in times: - events.append(Event('MOON_PERIGEE', [moon], translate_to_timezone(time.utc_datetime(), timezone))) + events.append(Event(EventType.MOON_PERIGEE, [moon], translate_to_timezone(time.utc_datetime(), timezone))) return events diff --git a/kosmorrolib/locales/messages.pot b/kosmorrolib/locales/messages.pot index 3481155..8858aac 100644 --- a/kosmorrolib/locales/messages.pot +++ b/kosmorrolib/locales/messages.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: kosmorro 0.8.1\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2020-12-01 12:01+0100\n" +"POT-Creation-Date: 2020-12-02 10:22+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -27,182 +27,116 @@ msgid "" "offset format." msgstr "" -#: kosmorrolib/data.py:32 -msgid "New Moon" -msgstr "" - -#: kosmorrolib/data.py:33 -msgid "Waxing crescent" -msgstr "" - -#: kosmorrolib/data.py:34 -msgid "First Quarter" -msgstr "" - -#: kosmorrolib/data.py:35 -msgid "Waxing gibbous" -msgstr "" - -#: kosmorrolib/data.py:36 -msgid "Full Moon" -msgstr "" - -#: kosmorrolib/data.py:37 -msgid "Waning gibbous" -msgstr "" - -#: kosmorrolib/data.py:38 -msgid "Last Quarter" -msgstr "" - -#: kosmorrolib/data.py:39 -msgid "Waning crescent" -msgstr "" - -#: kosmorrolib/data.py:40 -msgid "Unavailable" -msgstr "" - -#: kosmorrolib/data.py:44 -#, python-format -msgid "%s is in opposition" -msgstr "" - -#: kosmorrolib/data.py:45 -#, python-format -msgid "%s and %s are in conjunction" -msgstr "" - -#: kosmorrolib/data.py:46 -#, python-format -msgid "%s occults %s" -msgstr "" - -#: kosmorrolib/data.py:47 -#, python-format -msgid "%s's largest elongation" -msgstr "" - -#: kosmorrolib/data.py:48 -#, python-format -msgid "%s is at its perigee" -msgstr "" - -#: kosmorrolib/data.py:49 -#, python-format -msgid "%s is at its apogee" -msgstr "" - -#: kosmorrolib/data.py:274 +#: kosmorrolib/data.py:197 msgid "Sun" msgstr "" -#: kosmorrolib/data.py:275 +#: kosmorrolib/data.py:198 msgid "Moon" msgstr "" -#: kosmorrolib/data.py:276 +#: kosmorrolib/data.py:199 msgid "Mercury" msgstr "" -#: kosmorrolib/data.py:277 +#: kosmorrolib/data.py:200 msgid "Venus" msgstr "" -#: kosmorrolib/data.py:278 +#: kosmorrolib/data.py:201 msgid "Mars" msgstr "" -#: kosmorrolib/data.py:279 +#: kosmorrolib/data.py:202 msgid "Jupiter" msgstr "" -#: kosmorrolib/data.py:280 +#: kosmorrolib/data.py:203 msgid "Saturn" msgstr "" -#: kosmorrolib/data.py:281 +#: kosmorrolib/data.py:204 msgid "Uranus" msgstr "" -#: kosmorrolib/data.py:282 +#: kosmorrolib/data.py:205 msgid "Neptune" msgstr "" -#: kosmorrolib/data.py:283 +#: kosmorrolib/data.py:206 msgid "Pluto" msgstr "" -#: kosmorrolib/dumper.py:87 +#: kosmorrolib/dumper.py:88 msgid "Expected events:" msgstr "" -#: kosmorrolib/dumper.py:91 +#: kosmorrolib/dumper.py:92 msgid "Note: All the hours are given in UTC." msgstr "" -#: kosmorrolib/dumper.py:96 +#: kosmorrolib/dumper.py:97 msgid "Note: All the hours are given in the UTC{offset} timezone." msgstr "" -#: kosmorrolib/dumper.py:142 kosmorrolib/dumper.py:229 +#: kosmorrolib/dumper.py:143 kosmorrolib/dumper.py:227 msgid "Object" msgstr "" -#: kosmorrolib/dumper.py:143 kosmorrolib/dumper.py:230 +#: kosmorrolib/dumper.py:144 kosmorrolib/dumper.py:228 msgid "Rise time" msgstr "" -#: kosmorrolib/dumper.py:144 kosmorrolib/dumper.py:231 +#: kosmorrolib/dumper.py:145 kosmorrolib/dumper.py:229 msgid "Culmination time" msgstr "" -#: kosmorrolib/dumper.py:145 kosmorrolib/dumper.py:232 +#: kosmorrolib/dumper.py:146 kosmorrolib/dumper.py:230 msgid "Set time" msgstr "" -#: kosmorrolib/dumper.py:160 +#: kosmorrolib/dumper.py:161 msgid "Moon phase is unavailable for this date." msgstr "" -#: kosmorrolib/dumper.py:162 kosmorrolib/dumper.py:236 +#: kosmorrolib/dumper.py:163 kosmorrolib/dumper.py:234 msgid "Moon phase:" msgstr "" -#: kosmorrolib/dumper.py:163 +#: kosmorrolib/dumper.py:164 msgid "{next_moon_phase} on {next_moon_phase_date} at {next_moon_phase_time}" msgstr "" -#: kosmorrolib/dumper.py:216 +#: kosmorrolib/dumper.py:214 msgid "A Summary of your Sky" msgstr "" -#: kosmorrolib/dumper.py:220 +#: kosmorrolib/dumper.py:218 msgid "" "This document summarizes the ephemerides and the events of {date}. It " "aims to help you to prepare your observation session. All the hours are " "given in {timezone}." msgstr "" -#: kosmorrolib/dumper.py:226 +#: kosmorrolib/dumper.py:224 msgid "" "Don't forget to check the weather forecast before you go out with your " "equipment." msgstr "" -#: kosmorrolib/dumper.py:228 +#: kosmorrolib/dumper.py:226 msgid "Ephemerides of the day" msgstr "" -#: kosmorrolib/dumper.py:234 +#: kosmorrolib/dumper.py:232 msgid "hours" msgstr "" -#: kosmorrolib/dumper.py:238 +#: kosmorrolib/dumper.py:236 msgid "Expected events" msgstr "" -#: kosmorrolib/dumper.py:352 +#: kosmorrolib/dumper.py:350 msgid "" "Building PDFs was not possible, because some dependencies are not " "installed.\n" @@ -210,6 +144,68 @@ msgid "" "information." msgstr "" +#: kosmorrolib/enum.py:24 +msgid "New Moon" +msgstr "" + +#: kosmorrolib/enum.py:25 +msgid "Waxing crescent" +msgstr "" + +#: kosmorrolib/enum.py:26 +msgid "First Quarter" +msgstr "" + +#: kosmorrolib/enum.py:27 +msgid "Waxing gibbous" +msgstr "" + +#: kosmorrolib/enum.py:28 +msgid "Full Moon" +msgstr "" + +#: kosmorrolib/enum.py:29 +msgid "Waning gibbous" +msgstr "" + +#: kosmorrolib/enum.py:30 +msgid "Last Quarter" +msgstr "" + +#: kosmorrolib/enum.py:31 +msgid "Waning crescent" +msgstr "" + +#: kosmorrolib/enum.py:35 +#, python-format +msgid "%s is in opposition" +msgstr "" + +#: kosmorrolib/enum.py:36 +#, python-format +msgid "%s and %s are in conjunction" +msgstr "" + +#: kosmorrolib/enum.py:37 +#, python-format +msgid "%s occults %s" +msgstr "" + +#: kosmorrolib/enum.py:38 +#, python-format +msgid "%s's largest elongation" +msgstr "" + +#: kosmorrolib/enum.py:39 +#, python-format +msgid "%s is at its perigee" +msgstr "" + +#: kosmorrolib/enum.py:40 +#, python-format +msgid "%s is at its apogee" +msgstr "" + #: kosmorrolib/exceptions.py:34 msgid "The date must be between {minimum_date} and {maximum_date}" msgstr "" diff --git a/test/dumper.py b/test/dumper.py index a6d553e..e37b572 100644 --- a/test/dumper.py +++ b/test/dumper.py @@ -3,6 +3,7 @@ from datetime import date, datetime from kosmorrolib.data import AsterEphemerides, Planet, MoonPhase, Event from kosmorrolib.dumper import JsonDumper, TextDumper, _LatexDumper +from kosmorrolib.enum import MoonPhaseType, EventType class DumperTestCase(unittest.TestCase): @@ -283,14 +284,14 @@ class DumperTestCase(unittest.TestCase): @staticmethod def _get_moon_phase(): - return MoonPhase('FULL_MOON', datetime(2019, 10, 14), datetime(2019, 10, 21)) + return MoonPhase(MoonPhaseType.FULL_MOON, datetime(2019, 10, 14), datetime(2019, 10, 21)) @staticmethod def _get_events(): - return [Event('OPPOSITION', + return [Event(EventType.OPPOSITION, [Planet('Mars', 'MARS')], datetime(2019, 10, 14, 23, 00)), - Event('MAXIMAL_ELONGATION', + Event(EventType.MAXIMAL_ELONGATION, [Planet('Venus', 'VENUS')], datetime(2019, 10, 14, 12, 00), details='42.0°'), ] diff --git a/test/ephemerides.py b/test/ephemerides.py index 278ef57..a854ef7 100644 --- a/test/ephemerides.py +++ b/test/ephemerides.py @@ -1,7 +1,10 @@ import unittest + from .testutils import expect_assertions from kosmorrolib import ephemerides from kosmorrolib.data import EARTH, Position, MoonPhase +from kosmorrolib.enum import MoonPhaseType + from datetime import date from kosmorrolib.exceptions import OutOfRangeDateError @@ -29,84 +32,84 @@ class EphemeridesTestCase(unittest.TestCase): def test_moon_phase_new_moon(self): phase = ephemerides.get_moon_phase(date(2019, 11, 25)) - self.assertEqual('WANING_CRESCENT', phase.identifier) + self.assertEqual(MoonPhaseType.WANING_CRESCENT, phase.phase_type) self.assertIsNone(phase.time) self.assertRegexpMatches(phase.next_phase_date.isoformat(), '^2019-11-26T') phase = ephemerides.get_moon_phase(date(2019, 11, 26)) - self.assertEqual('NEW_MOON', phase.identifier) + self.assertEqual(MoonPhaseType.NEW_MOON, phase.phase_type) self.assertRegexpMatches(phase.next_phase_date.isoformat(), '^2019-12-04T') phase = ephemerides.get_moon_phase(date(2019, 11, 27)) - self.assertEqual('WAXING_CRESCENT', phase.identifier) + self.assertEqual(MoonPhaseType.WAXING_CRESCENT, phase.phase_type) self.assertIsNone(phase.time) self.assertRegexpMatches(phase.next_phase_date.isoformat(), '^2019-12-04T') def test_moon_phase_first_crescent(self): phase = ephemerides.get_moon_phase(date(2019, 11, 3)) - self.assertEqual('WAXING_CRESCENT', phase.identifier) + self.assertEqual(MoonPhaseType.WAXING_CRESCENT, phase.phase_type) self.assertIsNone(phase.time) self.assertRegexpMatches(phase.next_phase_date.isoformat(), '^2019-11-04T') phase = ephemerides.get_moon_phase(date(2019, 11, 4)) - self.assertEqual('FIRST_QUARTER', phase.identifier) + self.assertEqual(MoonPhaseType.FIRST_QUARTER, phase.phase_type) self.assertRegexpMatches(phase.next_phase_date.isoformat(), '^2019-11-12T') phase = ephemerides.get_moon_phase(date(2019, 11, 5)) - self.assertEqual('WAXING_GIBBOUS', phase.identifier) + self.assertEqual(MoonPhaseType.WAXING_GIBBOUS, phase.phase_type) self.assertIsNone(phase.time) self.assertRegexpMatches(phase.next_phase_date.isoformat(), '^2019-11-12T') def test_moon_phase_full_moon(self): phase = ephemerides.get_moon_phase(date(2019, 11, 11)) - self.assertEqual('WAXING_GIBBOUS', phase.identifier) + self.assertEqual(MoonPhaseType.WAXING_GIBBOUS, phase.phase_type) self.assertIsNone(phase.time) self.assertRegexpMatches(phase.next_phase_date.isoformat(), '^2019-11-12T') phase = ephemerides.get_moon_phase(date(2019, 11, 12)) - self.assertEqual('FULL_MOON', phase.identifier) + self.assertEqual(MoonPhaseType.FULL_MOON, phase.phase_type) self.assertRegexpMatches(phase.next_phase_date.isoformat(), '^2019-11-19T') phase = ephemerides.get_moon_phase(date(2019, 11, 13)) - self.assertEqual('WANING_GIBBOUS', phase.identifier) + self.assertEqual(MoonPhaseType.WANING_GIBBOUS, phase.phase_type) self.assertIsNone(phase.time) self.assertRegexpMatches(phase.next_phase_date.isoformat(), '^2019-11-19T') def test_moon_phase_last_quarter(self): phase = ephemerides.get_moon_phase(date(2019, 11, 18)) - self.assertEqual('WANING_GIBBOUS', phase.identifier) + self.assertEqual(MoonPhaseType.WANING_GIBBOUS, phase.phase_type) self.assertIsNone(phase.time) self.assertRegexpMatches(phase.next_phase_date.isoformat(), '^2019-11-19T') phase = ephemerides.get_moon_phase(date(2019, 11, 19)) - self.assertEqual('LAST_QUARTER', phase.identifier) + self.assertEqual(MoonPhaseType.LAST_QUARTER, phase.phase_type) self.assertRegexpMatches(phase.next_phase_date.isoformat(), '^2019-11-26T') phase = ephemerides.get_moon_phase(date(2019, 11, 20)) - self.assertEqual('WANING_CRESCENT', phase.identifier) + self.assertEqual(MoonPhaseType.WANING_CRESCENT, phase.phase_type) self.assertIsNone(phase.time) self.assertRegexpMatches(phase.next_phase_date.isoformat(), '^2019-11-26T') def test_moon_phase_prediction(self): - phase = MoonPhase('NEW_MOON', None, None) - self.assertEqual('First Quarter', phase.get_next_phase_name()) - phase = MoonPhase('WAXING_CRESCENT', None, None) - self.assertEqual('First Quarter', phase.get_next_phase_name()) - - phase = MoonPhase('FIRST_QUARTER', None, None) - self.assertEqual('Full Moon', phase.get_next_phase_name()) - phase = MoonPhase('WAXING_GIBBOUS', None, None) - self.assertEqual('Full Moon', phase.get_next_phase_name()) - - phase = MoonPhase('FULL_MOON', None, None) - self.assertEqual('Last Quarter', phase.get_next_phase_name()) - phase = MoonPhase('WANING_GIBBOUS', None, None) - self.assertEqual('Last Quarter', phase.get_next_phase_name()) - - phase = MoonPhase('LAST_QUARTER', None, None) - self.assertEqual('New Moon', phase.get_next_phase_name()) - phase = MoonPhase('WANING_CRESCENT', None, None) - self.assertEqual('New Moon', phase.get_next_phase_name()) + phase = MoonPhase(MoonPhaseType.NEW_MOON, None, None) + self.assertEqual(MoonPhaseType.FIRST_QUARTER, phase.get_next_phase()) + phase = MoonPhase(MoonPhaseType.WAXING_CRESCENT, None, None) + self.assertEqual(MoonPhaseType.FIRST_QUARTER, phase.get_next_phase()) + + phase = MoonPhase(MoonPhaseType.FIRST_QUARTER, None, None) + self.assertEqual(MoonPhaseType.FULL_MOON, phase.get_next_phase()) + phase = MoonPhase(MoonPhaseType.WAXING_GIBBOUS, None, None) + self.assertEqual(MoonPhaseType.FULL_MOON, phase.get_next_phase()) + + phase = MoonPhase(MoonPhaseType.FULL_MOON, None, None) + self.assertEqual(MoonPhaseType.LAST_QUARTER, phase.get_next_phase()) + phase = MoonPhase(MoonPhaseType.WANING_GIBBOUS, None, None) + self.assertEqual(MoonPhaseType.LAST_QUARTER, phase.get_next_phase()) + + phase = MoonPhase(MoonPhaseType.LAST_QUARTER, None, None) + self.assertEqual(MoonPhaseType.NEW_MOON, phase.get_next_phase()) + phase = MoonPhase(MoonPhaseType.WANING_CRESCENT, None, None) + self.assertEqual(MoonPhaseType.NEW_MOON, phase.get_next_phase()) def test_get_ephemerides_raises_exception_on_out_of_date_range(self): with self.assertRaises(OutOfRangeDateError): diff --git a/test/events.py b/test/events.py index 9a4ce9c..de397bd 100644 --- a/test/events.py +++ b/test/events.py @@ -1,12 +1,11 @@ import unittest from datetime import date, datetime -from json import dumps +from unittest_data_provider import data_provider from kosmorrolib import events from kosmorrolib.data import Event, ASTERS -from kosmorrolib.core import get_timescale -from unittest_data_provider import data_provider +from kosmorrolib.enum import EventType from kosmorrolib.exceptions import OutOfRangeDateError @@ -14,42 +13,38 @@ class EventTestCase(unittest.TestCase): def setUp(self) -> None: self.maxDiff = None - def test_event_only_accepts_valid_values(self): - with self.assertRaises(ValueError): - Event('SUPERNOVA', None, get_timescale().now()) - expected_events_provider = lambda: ( (date(2020, 2, 7), []), - (date(2020, 10, 13), [Event('OPPOSITION', [ASTERS[4]], datetime(2020, 10, 13, 23, 25))]), + (date(2020, 10, 13), [Event(EventType.OPPOSITION, [ASTERS[4]], datetime(2020, 10, 13, 23, 25))]), - (date(2022, 12, 8), [Event('CONJUNCTION', [ASTERS[1], ASTERS[4]], datetime(2022, 12, 8, 4, 18)), - Event('OPPOSITION', [ASTERS[4]], datetime(2022, 12, 8, 5, 41))]), + (date(2022, 12, 8), [Event(EventType.CONJUNCTION, [ASTERS[1], ASTERS[4]], datetime(2022, 12, 8, 4, 18)), + Event(EventType.OPPOSITION, [ASTERS[4]], datetime(2022, 12, 8, 5, 41))]), - (date(2025, 1, 16), [Event('OPPOSITION', [ASTERS[4]], datetime(2025, 1, 16, 2, 38))]), + (date(2025, 1, 16), [Event(EventType.OPPOSITION, [ASTERS[4]], datetime(2025, 1, 16, 2, 38))]), - (date(2027, 2, 19), [Event('MOON_PERIGEE', [ASTERS[1]], datetime(2027, 2, 19, 7, 38)), - Event('OPPOSITION', [ASTERS[4]], datetime(2027, 2, 19, 15, 50))]), + (date(2027, 2, 19), [Event(EventType.MOON_PERIGEE, [ASTERS[1]], datetime(2027, 2, 19, 7, 38)), + Event(EventType.OPPOSITION, [ASTERS[4]], datetime(2027, 2, 19, 15, 50))]), - (date(2020, 1, 2), [Event('MOON_APOGEE', [ASTERS[1]], datetime(2020, 1, 2, 1, 32)), - Event('CONJUNCTION', [ASTERS[2], ASTERS[5]], datetime(2020, 1, 2, 16, 41))]), + (date(2020, 1, 2), [Event(EventType.MOON_APOGEE, [ASTERS[1]], datetime(2020, 1, 2, 1, 32)), + Event(EventType.CONJUNCTION, [ASTERS[2], ASTERS[5]], datetime(2020, 1, 2, 16, 41))]), - (date(2020, 1, 12), [Event('CONJUNCTION', [ASTERS[2], ASTERS[6]], datetime(2020, 1, 12, 9, 51)), - Event('CONJUNCTION', [ASTERS[2], ASTERS[9]], datetime(2020, 1, 12, 10, 13)), - Event('CONJUNCTION', [ASTERS[6], ASTERS[9]], datetime(2020, 1, 12, 16, 57))]), + (date(2020, 1, 12), [Event(EventType.CONJUNCTION, [ASTERS[2], ASTERS[6]], datetime(2020, 1, 12, 9, 51)), + Event(EventType.CONJUNCTION, [ASTERS[2], ASTERS[9]], datetime(2020, 1, 12, 10, 13)), + Event(EventType.CONJUNCTION, [ASTERS[6], ASTERS[9]], datetime(2020, 1, 12, 16, 57))]), - (date(2020, 2, 10), [Event('MAXIMAL_ELONGATION', [ASTERS[2]], datetime(2020, 2, 10, 13, 46), details='18.2°'), - Event('MOON_PERIGEE', [ASTERS[1]], datetime(2020, 2, 10, 20, 34))]), + (date(2020, 2, 10), [Event(EventType.MAXIMAL_ELONGATION, [ASTERS[2]], datetime(2020, 2, 10, 13, 46), details='18.2°'), + Event(EventType.MOON_PERIGEE, [ASTERS[1]], datetime(2020, 2, 10, 20, 34))]), - (date(2020, 3, 24), [Event('MAXIMAL_ELONGATION', [ASTERS[2]], datetime(2020, 3, 24, 1, 56), details='27.8°'), - Event('MOON_APOGEE', [ASTERS[1]], datetime(2020, 3, 24, 15, 39)), - Event('MAXIMAL_ELONGATION', [ASTERS[3]], datetime(2020, 3, 24, 21, 58), details='46.1°')]), + (date(2020, 3, 24), [Event(EventType.MAXIMAL_ELONGATION, [ASTERS[2]], datetime(2020, 3, 24, 1, 56), details='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°')]), - (date(2005, 6, 16), [Event('OCCULTATION', [ASTERS[1], ASTERS[5]], datetime(2005, 6, 16, 6, 31))]), + (date(2005, 6, 16), [Event(EventType.OCCULTATION, [ASTERS[1], ASTERS[5]], datetime(2005, 6, 16, 6, 31))]), - (date(2020, 4, 7), [Event('MOON_PERIGEE', [ASTERS[1]], datetime(2020, 4, 7, 18, 14))]), + (date(2020, 4, 7), [Event(EventType.MOON_PERIGEE, [ASTERS[1]], datetime(2020, 4, 7, 18, 14))]), - (date(2020, 1, 29), [Event('MOON_APOGEE', [ASTERS[1]], datetime(2020, 1, 29, 21, 32))]) + (date(2020, 1, 29), [Event(EventType.MOON_APOGEE, [ASTERS[1]], datetime(2020, 1, 29, 21, 32))]) ) @data_provider(expected_events_provider)