@@ -146,7 +146,8 @@ disable=print-statement, | |||
too-many-branches, | |||
too-few-public-methods, | |||
protected-access, | |||
unnecessary-comprehension | |||
unnecessary-comprehension, | |||
too-many-arguments | |||
# Enable the message, report, category or checker with the given id(s). You can | |||
# either give multiple identifier separated by comma (,) or put this option | |||
@@ -79,6 +79,8 @@ assertSuccess "$PIP_BIN install dist/kosmorro-$VERSION.tar.gz" "CI" | |||
assertSuccess kosmorro | |||
assertSuccess "kosmorro --latitude=50.5876 --longitude=3.0624" | |||
assertSuccess "kosmorro --latitude=50.5876 --longitude=3.0624 -d 27 -m 1 -y 2020" | |||
assertSuccess "kosmorro --latitude=50.5876 --longitude=3.0624 -d 27 -m 1 -y 2020 --timezone=1" | |||
assertSuccess "kosmorro --latitude=50.5876 --longitude=3.0624 -d 27 -m 1 -y 2020 --timezone=-1" | |||
assertSuccess "kosmorro --latitude=50.5876 --longitude=3.0624 -d 27 -m 1 -y 2020 --format=json" | |||
assertFailure "kosmorro --latitude=50.5876 --longitude=3.0624 -d 27 -m 1 -y 2020 --format=pdf" | |||
# Missing dependencies, should fail | |||
@@ -95,7 +95,9 @@ def skyfield_to_moon_phase(times: [Time], vals: [int], now: Time) -> Union[MoonP | |||
next_phase_time = times[j] | |||
break | |||
return MoonPhase(current_phase, current_phase_time, next_phase_time) | |||
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 flatten_list(the_list: list): | |||
@@ -18,9 +18,9 @@ | |||
from abc import ABC, abstractmethod | |||
from typing import Union | |||
from datetime import datetime | |||
from skyfield.api import Topos | |||
from skyfield.timelib import Time | |||
from .i18n import _ | |||
@@ -42,7 +42,7 @@ EVENTS = { | |||
class MoonPhase: | |||
def __init__(self, identifier: str, time: Union[Time, None], next_phase_date: Union[Time, None]): | |||
def __init__(self, identifier: str, time: Union[datetime, None], next_phase_date: Union[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)) | |||
@@ -87,9 +87,9 @@ class Position: | |||
class AsterEphemerides: | |||
def __init__(self, | |||
rise_time: Union[Time, None], | |||
culmination_time: Union[Time, None], | |||
set_time: Union[Time, None]): | |||
rise_time: Union[datetime, None], | |||
culmination_time: Union[datetime, None], | |||
set_time: Union[datetime, None]): | |||
self.rise_time = rise_time | |||
self.culmination_time = culmination_time | |||
self.set_time = set_time | |||
@@ -141,7 +141,8 @@ class Satellite(Object): | |||
class Event: | |||
def __init__(self, event_type: str, objects: [Object], start_time: Time, end_time: Union[Time, None] = None): | |||
def __init__(self, event_type: str, objects: [Object], start_time: datetime, | |||
end_time: Union[datetime, None] = None): | |||
if event_type not in EVENTS.keys(): | |||
raise ValueError('event_type parameter must be one of the following: %s (got %s)' % ( | |||
', '.join(EVENTS.keys()), | |||
@@ -21,7 +21,6 @@ import datetime | |||
import json | |||
import os | |||
from tabulate import tabulate | |||
from skyfield.timelib import Time | |||
from numpy import int64 | |||
from termcolor import colored | |||
from .data import Object, AsterEphemerides, MoonPhase, Event | |||
@@ -35,17 +34,52 @@ except ImportError: | |||
FULL_DATE_FORMAT = _('{day_of_week} {month} {day_number}, {year}').format(day_of_week='%A', month='%B', | |||
day_number='%d', year='%Y') | |||
SHORT_DATETIME_FORMAT = _('{month} {day_number}, {hours}:{minutes}').format(month='%b', day_number='%d', | |||
hours='%H', minutes='%M') | |||
TIME_FORMAT = _('{hours}:{minutes}').format(hours='%H', minutes='%M') | |||
class Dumper(ABC): | |||
def __init__(self, ephemeris: dict, events: [Event], date: datetime.date = datetime.date.today(), | |||
def __init__(self, ephemeris: dict, events: [Event], date: datetime.date = datetime.date.today(), timezone: int = 0, | |||
with_colors: bool = True): | |||
self.ephemeris = ephemeris | |||
self.events = events | |||
self.date = date | |||
self.timezone = timezone | |||
self.with_colors = with_colors | |||
if self.timezone != 0: | |||
self._convert_dates_to_timezones() | |||
def _convert_dates_to_timezones(self): | |||
if self.ephemeris['moon_phase'].time is not None: | |||
self.ephemeris['moon_phase'].time = self._datetime_to_timezone(self.ephemeris['moon_phase'].time) | |||
if self.ephemeris['moon_phase'].next_phase_date is not None: | |||
self.ephemeris['moon_phase'].next_phase_date = self._datetime_to_timezone( | |||
self.ephemeris['moon_phase'].next_phase_date) | |||
for aster in self.ephemeris['details']: | |||
if aster.ephemerides.rise_time is not None: | |||
aster.ephemerides.rise_time = self._datetime_to_timezone(aster.ephemerides.rise_time) | |||
if aster.ephemerides.culmination_time is not None: | |||
aster.ephemerides.culmination_time = self._datetime_to_timezone(aster.ephemerides.culmination_time) | |||
if aster.ephemerides.set_time is not None: | |||
aster.ephemerides.set_time = self._datetime_to_timezone(aster.ephemerides.set_time) | |||
for event in self.events: | |||
event.start_time = self._datetime_to_timezone(event.start_time) | |||
if event.end_time is not None: | |||
event.end_time = self._datetime_to_timezone(event.end_time) | |||
def _datetime_to_timezone(self, time: datetime.datetime): | |||
return time.replace(tzinfo=datetime.timezone.utc).astimezone( | |||
tz=datetime.timezone( | |||
datetime.timedelta( | |||
hours=self.timezone | |||
) | |||
) | |||
) | |||
def get_date_as_string(self, capitalized: bool = False) -> str: | |||
date = self.date.strftime(FULL_DATE_FORMAT) | |||
@@ -77,8 +111,8 @@ class JsonDumper(Dumper): | |||
# See https://stackoverflow.com/a/50577730 | |||
if isinstance(obj, int64): | |||
return int(obj) | |||
if isinstance(obj, Time): | |||
return obj.utc_iso() | |||
if isinstance(obj, datetime.datetime): | |||
return obj.isoformat() | |||
if isinstance(obj, Object): | |||
obj = obj.__dict__ | |||
obj.pop('skyfield_name') | |||
@@ -113,7 +147,14 @@ class TextDumper(Dumper): | |||
text.append('\n'.join([self.style(_('Expected events:'), 'h2'), | |||
self.get_events(self.events)])) | |||
text.append(self.style(_('Note: All the hours are given in UTC.'), 'em')) | |||
if self.timezone == 0: | |||
text.append(self.style(_('Note: All the hours are given in UTC.'), 'em')) | |||
else: | |||
tz_offset = str(self.timezone) | |||
if self.timezone > 0: | |||
tz_offset = ''.join(['+', tz_offset]) | |||
text.append(self.style(_('Note: All the hours are given in the UTC{offset} timezone.').format( | |||
offset=tz_offset), 'em')) | |||
return '\n\n'.join(text) | |||
@@ -138,17 +179,21 @@ class TextDumper(Dumper): | |||
name = self.style(aster.name, 'th') | |||
if aster.ephemerides.rise_time is not None: | |||
planet_rise = aster.ephemerides.rise_time.utc_strftime(TIME_FORMAT) | |||
time_fmt = TIME_FORMAT if aster.ephemerides.rise_time.day == self.date.day else SHORT_DATETIME_FORMAT | |||
planet_rise = aster.ephemerides.rise_time.strftime(time_fmt) | |||
else: | |||
planet_rise = '-' | |||
if aster.ephemerides.culmination_time is not None: | |||
planet_culmination = aster.ephemerides.culmination_time.utc_strftime(TIME_FORMAT) | |||
time_fmt = TIME_FORMAT if aster.ephemerides.culmination_time.day == self.date.day \ | |||
else SHORT_DATETIME_FORMAT | |||
planet_culmination = aster.ephemerides.culmination_time.strftime(time_fmt) | |||
else: | |||
planet_culmination = '-' | |||
if aster.ephemerides.set_time is not None: | |||
planet_set = aster.ephemerides.set_time.utc_strftime(TIME_FORMAT) | |||
time_fmt = TIME_FORMAT if aster.ephemerides.set_time.day == self.date.day else SHORT_DATETIME_FORMAT | |||
planet_set = aster.ephemerides.set_time.strftime(time_fmt) | |||
else: | |||
planet_set = '-' | |||
@@ -164,7 +209,8 @@ class TextDumper(Dumper): | |||
data = [] | |||
for event in events: | |||
data.append([self.style(event.start_time.utc_strftime(TIME_FORMAT), 'th'), | |||
time_fmt = TIME_FORMAT if event.start_time.day == self.date.day else SHORT_DATETIME_FORMAT | |||
data.append([self.style(event.start_time.strftime(time_fmt), 'th'), | |||
event.get_description()]) | |||
return tabulate(data, tablefmt='plain', stralign='left') | |||
@@ -173,8 +219,8 @@ class TextDumper(Dumper): | |||
current_moon_phase = ' '.join([self.style(_('Moon phase:'), 'strong'), moon_phase.get_phase()]) | |||
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(), | |||
next_moon_phase_date=moon_phase.next_phase_date.utc_strftime(FULL_DATE_FORMAT), | |||
next_moon_phase_time=moon_phase.next_phase_date.utc_strftime(TIME_FORMAT) | |||
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) | |||
) | |||
return '\n'.join([current_moon_phase, new_moon_phase]) | |||
@@ -214,8 +260,11 @@ class _LatexDumper(Dumper): | |||
.replace('+++INTRODUCTION+++', | |||
'\n\n'.join([ | |||
_("This document summarizes the ephemerides and the events of {date}. " | |||
"It aims to help you to prepare your observation session.").format( | |||
date=self.get_date_as_string()), | |||
"It aims to help you to prepare your observation session. " | |||
"All the hours are given in {timezone}.").format( | |||
date=self.get_date_as_string(), | |||
timezone='UTC+%d timezone' % self.timezone if self.timezone != 0 else 'UTC' | |||
), | |||
_("Don't forget to check the weather forecast before you go out with your material.") | |||
])) \ | |||
.replace('+++SECTION-EPHEMERIDES+++', _('Ephemerides of the day')) \ | |||
@@ -237,17 +286,21 @@ class _LatexDumper(Dumper): | |||
for aster in self.ephemeris['details']: | |||
if aster.ephemerides.rise_time is not None: | |||
aster_rise = aster.ephemerides.rise_time.utc_strftime(TIME_FORMAT) | |||
time_fmt = TIME_FORMAT if aster.ephemerides.rise_time.day == self.date.day else SHORT_DATETIME_FORMAT | |||
aster_rise = aster.ephemerides.rise_time.strftime(time_fmt) | |||
else: | |||
aster_rise = '-' | |||
if aster.ephemerides.culmination_time is not None: | |||
aster_culmination = aster.ephemerides.culmination_time.utc_strftime(TIME_FORMAT) | |||
time_fmt = TIME_FORMAT if aster.ephemerides.culmination_time.day == self.date.day\ | |||
else SHORT_DATETIME_FORMAT | |||
aster_culmination = aster.ephemerides.culmination_time.strftime(time_fmt) | |||
else: | |||
aster_culmination = '-' | |||
if aster.ephemerides.set_time is not None: | |||
aster_set = aster.ephemerides.set_time.utc_strftime(TIME_FORMAT) | |||
time_fmt = TIME_FORMAT if aster.ephemerides.set_time.day == self.date.day else SHORT_DATETIME_FORMAT | |||
aster_set = aster.ephemerides.set_time.strftime(time_fmt) | |||
else: | |||
aster_set = '-' | |||
@@ -262,7 +315,7 @@ class _LatexDumper(Dumper): | |||
latex = [] | |||
for event in self.events: | |||
latex.append(r'\event{%s}{%s}' % (event.start_time.utc_strftime(TIME_FORMAT), | |||
latex.append(r'\event{%s}{%s}' % (event.start_time.strftime(TIME_FORMAT), | |||
event.get_description())) | |||
return ''.join(latex) | |||
@@ -288,9 +341,14 @@ class _LatexDumper(Dumper): | |||
class PdfDumper(Dumper): | |||
def __init__(self, ephemerides, events, date=datetime.datetime.now(), timezone=0, with_colors=True): | |||
super(PdfDumper, self).__init__(ephemerides, events, date=date, timezone=0, with_colors=with_colors) | |||
self.timezone = timezone | |||
def to_string(self): | |||
try: | |||
latex_dumper = _LatexDumper(self.ephemeris, self.events, self.date, self.with_colors) | |||
latex_dumper = _LatexDumper(self.ephemeris, self.events, | |||
date=self.date, timezone=self.timezone, with_colors=self.with_colors) | |||
return self._compile(latex_dumper.to_string()) | |||
except RuntimeError: | |||
raise UnavailableFeatureError(_("Building PDFs was not possible, because some dependencies are not" | |||
@@ -100,6 +100,11 @@ class EphemeridesComputer: | |||
culmination_time = culmination_time[0] if culmination_time is not None else None | |||
# Convert the Time instances to Python datetime objects | |||
rise_time = rise_time.utc_datetime().replace(microsecond=0) | |||
culmination_time = culmination_time.utc_datetime().replace(microsecond=0) | |||
set_time = set_time.utc_datetime().replace(microsecond=0) | |||
aster.ephemerides = AsterEphemerides(rise_time, culmination_time, set_time) | |||
return aster | |||
@@ -57,7 +57,7 @@ def _search_conjunction(start_time: Time, end_time: Time) -> [Event]: | |||
times, _ = find_discrete(start_time, end_time, is_in_conjunction) | |||
for time in times: | |||
conjunctions.append(Event('CONJUNCTION', [aster1, aster2], time)) | |||
conjunctions.append(Event('CONJUNCTION', [aster1, aster2], time.utc_datetime())) | |||
computed.append(aster1) | |||
@@ -86,7 +86,7 @@ def _search_oppositions(start_time: Time, end_time: Time) -> [Event]: | |||
times, _ = find_discrete(start_time, end_time, is_oppositing) | |||
for time in times: | |||
events.append(Event('OPPOSITION', [aster], time)) | |||
events.append(Event('OPPOSITION', [aster], time.utc_datetime())) | |||
return events | |||
@@ -98,4 +98,4 @@ def search_events(date: date_type) -> [Event]: | |||
return sorted(flatten_list([ | |||
_search_oppositions(start_time, end_time), | |||
_search_conjunction(start_time, end_time) | |||
]), key=lambda event: event.start_time.utc_datetime()) | |||
]), key=lambda event: event.start_time) |
@@ -8,7 +8,7 @@ msgid "" | |||
msgstr "" | |||
"Project-Id-Version: kosmorro 0.5.2\n" | |||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" | |||
"POT-Creation-Date: 2020-02-04 20:45+0100\n" | |||
"POT-Creation-Date: 2020-02-17 20:58+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" | |||
@@ -99,71 +99,80 @@ msgstr "" | |||
msgid "%s and %s are in conjunction" | |||
msgstr "" | |||
#: kosmorrolib/dumper.py:36 | |||
#: kosmorrolib/dumper.py:35 | |||
msgid "{day_of_week} {month} {day_number}, {year}" | |||
msgstr "" | |||
#: kosmorrolib/dumper.py:38 | |||
#: kosmorrolib/dumper.py:37 | |||
msgid "{month} {day_number}, {hours}:{minutes}" | |||
msgstr "" | |||
#: kosmorrolib/dumper.py:39 | |||
msgid "{hours}:{minutes}" | |||
msgstr "" | |||
#: kosmorrolib/dumper.py:113 | |||
#: kosmorrolib/dumper.py:147 | |||
msgid "Expected events:" | |||
msgstr "" | |||
#: kosmorrolib/dumper.py:116 | |||
#: kosmorrolib/dumper.py:151 | |||
msgid "Note: All the hours are given in UTC." | |||
msgstr "" | |||
#: kosmorrolib/dumper.py:157 kosmorrolib/dumper.py:222 | |||
#: kosmorrolib/dumper.py:156 | |||
msgid "Note: All the hours are given in the UTC{offset} timezone." | |||
msgstr "" | |||
#: kosmorrolib/dumper.py:202 kosmorrolib/dumper.py:271 | |||
msgid "Object" | |||
msgstr "" | |||
#: kosmorrolib/dumper.py:158 kosmorrolib/dumper.py:223 | |||
#: kosmorrolib/dumper.py:203 kosmorrolib/dumper.py:272 | |||
msgid "Rise time" | |||
msgstr "" | |||
#: kosmorrolib/dumper.py:159 kosmorrolib/dumper.py:224 | |||
#: kosmorrolib/dumper.py:204 kosmorrolib/dumper.py:273 | |||
msgid "Culmination time" | |||
msgstr "" | |||
#: kosmorrolib/dumper.py:160 kosmorrolib/dumper.py:225 | |||
#: kosmorrolib/dumper.py:205 kosmorrolib/dumper.py:274 | |||
msgid "Set time" | |||
msgstr "" | |||
#: kosmorrolib/dumper.py:173 kosmorrolib/dumper.py:228 | |||
#: kosmorrolib/dumper.py:219 kosmorrolib/dumper.py:277 | |||
msgid "Moon phase:" | |||
msgstr "" | |||
#: kosmorrolib/dumper.py:174 | |||
#: kosmorrolib/dumper.py:220 | |||
msgid "{next_moon_phase} on {next_moon_phase_date} at {next_moon_phase_time}" | |||
msgstr "" | |||
#: kosmorrolib/dumper.py:212 | |||
#: kosmorrolib/dumper.py:258 | |||
msgid "A Summary of your Sky" | |||
msgstr "" | |||
#: kosmorrolib/dumper.py:216 | |||
#: kosmorrolib/dumper.py:262 | |||
msgid "" | |||
"This document summarizes the ephemerides and the events of {date}. It " | |||
"aims to help you to prepare your observation session." | |||
"aims to help you to prepare your observation session. All the hours are " | |||
"given in {timezone}." | |||
msgstr "" | |||
#: kosmorrolib/dumper.py:219 | |||
#: kosmorrolib/dumper.py:268 | |||
msgid "" | |||
"Don't forget to check the weather forecast before you go out with your " | |||
"material." | |||
msgstr "" | |||
#: kosmorrolib/dumper.py:221 | |||
#: kosmorrolib/dumper.py:270 | |||
msgid "Ephemerides of the day" | |||
msgstr "" | |||
#: kosmorrolib/dumper.py:230 | |||
#: kosmorrolib/dumper.py:279 | |||
msgid "Expected events" | |||
msgstr "" | |||
#: kosmorrolib/dumper.py:296 | |||
#: kosmorrolib/dumper.py:354 | |||
msgid "" | |||
"Building PDFs was not possible, because some dependencies are not " | |||
"installed.\n" | |||
@@ -184,83 +193,87 @@ msgid "" | |||
"the observation coordinate." | |||
msgstr "" | |||
#: kosmorrolib/main.py:82 | |||
#: kosmorrolib/main.py:84 | |||
msgid "Could not save the output in \"{path}\": {error}" | |||
msgstr "" | |||
#: kosmorrolib/main.py:87 | |||
#: kosmorrolib/main.py:89 | |||
msgid "Selected output format needs an output file (--output)." | |||
msgstr "" | |||
#: kosmorrolib/main.py:104 | |||
#: kosmorrolib/main.py:106 | |||
msgid "Running on Python {python_version}" | |||
msgstr "" | |||
#: kosmorrolib/main.py:110 | |||
#: kosmorrolib/main.py:112 | |||
msgid "Do you really want to clear Kosmorro's cache? [yN] " | |||
msgstr "" | |||
#: kosmorrolib/main.py:117 | |||
#: kosmorrolib/main.py:119 | |||
msgid "Answer did not match expected options, cache not cleared." | |||
msgstr "" | |||
#: kosmorrolib/main.py:126 | |||
#: kosmorrolib/main.py:128 | |||
msgid "" | |||
"Compute the ephemerides and the events for a given date, at a given " | |||
"position on Earth." | |||
msgstr "" | |||
#: kosmorrolib/main.py:128 | |||
#: kosmorrolib/main.py:130 | |||
msgid "" | |||
"By default, only the events will be computed for today ({date}).\n" | |||
"To compute also the ephemerides, latitude and longitude arguments are " | |||
"needed." | |||
msgstr "" | |||
#: kosmorrolib/main.py:133 | |||
#: kosmorrolib/main.py:135 | |||
msgid "Show the program version" | |||
msgstr "" | |||
#: kosmorrolib/main.py:135 | |||
#: kosmorrolib/main.py:137 | |||
msgid "Delete all the files Kosmorro stored in the cache." | |||
msgstr "" | |||
#: kosmorrolib/main.py:137 | |||
#: kosmorrolib/main.py:139 | |||
msgid "The format under which the information have to be output" | |||
msgstr "" | |||
#: kosmorrolib/main.py:139 | |||
#: kosmorrolib/main.py:141 | |||
msgid "The observer's latitude on Earth" | |||
msgstr "" | |||
#: kosmorrolib/main.py:141 | |||
#: kosmorrolib/main.py:143 | |||
msgid "The observer's longitude on Earth" | |||
msgstr "" | |||
#: kosmorrolib/main.py:143 | |||
#: kosmorrolib/main.py:145 | |||
msgid "" | |||
"A number between 1 and 28, 29, 30 or 31 (depending on the month). The day" | |||
" you want to compute the ephemerides for. Defaults to {default_day} (the" | |||
" current day)." | |||
msgstr "" | |||
#: kosmorrolib/main.py:147 | |||
#: kosmorrolib/main.py:149 | |||
msgid "" | |||
"A number between 1 and 12. The month you want to compute the ephemerides " | |||
"for. Defaults to {default_month} (the current month)." | |||
msgstr "" | |||
#: kosmorrolib/main.py:150 | |||
#: kosmorrolib/main.py:152 | |||
msgid "" | |||
"The year you want to compute the ephemerides for. Defaults to " | |||
"{default_year} (the current year)." | |||
msgstr "" | |||
#: kosmorrolib/main.py:153 | |||
#: kosmorrolib/main.py:155 | |||
msgid "The timezone to display the hours in (e.g. 2 for UTC+2 or -3 for UTC-3)." | |||
msgstr "" | |||
#: kosmorrolib/main.py:157 | |||
msgid "Disable the colors in the console." | |||
msgstr "" | |||
#: kosmorrolib/main.py:155 | |||
#: kosmorrolib/main.py:159 | |||
msgid "" | |||
"A file to export the output to. If not given, the standard output is " | |||
"used. This argument is needed for PDF format." | |||
@@ -68,7 +68,9 @@ def main(): | |||
events_list = events.search_events(compute_date) | |||
selected_dumper = output_formats[args.format](ephemerides, events_list, compute_date, args.colors) | |||
selected_dumper = output_formats[args.format](ephemerides, events_list, | |||
date=compute_date, timezone=args.timezone, | |||
with_colors=args.colors) | |||
output = selected_dumper.to_string() | |||
except UnavailableFeatureError as error: | |||
print(colored(error.msg, 'red')) | |||
@@ -149,6 +151,8 @@ def get_args(output_formats: [str]): | |||
parser.add_argument('--year', '-y', type=int, default=today.year, | |||
help=_('The year you want to compute the ephemerides for.' | |||
' Defaults to {default_year} (the current year).').format(default_year=today.year)) | |||
parser.add_argument('--timezone', '-t', type=int, default=0, | |||
help=_('The timezone to display the hours in (e.g. 2 for UTC+2 or -3 for UTC-3).')) | |||
parser.add_argument('--no-colors', dest='colors', action='store_false', | |||
help=_('Disable the colors in the console.')) | |||
parser.add_argument('--output', '-o', type=str, default=None, | |||
@@ -1,9 +1,8 @@ | |||
import unittest | |||
from datetime import date | |||
from datetime import date, datetime | |||
from kosmorrolib.data import AsterEphemerides, Planet, MoonPhase, Event | |||
from kosmorrolib.dumper import JsonDumper, TextDumper, _LatexDumper | |||
from kosmorrolib.core import get_timescale | |||
class DumperTestCase(unittest.TestCase): | |||
@@ -11,12 +10,11 @@ class DumperTestCase(unittest.TestCase): | |||
self.maxDiff = None | |||
def test_json_dumper_returns_correct_json(self): | |||
data = self._get_data() | |||
self.assertEqual('{\n' | |||
' "moon_phase": {\n' | |||
' "next_phase_date": "2019-10-21T00:00:00Z",\n' | |||
' "next_phase_date": "2019-10-21T00:00:00",\n' | |||
' "phase": "FULL_MOON",\n' | |||
' "date": "2019-10-14T00:00:00Z"\n' | |||
' "date": "2019-10-14T00:00:00"\n' | |||
' },\n' | |||
' "events": [\n' | |||
' {\n' | |||
@@ -24,7 +22,7 @@ class DumperTestCase(unittest.TestCase): | |||
' "objects": [\n' | |||
' "Mars"\n' | |||
' ],\n' | |||
' "start_time": "2018-07-27T05:12:00Z",\n' | |||
' "start_time": "2019-10-14T23:00:00",\n' | |||
' "end_time": null\n' | |||
' }\n' | |||
' ],\n' | |||
@@ -38,16 +36,14 @@ class DumperTestCase(unittest.TestCase): | |||
' }\n' | |||
' }\n' | |||
' ]\n' | |||
'}', JsonDumper(data, | |||
self._get_events() | |||
).to_string()) | |||
'}', JsonDumper(self._get_data(), self._get_events()).to_string()) | |||
data = self._get_data(aster_rise_set=True) | |||
self.assertEqual('{\n' | |||
' "moon_phase": {\n' | |||
' "next_phase_date": "2019-10-21T00:00:00Z",\n' | |||
' "next_phase_date": "2019-10-21T00:00:00",\n' | |||
' "phase": "FULL_MOON",\n' | |||
' "date": "2019-10-14T00:00:00Z"\n' | |||
' "date": "2019-10-14T00:00:00"\n' | |||
' },\n' | |||
' "events": [\n' | |||
' {\n' | |||
@@ -55,7 +51,7 @@ class DumperTestCase(unittest.TestCase): | |||
' "objects": [\n' | |||
' "Mars"\n' | |||
' ],\n' | |||
' "start_time": "2018-07-27T05:12:00Z",\n' | |||
' "start_time": "2019-10-14T23:00:00",\n' | |||
' "end_time": null\n' | |||
' }\n' | |||
' ],\n' | |||
@@ -63,9 +59,9 @@ class DumperTestCase(unittest.TestCase): | |||
' {\n' | |||
' "object": "Mars",\n' | |||
' "details": {\n' | |||
' "rise_time": "2019-10-14T08:00:00Z",\n' | |||
' "culmination_time": "2019-10-14T13:00:00Z",\n' | |||
' "set_time": "2019-10-14T23:00:00Z"\n' | |||
' "rise_time": "2019-10-14T08:00:00",\n' | |||
' "culmination_time": "2019-10-14T13:00:00",\n' | |||
' "set_time": "2019-10-14T23:00:00"\n' | |||
' }\n' | |||
' }\n' | |||
' ]\n' | |||
@@ -103,7 +99,7 @@ class DumperTestCase(unittest.TestCase): | |||
'Moon phase: Full Moon\n' | |||
'Last Quarter on Monday October 21, 2019 at 00:00\n\n' | |||
'Expected events:\n' | |||
'05:12 Mars is in opposition\n\n' | |||
'23:00 Mars is in opposition\n\n' | |||
'Note: All the hours are given in UTC.', | |||
TextDumper(ephemerides, self._get_events(), date=date(2019, 10, 14), with_colors=False).to_string()) | |||
@@ -113,10 +109,35 @@ class DumperTestCase(unittest.TestCase): | |||
'Moon phase: Full Moon\n' | |||
'Last Quarter on Monday October 21, 2019 at 00:00\n\n' | |||
'Expected events:\n' | |||
'05:12 Mars is in opposition\n\n' | |||
'23:00 Mars is in opposition\n\n' | |||
'Note: All the hours are given in UTC.', | |||
TextDumper(ephemerides, self._get_events(), date=date(2019, 10, 14), with_colors=False).to_string()) | |||
def test_timezone_is_taken_in_account(self): | |||
ephemerides = self._get_data(aster_rise_set=True) | |||
self.assertEqual('Monday October 14, 2019\n\n' | |||
'Object Rise time Culmination time Set time\n' | |||
'-------- ----------- ------------------ -------------\n' | |||
'Mars 09:00 14:00 Oct 15, 00:00\n\n' | |||
'Moon phase: Full Moon\n' | |||
'Last Quarter on Monday October 21, 2019 at 01:00\n\n' | |||
'Expected events:\n' | |||
'Oct 15, 00:00 Mars is in opposition\n\n' | |||
'Note: All the hours are given in the UTC+1 timezone.', | |||
TextDumper(ephemerides, self._get_events(), date=date(2019, 10, 14), with_colors=False, timezone=1).to_string()) | |||
ephemerides = self._get_data(aster_rise_set=True) | |||
self.assertEqual('Monday October 14, 2019\n\n' | |||
'Object Rise time Culmination time Set time\n' | |||
'-------- ----------- ------------------ ----------\n' | |||
'Mars 07:00 12:00 22:00\n\n' | |||
'Moon phase: Full Moon\n' | |||
'Last Quarter on Sunday October 20, 2019 at 23:00\n\n' | |||
'Expected events:\n' | |||
'22:00 Mars is in opposition\n\n' | |||
'Note: All the hours are given in the UTC-1 timezone.', | |||
TextDumper(ephemerides, self._get_events(), date=date(2019, 10, 14), with_colors=False, timezone=-1).to_string()) | |||
def test_latex_dumper(self): | |||
latex = _LatexDumper(self._get_data(), self._get_events(), date=date(2019, 10, 14)).to_string() | |||
self.assertRegex(latex, 'Monday October 14, 2019') | |||
@@ -124,7 +145,7 @@ class DumperTestCase(unittest.TestCase): | |||
self.assertRegex(latex, r'\\section{\\sffamily Expected events}') | |||
self.assertRegex(latex, r'\\section{\\sffamily Ephemerides of the day}') | |||
self.assertRegex(latex, r'\\object\{Mars\}\{-\}\{-\}\{-\}') | |||
self.assertRegex(latex, r'\\event\{05:12\}\{Mars is in opposition\}') | |||
self.assertRegex(latex, r'\\event\{23:00\}\{Mars is in opposition\}') | |||
latex = _LatexDumper(self._get_data(aster_rise_set=True), | |||
self._get_events(), date=date(2019, 10, 14)).to_string() | |||
@@ -135,7 +156,7 @@ class DumperTestCase(unittest.TestCase): | |||
self.assertRegex(latex, 'Monday October 14, 2019') | |||
self.assertRegex(latex, 'Full Moon') | |||
self.assertRegex(latex, r'\\section{\\sffamily Expected events}') | |||
self.assertRegex(latex, r'\\event\{05:12\}\{Mars is in opposition\}') | |||
self.assertRegex(latex, r'\\event\{23:00\}\{Mars is in opposition\}') | |||
self.assertNotRegex(latex, r'\\object\{Mars\}\{-\}\{-\}\{-\}') | |||
self.assertNotRegex(latex, r'\\section{\\sffamily Ephemerides of the day}') | |||
@@ -151,12 +172,12 @@ class DumperTestCase(unittest.TestCase): | |||
@staticmethod | |||
def _get_data(has_ephemerides: bool = True, aster_rise_set=False): | |||
rise_time = get_timescale().utc(2019, 10, 14, 8) if aster_rise_set else None | |||
culmination_time = get_timescale().utc(2019, 10, 14, 13) if aster_rise_set else None | |||
set_time = get_timescale().utc(2019, 10, 14, 23) if aster_rise_set else None | |||
rise_time = datetime(2019, 10, 14, 8) if aster_rise_set else None | |||
culmination_time = datetime(2019, 10, 14, 13) if aster_rise_set else None | |||
set_time = datetime(2019, 10, 14, 23) if aster_rise_set else None | |||
return { | |||
'moon_phase': MoonPhase('FULL_MOON', get_timescale().utc(2019, 10, 14), get_timescale().utc(2019, 10, 21)), | |||
'moon_phase': MoonPhase('FULL_MOON', datetime(2019, 10, 14), datetime(2019, 10, 21)), | |||
'details': [Planet('Mars', 'MARS', | |||
AsterEphemerides(rise_time, culmination_time, set_time))] if has_ephemerides else [] | |||
} | |||
@@ -165,7 +186,7 @@ class DumperTestCase(unittest.TestCase): | |||
def _get_events(): | |||
return [Event('OPPOSITION', | |||
[Planet('Mars', 'MARS')], | |||
get_timescale().utc(2018, 7, 27, 5, 12)) | |||
datetime(2019, 10, 14, 23, 00)) | |||
] | |||
@@ -13,9 +13,9 @@ class EphemeridesComputerTestCase(unittest.TestCase): | |||
date=date(2019, 11, 18), | |||
position=position) | |||
self.assertEqual('2019-11-18T05:41:31Z', star.ephemerides.rise_time.utc_iso()) | |||
self.assertEqual('2019-11-18T11:45:02Z', star.ephemerides.culmination_time.utc_iso()) | |||
self.assertEqual('2019-11-18T17:48:39Z', star.ephemerides.set_time.utc_iso()) | |||
self.assertEqual('2019-11-18T05:41:30+00:00', star.ephemerides.rise_time.isoformat()) | |||
self.assertEqual('2019-11-18T11:45:02+00:00', star.ephemerides.culmination_time.isoformat()) | |||
self.assertEqual('2019-11-18T17:48:39+00:00', star.ephemerides.set_time.isoformat()) | |||
################################################################################################################### | |||
### MOON PHASE TESTS ### | |||
@@ -25,61 +25,61 @@ class EphemeridesComputerTestCase(unittest.TestCase): | |||
phase = EphemeridesComputer.get_moon_phase(2019, 11, 25) | |||
self.assertEqual('WANING_CRESCENT', phase.identifier) | |||
self.assertIsNone(phase.time) | |||
self.assertRegexpMatches(phase.next_phase_date.utc_iso(), '^2019-11-26T') | |||
self.assertRegexpMatches(phase.next_phase_date.isoformat(), '^2019-11-26T') | |||
phase = EphemeridesComputer.get_moon_phase(2019, 11, 26) | |||
self.assertEqual('NEW_MOON', phase.identifier) | |||
self.assertRegexpMatches(phase.next_phase_date.utc_iso(), '^2019-12-04T') | |||
self.assertRegexpMatches(phase.next_phase_date.isoformat(), '^2019-12-04T') | |||
phase = EphemeridesComputer.get_moon_phase(2019, 11, 27) | |||
self.assertEqual('WAXING_CRESCENT', phase.identifier) | |||
self.assertIsNone(phase.time) | |||
self.assertRegexpMatches(phase.next_phase_date.utc_iso(), '^2019-12-04T') | |||
self.assertRegexpMatches(phase.next_phase_date.isoformat(), '^2019-12-04T') | |||
def test_moon_phase_first_crescent(self): | |||
phase = EphemeridesComputer.get_moon_phase(2019, 11, 3) | |||
self.assertEqual('WAXING_CRESCENT', phase.identifier) | |||
self.assertIsNone(phase.time) | |||
self.assertRegexpMatches(phase.next_phase_date.utc_iso(), '^2019-11-04T') | |||
self.assertRegexpMatches(phase.next_phase_date.isoformat(), '^2019-11-04T') | |||
phase = EphemeridesComputer.get_moon_phase(2019, 11, 4) | |||
self.assertEqual('FIRST_QUARTER', phase.identifier) | |||
self.assertRegexpMatches(phase.next_phase_date.utc_iso(), '^2019-11-12T') | |||
self.assertRegexpMatches(phase.next_phase_date.isoformat(), '^2019-11-12T') | |||
phase = EphemeridesComputer.get_moon_phase(2019, 11, 5) | |||
self.assertEqual('WAXING_GIBBOUS', phase.identifier) | |||
self.assertIsNone(phase.time) | |||
self.assertRegexpMatches(phase.next_phase_date.utc_iso(), '^2019-11-12T') | |||
self.assertRegexpMatches(phase.next_phase_date.isoformat(), '^2019-11-12T') | |||
def test_moon_phase_full_moon(self): | |||
phase = EphemeridesComputer.get_moon_phase(2019, 11, 11) | |||
self.assertEqual('WAXING_GIBBOUS', phase.identifier) | |||
self.assertIsNone(phase.time) | |||
self.assertRegexpMatches(phase.next_phase_date.utc_iso(), '^2019-11-12T') | |||
self.assertRegexpMatches(phase.next_phase_date.isoformat(), '^2019-11-12T') | |||
phase = EphemeridesComputer.get_moon_phase(2019, 11, 12) | |||
self.assertEqual('FULL_MOON', phase.identifier) | |||
self.assertRegexpMatches(phase.next_phase_date.utc_iso(), '^2019-11-19T') | |||
self.assertRegexpMatches(phase.next_phase_date.isoformat(), '^2019-11-19T') | |||
phase = EphemeridesComputer.get_moon_phase(2019, 11, 13) | |||
self.assertEqual('WANING_GIBBOUS', phase.identifier) | |||
self.assertIsNone(phase.time) | |||
self.assertRegexpMatches(phase.next_phase_date.utc_iso(), '^2019-11-19T') | |||
self.assertRegexpMatches(phase.next_phase_date.isoformat(), '^2019-11-19T') | |||
def test_moon_phase_last_quarter(self): | |||
phase = EphemeridesComputer.get_moon_phase(2019, 11, 18) | |||
self.assertEqual('WANING_GIBBOUS', phase.identifier) | |||
self.assertIsNone(phase.time) | |||
self.assertRegexpMatches(phase.next_phase_date.utc_iso(), '^2019-11-19T') | |||
self.assertRegexpMatches(phase.next_phase_date.isoformat(), '^2019-11-19T') | |||
phase = EphemeridesComputer.get_moon_phase(2019, 11, 19) | |||
self.assertEqual('LAST_QUARTER', phase.identifier) | |||
self.assertRegexpMatches(phase.next_phase_date.utc_iso(), '^2019-11-26T') | |||
self.assertRegexpMatches(phase.next_phase_date.isoformat(), '^2019-11-26T') | |||
phase = EphemeridesComputer.get_moon_phase(2019, 11, 20) | |||
self.assertEqual('WANING_CRESCENT', phase.identifier) | |||
self.assertIsNone(phase.time) | |||
self.assertRegexpMatches(phase.next_phase_date.utc_iso(), '^2019-11-26T') | |||
self.assertRegexpMatches(phase.next_phase_date.isoformat(), '^2019-11-26T') | |||
def test_moon_phase_prediction(self): | |||
phase = MoonPhase('NEW_MOON', None, None) | |||
@@ -24,7 +24,7 @@ class MyTestCase(unittest.TestCase): | |||
self.assertEqual(1, len(o), 'Expected 1 event for %s, got %d' % (expected_date, len(o))) | |||
self.assertEqual('OPPOSITION', o[0].event_type) | |||
self.assertEqual('MARS', o[0].objects[0].skyfield_name) | |||
self.assertRegex(o[0].start_time.utc_iso(), expected_date) | |||
self.assertRegex(o[0].start_time.isoformat(), expected_date) | |||
self.assertIsNone(o[0].end_time) | |||
self.assertEqual('Mars is in opposition', o[0].get_description()) | |||
@@ -45,7 +45,7 @@ class MyTestCase(unittest.TestCase): | |||
objects, expected_date = expected_dates[i] | |||
j = 0 | |||
self.assertRegex(conjunction.start_time.utc_iso(), expected_date) | |||
self.assertRegex(conjunction.start_time.isoformat(), expected_date) | |||
for object in objects: | |||
self.assertEqual(object, conjunction.objects[j].skyfield_name) | |||
j += 1 | |||