Переглянути джерело

Merge pull request #6 from Deuchnord/json-dumper

Add JSON output
tags/v0.2.0
Jérôme Deuchnord 4 роки тому
committed by GitHub
джерело
коміт
61e03d585f
Не вдалося знайти GPG ключ що відповідає даному підпису Ідентифікатор GPG ключа: 4AEE18F83AFDEB23
8 змінених файлів з 113 додано та 28 видалено
  1. +3
    -1
      .github/workflows/pythonapp.yml
  2. +11
    -0
      CHANGELOG.md
  3. +13
    -12
      kosmorro
  4. +13
    -1
      kosmorrolib/data.py
  5. +36
    -10
      kosmorrolib/dumper.py
  6. +4
    -4
      kosmorrolib/ephemerides.py
  7. +1
    -0
      test/__init__.py
  8. +32
    -0
      test/dumper.py

+ 3
- 1
.github/workflows/pythonapp.yml Переглянути файл

@@ -4,7 +4,6 @@ on: [push]


jobs: jobs:
build: build:

runs-on: ubuntu-latest runs-on: ubuntu-latest


steps: steps:
@@ -17,6 +16,9 @@ jobs:
run: | run: |
pip install --upgrade pip pipenv pip install --upgrade pip pipenv
pipenv sync -d pipenv sync -d
- name: Unit tests
run: |
pipenv run python -m unittest -v test
- name: Lint - name: Lint
run: | run: |
pipenv run pylint kosmorro *.py kosmorrolib/*.py pipenv run pylint kosmorro *.py kosmorrolib/*.py

+ 11
- 0
CHANGELOG.md Переглянути файл

@@ -6,7 +6,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0


## [Unreleased] ## [Unreleased]


### Added

- Update Numpy to v1.17.4 - Update Numpy to v1.17.4
- Add JSON output (#6)

### Changed



### Removed




## [0.1.0] - 2019-11-10 ## [0.1.0] - 2019-11-10




+ 13
- 12
kosmorro Переглянути файл

@@ -18,21 +18,13 @@


import argparse import argparse
from datetime import date from datetime import date
import numpy
from kosmorrolib import dumper from kosmorrolib import dumper
from kosmorrolib.ephemerides import EphemeridesComputer, Position from kosmorrolib.ephemerides import EphemeridesComputer, Position




# Fixes the "TypeError: Object of type int64 is not JSON serializable"
# See https://stackoverflow.com/a/50577730
def json_default(obj):
if isinstance(obj, numpy.int64):
return int(obj)
raise TypeError('Object of type ' + str(type(obj)) + ' could not be integrated in the JSON')


def main(): def main():
args = get_args()
output_formats = get_dumpers()
args = get_args(list(output_formats.keys()))
year = args.year year = args.year
month = args.month month = args.month
day = args.day day = args.day
@@ -43,11 +35,18 @@ def main():
ephemeris = EphemeridesComputer(Position(args.latitude, args.longitude, altitude=args.altitude)) ephemeris = EphemeridesComputer(Position(args.latitude, args.longitude, altitude=args.altitude))
ephemerides = ephemeris.compute_ephemerides(year, month, day) ephemerides = ephemeris.compute_ephemerides(year, month, day)


dump = dumper.TextDumper(ephemerides)
dump = output_formats[args.format](ephemerides)
print(dump.to_string()) print(dump.to_string())




def get_args():
def get_dumpers() -> {str: dumper.Dumper}:
return {
'text': dumper.TextDumper,
'json': dumper.JsonDumper
}


def get_args(output_formats: [str]):
today = date.today() today = date.today()


parser = argparse.ArgumentParser(description='Compute the ephemerides for a given date, at a given position' parser = argparse.ArgumentParser(description='Compute the ephemerides for a given date, at a given position'
@@ -56,6 +55,8 @@ def get_args():
' observer positioned at coordinates (0,0), with an altitude of 0.' ' observer positioned at coordinates (0,0), with an altitude of 0.'
% today.strftime('%a %b %d, %Y')) % today.strftime('%a %b %d, %Y'))


parser.add_argument('--format', '-f', type=str, default=output_formats[0], choices=output_formats,
help='The format under which the information have to be output')
parser.add_argument('--latitude', '-lat', type=float, default=0., parser.add_argument('--latitude', '-lat', type=float, default=0.,
help="The observer's latitude on Earth") help="The observer's latitude on Earth")
parser.add_argument('--longitude', '-lon', type=float, default=0., parser.add_argument('--longitude', '-lon', type=float, default=0.,


+ 13
- 1
kosmorrolib/data.py Переглянути файл

@@ -22,6 +22,18 @@ from typing import Union
from skyfield.api import Topos from skyfield.api import Topos
from skyfield.timelib import Time from skyfield.timelib import Time


MOON_PHASES = {
'NEW_MOON': 'New Moon',
'FIRST_QUARTER': 'First Quarter',
'FULL_MOON': 'Full Moon',
'LAST_QUARTER': 'Last Quarter'
}


def skyfield_to_moon_phase(val: int) -> str:
phases = list(MOON_PHASES.keys())
return phases[val]



class Position: class Position:
def __init__(self, latitude: float, longitude: float, altitude: float = 0): def __init__(self, latitude: float, longitude: float, altitude: float = 0):
@@ -43,7 +55,7 @@ class AsterEphemerides:
culmination_time: Union[Time, None], culmination_time: Union[Time, None],
set_time: Union[Time, None]): set_time: Union[Time, None]):
self.rise_time = rise_time self.rise_time = rise_time
self.maximum_time = culmination_time
self.culmination_time = culmination_time
self.set_time = set_time self.set_time = set_time






+ 36
- 10
kosmorrolib/dumper.py Переглянути файл

@@ -18,13 +18,15 @@


from abc import ABC, abstractmethod from abc import ABC, abstractmethod
import datetime import datetime
import json
from tabulate import tabulate from tabulate import tabulate
from skyfield import almanac
from .data import Object
from skyfield.timelib import Time
from numpy import int64
from .data import Object, AsterEphemerides, MOON_PHASES




class Dumper(ABC): class Dumper(ABC):
def __init__(self, ephemeris, date: datetime.date = datetime.date.today()):
def __init__(self, ephemeris: dict, date: datetime.date = datetime.date.today()):
self.ephemeris = ephemeris self.ephemeris = ephemeris
self.date = date self.date = date


@@ -33,10 +35,34 @@ class Dumper(ABC):
pass pass




class JsonDumper(Dumper):
def to_string(self):
return json.dumps(self.ephemeris,
default=self._json_default,
indent=4)

@staticmethod
def _json_default(obj):
# Fixes the "TypeError: Object of type int64 is not JSON serializable"
# See https://stackoverflow.com/a/50577730
if isinstance(obj, int64):
return int(obj)
if isinstance(obj, Time):
return obj.utc_iso()
if isinstance(obj, Object):
obj = obj.__dict__
obj.pop('skyfield_name')
return obj
if isinstance(obj, AsterEphemerides):
return obj.__dict__

raise TypeError('Object of type "%s" could not be integrated in the JSON' % str(type(obj)))


class TextDumper(Dumper): class TextDumper(Dumper):
def to_string(self): def to_string(self):
return '\n\n'.join(['Ephemerides of %s' % self.date.strftime('%A %B %d, %Y'), return '\n\n'.join(['Ephemerides of %s' % self.date.strftime('%A %B %d, %Y'),
self.get_asters(self.ephemeris['planets']),
self.get_asters(self.ephemeris['details']),
self.get_moon(self.ephemeris['moon_phase']), self.get_moon(self.ephemeris['moon_phase']),
'Note: All the hours are given in UTC.']) 'Note: All the hours are given in UTC.'])


@@ -52,21 +78,21 @@ class TextDumper(Dumper):
else: else:
planet_rise = '-' planet_rise = '-'


if aster.ephemerides.maximum_time is not None:
planet_maximum = aster.ephemerides.maximum_time.utc_strftime('%H:%M')
if aster.ephemerides.culmination_time is not None:
planet_culmination = aster.ephemerides.culmination_time.utc_strftime('%H:%M')
else: else:
planet_maximum = '-'
planet_culmination = '-'


if aster.ephemerides.set_time is not None: if aster.ephemerides.set_time is not None:
planet_set = aster.ephemerides.set_time.utc_strftime('%H:%M') planet_set = aster.ephemerides.set_time.utc_strftime('%H:%M')
else: else:
planet_set = '-' planet_set = '-'


data.append([name, planet_rise, planet_maximum, planet_set])
data.append([name, planet_rise, planet_culmination, planet_set])


return tabulate(data, headers=['Planet', 'Rise time', 'Culmination time', 'Set time'], tablefmt='simple', return tabulate(data, headers=['Planet', 'Rise time', 'Culmination time', 'Set time'], tablefmt='simple',
stralign='center', colalign=('left',)) stralign='center', colalign=('left',))


@staticmethod @staticmethod
def get_moon(moon):
return 'Moon phase: %s' % almanac.MOON_PHASES[moon['phase']]
def get_moon(moon_phase: str) -> str:
return 'Moon phase: %s' % MOON_PHASES[moon_phase]

+ 4
- 4
kosmorrolib/ephemerides.py Переглянути файл

@@ -20,7 +20,7 @@ import datetime
from skyfield import almanac from skyfield import almanac
from skyfield.timelib import Time from skyfield.timelib import Time


from .data import Object, Position, AsterEphemerides
from .data import Object, Position, AsterEphemerides, skyfield_to_moon_phase
from .core import get_skf_objects, get_timescale, ASTERS, MONTHS from .core import get_skf_objects, get_timescale, ASTERS, MONTHS


RISEN_ANGLE = -0.8333 RISEN_ANGLE = -0.8333
@@ -42,13 +42,13 @@ class EphemeridesComputer:
return {'rise': sunrise, 'set': sunset} return {'rise': sunrise, 'set': sunset}


@staticmethod @staticmethod
def get_moon(year, month, day) -> dict:
def get_moon(year, month, day) -> str:
time1 = get_timescale().utc(year, month, day - 10) time1 = get_timescale().utc(year, month, day - 10)
time2 = get_timescale().utc(year, month, day) time2 = get_timescale().utc(year, month, day)


_, moon_phase = almanac.find_discrete(time1, time2, almanac.moon_phases(get_skf_objects())) _, moon_phase = almanac.find_discrete(time1, time2, almanac.moon_phases(get_skf_objects()))


return {'phase': moon_phase[-1]}
return skyfield_to_moon_phase(moon_phase[-1])


@staticmethod @staticmethod
def get_asters_ephemerides_for_aster(aster, date: datetime.date, position: Position) -> Object: def get_asters_ephemerides_for_aster(aster, date: datetime.date, position: Position) -> Object:
@@ -90,7 +90,7 @@ class EphemeridesComputer:


def compute_ephemerides_for_day(self, year: int, month: int, day: int) -> dict: def compute_ephemerides_for_day(self, year: int, month: int, day: int) -> dict:
return {'moon_phase': self.get_moon(year, month, day), return {'moon_phase': self.get_moon(year, month, day),
'planets': [self.get_asters_ephemerides_for_aster(aster, datetime.date(year, month, day), self.position)
'details': [self.get_asters_ephemerides_for_aster(aster, datetime.date(year, month, day), self.position)
for aster in ASTERS]} for aster in ASTERS]}


def compute_ephemerides_for_month(self, year: int, month: int) -> [dict]: def compute_ephemerides_for_month(self, year: int, month: int) -> [dict]:


+ 1
- 0
test/__init__.py Переглянути файл

@@ -0,0 +1 @@
from .dumper import *

+ 32
- 0
test/dumper.py Переглянути файл

@@ -0,0 +1,32 @@
import unittest
from kosmorrolib.data import AsterEphemerides, Planet
from kosmorrolib.dumper import JsonDumper


class DumperTestCase(unittest.TestCase):
def test_json_dumper_returns_correct_json(self):
data = self._get_data()
self.assertEqual('{\n'
' "moon_phase": "FULL_MOON",\n'
' "details": [\n'
' {\n'
' "name": "Mars",\n'
' "ephemerides": {\n'
' "rise_time": null,\n'
' "culmination_time": null,\n'
' "set_time": null\n'
' }\n'
' }\n'
' ]\n'
'}', JsonDumper(data).to_string())

@staticmethod
def _get_data():
return {
'moon_phase': 'FULL_MOON',
'details': [Planet('Mars', 'MARS', AsterEphemerides(None, None, None))]
}


if __name__ == '__main__':
unittest.main()

Завантаження…
Відмінити
Зберегти