@@ -20,5 +20,5 @@ jobs: | |||||
env: | env: | ||||
COVERALLS_TOKEN: ${{ secrets.COVERALLS_TOKEN }} | COVERALLS_TOKEN: ${{ secrets.COVERALLS_TOKEN }} | ||||
run: | | run: | | ||||
pipenv run python -m coverage run -m unittest test | |||||
make test | |||||
COVERALLS_REPO_TOKEN=$COVERALLS_TOKEN pipenv run coveralls | COVERALLS_REPO_TOKEN=$COVERALLS_TOKEN pipenv run coveralls |
@@ -81,6 +81,8 @@ assertSuccess "kosmorro -h" | |||||
assertSuccess "kosmorro -d 2020-01-27" | assertSuccess "kosmorro -d 2020-01-27" | ||||
assertFailure "kosmorro -d yolo-yo-lo" | assertFailure "kosmorro -d yolo-yo-lo" | ||||
assertFailure "kosmorro -d 2020-13-32" | assertFailure "kosmorro -d 2020-13-32" | ||||
assertFailure "kosmorro --date=1789-05-05" | |||||
assertFailure "kosmorro --date=3000-01-01" | |||||
assertSuccess "kosmorro --date='+3y 5m3d'" | assertSuccess "kosmorro --date='+3y 5m3d'" | ||||
assertSuccess "kosmorro --date='-1y3d'" | assertSuccess "kosmorro --date='-1y3d'" | ||||
assertFailure "kosmorro --date='+3d4m" | assertFailure "kosmorro --date='+3d4m" | ||||
@@ -80,7 +80,7 @@ Kosmorro's unit tests use Python's official `unittest` module. They live in the | |||||
You can also run them by invoking the following command: | You can also run them by invoking the following command: | ||||
```shell | ```shell | ||||
pipenv run python -m unittest test | |||||
make test | |||||
``` | ``` | ||||
Note: there are currently some memory leaks in the unit tests, making the result quite difficult to read. I am working to fix this. | Note: there are currently some memory leaks in the unit tests, making the result quite difficult to read. I am working to fix this. | ||||
@@ -1,3 +1,11 @@ | |||||
.PHONY: test | |||||
test: | |||||
unset KOSMORRO_LATITUDE; \ | |||||
unset KOSMORRO_LONGITUDE; \ | |||||
unset KOSMORRO_TIMEZONE; \ | |||||
LANG=C pipenv run python3 -m coverage run -m unittest test | |||||
build: | build: | ||||
ronn --roff manpage/kosmorro.1.md | ronn --roff manpage/kosmorro.1.md | ||||
ronn --roff manpage/kosmorro.7.md | ronn --roff manpage/kosmorro.7.md | ||||
@@ -11,7 +11,7 @@ unittest-data-provider = "*" | |||||
coveralls = "*" | coveralls = "*" | ||||
[packages] | [packages] | ||||
skyfield = ">=1.13.0,<2.0.0" | |||||
skyfield = ">=1.21.0,<2.0.0" | |||||
tabulate = "*" | tabulate = "*" | ||||
numpy = ">=1.17.0,<2.0.0" | numpy = ">=1.17.0,<2.0.0" | ||||
termcolor = "*" | termcolor = "*" | ||||
@@ -1,7 +1,7 @@ | |||||
{ | { | ||||
"_meta": { | "_meta": { | ||||
"hash": { | "hash": { | ||||
"sha256": "afd17771258a86b04cab79d21d2afbfb1faf44bbe6e760da9140e72b0999a5b1" | |||||
"sha256": "9ed5ee6bbfde75ee77c89fdc09a793f5c00f9782968dc310e1eb8d3386378d9e" | |||||
}, | }, | ||||
"pipfile-spec": 6, | "pipfile-spec": 6, | ||||
"requires": { | "requires": { | ||||
@@ -0,0 +1,31 @@ | |||||
<?xml version="1.0" encoding="UTF-8"?> | |||||
<svg width="92.682mm" height="92.684mm" version="1.1" viewBox="0 0 92.682 92.684" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:xlink="http://www.w3.org/1999/xlink"> | |||||
<defs> | |||||
<radialGradient id="radialGradient4622" cx="245.39" cy="354.57" r="234.94" fx="248.94" gradientTransform="translate(-41.85,-50.912)" gradientUnits="userSpaceOnUse"> | |||||
<stop stop-color="#b7c3cc" offset="0"/> | |||||
<stop stop-color="#001e29" offset="1"/> | |||||
</radialGradient> | |||||
</defs> | |||||
<metadata> | |||||
<rdf:RDF> | |||||
<cc:Work rdf:about=""> | |||||
<dc:format>image/svg+xml</dc:format> | |||||
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/> | |||||
<dc:title/> | |||||
</cc:Work> | |||||
</rdf:RDF> | |||||
</metadata> | |||||
<g transform="translate(-15.647 -114.59)"> | |||||
<g transform="matrix(.26458 0 0 .26458 7.4191 107.94)"> | |||||
<g fill-rule="evenodd"> | |||||
<path d="m362.49 200.25a156.25 156.25 0 1 1-312.5 0 156.25 156.25 0 1 1 312.5 0z" fill="#000024" stroke-width="1.0102"/> | |||||
<path d="m164.32 56.438c-25.06 6.447-47.957 20.982-65.936 39.456-33.589 34.689-49.415 85.84-39.274 133.3 7.7684 38.332 30.319 73.818 63.261 95.34 39.082 26.542 90.824 33.344 135.13 16.523 44.589-15.935 80.583-53.959 92.968-99.846 14.03-47.365 2.42-101-30.04-138.25-27.522-32.74-69.296-52.414-112.07-52.8-2.4338-0.40982-30.229 1.5565-44.039 6.2796z" fill="url(#radialGradient4622)"/> | |||||
<path d="m297.02 188.91c-10.37 17.497 15.281 19.939 24.511 25.141-3.4576-31.524 10.512-20.807 13.141-30.822-2.4603-15.476-20.24-18.289-30.195-28.185-0.36892-19.454-33.444-17.876-30.691-31.892-5.3458-59.729-15.582-5.0632-17.324-14.89-11.086 12.896-14.512-1.6954-11.166-10.995 0.26364-11.365-18.639-11.91-25.607-6.5757-5.8842 3.1388-14.403 3.9638-17.373 11.046-13.66 2.7421-12.28-17.783-26.048-16.916-15.276-6.4266-21.571-23.77-33.99-13.45-8.9365 2.8834-27.358 38.752-33.694 23.765-13.5-9.5163-20.451 15.279-20.026 25.654-6.3485 13.802-17.899 25.142-24.397 39.356-8.0969 13.6-8.0574 31.852-2.7715 46.369 16.911-8.3049 18.513 27.307 24.313 42.266 8.439 10.014 21.898-12.821 25.417-6.9099 22.751 14.162-5.2534 18.337-1.2166 29.083 4.539 15.527 24.377 5.6814 35.735 9.2032 4.7599 1.8134 10.807 12.467 13.777 3.1751-1.815-6.8086-5.6283-13.445 4.7747-13.371 7.3152-6.3929 17.349-5.3491 24.821-7.2415 9.0393-11.572-10.092-19.333-4.8746-30.443 2.8779-11.759-12.73-8.4941-10.462-18.846-7.0124-5.8828-11.048-16.424 0.22233-20.807 7.9487-5.15 29.707 5.1288 24.31-11.199-6.8963-0.46855-16.33-6.6026-8.8168-14.327 10.756-7.5046 26.836 6.3362 36.887-4.9453 1.0925-5.4659 4.6002-20.74 11.751-10.347-2.0036 12.706 4.4139 23.496 16.934 28.605 5.6256 3.7827 11.442 24.659 18.963 12.144-10.579-15.807-1.3253-36.935 21.493-9.7167-0.27777 2.0581-0.63554 4.9085 1.6013 6.0725zm-112.57-59.732c-3.3727-9.642-15.855 0.88715-7.0124 5.9448-1.4949 18.168-23.383 19.29-37.295 19.685-8.7173 4.2923-20.669 6.5967-27.583 10.788-1.5637 11.397 17.747 10.622 15.712 22.827 8.4646 11.957 22.01 19.653 28.373 33.172 12.227 10.86 6.5682-16.046-1.1103-17.921-11.846-4.4912-26.133-24.017-7.6284-30.074 12.233-3.4412 6.304-25.137 22.916-20.183 15.144 3.9218 14.776-14.074 24.42-20.796 2.2128-9.6425-5.7023-6.6942-10.791-3.4427z" fill-opacity=".25126"/> | |||||
</g> | |||||
<path d="m358.45 201.09c0 83.168-68.058 150.67-151.91 150.67s-151.91-67.499-151.91-150.67 64.682-150.67 151.91-150.67c89.933 0 151.91 67.499 151.91 150.67z" fill="#000024" fill-opacity=".86432"/> | |||||
</g> | |||||
</g> | |||||
<g fill="#fff" fill-opacity=".33333" aria-label="?"> | |||||
<path d="m37.427 57.422q0-4.3215 1.4111-7.7611 1.4111-3.4396 5.8208-5.9972 1.3229-0.79375 3.2632-1.7639 1.9403-1.0583 3.7924-2.4694 1.8521-1.4111 3.0868-3.2632 1.3229-1.9403 1.3229-4.4097 0-1.8521-0.70556-3.2632-0.70556-1.4111-1.8521-2.3812t-2.734-1.4111q-1.4993-0.52917-3.0868-0.52917-1.9403 0-3.5278 0.70556-1.5875 0.61736-2.9104 1.6757-1.2347 0.97014-2.2931 2.2931-0.97014 1.2347-1.6757 2.3812l-7.6729-5.2917q1.1465-2.734 2.9986-4.8507 1.9403-2.1167 4.3215-3.5278 2.4694-1.4993 5.2917-2.2049 2.8222-0.79375 5.8208-0.79375 3.3514 0 6.6146 0.97014 3.2632 0.97014 5.8208 3.0868 2.6458 2.1167 4.2333 5.4681 1.6757 3.2632 1.6757 7.8493 0 2.8222-0.70556 5.0271-0.61736 2.1167-1.8521 3.8806-1.2347 1.7639-2.9104 3.175-1.6757 1.4111-3.7924 2.734-1.7639 1.0583-3.5278 2.0285-1.7639 0.97014-3.2632 2.2049-1.4111 1.1465-2.3812 2.734-0.88194 1.4993-0.88194 3.7042zm0.17639 20.549v-12.171h9.7896v12.171z" fill="#fff" fill-opacity=".33333" stroke-width=".26458"/> | |||||
</g> | |||||
</svg> |
@@ -36,7 +36,8 @@ MOON_PHASES = { | |||||
'FULL_MOON': _('Full Moon'), | 'FULL_MOON': _('Full Moon'), | ||||
'WANING_GIBBOUS': _('Waning gibbous'), | 'WANING_GIBBOUS': _('Waning gibbous'), | ||||
'LAST_QUARTER': _('Last Quarter'), | 'LAST_QUARTER': _('Last Quarter'), | ||||
'WANING_CRESCENT': _('Waning crescent') | |||||
'WANING_CRESCENT': _('Waning crescent'), | |||||
'UNKNOWN': _('Unavailable') | |||||
} | } | ||||
EVENTS = { | EVENTS = { | ||||
@@ -54,7 +55,7 @@ class Serializable(ABC): | |||||
class MoonPhase(Serializable): | class MoonPhase(Serializable): | ||||
def __init__(self, identifier: str, time: Union[datetime, None], next_phase_date: Union[datetime, None]): | |||||
def __init__(self, identifier: str, time: datetime = None, next_phase_date: datetime = None): | |||||
if identifier not in MOON_PHASES.keys(): | if identifier not in MOON_PHASES.keys(): | ||||
raise ValueError('identifier parameter must be one of %s (got %s)' % (', '.join(MOON_PHASES.keys()), | raise ValueError('identifier parameter must be one of %s (got %s)' % (', '.join(MOON_PHASES.keys()), | ||||
identifier)) | identifier)) | ||||
@@ -25,7 +25,7 @@ from tabulate import tabulate | |||||
from numpy import int64 | from numpy import int64 | ||||
from termcolor import colored | from termcolor import colored | ||||
from .data import ASTERS, Object, AsterEphemerides, MoonPhase, Event | from .data import ASTERS, Object, AsterEphemerides, MoonPhase, Event | ||||
from .i18n import _ | |||||
from .i18n import _, FULL_DATE_FORMAT, SHORT_DATETIME_FORMAT, TIME_FORMAT | |||||
from .version import VERSION | from .version import VERSION | ||||
from .exceptions import UnavailableFeatureError | from .exceptions import UnavailableFeatureError | ||||
try: | try: | ||||
@@ -33,12 +33,6 @@ try: | |||||
except ImportError: | except ImportError: | ||||
build_pdf = None | build_pdf = None | ||||
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): | class Dumper(ABC): | ||||
def __init__(self, ephemerides: [AsterEphemerides] = None, moon_phase: MoonPhase = None, events: [Event] = None, | def __init__(self, ephemerides: [AsterEphemerides] = None, moon_phase: MoonPhase = None, events: [Event] = None, | ||||
@@ -60,6 +54,9 @@ class Dumper(ABC): | |||||
return date | return date | ||||
def __str__(self): | |||||
return self.to_string() | |||||
@abstractmethod | @abstractmethod | ||||
def to_string(self): | def to_string(self): | ||||
pass | pass | ||||
@@ -189,6 +186,9 @@ class TextDumper(Dumper): | |||||
return tabulate(data, tablefmt='plain', stralign='left') | return tabulate(data, tablefmt='plain', stralign='left') | ||||
def get_moon(self, moon_phase: MoonPhase) -> str: | def get_moon(self, moon_phase: MoonPhase) -> str: | ||||
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.get_phase()]) | ||||
new_moon_phase = _('{next_moon_phase} on {next_moon_phase_date} at {next_moon_phase_time}').format( | 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_name(), | ||||
@@ -212,6 +212,10 @@ class _LatexDumper(Dumper): | |||||
def _make_document(self, template: str) -> str: | def _make_document(self, template: str) -> str: | ||||
kosmorro_logo_path = os.path.join(os.path.abspath(os.path.dirname(__file__)), | kosmorro_logo_path = os.path.join(os.path.abspath(os.path.dirname(__file__)), | ||||
'assets', 'png', 'kosmorro-logo.png') | '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__)), | moon_phase_graphics = os.path.join(os.path.abspath(os.path.dirname(__file__)), | ||||
'assets', 'moonphases', 'png', | 'assets', 'moonphases', 'png', | ||||
'.'.join([self.moon_phase.identifier.lower().replace('_', '-'), | '.'.join([self.moon_phase.identifier.lower().replace('_', '-'), | ||||
@@ -21,15 +21,17 @@ import datetime | |||||
from skyfield.searchlib import find_discrete, find_maxima | from skyfield.searchlib import find_discrete, find_maxima | ||||
from skyfield.timelib import Time | from skyfield.timelib import Time | ||||
from skyfield.constants import tau | 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, skyfield_to_moon_phase | ||||
from .dateutil import translate_to_timezone | from .dateutil import translate_to_timezone | ||||
from .core import get_skf_objects, get_timescale, get_iau2000b | from .core import get_skf_objects, get_timescale, get_iau2000b | ||||
from .exceptions import OutOfRangeDateError | |||||
RISEN_ANGLE = -0.8333 | RISEN_ANGLE = -0.8333 | ||||
def get_moon_phase(compute_date: datetime.date) -> MoonPhase: | |||||
def get_moon_phase(compute_date: datetime.date, timezone: int = 0) -> MoonPhase: | |||||
earth = get_skf_objects()['earth'] | earth = get_skf_objects()['earth'] | ||||
moon = get_skf_objects()['moon'] | moon = get_skf_objects()['moon'] | ||||
sun = get_skf_objects()['sun'] | sun = get_skf_objects()['sun'] | ||||
@@ -47,7 +49,16 @@ def get_moon_phase(compute_date: datetime.date) -> MoonPhase: | |||||
time1 = get_timescale().utc(compute_date.year, compute_date.month, compute_date.day - 10) | time1 = get_timescale().utc(compute_date.year, compute_date.month, compute_date.day - 10) | ||||
time2 = get_timescale().utc(compute_date.year, compute_date.month, compute_date.day + 10) | time2 = get_timescale().utc(compute_date.year, compute_date.month, compute_date.day + 10) | ||||
times, phase = find_discrete(time1, time2, moon_phase_at) | |||||
try: | |||||
times, phase = find_discrete(time1, time2, moon_phase_at) | |||||
except EphemerisRangeError as error: | |||||
start = translate_to_timezone(error.start_time.utc_datetime(), timezone) | |||||
end = translate_to_timezone(error.end_time.utc_datetime(), timezone) | |||||
start = datetime.date(start.year, start.month, start.day) + datetime.timedelta(days=12) | |||||
end = datetime.date(end.year, end.month, end.day) - datetime.timedelta(days=12) | |||||
raise OutOfRangeDateError(start, end) | |||||
return skyfield_to_moon_phase(times, phase, today) | return skyfield_to_moon_phase(times, phase, today) | ||||
@@ -71,34 +82,43 @@ def get_ephemerides(date: datetime.date, position: Position, timezone: int = 0) | |||||
start_time = get_timescale().utc(date.year, date.month, date.day, -timezone) | start_time = get_timescale().utc(date.year, date.month, date.day, -timezone) | ||||
end_time = get_timescale().utc(date.year, date.month, date.day, 23 - timezone, 59, 59) | end_time = get_timescale().utc(date.year, date.month, date.day, 23 - timezone, 59, 59) | ||||
for aster in ASTERS: | |||||
rise_times, arr = find_discrete(start_time, end_time, is_risen(aster)) | |||||
try: | |||||
culmination_time, _ = find_maxima(start_time, end_time, f=get_angle(aster), epsilon=1./3600/24, num=12) | |||||
culmination_time = culmination_time[0] if len(culmination_time) > 0 else None | |||||
except ValueError: | |||||
culmination_time = None | |||||
if len(rise_times) == 2: | |||||
rise_time = rise_times[0 if arr[0] else 1] | |||||
set_time = rise_times[1 if not arr[1] else 0] | |||||
else: | |||||
rise_time = rise_times[0] if arr[0] else None | |||||
set_time = rise_times[0] if not arr[0] else None | |||||
# Convert the Time instances to Python datetime objects | |||||
if rise_time is not None: | |||||
rise_time = translate_to_timezone(rise_time.utc_datetime().replace(microsecond=0), | |||||
to_tz=timezone) | |||||
if culmination_time is not None: | |||||
culmination_time = translate_to_timezone(culmination_time.utc_datetime().replace(microsecond=0), | |||||
to_tz=timezone) | |||||
if set_time is not None: | |||||
set_time = translate_to_timezone(set_time.utc_datetime().replace(microsecond=0), | |||||
to_tz=timezone) | |||||
ephemerides.append(AsterEphemerides(rise_time, culmination_time, set_time, aster=aster)) | |||||
try: | |||||
for aster in ASTERS: | |||||
rise_times, arr = find_discrete(start_time, end_time, is_risen(aster)) | |||||
try: | |||||
culmination_time, _ = find_maxima(start_time, end_time, f=get_angle(aster), epsilon=1./3600/24, num=12) | |||||
culmination_time = culmination_time[0] if len(culmination_time) > 0 else None | |||||
except ValueError: | |||||
culmination_time = None | |||||
if len(rise_times) == 2: | |||||
rise_time = rise_times[0 if arr[0] else 1] | |||||
set_time = rise_times[1 if not arr[1] else 0] | |||||
else: | |||||
rise_time = rise_times[0] if arr[0] else None | |||||
set_time = rise_times[0] if not arr[0] else None | |||||
# Convert the Time instances to Python datetime objects | |||||
if rise_time is not None: | |||||
rise_time = translate_to_timezone(rise_time.utc_datetime().replace(microsecond=0), | |||||
to_tz=timezone) | |||||
if culmination_time is not None: | |||||
culmination_time = translate_to_timezone(culmination_time.utc_datetime().replace(microsecond=0), | |||||
to_tz=timezone) | |||||
if set_time is not None: | |||||
set_time = translate_to_timezone(set_time.utc_datetime().replace(microsecond=0), | |||||
to_tz=timezone) | |||||
ephemerides.append(AsterEphemerides(rise_time, culmination_time, set_time, aster=aster)) | |||||
except EphemerisRangeError as error: | |||||
start = translate_to_timezone(error.start_time.utc_datetime(), timezone) | |||||
end = translate_to_timezone(error.end_time.utc_datetime(), timezone) | |||||
start = datetime.date(start.year, start.month, start.day + 1) | |||||
end = datetime.date(end.year, end.month, end.day - 1) | |||||
raise OutOfRangeDateError(start, end) | |||||
return ephemerides | return ephemerides |
@@ -18,12 +18,14 @@ | |||||
from datetime import date as date_type | from datetime import date as date_type | ||||
from skyfield.errors import EphemerisRangeError | |||||
from skyfield.timelib import Time | from skyfield.timelib import Time | ||||
from skyfield.searchlib import find_discrete, find_maxima | from skyfield.searchlib import find_discrete, find_maxima | ||||
from numpy import pi | from numpy import pi | ||||
from .data import Event, Star, Planet, ASTERS | from .data import Event, Star, Planet, ASTERS | ||||
from .dateutil import translate_to_timezone | from .dateutil import translate_to_timezone | ||||
from .exceptions import OutOfRangeDateError | |||||
from .core import get_timescale, get_skf_objects, flatten_list | from .core import get_timescale, get_skf_objects, flatten_list | ||||
@@ -137,8 +139,17 @@ def search_events(date: date_type, timezone: int = 0) -> [Event]: | |||||
start_time = get_timescale().utc(date.year, date.month, date.day, -timezone) | start_time = get_timescale().utc(date.year, date.month, date.day, -timezone) | ||||
end_time = get_timescale().utc(date.year, date.month, date.day + 1, -timezone) | end_time = get_timescale().utc(date.year, date.month, date.day + 1, -timezone) | ||||
return sorted(flatten_list([ | |||||
_search_oppositions(start_time, end_time, timezone), | |||||
_search_conjunction(start_time, end_time, timezone), | |||||
_search_maximal_elongations(start_time, end_time, timezone) | |||||
]), key=lambda event: event.start_time) | |||||
try: | |||||
return sorted(flatten_list([ | |||||
_search_oppositions(start_time, end_time, timezone), | |||||
_search_conjunction(start_time, end_time, timezone), | |||||
_search_maximal_elongations(start_time, end_time, timezone) | |||||
]), key=lambda event: event.start_time) | |||||
except EphemerisRangeError as error: | |||||
start_date = translate_to_timezone(error.start_time.utc_datetime(), timezone) | |||||
end_date = translate_to_timezone(error.end_time.utc_datetime(), timezone) | |||||
start_date = date_type(start_date.year, start_date.month, start_date.day) | |||||
end_date = date_type(end_date.year, end_date.month, end_date.day) | |||||
raise OutOfRangeDateError(start_date, end_date) |
@@ -16,8 +16,21 @@ | |||||
# You should have received a copy of the GNU Affero General Public License | # 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/>. | # along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
from datetime import date | |||||
from .i18n import _, SHORT_DATE_FORMAT | |||||
class UnavailableFeatureError(RuntimeError): | class UnavailableFeatureError(RuntimeError): | ||||
def __init__(self, msg: str): | def __init__(self, msg: str): | ||||
super(UnavailableFeatureError, self).__init__() | super(UnavailableFeatureError, self).__init__() | ||||
self.msg = msg | self.msg = msg | ||||
class OutOfRangeDateError(RuntimeError): | |||||
def __init__(self, min_date: date, max_date: date): | |||||
super(OutOfRangeDateError, self).__init__() | |||||
self.min_date = min_date | |||||
self.max_date = max_date | |||||
self.msg = _('The date must be between {minimum_date}' | |||||
' and {maximum_date}').format(minimum_date=min_date.strftime(SHORT_DATE_FORMAT), | |||||
maximum_date=max_date.strftime(SHORT_DATE_FORMAT)) |
@@ -24,6 +24,13 @@ _TRANSLATION = gettext.translation('messages', localedir=_LOCALE_DIR, fallback=T | |||||
_ = _TRANSLATION.gettext | _ = _TRANSLATION.gettext | ||||
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') | |||||
SHORT_DATE_FORMAT = _('{month} {day_number}, {year}').format(month='%b', day_number='%d', year='%Y') | |||||
TIME_FORMAT = _('{hours}:{minutes}').format(hours='%H', minutes='%M') | |||||
def ngettext(msgid1, msgid2, number): | def ngettext(msgid1, msgid2, number): | ||||
# Not using ngettext = _TRANSLATION.ngettext because the linter will give an invalid-name error otherwise | # Not using ngettext = _TRANSLATION.ngettext because the linter will give an invalid-name error otherwise | ||||
@@ -8,7 +8,7 @@ msgid "" | |||||
msgstr "" | msgstr "" | ||||
"Project-Id-Version: kosmorro 0.8.0\n" | "Project-Id-Version: kosmorro 0.8.0\n" | ||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" | "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" | ||||
"POT-Creation-Date: 2020-06-02 20:49+0200\n" | |||||
"POT-Creation-Date: 2020-06-06 16:43+0200\n" | |||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" | ||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" | "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" | ||||
"Language-Team: LANGUAGE <LL@li.org>\n" | "Language-Team: LANGUAGE <LL@li.org>\n" | ||||
@@ -59,107 +59,103 @@ msgstr "" | |||||
msgid "Waning crescent" | msgid "Waning crescent" | ||||
msgstr "" | msgstr "" | ||||
#: kosmorrolib/data.py:43 | |||||
#: kosmorrolib/data.py:40 | |||||
msgid "Unavailable" | |||||
msgstr "" | |||||
#: kosmorrolib/data.py:44 | |||||
#, python-format | #, python-format | ||||
msgid "%s is in opposition" | msgid "%s is in opposition" | ||||
msgstr "" | msgstr "" | ||||
#: kosmorrolib/data.py:44 | |||||
#: kosmorrolib/data.py:45 | |||||
#, python-format | #, python-format | ||||
msgid "%s and %s are in conjunction" | msgid "%s and %s are in conjunction" | ||||
msgstr "" | msgstr "" | ||||
#: kosmorrolib/data.py:45 | |||||
#: kosmorrolib/data.py:46 | |||||
#, python-format | #, python-format | ||||
msgid "%s occults %s" | msgid "%s occults %s" | ||||
msgstr "" | msgstr "" | ||||
#: kosmorrolib/data.py:46 | |||||
#: kosmorrolib/data.py:47 | |||||
#, python-format | #, python-format | ||||
msgid "%s's largest elongation" | msgid "%s's largest elongation" | ||||
msgstr "" | msgstr "" | ||||
#: kosmorrolib/data.py:261 | |||||
#: kosmorrolib/data.py:262 | |||||
msgid "Sun" | msgid "Sun" | ||||
msgstr "" | msgstr "" | ||||
#: kosmorrolib/data.py:262 | |||||
#: kosmorrolib/data.py:263 | |||||
msgid "Moon" | msgid "Moon" | ||||
msgstr "" | msgstr "" | ||||
#: kosmorrolib/data.py:263 | |||||
#: kosmorrolib/data.py:264 | |||||
msgid "Mercury" | msgid "Mercury" | ||||
msgstr "" | msgstr "" | ||||
#: kosmorrolib/data.py:264 | |||||
#: kosmorrolib/data.py:265 | |||||
msgid "Venus" | msgid "Venus" | ||||
msgstr "" | msgstr "" | ||||
#: kosmorrolib/data.py:265 | |||||
#: kosmorrolib/data.py:266 | |||||
msgid "Mars" | msgid "Mars" | ||||
msgstr "" | msgstr "" | ||||
#: kosmorrolib/data.py:266 | |||||
#: kosmorrolib/data.py:267 | |||||
msgid "Jupiter" | msgid "Jupiter" | ||||
msgstr "" | msgstr "" | ||||
#: kosmorrolib/data.py:267 | |||||
#: kosmorrolib/data.py:268 | |||||
msgid "Saturn" | msgid "Saturn" | ||||
msgstr "" | msgstr "" | ||||
#: kosmorrolib/data.py:268 | |||||
#: kosmorrolib/data.py:269 | |||||
msgid "Uranus" | msgid "Uranus" | ||||
msgstr "" | msgstr "" | ||||
#: kosmorrolib/data.py:269 | |||||
#: kosmorrolib/data.py:270 | |||||
msgid "Neptune" | msgid "Neptune" | ||||
msgstr "" | msgstr "" | ||||
#: kosmorrolib/data.py:270 | |||||
#: kosmorrolib/data.py:271 | |||||
msgid "Pluto" | msgid "Pluto" | ||||
msgstr "" | msgstr "" | ||||
#: kosmorrolib/dumper.py:36 | |||||
msgid "{day_of_week} {month} {day_number}, {year}" | |||||
msgstr "" | |||||
#: kosmorrolib/dumper.py:38 | |||||
msgid "{month} {day_number}, {hours}:{minutes}" | |||||
msgstr "" | |||||
#: kosmorrolib/dumper.py:40 | |||||
msgid "{hours}:{minutes}" | |||||
msgstr "" | |||||
#: kosmorrolib/dumper.py:120 | |||||
#: kosmorrolib/dumper.py:117 | |||||
msgid "Expected events:" | msgid "Expected events:" | ||||
msgstr "" | msgstr "" | ||||
#: kosmorrolib/dumper.py:124 | |||||
#: kosmorrolib/dumper.py:121 | |||||
msgid "Note: All the hours are given in UTC." | msgid "Note: All the hours are given in UTC." | ||||
msgstr "" | msgstr "" | ||||
#: kosmorrolib/dumper.py:129 | |||||
#: kosmorrolib/dumper.py:126 | |||||
msgid "Note: All the hours are given in the UTC{offset} timezone." | msgid "Note: All the hours are given in the UTC{offset} timezone." | ||||
msgstr "" | msgstr "" | ||||
#: kosmorrolib/dumper.py:175 kosmorrolib/dumper.py:255 | |||||
#: kosmorrolib/dumper.py:172 kosmorrolib/dumper.py:259 | |||||
msgid "Object" | msgid "Object" | ||||
msgstr "" | msgstr "" | ||||
#: kosmorrolib/dumper.py:176 kosmorrolib/dumper.py:256 | |||||
#: kosmorrolib/dumper.py:173 kosmorrolib/dumper.py:260 | |||||
msgid "Rise time" | msgid "Rise time" | ||||
msgstr "" | msgstr "" | ||||
#: kosmorrolib/dumper.py:177 kosmorrolib/dumper.py:257 | |||||
#: kosmorrolib/dumper.py:174 kosmorrolib/dumper.py:261 | |||||
msgid "Culmination time" | msgid "Culmination time" | ||||
msgstr "" | msgstr "" | ||||
#: kosmorrolib/dumper.py:178 kosmorrolib/dumper.py:258 | |||||
#: kosmorrolib/dumper.py:175 kosmorrolib/dumper.py:262 | |||||
msgid "Set time" | msgid "Set time" | ||||
msgstr "" | msgstr "" | ||||
#: kosmorrolib/dumper.py:192 kosmorrolib/dumper.py:262 | |||||
#: kosmorrolib/dumper.py:190 | |||||
msgid "Moon phase is unavailable for this date." | |||||
msgstr "" | |||||
#: kosmorrolib/dumper.py:192 kosmorrolib/dumper.py:266 | |||||
msgid "Moon phase:" | msgid "Moon phase:" | ||||
msgstr "" | msgstr "" | ||||
@@ -167,36 +163,36 @@ msgstr "" | |||||
msgid "{next_moon_phase} on {next_moon_phase_date} at {next_moon_phase_time}" | msgid "{next_moon_phase} on {next_moon_phase_date} at {next_moon_phase_time}" | ||||
msgstr "" | msgstr "" | ||||
#: kosmorrolib/dumper.py:242 | |||||
#: kosmorrolib/dumper.py:246 | |||||
msgid "A Summary of your Sky" | msgid "A Summary of your Sky" | ||||
msgstr "" | msgstr "" | ||||
#: kosmorrolib/dumper.py:246 | |||||
#: kosmorrolib/dumper.py:250 | |||||
msgid "" | msgid "" | ||||
"This document summarizes the ephemerides and the events of {date}. It " | "This document summarizes the ephemerides and the events of {date}. It " | ||||
"aims to help you to prepare your observation session. All the hours are " | "aims to help you to prepare your observation session. All the hours are " | ||||
"given in {timezone}." | "given in {timezone}." | ||||
msgstr "" | msgstr "" | ||||
#: kosmorrolib/dumper.py:252 | |||||
#: kosmorrolib/dumper.py:256 | |||||
msgid "" | msgid "" | ||||
"Don't forget to check the weather forecast before you go out with your " | "Don't forget to check the weather forecast before you go out with your " | ||||
"equipment." | "equipment." | ||||
msgstr "" | msgstr "" | ||||
#: kosmorrolib/dumper.py:254 | |||||
#: kosmorrolib/dumper.py:258 | |||||
msgid "Ephemerides of the day" | msgid "Ephemerides of the day" | ||||
msgstr "" | msgstr "" | ||||
#: kosmorrolib/dumper.py:260 | |||||
#: kosmorrolib/dumper.py:264 | |||||
msgid "hours" | msgid "hours" | ||||
msgstr "" | msgstr "" | ||||
#: kosmorrolib/dumper.py:264 | |||||
#: kosmorrolib/dumper.py:268 | |||||
msgid "Expected events" | msgid "Expected events" | ||||
msgstr "" | msgstr "" | ||||
#: kosmorrolib/dumper.py:378 | |||||
#: kosmorrolib/dumper.py:382 | |||||
msgid "" | msgid "" | ||||
"Building PDFs was not possible, because some dependencies are not " | "Building PDFs was not possible, because some dependencies are not " | ||||
"installed.\n" | "installed.\n" | ||||
@@ -204,6 +200,26 @@ msgid "" | |||||
"information." | "information." | ||||
msgstr "" | msgstr "" | ||||
#: kosmorrolib/exceptions.py:34 | |||||
msgid "The date must be between {minimum_date} and {maximum_date}" | |||||
msgstr "" | |||||
#: kosmorrolib/i18n.py:27 | |||||
msgid "{day_of_week} {month} {day_number}, {year}" | |||||
msgstr "" | |||||
#: kosmorrolib/i18n.py:29 | |||||
msgid "{month} {day_number}, {hours}:{minutes}" | |||||
msgstr "" | |||||
#: kosmorrolib/i18n.py:31 | |||||
msgid "{month} {day_number}, {year}" | |||||
msgstr "" | |||||
#: kosmorrolib/i18n.py:32 | |||||
msgid "{hours}:{minutes}" | |||||
msgstr "" | |||||
#: kosmorrolib/main.py:61 | #: kosmorrolib/main.py:61 | ||||
msgid "" | msgid "" | ||||
"Save the planet and paper!\n" | "Save the planet and paper!\n" | ||||
@@ -217,87 +233,91 @@ msgid "" | |||||
"the observation coordinate." | "the observation coordinate." | ||||
msgstr "" | msgstr "" | ||||
#: kosmorrolib/main.py:98 | |||||
#: kosmorrolib/main.py:90 | |||||
msgid "Could not save the output in \"{path}\": {error}" | msgid "Could not save the output in \"{path}\": {error}" | ||||
msgstr "" | msgstr "" | ||||
#: kosmorrolib/main.py:103 | |||||
#: kosmorrolib/main.py:95 | |||||
msgid "Selected output format needs an output file (--output)." | msgid "Selected output format needs an output file (--output)." | ||||
msgstr "" | msgstr "" | ||||
#: kosmorrolib/main.py:120 | |||||
#: kosmorrolib/main.py:112 | |||||
msgid "Moon phase can only be displayed between {min_date} and {max_date}" | |||||
msgstr "" | |||||
#: kosmorrolib/main.py:134 | |||||
msgid "Running on Python {python_version}" | msgid "Running on Python {python_version}" | ||||
msgstr "" | msgstr "" | ||||
#: kosmorrolib/main.py:126 | |||||
#: kosmorrolib/main.py:140 | |||||
msgid "Do you really want to clear Kosmorro's cache? [yN] " | msgid "Do you really want to clear Kosmorro's cache? [yN] " | ||||
msgstr "" | msgstr "" | ||||
#: kosmorrolib/main.py:133 | |||||
#: kosmorrolib/main.py:147 | |||||
msgid "Answer did not match expected options, cache not cleared." | msgid "Answer did not match expected options, cache not cleared." | ||||
msgstr "" | msgstr "" | ||||
#: kosmorrolib/main.py:142 | |||||
#: kosmorrolib/main.py:156 | |||||
msgid "" | msgid "" | ||||
"Compute the ephemerides and the events for a given date, at a given " | "Compute the ephemerides and the events for a given date, at a given " | ||||
"position on Earth." | "position on Earth." | ||||
msgstr "" | msgstr "" | ||||
#: kosmorrolib/main.py:144 | |||||
#: kosmorrolib/main.py:158 | |||||
msgid "" | msgid "" | ||||
"By default, only the events will be computed for today ({date}).\n" | "By default, only the events will be computed for today ({date}).\n" | ||||
"To compute also the ephemerides, latitude and longitude arguments are " | "To compute also the ephemerides, latitude and longitude arguments are " | ||||
"needed." | "needed." | ||||
msgstr "" | msgstr "" | ||||
#: kosmorrolib/main.py:149 | |||||
#: kosmorrolib/main.py:163 | |||||
msgid "Show the program version" | msgid "Show the program version" | ||||
msgstr "" | msgstr "" | ||||
#: kosmorrolib/main.py:151 | |||||
#: kosmorrolib/main.py:165 | |||||
msgid "Delete all the files Kosmorro stored in the cache." | msgid "Delete all the files Kosmorro stored in the cache." | ||||
msgstr "" | msgstr "" | ||||
#: kosmorrolib/main.py:153 | |||||
#: kosmorrolib/main.py:167 | |||||
msgid "The format under which the information have to be output" | msgid "The format under which the information have to be output" | ||||
msgstr "" | msgstr "" | ||||
#: kosmorrolib/main.py:155 | |||||
#: kosmorrolib/main.py:169 | |||||
msgid "" | msgid "" | ||||
"The observer's latitude on Earth. Can also be set in the " | "The observer's latitude on Earth. Can also be set in the " | ||||
"KOSMORRO_LATITUDE environment variable." | "KOSMORRO_LATITUDE environment variable." | ||||
msgstr "" | msgstr "" | ||||
#: kosmorrolib/main.py:158 | |||||
#: kosmorrolib/main.py:172 | |||||
msgid "" | msgid "" | ||||
"The observer's longitude on Earth. Can also be set in the " | "The observer's longitude on Earth. Can also be set in the " | ||||
"KOSMORRO_LONGITUDE environment variable." | "KOSMORRO_LONGITUDE environment variable." | ||||
msgstr "" | msgstr "" | ||||
#: kosmorrolib/main.py:161 | |||||
#: kosmorrolib/main.py:175 | |||||
msgid "" | msgid "" | ||||
"The date for which the ephemerides must be computed (in the YYYY-MM-DD " | "The date for which the ephemerides must be computed (in the YYYY-MM-DD " | ||||
"format), or as an interval in the \"[+-]YyMmDd\" format (with Y, M, and D" | "format), or as an interval in the \"[+-]YyMmDd\" format (with Y, M, and D" | ||||
" numbers). Defaults to the current date ({default_date})" | " numbers). Defaults to the current date ({default_date})" | ||||
msgstr "" | msgstr "" | ||||
#: kosmorrolib/main.py:166 | |||||
#: kosmorrolib/main.py:180 | |||||
msgid "" | msgid "" | ||||
"The timezone to display the hours in (e.g. 2 for UTC+2 or -3 for UTC-3). " | "The timezone to display the hours in (e.g. 2 for UTC+2 or -3 for UTC-3). " | ||||
"Can also be set in the KOSMORRO_TIMEZONE environment variable." | "Can also be set in the KOSMORRO_TIMEZONE environment variable." | ||||
msgstr "" | msgstr "" | ||||
#: kosmorrolib/main.py:169 | |||||
#: kosmorrolib/main.py:183 | |||||
msgid "Disable the colors in the console." | msgid "Disable the colors in the console." | ||||
msgstr "" | msgstr "" | ||||
#: kosmorrolib/main.py:171 | |||||
#: kosmorrolib/main.py:185 | |||||
msgid "" | msgid "" | ||||
"A file to export the output to. If not given, the standard output is " | "A file to export the output to. If not given, the standard output is " | ||||
"used. This argument is needed for PDF format." | "used. This argument is needed for PDF format." | ||||
msgstr "" | msgstr "" | ||||
#: kosmorrolib/main.py:174 | |||||
#: kosmorrolib/main.py:188 | |||||
msgid "" | msgid "" | ||||
"Do not generate a graph to represent the rise and set times in the PDF " | "Do not generate a graph to represent the rise and set times in the PDF " | ||||
"format." | "format." | ||||
@@ -29,7 +29,7 @@ from . import core | |||||
from . import events | from . import events | ||||
from .data import Position, EARTH | from .data import Position, EARTH | ||||
from .exceptions import UnavailableFeatureError | |||||
from .exceptions import UnavailableFeatureError, OutOfRangeDateError | |||||
from .i18n import _ | from .i18n import _ | ||||
from . import ephemerides | from . import ephemerides | ||||
from .version import VERSION | from .version import VERSION | ||||
@@ -65,39 +65,31 @@ def main(): | |||||
print(colored(_("PDF output will not contain the ephemerides, because you didn't provide the observation " | print(colored(_("PDF output will not contain the ephemerides, because you didn't provide the observation " | ||||
"coordinate."), 'yellow')) | "coordinate."), 'yellow')) | ||||
try: | |||||
timezone = args.timezone | |||||
if timezone is None and environment.timezone is not None: | |||||
timezone = int(environment.timezone) | |||||
elif timezone is None: | |||||
timezone = 0 | |||||
if position is not None: | |||||
eph = ephemerides.get_ephemerides(date=compute_date, position=position, timezone=timezone) | |||||
else: | |||||
eph = None | |||||
moon_phase = ephemerides.get_moon_phase(compute_date) | |||||
timezone = args.timezone | |||||
events_list = events.search_events(compute_date, timezone) | |||||
if timezone is None and environment.timezone is not None: | |||||
timezone = int(environment.timezone) | |||||
elif timezone is None: | |||||
timezone = 0 | |||||
format_dumper = output_formats[output_format](ephemerides=eph, moon_phase=moon_phase, events=events_list, | |||||
date=compute_date, timezone=timezone, with_colors=args.colors, | |||||
show_graph=args.show_graph) | |||||
output = format_dumper.to_string() | |||||
try: | |||||
output = get_information(compute_date, position, timezone, output_format, | |||||
args.colors, args.show_graph) | |||||
except UnavailableFeatureError as error: | except UnavailableFeatureError as error: | ||||
print(colored(error.msg, 'red')) | print(colored(error.msg, 'red')) | ||||
return 2 | return 2 | ||||
except OutOfRangeDateError as error: | |||||
print(colored(error.msg, 'red')) | |||||
return 1 | |||||
if args.output is not None: | if args.output is not None: | ||||
try: | try: | ||||
with open(args.output, 'wb') as output_file: | with open(args.output, 'wb') as output_file: | ||||
output_file.write(output) | |||||
output_file.write(output.to_string()) | |||||
except OSError as error: | except OSError as error: | ||||
print(_('Could not save the output in "{path}": {error}').format(path=args.output, | print(_('Could not save the output in "{path}": {error}').format(path=args.output, | ||||
error=error.strerror)) | error=error.strerror)) | ||||
elif not format_dumper.is_file_output_needed(): | |||||
elif not output.is_file_output_needed(): | |||||
print(output) | print(output) | ||||
else: | else: | ||||
print(colored(_('Selected output format needs an output file (--output).'), color='red')) | print(colored(_('Selected output format needs an output file (--output).'), color='red')) | ||||
@@ -106,6 +98,28 @@ def main(): | |||||
return 0 | return 0 | ||||
def get_information(compute_date: date, position: Position, timezone: int, | |||||
output_format: str, colors: bool, show_graph: bool) -> dumper.Dumper: | |||||
if position is not None: | |||||
eph = ephemerides.get_ephemerides(date=compute_date, position=position, timezone=timezone) | |||||
else: | |||||
eph = None | |||||
try: | |||||
moon_phase = ephemerides.get_moon_phase(compute_date) | |||||
except OutOfRangeDateError as error: | |||||
moon_phase = None | |||||
print(colored(_('Moon phase can only be displayed' | |||||
' between {min_date} and {max_date}').format(min_date=error.min_date, | |||||
max_date=error.max_date), 'yellow')) | |||||
events_list = events.search_events(compute_date, timezone) | |||||
return get_dumpers()[output_format](ephemerides=eph, moon_phase=moon_phase, events=events_list, | |||||
date=compute_date, timezone=timezone, with_colors=colors, | |||||
show_graph=show_graph) | |||||
def get_dumpers() -> {str: dumper.Dumper}: | def get_dumpers() -> {str: dumper.Dumper}: | ||||
return { | return { | ||||
'text': dumper.TextDumper, | 'text': dumper.TextDumper, | ||||
@@ -42,7 +42,7 @@ setup( | |||||
('man/man1', ['manpage/kosmorro.1']), | ('man/man1', ['manpage/kosmorro.1']), | ||||
('man/man7', ['manpage/kosmorro.7']) | ('man/man7', ['manpage/kosmorro.7']) | ||||
], | ], | ||||
install_requires=['skyfield>=1.17.0,<2.0.0', 'tabulate', 'numpy>=1.17.0,<2.0.0', 'termcolor', 'python-dateutil'], | |||||
install_requires=['skyfield>=1.21.0,<2.0.0', 'tabulate', 'numpy>=1.17.0,<2.0.0', 'termcolor', 'python-dateutil'], | |||||
classifiers=[ | classifiers=[ | ||||
'Development Status :: 3 - Alpha', | 'Development Status :: 3 - Alpha', | ||||
'Operating System :: POSIX :: Linux', | 'Operating System :: POSIX :: Linux', | ||||
@@ -269,6 +269,10 @@ class DumperTestCase(unittest.TestCase): | |||||
self._get_events(), date=date(2019, 10, 14)).to_string() | self._get_events(), date=date(2019, 10, 14)).to_string() | ||||
self.assertRegex(latex, r'\\object\{Mars\}\{08:00\}\{13:00\}\{23:00\}') | self.assertRegex(latex, r'\\object\{Mars\}\{08:00\}\{13:00\}\{23:00\}') | ||||
def test_get_moon_with_moon_phase_none(self): | |||||
dumper = TextDumper() | |||||
self.assertEqual('Moon phase is unavailable for this date.', dumper.get_moon(None)) | |||||
@staticmethod | @staticmethod | ||||
def _get_ephemerides(aster_rise_set=False) -> [AsterEphemerides]: | def _get_ephemerides(aster_rise_set=False) -> [AsterEphemerides]: | ||||
rise_time = datetime(2019, 10, 14, 8) if aster_rise_set else None | rise_time = datetime(2019, 10, 14, 8) if aster_rise_set else None | ||||
@@ -3,6 +3,7 @@ from .testutils import expect_assertions | |||||
from kosmorrolib import ephemerides | from kosmorrolib import ephemerides | ||||
from kosmorrolib.data import EARTH, Position, MoonPhase | from kosmorrolib.data import EARTH, Position, MoonPhase | ||||
from datetime import date | from datetime import date | ||||
from kosmorrolib.exceptions import OutOfRangeDateError | |||||
class EphemeridesTestCase(unittest.TestCase): | class EphemeridesTestCase(unittest.TestCase): | ||||
@@ -107,6 +108,14 @@ class EphemeridesTestCase(unittest.TestCase): | |||||
phase = MoonPhase('WANING_CRESCENT', None, None) | phase = MoonPhase('WANING_CRESCENT', None, None) | ||||
self.assertEqual('New Moon', phase.get_next_phase_name()) | self.assertEqual('New Moon', phase.get_next_phase_name()) | ||||
def test_get_ephemerides_raises_exception_on_out_of_date_range(self): | |||||
with self.assertRaises(OutOfRangeDateError): | |||||
ephemerides.get_ephemerides(date(1789, 5, 5), Position(0, 0, EARTH)) | |||||
def test_get_moon_phase_raises_exception_on_out_of_date_range(self): | |||||
with self.assertRaises(OutOfRangeDateError): | |||||
ephemerides.get_moon_phase(date(1789, 5, 5)) | |||||
if __name__ == '__main__': | if __name__ == '__main__': | ||||
unittest.main() | unittest.main() |
@@ -6,6 +6,7 @@ from kosmorrolib import events | |||||
from kosmorrolib.data import Event, ASTERS | from kosmorrolib.data import Event, ASTERS | ||||
from kosmorrolib.core import get_timescale | from kosmorrolib.core import get_timescale | ||||
from unittest_data_provider import data_provider | from unittest_data_provider import data_provider | ||||
from kosmorrolib.exceptions import OutOfRangeDateError | |||||
class EventTestCase(unittest.TestCase): | class EventTestCase(unittest.TestCase): | ||||
@@ -61,6 +62,10 @@ class EventTestCase(unittest.TestCase): | |||||
self.assertEqual(expected_event.__dict__, actual_event.__dict__) | self.assertEqual(expected_event.__dict__, actual_event.__dict__) | ||||
def test_get_events_raises_exception_on_out_of_date_range(self): | |||||
with self.assertRaises(OutOfRangeDateError): | |||||
events.search_events(date(1789, 5, 5)) | |||||
if __name__ == '__main__': | if __name__ == '__main__': | ||||
unittest.main() | unittest.main() |