#!/usr/bin/env python """ pyphoon - Phase of the Moon (Python version) Igor Chubin <igor@chub.in>, 05.03.2016, Based on the original version of Jef Poskanzer <jef@mail.acme.com> written in Pascal in 1979 (and later translated to C) """ import sys import os import argparse import datetime import time import locale from math import cos, sqrt import dateutil.parser # sys.path.append(os.path.join(os.path.dirname(os.path.dirname(__file__)), "lib")) from src.lib.astro import unix_to_julian, phase, phasehunt2 from src.lib.moons import background6, background18, background19, background21, background22, background23 from src.lib.moons import background24, background29, background32 from src.lib.translations import LITS def fatal(message): """ Print error message and exit signaling failure """ print(message, file=sys.stderr) sys.exit(1) # # Global defines and declarations. # SECSPERMINUTE = 60 SECSPERHOUR = (60 * SECSPERMINUTE) SECSPERDAY = (24 * SECSPERHOUR) PI = 3.1415926535897932384626433 DEFAULTNUMLINES = 23 DEFAULTNOTEXT = False QUARTERLITLEN = 16 QUARTERLITLENPLUSONE = 17 # If you change the aspect ratio, the canned backgrounds won't work. ASPECTRATIO = 0.5 def putseconds(secs): """ Create a datestring for format 'dd HH:MM:SS' """ days = int(secs / SECSPERDAY) secs = int(secs - days * SECSPERDAY) hours = int(secs / SECSPERHOUR) secs = int(secs - hours * SECSPERHOUR) minutes = int(secs / SECSPERMINUTE) secs = int(secs - minutes * SECSPERMINUTE) return f"{days:d} {hours:2d}:{minutes:02d}:{secs:02d}" def putmoon(datetimeobj, numlines, atfiller, notext, lang=None): # pylint: disable=too-many-locals,too-many-branches,too-many-statements """ Print the moon """ output = [""] def putchar(char): output[0] += char def fputs(string): output[0] += string if not lang: try: lang = locale.getdefaultlocale()[0] except IndexError: lang = 'en' if lang not in LITS and '_' in lang: lang = lang.split('_', 1)[0] lits = LITS.get(lang, LITS.get('en')) qlits = [x + " +" for x in lits] nqlits = [x + " -" for x in lits] # Find the length of the atfiller string atflrlen = len(atfiller) # Figure out the phase juliandate = unix_to_julian(datetimeobj) pctphase, _, _, _, _, _, _ = phase(juliandate) angphase = pctphase * 2.0 * PI mcap = -cos(angphase) # Figure out how big the moon is yrad = numlines / 2.0 xrad = yrad / ASPECTRATIO # Figure out some other random stuff midlin = int(numlines / 2) phases, which = phasehunt2(juliandate) # Now output the moon, a slice at a time atflridx = 0 lin = 0 while lin < numlines: # Compute the edges of this slice ycoord = lin + 0.5 - yrad xright = xrad * sqrt(1.0 - (ycoord * ycoord) / (yrad * yrad)) xleft = -xright if PI > angphase >= 0.0: xleft = mcap * xleft else: xright = mcap * xright colleft = int(xrad + 0.5) + int(xleft + 0.5) colright = int(xrad + 0.5) + int(xright + 0.5) # Now output the slice col = 0 while col < colleft: putchar(' ') col += 1 while col <= colright: if numlines == 6: char = background6[lin][col] elif numlines == 18: char = background18[lin][col] elif numlines == 19: char = background19[lin][col] elif numlines == 21: char = background21[lin][col] elif numlines == 22: char = background22[lin][col] elif numlines == 23: char = background23[lin][col] elif numlines == 24: char = background24[lin][col] elif numlines == 29: char = background29[lin][col] elif numlines == 32: char = background32[lin][col] else: char = '@' if char != '@': putchar(char) else: putchar(atfiller[atflridx]) atflridx = (atflridx + 1) % atflrlen col += 1 if (numlines <= 27 and not notext): # Output the end-of-line information, if any fputs("\t ") if lin == midlin - 2: fputs(qlits[int(which[0] * 4.0 + 0.001)]) elif lin == midlin - 1: fputs(putseconds(int((juliandate - phases[0]) * SECSPERDAY))) elif lin == midlin: fputs(nqlits[int(which[1] * 4.0 + 0.001)]) elif lin == midlin + 1: fputs(putseconds(int((phases[1] - juliandate) * SECSPERDAY))) putchar('\n') lin += 1 return output[0] def main(): """ Main entry point :param args: argparse.ArgumentParser object """ parser = argparse.ArgumentParser(description='Show Phase of the Moon') parser.add_argument( '-n', '--lines', help='Number of lines to display (size of the moon)', required=False, default=DEFAULTNUMLINES ) parser.add_argument( '-x', '--notext', help='Print no additional information, just the moon', required=False, default=DEFAULTNOTEXT, action="store_true" ) parser.add_argument( 'date', help='Date for that the phase of the Moon must be shown. Today by default', nargs='?', default=time.strftime("%Y-%m-%d %H:%M:%S") ) parser.add_argument( '-l', '--language', help='locale for that the phase of the Moon must be shown. English by default', nargs='?', default=None ) args = vars(parser.parse_args()) try: dateobj = time.mktime(dateutil.parser.parse(args['date']).timetuple()) except Exception as err: # pylint: disable=broad-except fatal(f"Can't parse date: {args['date']}") try: numlines = int(args['lines']) lang = args['language'] except Exception as err: # pylint: disable=broad-except print(err) fatal("Number of lines must be integer") try: notext = bool(args['notext']) except Exception as err: # pylint: disable=broad-except print(err) print(putmoon(dateobj, numlines, '@', notext, lang))