From c063f6e0f50e0c11c0e77c9fe16dedef0133a9af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Deuchnord?= Date: Mon, 9 Mar 2020 19:16:46 +0100 Subject: [PATCH] feat: add support for Moon conjunctions --- Pipfile | 1 + Pipfile.lock | 9 ++- kosmorrolib/events.py | 6 +- kosmorrolib/locales/messages.pot | 2 +- test/events.py | 126 ++++++++++++------------------- 5 files changed, 62 insertions(+), 82 deletions(-) diff --git a/Pipfile b/Pipfile index f72dd1a..3cedc4d 100644 --- a/Pipfile +++ b/Pipfile @@ -8,6 +8,7 @@ pylintfileheader = "*" pylint = "*" codecov = "*" babel = "*" +unittest-data-provider = "*" [packages] skyfield = ">=1.13.0,<2.0.0" diff --git a/Pipfile.lock b/Pipfile.lock index 236545e..2959518 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "789ae3ae412a3b57df763f776aa8ce0497d680de59fa09ad22a9b91a4e3d6b4e" + "sha256": "67748949d467fbdca22ccc963430b21be215566f8a49b56ac734b44023ce5ff2" }, "pipfile-spec": 6, "requires": { @@ -315,6 +315,13 @@ ], "version": "==1.14.0" }, + "unittest-data-provider": { + "hashes": [ + "sha256:86bc7fb6608c2570aeedadea346fe3034afc940807dd7519e95e5dbc899ac2be" + ], + "index": "pypi", + "version": "==1.0.1" + }, "urllib3": { "hashes": [ "sha256:2f3db8b19923a873b3e5256dc9c2dedfa883e33d87c690d9c7913e1f40673cdc", diff --git a/kosmorrolib/events.py b/kosmorrolib/events.py index 41e0e2d..82a1800 100644 --- a/kosmorrolib/events.py +++ b/kosmorrolib/events.py @@ -22,7 +22,7 @@ from skyfield.timelib import Time from skyfield.searchlib import find_discrete, find_maxima from numpy import pi -from .data import Event, Planet, ASTERS +from .data import Event, Star, Planet, ASTERS from .core import get_timescale, get_skf_objects, flatten_list @@ -45,11 +45,11 @@ def _search_conjunction(start_time: Time, end_time: Time) -> [Event]: for aster1 in ASTERS: # Ignore the Sun - if not isinstance(aster1, Planet): + if isinstance(aster1, Star): continue for aster2 in ASTERS: - if not isinstance(aster2, Planet) or aster2 == aster1 or aster2 in computed: + if isinstance(aster2, Star) or aster2 == aster1 or aster2 in computed: continue times, is_conjs = find_discrete(start_time, end_time, is_in_conjunction) diff --git a/kosmorrolib/locales/messages.pot b/kosmorrolib/locales/messages.pot index 4a1cd62..e2c73fe 100644 --- a/kosmorrolib/locales/messages.pot +++ b/kosmorrolib/locales/messages.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: kosmorro 0.6.1\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2020-03-08 10:46+0100\n" +"POT-Creation-Date: 2020-03-09 21:52+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" diff --git a/test/events.py b/test/events.py index 1de895e..638c097 100644 --- a/test/events.py +++ b/test/events.py @@ -1,91 +1,63 @@ import unittest -from datetime import date +from datetime import date, datetime from kosmorrolib import events -from kosmorrolib.data import Event +from kosmorrolib.data import Event, ASTERS from kosmorrolib.core import get_timescale +from unittest_data_provider import data_provider -class MyTestCase(unittest.TestCase): +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()) - def test_find_oppositions(self): - # Test case: Mars opposition - # Source of the information: https://promenade.imcce.fr/en/pages6/887.html#mar - o1 = (events.search_events(date(2020, 10, 13)), '^2020-10-13T23:25') - o2 = (events.search_events(date(2022, 12, 8)), '^2022-12-08T05:41') - o3 = (events.search_events(date(2025, 1, 16)), '^2025-01-16T02:38') - o4 = (events.search_events(date(2027, 2, 19)), '^2027-02-19T15:50') - - for (o, expected_date) in [o1, o2, o3, o4]: - 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.isoformat(), expected_date) - self.assertIsNone(o[0].end_time) - self.assertEqual('Mars is in opposition', o[0].get_description()) - - def test_find_conjunctions(self): - MERCURY = 'MERCURY' - JUPITER = 'JUPITER BARYCENTER' - SATURN = 'SATURN BARYCENTER' - PLUTO = 'PLUTO BARYCENTER' - - c1 = (events.search_events(date(2020, 1, 2)), [([MERCURY, JUPITER], '^2020-01-02T16:41')]) - c2 = (events.search_events(date(2020, 1, 12)), [([MERCURY, SATURN], '^2020-01-12T09:51'), - ([MERCURY, PLUTO], '^2020-01-12T10:13'), - ([SATURN, PLUTO], '^2020-01-12T16:57')]) - c3 = (events.search_events(date(2020, 2, 7)), []) - - for (c, expected_dates) in [c1, c2]: - self.assertEqual(len(expected_dates), len(c), - 'Expected %d event(s) for %s, got %d' % (len(expected_dates), expected_dates, len(c))) - - i = 0 - for conjunction in c: - self.assertEqual('CONJUNCTION', conjunction.event_type) - objects, expected_date = expected_dates[i] - - j = 0 - self.assertRegex(conjunction.start_time.isoformat(), expected_date) - for object in objects: - self.assertEqual(object, conjunction.objects[j].skyfield_name) - j += 1 - - self.assertIsNone(conjunction.end_time) - self.assertRegex(conjunction.get_description(), ' are in conjunction$') - - i += 1 - - def test_find_maximal_elongation(self): - e = events.search_events(date(2020, 2, 10)) - self.assertEquals(1, len(e), 'Expected 1 events, got %d.' % len(e)) - e = e[0] - self.assertEquals('MAXIMAL_ELONGATION', e.event_type) - self.assertEquals(1, len(e.objects)) - self.assertEquals('MERCURY', e.objects[0].skyfield_name) - self.assertEqual('18.2°', e.details) - self.assertEquals((2020, 2, 10, 13, 46), (e.start_time.year, e.start_time.month, e.start_time.day, - e.start_time.hour, e.start_time.minute)) - - e = events.search_events(date(2020, 3, 24)) - self.assertEquals(2, len(e), 'Expected 2 events, got %d.' % len(e)) - self.assertEquals('MAXIMAL_ELONGATION', e[0].event_type) - self.assertEquals(1, len(e[0].objects)) - self.assertEquals('MERCURY', e[0].objects[0].skyfield_name) - self.assertEqual('27.8°', e[0].details) - self.assertEquals((2020, 3, 24, 1, 56), (e[0].start_time.year, e[0].start_time.month, e[0].start_time.day, - e[0].start_time.hour, e[0].start_time.minute)) - - self.assertEquals('MAXIMAL_ELONGATION', e[1].event_type) - self.assertEquals(1, len(e[1].objects)) - self.assertEquals('VENUS', e[1].objects[0].skyfield_name) - self.assertEqual('46.1°', e[1].details) - self.assertEquals((2020, 3, 24, 21, 58), (e[1].start_time.year, e[1].start_time.month, e[1].start_time.day, - e[1].start_time.hour, e[1].start_time.minute)) + expected_events_provider = lambda: ( + (date(2020, 2, 7), []), + + (date(2020, 10, 13), [Event('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(2025, 1, 16), [Event('OPPOSITION', [ASTERS[4]], datetime(2025, 1, 16, 2, 38))]), + + (date(2027, 2, 19), [Event('OPPOSITION', [ASTERS[4]], datetime(2027, 2, 19, 15, 50))]), + + (date(2020, 1, 2), [Event('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, 2, 10), [Event('MAXIMAL_ELONGATION', [ASTERS[2]], datetime(2020, 2, 10, 13, 46), details='18.2°')]), + + (date(2020, 3, 24), [Event('MAXIMAL_ELONGATION', [ASTERS[2]], datetime(2020, 3, 24, 1, 56), details='27.8°'), + Event('MAXIMAL_ELONGATION', [ASTERS[3]], datetime(2020, 3, 24, 21, 58), details='46.1°')]), + ) + + @data_provider(expected_events_provider) + def test_search_events(self, d: date, expected_events: [Event]): + actual_events = events.search_events(d) + self.assertEqual(len(expected_events), len(actual_events), + 'Expected %d elements, got %d for date %s.' % (len(expected_events), + len(actual_events), + d.isoformat())) + + for i, expected_event in enumerate(expected_events): + actual_event = actual_events[i] + # Remove unnecessary precision (seconds and microseconds) + actual_event.start_time = datetime(actual_event.start_time.year, + actual_event.start_time.month, + actual_event.start_time.day, + actual_event.start_time.hour, + actual_event.start_time.minute) + + self.assertEqual(expected_event.__dict__, actual_event.__dict__) if __name__ == '__main__':