Browse Source

feat(i18n): make the strings translatable

tags/v0.5.0
Jérôme Deuchnord 4 years ago
parent
commit
c4f07a1d64
No known key found for this signature in database GPG Key ID: BC6F3C345B7D33B0
18 changed files with 551 additions and 141 deletions
  1. +13
    -0
      .github/workflows/pythonapp.yml
  2. +4
    -1
      .github/workflows/release.yml
  3. +4
    -0
      .gitignore
  4. +52
    -0
      .scripts/build/getlangs.py
  5. +31
    -0
      CONTRIBUTING.md
  6. +1
    -0
      MANIFEST.in
  7. +1
    -0
      Pipfile
  8. +17
    -2
      Pipfile.lock
  9. +3
    -102
      kosmorro
  10. +11
    -10
      kosmorrolib/core.py
  11. +12
    -10
      kosmorrolib/data.py
  12. +26
    -13
      kosmorrolib/dumper.py
  13. +30
    -0
      kosmorrolib/i18n.py
  14. +205
    -0
      kosmorrolib/locales/messages.pot
  15. +125
    -0
      kosmorrolib/main.py
  16. +12
    -0
      setup.cfg
  17. +1
    -0
      setup.py
  18. +3
    -3
      test/dumper.py

+ 13
- 0
.github/workflows/pythonapp.yml View File

@@ -26,4 +26,17 @@ jobs:
- name: Lint
run: |
pipenv run pylint kosmorro *.py kosmorrolib/*.py
- name: Check i18n
run: |
pipenv run python setup.py extract_messages --output-file=/tmp/kosmorro-messages.pot > /dev/null
n=$(diff -y --suppress-common-lines kosmorrolib/locales/messages.pot /tmp/kosmorro-messages.pot | grep -v -E '^"POT-Creation-Date: ' | wc -l)

if [ "$n" -ne "0" ]; then
echo "❌ The messages file is not up-to-date!"
echo " Please run the following command to fix this:"
echo
echo " pipenv run python setup.py extract_messages --output-file=kosmorrolib/locales/messages.pot"
exit 1
fi

echo "✔ Messages file up-to-date."

+ 4
- 1
.github/workflows/release.yml View File

@@ -16,11 +16,14 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install setuptools wheel twine skyfield numpy tabulate
pip install setuptools wheel twine skyfield numpy tabulate Babel requests
- name: Build and publish
env:
TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
POEDITOR_API_ACCESS: ${{ secrets.POEDITOR_API_ACCESS }}
POEDITOR_PROJECT_ID: 306433
run: |
python .scripts/build/getlangs.py
python setup.py sdist bdist_wheel
twine upload dist/*

+ 4
- 0
.gitignore View File

@@ -6,3 +6,7 @@ kosmorro.egg-info
coverage.xml
node_modules/
package-lock.json

# Translation files are taken care on https://poeditor.com/join/project/GXuhLpdaoh
*.mo
*.po

+ 52
- 0
.scripts/build/getlangs.py View File

@@ -0,0 +1,52 @@
#!/usr/bin/env python3

# This script's purpose is to retrieve the translations from POEditor (https://poeditor.com).
# It is mainly used in the release process.
# (c) Jérôme Deuchnord - MIT License

import os
import requests

POEDITOR_URL = 'https://api.poeditor.com/v2'
API_TOKEN = os.environ['POEDITOR_API_ACCESS']
PROJECT_ID = os.environ['POEDITOR_PROJECT_ID']

languages = requests.post('%s/languages/list' % POEDITOR_URL,
data={'api_token': API_TOKEN,
'id': PROJECT_ID})

json = languages.json()

if languages.status_code != 200:
raise AssertionError(json['response']['message'])

for language in json['result']['languages']:
if language['percentage'] < 100:
# Ignore unfinished translations
continue

print('Importing finished translation for %s... ' % language['name'], end='')

translations = requests.post('%s/projects/export' % POEDITOR_URL,
data={'api_token': API_TOKEN,
'id': PROJECT_ID,
'language': language['code'],
'type': 'po'})

if translations.status_code != 200:
print('Failed!')
raise AssertionError(translations.json()['response']['message'])

translations = requests.get(translations.json()['result']['url'])

if translations.status_code != 200:
print('Failed!')
raise AssertionError('URL given by the API returned a %d status code' % translations.status_code)

os.makedirs('kosmorrolib/locales/%s/LC_MESSAGES' % language['code'], exist_ok=True)

with open('kosmorrolib/locales/%s/LC_MESSAGES/messages.po' % language['code'], 'w') as file:
file.write(translations.text)

print('OK')


+ 31
- 0
CONTRIBUTING.md View File

@@ -15,6 +15,10 @@ If it is not, [create a bug report](https://github.com/Deuchnord/kosmorro/issues
Have an idea of feature you think would be nice on Kosmorro? Time to suggest it!
First, please check someone didn't suggest your next revolution in the _Issues_ tab. If it's not, [create a feature request](https://github.com/Deuchnord/kosmorro/issues/new/choose) and fill in the templace that offers to you.

## Translating

If you speak another language than English, another nice way to enhance Kosmorro is to translate its messages. The recommended way to begin translating Kosmorro is to [join the POEditor team](https://poeditor.com/join/project/GXuhLpdaoh).

## Writing code

First of all, if you are fixing an opened issue, check that nobody is already working on it — if someone seems to be but their Pull Request seems stuck, please ask them first if you can continue the development. If you retake the code they produced, **don't change the author of the commits**.
@@ -22,6 +26,33 @@ First of all, if you are fixing an opened issue, check that nobody is already wo
Before writing the code, first create a fork of the repository and clone it. You may also want to add the original repository (`Deuchnord/kosmorro`), so you can update your fork with the last upstream commits.
Then create a new branch and start coding. Finally, commit and push, then open a PR on this project. If your project is not complete, feel free to open it as Draft (if you forgot to activate the Draft status, just edit the first comment to say it), then mark it as ready for review when you're done.

### Dealing with the translations

The messages file contains all the messages Kosmorro can display, in order to make them translatable. When you change code, you may change also the messages displayed by the software.

When you add a new string that will be displayed to the end user, please pass it to the `_()` function made available in the `kosmorrolib.i18n` package, for instance:

```python
# Dont:
print('Note: All the hours are given in UTC.')

# Do:
from kosmorrolib.i18n import _
print(_('Note: All the hours are given in UTC.'))
```

This will allow Python's internationalization tool to translate the string in any available language.

Once you have done your work, please remember to tell [Babel](http://babel.pocoo.org) to extract the new strings:

```console
$ pipenv run python setup.py extract_messages --output-file=kosmorrolib/locales/messages.pot
```

> If the `setup.py` script tells you that the `extract_messages` command does not exist, then run `kosmorro sync` to ensure all the dev dependencies are installed and try again.

Note that if you forget to update the messages file, the CI will fail.

### Matching the coding standards

Kosmorro's source code follows the major coding standards of Python (PEPs). Before marking your Pull Request as ready for review, don't forget to check that the code respects the coding standards with PyLint (it is run on the CI, but feel free to run it on your local machine too). Your PR must have a global note of 10/10 to be elligible to merge.


+ 1
- 0
MANIFEST.in View File

@@ -0,0 +1 @@
recursive-include kosmorrolib/locales *

+ 1
- 0
Pipfile View File

@@ -7,6 +7,7 @@ verify_ssl = true
pylintfileheader = "*"
pylint = "*"
codecov = "*"
babel = "*"

[packages]
skyfield = ">=1.13.0,<2.0.0"


+ 17
- 2
Pipfile.lock View File

@@ -1,11 +1,11 @@
{
"_meta": {
"hash": {
"sha256": "127a13b5d3a3504bc6ccd84d14ed70b7d4561d1fc2a2c2dd5b2b55996a3a042d"
"sha256": "d5c2451a4f189a6d10a9205879f066fd9b595723f2e1077416aa3f48cdcbfb9f"
},
"pipfile-spec": 6,
"requires": {
"python_version": "3.8"
"python_version": "3"
},
"sources": [
{
@@ -78,6 +78,14 @@
],
"version": "==2.3.3"
},
"babel": {
"hashes": [
"sha256:1aac2ae2d0d8ea368fa90906567f5c08463d98ade155c0c4bfedd6a0f7160e38",
"sha256:d670ea0b10f8b723672d3a6abeb87b565b244da220d76b4dba1b66269ec152d4"
],
"index": "pypi",
"version": "==2.8.0"
},
"certifi": {
"hashes": [
"sha256:017c25db2a153ce562900032d5bc68e9f191e44e9a0f762f373977de9df1fbb3",
@@ -199,6 +207,13 @@
"index": "pypi",
"version": "==0.1.0"
},
"pytz": {
"hashes": [
"sha256:1c557d7d0e871de1f5ccd5833f60fb2550652da6be2693c1e02300743d21500d",
"sha256:b02c06db6cf09c12dd25137e563b31700d3b80fcc4ad23abb7a315f2789819be"
],
"version": "==2019.3"
},
"requests": {
"hashes": [
"sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4",


+ 3
- 102
kosmorro View File

@@ -16,110 +16,11 @@
# 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/>.

import argparse
import sys
from datetime import date

from kosmorrolib.version import VERSION
from kosmorrolib import dumper
from kosmorrolib import core
from kosmorrolib.ephemerides import EphemeridesComputer, Position
from kosmorrolib import events


def main():
output_formats = get_dumpers()
args = get_args(list(output_formats.keys()))

if args.special_action is not None:
return 0 if args.special_action() else 1

year = args.year
month = args.month
day = args.day

compute_date = date(year, month, day)

if day is not None and month is None:
month = date.today().month

if args.latitude is None or args.longitude is None:
position = None
else:
position = Position(args.latitude, args.longitude)

ephemeris = EphemeridesComputer(position)
ephemerides = ephemeris.compute_ephemerides(year, month, day)

events_list = events.search_events(compute_date)

dump = output_formats[args.format](ephemerides, events_list, compute_date)
print(dump.to_string())

return 0


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


def output_version() -> bool:
python_version = '%d.%d.%d' % (sys.version_info[0], sys.version_info[1], sys.version_info[2])
print('Kosmorro %s' % VERSION)
print('Running on Python %s' % python_version)

return True


def clear_cache() -> bool:
confirm = input("Do you really want to clear Kosmorro's cache? [yN] ").upper()
if confirm == 'Y':
try:
core.clear_cache()
except FileNotFoundError:
pass
elif confirm not in ('N', ''):
print('Answer did not match expected options, cache not cleared.')
return False

return True


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

parser = argparse.ArgumentParser(description='Compute the ephemerides and the events for a given date,'
' at a given position on Earth.',
epilog='By default, only the events will be computed for today (%s).\n'
'To compute also the ephemerides, latitude and longitude arguments'
' are needed.'
% today.strftime('%a %b %d, %Y'))

parser.add_argument('--version', '-v', dest='special_action', action='store_const', const=output_version,
default=None, help='Show the program version')
parser.add_argument('--clear-cache', dest='special_action', action='store_const', const=clear_cache, default=None,
help='Delete all the files Kosmorro stored in the cache.')
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=None,
help="The observer's latitude on Earth")
parser.add_argument('--longitude', '-lon', type=float, default=None,
help="The observer's longitude on Earth")
parser.add_argument('--day', '-d', type=int, default=today.day,
help='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 %d (the current day).' % today.day)
parser.add_argument('--month', '-m', type=int, default=today.month,
help='A number between 1 and 12. The month you want to compute the ephemerides for. Defaults to'
' %d (the current month).' % today.month)
parser.add_argument('--year', '-y', type=int, default=today.year,
help='The year you want to compute the ephemerides for.'
' Defaults to %d (the current year).' % today.year)

return parser.parse_args()
import locale
from kosmorrolib.main import main

locale.setlocale(locale.LC_ALL, '')

if __name__ == '__main__':
sys.exit(main())

+ 11
- 10
kosmorrolib/core.py View File

@@ -25,21 +25,22 @@ from skyfield.timelib import Time
from skyfield.nutationlib import iau2000b

from .data import Star, Planet, Satellite, MOON_PHASES, MoonPhase
from .i18n import _

CACHE_FOLDER = str(Path.home()) + '/.kosmorro-cache'

MONTHS = ['JAN', 'FEB', 'MAR', 'APR', 'MAY', 'JUN', 'JUL', 'AUG', 'SEP', 'OCT', 'NOV', 'DEC']

ASTERS = [Star('Sun', 'SUN'),
Satellite('Moon', 'MOON'),
Planet('Mercury', 'MERCURY'),
Planet('Venus', 'VENUS'),
Planet('Mars', 'MARS'),
Planet('Jupiter', 'JUPITER BARYCENTER'),
Planet('Saturn', 'SATURN BARYCENTER'),
Planet('Uranus', 'URANUS BARYCENTER'),
Planet('Neptune', 'NEPTUNE BARYCENTER'),
Planet('Pluto', 'PLUTO BARYCENTER')]
ASTERS = [Star(_('Sun'), 'SUN'),
Satellite(_('Moon'), 'MOON'),
Planet(_('Mercury'), 'MERCURY'),
Planet(_('Venus'), 'VENUS'),
Planet(_('Mars'), 'MARS'),
Planet(_('Jupiter'), 'JUPITER BARYCENTER'),
Planet(_('Saturn'), 'SATURN BARYCENTER'),
Planet(_('Uranus'), 'URANUS BARYCENTER'),
Planet(_('Neptune'), 'NEPTUNE BARYCENTER'),
Planet(_('Pluto'), 'PLUTO BARYCENTER')]


def get_loader():


+ 12
- 10
kosmorrolib/data.py View File

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

from .i18n import _

MOON_PHASES = {
'NEW_MOON': 'New Moon',
'WAXING_CRESCENT': 'Waxing crescent',
'FIRST_QUARTER': 'First Quarter',
'WAXING_GIBBOUS': 'Waxing gibbous',
'FULL_MOON': 'Full Moon',
'WANING_GIBBOUS': 'Waning gibbous',
'LAST_QUARTER': 'Last Quarter',
'WANING_CRESCENT': 'Waning crescent'
'NEW_MOON': _('New Moon'),
'WAXING_CRESCENT': _('Waxing crescent'),
'FIRST_QUARTER': _('First Quarter'),
'WAXING_GIBBOUS': _('Waxing gibbous'),
'FULL_MOON': _('Full Moon'),
'WANING_GIBBOUS': _('Waning gibbous'),
'LAST_QUARTER': _('Last Quarter'),
'WANING_CRESCENT': _('Waning crescent')
}

EVENTS = {
'OPPOSITION': {'message': '%s is in opposition'},
'CONJUNCTION': {'message': '%s and %s are in conjunction'}
'OPPOSITION': {'message': _('%s is in opposition')},
'CONJUNCTION': {'message': _('%s and %s are in conjunction')}
}




+ 26
- 13
kosmorrolib/dumper.py View File

@@ -23,6 +23,11 @@ from tabulate import tabulate
from skyfield.timelib import Time
from numpy import int64
from .data import Object, AsterEphemerides, MoonPhase, Event
from .i18n import _

FULL_DATE_FORMAT = _('{day_of_week} {month} {day_number}, {year}').format(day_of_week='%A', month='%B',
day_number='%d', year='%Y')
TIME_FORMAT = _('{hours}:{minutes}').format(hours='%H', minutes='%M')


class Dumper(ABC):
@@ -75,7 +80,9 @@ class JsonDumper(Dumper):

class TextDumper(Dumper):
def to_string(self):
text = self.date.strftime('%A %B %d, %Y')
text = self.date.strftime(FULL_DATE_FORMAT)
# Always capitalize the first character
text = ''.join([text[0].upper(), text[1:]])

if len(self.ephemeris['details']) > 0:
text = '\n\n'.join([text,
@@ -88,11 +95,11 @@ class TextDumper(Dumper):

if len(self.events) > 0:
text = '\n\n'.join([text,
'Expected events:',
_('Expected events:'),
self.get_events(self.events)
])

text = '\n\n'.join([text, 'Note: All the hours are given in UTC.'])
text = '\n\n'.join([text, _('Note: All the hours are given in UTC.')])

return text

@@ -104,37 +111,43 @@ class TextDumper(Dumper):
name = aster.name

if aster.ephemerides.rise_time is not None:
planet_rise = aster.ephemerides.rise_time.utc_strftime('%H:%M')
planet_rise = aster.ephemerides.rise_time.utc_strftime(TIME_FORMAT)
else:
planet_rise = '-'

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

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(TIME_FORMAT)
else:
planet_set = '-'

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

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

@staticmethod
def get_events(events: [Event]) -> str:
data = []

for event in events:
data.append([event.start_time.utc_strftime('%H:%M'), event.get_description()])
data.append([event.start_time.utc_strftime(TIME_FORMAT), event.get_description()])

return tabulate(data, tablefmt='plain', stralign='left')

@staticmethod
def get_moon(moon_phase: MoonPhase) -> str:
return 'Moon phase: %s\n' \
'%s on %s' % (moon_phase.get_phase(),
moon_phase.get_next_phase(),
moon_phase.next_phase_date.utc_strftime('%a %b %-d, %Y %H:%M'))
current_moon_phase = _('Moon phase: {current_moon_phase}').format(
current_moon_phase=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)
)

return '\n'.join([current_moon_phase, new_moon_phase])

+ 30
- 0
kosmorrolib/i18n.py View File

@@ -0,0 +1,30 @@
#!/usr/bin/env python3

# Kosmorro - Compute The Next Ephemerides
# Copyright (C) 2019 Jérôme Deuchnord <jerome@deuchnord.fr>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.

import gettext
import os

_LOCALE_DIR = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'locales')
_TRANSLATION = gettext.translation('messages', localedir=_LOCALE_DIR, fallback=True)

_ = _TRANSLATION.gettext


def ngettext(msgid1, msgid2, number):
# Not using ngettext = _TRANSLATION.ngettext because the linter will give an invalid-name error otherwise
return _TRANSLATION.ngettext(msgid1, msgid2, number)

+ 205
- 0
kosmorrolib/locales/messages.pot View File

@@ -0,0 +1,205 @@
# Translations template for kosmorro.
# Copyright (C) 2020 ORGANIZATION
# This file is distributed under the same license as the kosmorro project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2020.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: kosmorro 0.4.0\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2020-01-02 14:05+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"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.8.0\n"

#: kosmorrolib/core.py:34
msgid "Sun"
msgstr ""

#: kosmorrolib/core.py:35
msgid "Moon"
msgstr ""

#: kosmorrolib/core.py:36
msgid "Mercury"
msgstr ""

#: kosmorrolib/core.py:37
msgid "Venus"
msgstr ""

#: kosmorrolib/core.py:38
msgid "Mars"
msgstr ""

#: kosmorrolib/core.py:39
msgid "Jupiter"
msgstr ""

#: kosmorrolib/core.py:40
msgid "Saturn"
msgstr ""

#: kosmorrolib/core.py:41
msgid "Uranus"
msgstr ""

#: kosmorrolib/core.py:42
msgid "Neptune"
msgstr ""

#: kosmorrolib/core.py:43
msgid "Pluto"
msgstr ""

#: kosmorrolib/data.py:28
msgid "New Moon"
msgstr ""

#: kosmorrolib/data.py:29
msgid "Waxing crescent"
msgstr ""

#: kosmorrolib/data.py:30
msgid "First Quarter"
msgstr ""

#: kosmorrolib/data.py:31
msgid "Waxing gibbous"
msgstr ""

#: kosmorrolib/data.py:32
msgid "Full Moon"
msgstr ""

#: kosmorrolib/data.py:33
msgid "Waning gibbous"
msgstr ""

#: kosmorrolib/data.py:34
msgid "Last Quarter"
msgstr ""

#: kosmorrolib/data.py:35
msgid "Waning crescent"
msgstr ""

#: kosmorrolib/data.py:39
#, python-format
msgid "%s is in opposition"
msgstr ""

#: kosmorrolib/data.py:40
#, python-format
msgid "%s and %s are in conjunction"
msgstr ""

#: kosmorrolib/dumper.py:28
msgid "{day_of_week} {month} {day_number}, {year}"
msgstr ""

#: kosmorrolib/dumper.py:30
msgid "{hours}:{minutes}"
msgstr ""

#: kosmorrolib/dumper.py:98
msgid "Expected events:"
msgstr ""

#: kosmorrolib/dumper.py:102
msgid "Note: All the hours are given in UTC."
msgstr ""

#: kosmorrolib/dumper.py:130
msgid "Object"
msgstr ""

#: kosmorrolib/dumper.py:130
msgid "Rise time"
msgstr ""

#: kosmorrolib/dumper.py:130
msgid "Culmination time"
msgstr ""

#: kosmorrolib/dumper.py:130
msgid "Set time"
msgstr ""

#: kosmorrolib/dumper.py:144
msgid "Moon phase: {current_moon_phase}"
msgstr ""

#: kosmorrolib/dumper.py:147
msgid "{next_moon_phase} on {next_moon_phase_date} at {next_moon_phase_time}"
msgstr ""

#: kosmorrolib/main.py:76
msgid "Running on Python {python_version}"
msgstr ""

#: kosmorrolib/main.py:82
msgid "Do you really want to clear Kosmorro's cache? [yN] "
msgstr ""

#: kosmorrolib/main.py:89
msgid "Answer did not match expected options, cache not cleared."
msgstr ""

#: kosmorrolib/main.py:98
msgid ""
"Compute the ephemerides and the events for a given date, at a given "
"position on Earth."
msgstr ""

#: kosmorrolib/main.py:100
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:105
msgid "Show the program version"
msgstr ""

#: kosmorrolib/main.py:107
msgid "Delete all the files Kosmorro stored in the cache."
msgstr ""

#: kosmorrolib/main.py:109
msgid "The format under which the information have to be output"
msgstr ""

#: kosmorrolib/main.py:111
msgid "The observer's latitude on Earth"
msgstr ""

#: kosmorrolib/main.py:113
msgid "The observer's longitude on Earth"
msgstr ""

#: kosmorrolib/main.py:115
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:119
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:122
msgid ""
"The year you want to compute the ephemerides for. Defaults to "
"{default_year} (the current year)."
msgstr ""


+ 125
- 0
kosmorrolib/main.py View File

@@ -0,0 +1,125 @@
#!/usr/bin/env python3

# Kosmorro - Compute The Next Ephemerides
# Copyright (C) 2019 Jérôme Deuchnord <jerome@deuchnord.fr>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.

import argparse
import locale
import re
import sys

from datetime import date

from kosmorrolib.version import VERSION
from kosmorrolib import dumper
from kosmorrolib import core
from kosmorrolib import events
from kosmorrolib.i18n import _
from .ephemerides import EphemeridesComputer, Position


def main():
output_formats = get_dumpers()
args = get_args(list(output_formats.keys()))

if args.special_action is not None:
return 0 if args.special_action() else 1

year = args.year
month = args.month
day = args.day

compute_date = date(year, month, day)

if day is not None and month is None:
month = date.today().month

if args.latitude is None or args.longitude is None:
position = None
else:
position = Position(args.latitude, args.longitude)

ephemeris = EphemeridesComputer(position)
ephemerides = ephemeris.compute_ephemerides(year, month, day)

events_list = events.search_events(compute_date)

dump = output_formats[args.format](ephemerides, events_list, compute_date)
print(dump.to_string())

return 0


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


def output_version() -> bool:
python_version = '%d.%d.%d' % (sys.version_info[0], sys.version_info[1], sys.version_info[2])
print('Kosmorro %s' % VERSION)
print(_('Running on Python {python_version}').format(python_version=python_version))

return True


def clear_cache() -> bool:
confirm = input(_("Do you really want to clear Kosmorro's cache? [yN] ")).upper()
if re.match(locale.nl_langinfo(locale.YESEXPR), confirm) is not None:
try:
core.clear_cache()
except FileNotFoundError:
pass
elif confirm != '' and re.match(locale.nl_langinfo(locale.NOEXPR), confirm) is None:
print(_('Answer did not match expected options, cache not cleared.'))
return False

return True


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

parser = argparse.ArgumentParser(description=_('Compute the ephemerides and the events for a given date,'
' at a given position on Earth.'),
epilog=_('By default, only the events will be computed for today ({date}).\n'
'To compute also the ephemerides, latitude and longitude arguments'
' are needed.').format(date=today.strftime(dumper.FULL_DATE_FORMAT)))

parser.add_argument('--version', '-v', dest='special_action', action='store_const', const=output_version,
default=None, help=_('Show the program version'))
parser.add_argument('--clear-cache', dest='special_action', action='store_const', const=clear_cache, default=None,
help=_('Delete all the files Kosmorro stored in the cache.'))
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=None,
help=_("The observer's latitude on Earth"))
parser.add_argument('--longitude', '-lon', type=float, default=None,
help=_("The observer's longitude on Earth"))
parser.add_argument('--day', '-d', type=int, default=today.day,
help=_('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).').format(
default_day=today.day))
parser.add_argument('--month', '-m', type=int, default=today.month,
help=_('A number between 1 and 12. The month you want to compute the ephemerides for.'
' Defaults to {default_month} (the current month).').format(default_month=today.month))
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))

return parser.parse_args()

+ 12
- 0
setup.cfg View File

@@ -0,0 +1,12 @@
[extract_message]
charset = utf-8
keywords = _ ngettext
width = 120
output_file = kosmorrolib/locales/messages.pot
omit_header = true
copyright_holder = Jérôme Deuchnord
input_paths=kosmorro,kosmorrolib

[compile_catalog]
domain = messages
directory = kosmorrolib/locales

+ 1
- 0
setup.py View File

@@ -37,6 +37,7 @@ setup(
keywords='kosmorro astronomy ephemerides ephemeris',
packages=find_packages(),
scripts=['kosmorro'],
include_package_data=True,
install_requires=['skyfield>=1.13.0,<2.0.0', 'tabulate', 'numpy>=1.17.0,<2.0.0'],
classifiers=[
'Development Status :: 3 - Alpha',


+ 3
- 3
test/dumper.py View File

@@ -50,7 +50,7 @@ class DumperTestCase(unittest.TestCase):
'-------- ----------- ------------------ ----------\n'
'Mars - - -\n\n'
'Moon phase: Full Moon\n'
'Last Quarter on Mon Oct 21, 2019 00:00\n\n'
'Last Quarter on Monday October 21, 2019 at 00:00\n\n'
'Note: All the hours are given in UTC.',
TextDumper(ephemerides, [], date=date(2019, 10, 14)).to_string())

@@ -61,7 +61,7 @@ class DumperTestCase(unittest.TestCase):
'-------- ----------- ------------------ ----------\n'
'Mars - - -\n\n'
'Moon phase: Full Moon\n'
'Last Quarter on Mon Oct 21, 2019 00:00\n\n'
'Last Quarter on Monday October 21, 2019 at 00:00\n\n'
'Expected events:\n\n'
'05:12 Mars is in opposition\n\n'
'Note: All the hours are given in UTC.',
@@ -74,7 +74,7 @@ class DumperTestCase(unittest.TestCase):
ephemerides = self._get_data(False)
self.assertEqual('Monday October 14, 2019\n\n'
'Moon phase: Full Moon\n'
'Last Quarter on Mon Oct 21, 2019 00:00\n\n'
'Last Quarter on Monday October 21, 2019 at 00:00\n\n'
'Expected events:\n\n'
'05:12 Mars is in opposition\n\n'
'Note: All the hours are given in UTC.',


Loading…
Cancel
Save