BREAKING CHANGE: some methodes in Event and MoonPhase have been dropped in favor of `enum.Enum`'s `name` and `value` properties.pull/130/head
| @@ -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 | |||
| @@ -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; \ | |||
| @@ -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), | |||
| @@ -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()) | |||
| @@ -0,0 +1,40 @@ | |||
| #!/usr/bin/env python3 | |||
| # Kosmorro - Compute The Next Ephemerides | |||
| # Copyright (C) 2019 Jérôme Deuchnord <jerome@deuchnord.fr> | |||
| # | |||
| # 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 <https://www.gnu.org/licenses/>. | |||
| 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") | |||
| @@ -17,20 +17,59 @@ | |||
| # along with this program. If not, see <https://www.gnu.org/licenses/>. | |||
| 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]: | |||
| @@ -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 | |||
| @@ -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 <EMAIL@ADDRESS>\n" | |||
| "Language-Team: LANGUAGE <LL@li.org>\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 "" | |||
| @@ -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°'), | |||
| ] | |||
| @@ -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): | |||
| @@ -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) | |||