Example #1
0
class plugin:
    '''
    A class to display the time of sunrise/sunset
    Uses the astral library to retrieve information...
    '''

    def __init__(self, config):
        '''
        Initializations for the startup of the weather forecast
        '''
        # Get plugin name (according to the folder, it is contained in)
        self.name = os.path.dirname(__file__).split('/')[-1]
        self.pretty_name = "Sunrise"
        self.description = "Displays the current times of sunrise and sunset."

        self.astral_at_location = Astral()[config.get('plugin_' + self.name, 'location')]

        # Choose language to display sunrise
        language = config.get('plugin_time_default', 'language')
        if language == 'german':
            self.taw = wcp_time_german.time_german()
        elif language == 'dutch':
            self.taw = wcp_time_dutch.time_dutch()
        elif language == 'swiss_german':
            self.taw = wcp_swiss_german.time_swiss_german()
        else:
            print('Could not detect language: ' + language + '.')
            print('Choosing default: german')
            self.taw = wcp_time_german.time_german()

        self.bg_color_index     = 0 # default background color: black
        self.word_color_index   = 2 # default word color: warm white
        self.minute_color_index = 2 # default minute color: warm white

    def run(self, wcd, wci):
        '''
        Displaying current time for sunrise/sunset
        '''
        # Get data of sunrise
        sun_data = self.astral_at_location.sun(date=datetime.datetime.now(), local=True)
        # Display data of sunrise
        wcd.animate(self.name, 'sunrise', invert=True)
        wcd.setColorToAll(wcc.colors[self.bg_color_index], includeMinutes=True)
        taw_indices = self.taw.get_time(sun_data['sunrise'], withPrefix=False)
        wcd.setColorBy1DCoordinates(wcd.strip, taw_indices, wcc.colors[self.word_color_index])
        wcd.show()
        time.sleep(3)
        # Display data of sunset
        wcd.animate(self.name, 'sunrise')
        wcd.setColorToAll(wcc.colors[self.bg_color_index], includeMinutes=True)
        taw_indices = self.taw.get_time(sun_data['sunset'], withPrefix=False)
        wcd.setColorBy1DCoordinates(wcd.strip, taw_indices, wcc.colors[self.word_color_index])
        wcd.show()
        time.sleep(3)
        # Display current moon phase
        moon_phase = int(self.astral_at_location.moon_phase(datetime.datetime.now()))
        for i in range(0, moon_phase):
            wcd.showIcon('sunrise', 'moon_'+str(i).zfill(2))
            time.sleep(0.1)
        time.sleep(3)
Example #2
0
class plugin:
    '''
    A class to display the time of sunrise/sunset
    Uses the astral library to retrieve information...
    '''

    def __init__(self, config):
        '''
        Initializations for the startup of the weather forecast
        '''
        # Get plugin name (according to the folder, it is contained in)
        self.name = os.path.dirname(__file__).split('/')[-1]

        self.astral_at_location = Astral()[config.get('plugin_' + self.name, 'location')]

        # Choose language to display sunrise
        language = config.get('plugin_time_default', 'language')
        if language == 'german':
            self.taw = wcp_time_german.time_german()
        elif language == 'dutch':
            self.taw = wcp_time_dutch.time_dutch()
        elif language == 'swiss_german':
            self.taw = wcp_swiss_german.time_swiss_german()
        else:
            print('Could not detect language: ' + language + '.')
            print('Choosing default: german')
            self.taw = wcp_time_german.time_german()

        self.bg_color_index     = 0 # default background color: black
        self.word_color_index   = 2 # default word color: warm white
        self.minute_color_index = 2 # default minute color: warm white

    def run(self, wcd, wci):
        '''
        Displaying current time for sunrise/sunset
        '''
        # Get data of sunrise
        sun_data = self.astral_at_location.sun(date=datetime.datetime.now(), local=True)
        # Display data of sunrise
        wcd.animate(self.name, 'sunrise', invert=True)
        wcd.setColorToAll(wcc.colors[self.bg_color_index], includeMinutes=True)
        taw_indices = self.taw.get_time(sun_data['sunrise'], withPrefix=False)
        wcd.setColorBy1DCoordinates(wcd.strip, taw_indices, wcc.colors[self.word_color_index])
        wcd.show()
        time.sleep(3)
        # Display data of sunset
        wcd.animate(self.name, 'sunrise')
        wcd.setColorToAll(wcc.colors[self.bg_color_index], includeMinutes=True)
        taw_indices = self.taw.get_time(sun_data['sunset'], withPrefix=False)
        wcd.setColorBy1DCoordinates(wcd.strip, taw_indices, wcc.colors[self.word_color_index])
        wcd.show()
        time.sleep(3)
        # Display current moon phase
        moon_phase = int(self.astral_at_location.moon_phase(datetime.datetime.now()))
        for i in range(0, moon_phase):
            wcd.showIcon('sunrise', 'moon_'+str(i).zfill(2))
            time.sleep(0.1)
        time.sleep(3)
Example #3
0
def render_moonphase(_, query):
    """
    A symbol describing the phase of the moon
    """
    astral = Astral()
    moon_index = int(
        int(32.0*astral.moon_phase(date=datetime.datetime.today())/28+2)%32/4
    )
    return MOON_PHASES[moon_index]
Example #4
0
class plugin:
    """
    A class to display the time of sunrise/sunset
    Uses the astral library to retrieve information...
    """
    def __init__(self, config):
        """
        Initializations for the startup of the weather forecast
        """
        # Get plugin name (according to the folder, it is contained in)
        self.name = os.path.dirname(__file__).split('/')[-1]
        self.pretty_name = "Sunrise"
        self.description = "Displays the current times of sunrise and sunset."

        self.astral_at_location = Astral()[config.get('plugin_' + self.name,
                                                      'location')]

        self.bg_color_index = 0  # default background color: black
        self.word_color_index = 2  # default word color: warm white
        self.minute_color_index = 2  # default minute color: warm white

    def run(self, wcd, wci):
        """
        Displaying current time for sunrise/sunset
        """
        # Get data of sunrise
        sun_data = self.astral_at_location.sun(date=datetime.datetime.now(),
                                               local=True)
        # Display data of sunrise
        wcd.animate(self.name, 'sunrise', invert=True)
        wcd.setColorToAll(wcc.colors[self.bg_color_index], includeMinutes=True)
        taw_indices = wcd.taw.get_time(sun_data['sunrise'], purist=True)
        wcd.setColorBy1DCoordinates(taw_indices,
                                    wcc.colors[self.word_color_index])
        wcd.show()
        if wci.waitForExit(3.0):
            return
        # Display data of sunset
        wcd.animate(self.name, 'sunrise')
        wcd.setColorToAll(wcc.colors[self.bg_color_index], includeMinutes=True)
        taw_indices = wcd.taw.get_time(sun_data['sunset'], purist=True)
        wcd.setColorBy1DCoordinates(taw_indices,
                                    wcc.colors[self.word_color_index])
        wcd.show()
        if wci.waitForExit(3.0):
            return
        # Display current moon phase
        moon_phase = int(
            self.astral_at_location.moon_phase(datetime.datetime.now()))
        for i in range(0, moon_phase):
            wcd.showIcon('sunrise', 'moon_' + str(i).zfill(2))
            if wci.waitForExit(0.1):
                return
        if wci.waitForExit(3.0):
            return
Example #5
0
def get_phase_segment():
    a = Astral()
    moon_phase = a.moon_phase(datetime.datetime.now())
    if 0 < moon_phase < 4:
        return new_moon_icon
    if 4 < moon_phase < 10:
        return first_quarter_icon
    if 10 < moon_phase < 17:
        return full_moon_icon
    if 17 < moon_phase < 26:
        return last_quarter_icon
    if 26 < moon_phase < 29:
        return new_moon_icon
Example #6
0
class plugin:
    """
    A class to display the time of sunrise/sunset
    Uses the astral library to retrieve information...
    """

    def __init__(self, config):
        """
        Initializations for the startup of the weather forecast
        """
        # Get plugin name (according to the folder, it is contained in)
        self.name = os.path.dirname(__file__).split('/')[-1]
        self.pretty_name = "Sunrise"
        self.description = "Displays the current times of sunrise and sunset."

        self.astral_at_location = Astral()[config.get('plugin_' + self.name, 'location')]

        self.bg_color_index = 0  # default background color: black
        self.word_color_index = 2  # default word color: warm white
        self.minute_color_index = 2  # default minute color: warm white

    def run(self, wcd, wci):
        """
        Displaying current time for sunrise/sunset
        """
        # Get data of sunrise
        sun_data = self.astral_at_location.sun(date=datetime.datetime.now(), local=True)
        # Display data of sunrise
        wcd.animate(self.name, 'sunrise', invert=True)
        wcd.setColorToAll(wcc.colors[self.bg_color_index], includeMinutes=True)
        taw_indices = wcd.taw.get_time(sun_data['sunrise'], purist=True)
        wcd.setColorBy1DCoordinates(wcd.strip, taw_indices, wcc.colors[self.word_color_index])
        wcd.show()
        if wci.waitForExit(3.0):
            return
        # Display data of sunset
        wcd.animate(self.name, 'sunrise')
        wcd.setColorToAll(wcc.colors[self.bg_color_index], includeMinutes=True)
        taw_indices = wcd.taw.get_time(sun_data['sunset'], purist=True)
        wcd.setColorBy1DCoordinates(wcd.strip, taw_indices, wcc.colors[self.word_color_index])
        wcd.show()
        if wci.waitForExit(3.0):
            return
        # Display current moon phase
        moon_phase = int(self.astral_at_location.moon_phase(datetime.datetime.now()))
        for i in range(0, moon_phase):
            wcd.showIcon('sunrise', 'moon_' + str(i).zfill(2))
            if wci.waitForExit(0.1):
                return
        if wci.waitForExit(3.0):
            return
Example #7
0
def test_Astral_Moon_PhaseNumber():
    dates = {
        datetime.date(2015, 12, 1): 19,
        datetime.date(2015, 12, 2): 20,
        datetime.date(2015, 12, 3): 21,
        datetime.date(2014, 12, 1): 9,
        datetime.date(2014, 12, 2): 10,
        datetime.date(2014, 1, 1): 0,
    }

    a = Astral()

    for date_, moon in dates.items():
        assert a.moon_phase(date_) == moon
Example #8
0
class MoonSensor(SensorEntity):
    """Representation of a Moon sensor."""
    def __init__(self, name):
        """Initialize the moon sensor."""
        self._name = name
        self._state = None
        self._astral = Astral()

    @property
    def name(self):
        """Return the name of the entity."""
        return self._name

    @property
    def device_class(self):
        """Return the device class of the entity."""
        return "moon__phase"

    @property
    def state(self):
        """Return the state of the device."""
        if self._state == 0:
            return STATE_NEW_MOON
        if self._state < 7:
            return STATE_WAXING_CRESCENT
        if self._state == 7:
            return STATE_FIRST_QUARTER
        if self._state < 14:
            return STATE_WAXING_GIBBOUS
        if self._state == 14:
            return STATE_FULL_MOON
        if self._state < 21:
            return STATE_WANING_GIBBOUS
        if self._state == 21:
            return STATE_LAST_QUARTER
        return STATE_WANING_CRESCENT

    @property
    def icon(self):
        """Icon to use in the frontend, if any."""
        return MOON_ICONS.get(self.state)

    async def async_update(self):
        """Get the time and updates the states."""
        today = dt_util.as_local(dt_util.utcnow()).date()
        self._state = self._astral.moon_phase(today)
Example #9
0
def render_moonday(_, query):
    """
    An number describing the phase of the moon (days after the New Moon)
    """
    astral = Astral()
    return str(int(astral.moon_phase(date=datetime.datetime.today())))
Example #10
0
File: moon.py Project: xe1gyq/iot
from datetime import date, timedelta
from astral import Astral
now = date.today()
a = Astral()
for i in range(30):
    day = now + timedelta(days=i)
    moon = a.moon_phase(day)
    print(day.isoformat() + ' Moon Phase: %d' % moon)
Example #11
0
class Interpreter:

    def __init__(self, baseUrl):
        self.baseUrl = baseUrl
        self._webLines = None
        self._astral = Astral()  # https://astral.readthedocs.io/en/latest
        self._astralCity = self._astral["Seattle"]

    # ----------------------- Stub functions child classes must implement ----------------------------------------------
    # Returns the datetime object parsed from the given data line
    def _parseTime(self, tokens):
        raise NotImplementedError

    # Returns the day-specific URL for the base URL
    @staticmethod
    def getDayUrl(baseUrl, day):
        raise NotImplementedError

    # Returns the current data from the given url
    def _getWebLines(self, url, day):
        raise NotImplementedError

    # Returns a list of Slack objects corresponding to the slack indexes within the list of data lines
    def _getSlackData(self, lines, indexes, sunrise, sunset, moonPhase):
        raise NotImplementedError
    # ----------------------- end stub functions -----------------------------------------------------------------------

    # Returns the line before index i in lines that contains an ebb or flood current speed prediction. Returns None if
    # no such prediction exists before index i.
    def _getCurrentBefore(self, i, lines):
        pre = i - 1
        if pre < 0:
            return None
        while 'ebb' not in lines[pre] and 'flood' not in lines[pre]:
            pre -= 1
            if pre < 0:
                return None
        return lines[pre]

    # Returns the line after index i in lines that contains an ebb or flood current speed prediction. Returns None if
    # no such prediction exists after index i.
    def _getCurrentAfter(self, i, lines):
        post = i + 1
        if post >= len(lines):
            return None
        while 'ebb' not in lines[post] and 'flood' not in lines[post]:
            post += 1
            if post >= len(lines):
                return None
        return lines[post]

    # Returns list with indexes of the slack currents in the given list of data lines.
    def _getAllSlacks(self, webLines):
        slacks = []
        for i, line in enumerate(webLines):
            if 'slack' in line or 'min ebb' in line or 'min flood' in line:
                slacks.append(i)
        return slacks

    # Returns list with indexes of the slack currents in the first 24hrs of the given list of data lines.
    def _getAllDaySlacks(self, webLines):
        day = webLines[0].split()[0]
        slacksIndexes = []
        for i, line in enumerate(webLines):
            if line.split()[0] != day:
                return slacksIndexes
            elif 'slack' in line or 'min ebb' in line or 'min flood' in line:
                slacksIndexes.append(i)
        return slacksIndexes

    # Returns list with indexes of the daytime (between given sunrise and sunset) slack currents in the given
    # list of data lines.
    def _getDaySlacks(self, webLines, sunrise, sunset):
        slacksIndexes = []
        for i, line in enumerate(webLines):
            if 'slack' in line or 'min ebb' in line or 'min flood' in line:
                time = self._parseTime(line.split())
                timeDate = dt.strftime(time, DATEFMT)  # sanity checks to surface errors with mismatched web dates early
                sunriseDate, sunsetDate = dt.strftime(sunrise, DATEFMT), dt.strftime(sunset, DATEFMT)
                if time > sunset:
                    return slacksIndexes
                elif time > sunrise:
                    slacksIndexes.append(i)
                elif sunriseDate != timeDate or sunriseDate != sunsetDate:
                    print('ERROR: website date {} does not match requested date {}'.format(timeDate, sunriseDate))
                    return []
        return slacksIndexes

    # Returns true if self._webData contains the data for the given day, false otherwise
    def _canReuseWebData(self, day):
        if not self._webLines:
            return False
        dayStr = dt.strftime(day, DATEFMT)
        for i, line in enumerate(self._webLines):
            if dayStr in line:
                self._webLines = self._webLines[i:]
                return True
        return False

    # Returns all slacks retrieved from the web beginning with the startDay (7 days for NOAA and 4 days for MobileGeo)
    def allSlacks(self, startDay):
        url = self.getDayUrl(self.baseUrl, startDay)
        lines = self._getWebLines(url, startDay)
        slackIndexes = self._getAllSlacks(lines)
        return self._getSlackData(lines, slackIndexes, None, None, -1)

    # Returns a list of slacks for the given day, retrieves new web data if the current data doesn't have info for day.
    # Includes night slacks if night=True
    def getSlacks(self, day, night):
        if not self._canReuseWebData(day):
            if not self.baseUrl:
                print('Base url empty')
                return []
            url = self.getDayUrl(self.baseUrl, day)
            self._webLines = self._getWebLines(url, day)
        if not self._webLines:
            print('Error getting web data')
            return []

        # Note: astral sunrise and sunset times do account for daylight savings
        sun = self._astralCity.sun(date=day, local=True)
        sunrise = sun['sunrise'].replace(tzinfo=None)
        sunset = sun['sunset'].replace(tzinfo=None)
        if night:
            slackIndexes = self._getAllDaySlacks(self._webLines)
        else:
            slackIndexes = self._getDaySlacks(self._webLines, sunrise, sunset)
        if not slackIndexes:
            print('ERROR: no slacks for {} found in webLines: {}'.format(day, self._webLines))
            return []
        return self._getSlackData(self._webLines, slackIndexes, sunrise, sunset, self._astral.moon_phase(date=day))
Example #12
0
def testMoon():
    dd = Astral()
    dd.moon_phase(datetime.date(2011, 02, 24))
Example #13
0
print time.ctime(),"startup!"

TCP_IP = '192.168.0.5'
TCP_PORT = 1337
BUFFER_SIZE = 20  # Normally 1024, but we want fast response

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) #make socket reuseable, for debugging (enables you to rerun the program before the socket has timed out)
s.bind((TCP_IP, TCP_PORT))
s.listen(1)

print 'Opening TCP port', TCP_PORT

while True:
    try:
        conn, addr = s.accept()
        print time.ctime(),'Connection from:', addr
        #txString = str(22)
        txString = str(int(a.moon_phase(datetime.datetime.now())))+'\r'
        conn.send(txString)#+'\n')  # echo
        print 'TX: ', txString #txString.replace('\r','\r\n')
        print 'Client disconnected.'
        print '--------------------------'
        conn.close()
    except Exception as e:
        #print "hmm.. It looks like there was an error: " + str(e)
        print 'Client disconnected... :',str(e)
        print '--------------------------'
        conn.close()
Example #14
0
class MoonPhase(QObject):
    """
    Класс отвечающий за соответствие даты и фазы луны
    """
    def __init__(self, in_model, parent=None):
        QObject.__init__(self, parent)
        self._mModel = in_model
        self.astral = Astral()
        self.quarter_phase = []
        self.variantY = [[1.9, 3.8, 5.6, 7.1, 8.3, 9.2, 9.8],
                         [2.2, 4.3, 6.2, 7.8, 9.0, 9.7],
                         [2.6, 5.0, 7.1, 8.7, 9.7],
                         [1.7, 3.4, 5.0, 6.4, 7.7, 8.7, 9.4, 9.8]]

        # связываем событие очищения модели луны и очищение фаз луны
        self._mModel.cleanPhaseRequest.connect(self.cleanPhase)

        # связываем событие пустой первой строки и очищение фаз луны
        self._mModel.setNullPhaseRequest.connect(self.setNullQuarterPhase)

        # связываем событие пустой первой строки и очищение фаз луны
        self._mModel.calculatePhaseRequest.connect(self.calculatePhase)

    @pyqtSlot(QDate)
    def calculatePhase(self, date):
        """
        Метод считает фазы луны по датам
        """
        date = date.toPyDate()
        if self.checkDateinQuarterPhase(date) is False:
            self.checkMoonPhase(date)
        self.checkFirstDay(date)
        self.checkLastDay(date)
        self.fillModelMoon(date)

    @pyqtSlot()
    def setNullQuarterPhase(self):
        """
        Метод вызывается при сбросе данных
        """
        self.quarter_phase = []

    @pyqtSlot(QDate)
    def cleanPhase(self, date):
        """
        Метод удаляет фазы лун
        """
        if date < self.quarter_phase[-2][0]:
            self.quarter_phase.pop()

    def checkDateinQuarterPhase(self, date):
        found = False
        if self.quarter_phase != []:
            for i in range(0, len(self.quarter_phase)):
                if self.quarter_phase[i][0] == date:
                    found = True
        return found

    def checkMoonPhase(self, date):
        """
        Метод проверяет, какие даты соответствуют ровно четвертям
        (новолуние, первая четверть, полнолуние, вторая четверть)
        Записывает их в список quarter_phase
        """
        moon_phase = self.astral.moon_phase(date=date)
        if moon_phase == 0:
            self.quarter_phase.append((date, -10))
        elif moon_phase == 7 or moon_phase == 21:
            self.quarter_phase.append((date, 0))
        elif moon_phase == 14:
            self.quarter_phase.append((date, 10))

    def checkFirstDay(self, first_day):
        """
        Метод проверяет, является ли переданная дата четвертью, если нет, то проверяет ПРЕДЫДУЩУЮ дату,
        пока не найдет четверть. И запишет ее в quarter_phase
        """
        if self.quarter_phase == []:  #  or self.quarter_phase[0][0] != first_day  or self.quarter_phase[-2][0] > first_day
            date = first_day - datetime.timedelta(1)

            moon_phase = self.astral.moon_phase(date=date)
            if moon_phase == 0:
                self.quarter_phase = [(date, -10)] + self.quarter_phase
            elif moon_phase == 7 or moon_phase == 21:
                self.quarter_phase = [(date, 0)] + self.quarter_phase
            elif moon_phase == 14:
                self.quarter_phase = [(date, 10)] + self.quarter_phase
            else:
                self.checkFirstDay(date)

    def checkLastDay(self, last_day):
        """
        Метод проверяет, является ли переданная дата четвертью, если нет, то проверяет СЛЕДУЮЩУЮ дату,
        пока не найдет четверть. И запишет ее в quarter_phase
        """
        if self.quarter_phase[-1][0] < last_day:
            date = last_day + datetime.timedelta(1)
            self.checkMoonPhase(date)
            self.checkLastDay(date)

    def fillModelMoon(self, date):
        """
        Метод заполняет модель Moon
        """
        for j in range(0, len(self.quarter_phase) - 1):
            if self.quarter_phase[j][0] < date < self.quarter_phase[j + 1][0]:
                length = self.checkLengthQuater(self.quarter_phase[j + 1][0],
                                                self.quarter_phase[j][0])
                y = date - self.quarter_phase[j][0]
                y = y.days - 1
                side = self.checkSideQuarter(j, length)
                self._mModel.addMoon(side[y])
            elif date == self.quarter_phase[j][0]:
                self._mModel.addMoon(self.quarter_phase[j][1])
            elif date == self.quarter_phase[j + 1][0]:
                self._mModel.addMoon(self.quarter_phase[j + 1][1])

    def checkLengthQuater(self, date_phase1, date_phase2):
        """
        Метод проверяет, какой длины четверть и возвращает соответствущий индекс списка variantY
        """
        delta = date_phase1 - date_phase2
        delta = delta.days
        if delta == 8:
            return 0
        elif delta == 7:
            return 1
        elif delta == 6:
            return 2
        elif delta == 9:
            return 3

    def checkSideQuarter(self, index_quarter, index_lenght):
        """
        Метод проверяет, направленность кривой луны.
        """
        other_variantY = []
        if self.quarter_phase[index_quarter][1] == 0 and self.quarter_phase[
                index_quarter + 1][1] == 10:
            return self.variantY[index_lenght]
        elif self.quarter_phase[index_quarter][1] == 10 and self.quarter_phase[
                index_quarter + 1][1] == 0:
            other_variantY = self.variantY[index_lenght].copy()
            other_variantY.reverse()
            return other_variantY
        elif self.quarter_phase[index_quarter][1] == 0 and self.quarter_phase[
                index_quarter + 1][1] == -10:
            for i in self.variantY[index_lenght]:
                i = i * -1
                other_variantY.append(i)
            return other_variantY
        elif self.quarter_phase[index_quarter][
                1] == -10 and self.quarter_phase[index_quarter + 1][1] == 0:
            other_variantY = self.variantY[index_lenght].copy()
            other_variantY.reverse()
            for i in range(0, len(other_variantY)):
                other_variantY[i] = other_variantY[i] * -1
            return other_variantY
Example #15
0
def moon_phase_api():
    """
    Show the moon's phase for a given date
    ---
    tags:
      - astronomy
    parameters:
      - in: query
        name: year
        required: true
        schema:
          type: integer
          example: 2019
        description: the year you would like the moon phase for
      - in: query
        name: month
        required: true
        schema:
          type: integer
          example: 1
        description: the month you would like the moon phase for
      - in: query
        name: day
        required: true
        schema:
          type: integer
          example: 13
        description: the day of the month you would like the moon phase for
    responses:
      200:
        description: The moon phase
        content:
          text/plain:
            schema:
              type: string
              example: Full Moon
  """
    a = Astral()
    year = request.args.get('year')
    month = request.args.get('month')
    day = request.args.get('day')

    if not year:
        abort(400, 'No year parameter given')
    if not day:
        abort(400, 'No day parameter given')
    if not month:
        abort(400, 'No month parameter given')

    try:
        year = int(year)
    except:
        abort(400, 'Year is an invalid number')
    try:
        month = int(month)
    except:
        abort(400, 'Month is an invalid number')
    try:
        day = int(day)
    except:
        abort(400, 'Day is an invalid number')

    try:
        dt = datetime(year, month, day)
    except ValueError:
        abort(400, 'Invalid date')

    phase = a.moon_phase(dt)

    if phase < 3.5:
        phase_text = "New Moon"
    elif phase < 10.5:
        phase_text = "First Quarter"
    elif phase < 17.5:
        phase_text = "Full Moon"
    elif phase < 24.5:
        phase_text = "Last Quarter"
    else:
        phase_text = "New Moon"

    return plain_textify(phase_text)