# -*- coding: utf-8 -*-
"""reV-losses utilities.
"""
import calendar
import logging
import numpy as np
from reV.utilities.exceptions import reVLossesValueError
logger = logging.getLogger(__name__)
# 1900 is just a representative year, since a year input is required
DAYS_PER_MONTH = [calendar.monthrange(1900, i)[1] for i in range(1, 13)]
FIRST_DAY_INDEX_OF_MONTH = np.cumsum([0] + DAYS_PER_MONTH[:-1])
[docs]def convert_to_full_month_names(month_names):
"""Format an iterable of month names to match those in :mod:`calendar`.
This function will format each input name to match the formatting
in :obj:`calendar.month_name` (upper case, no extra whitespace), and
it will convert all abbreviations to full month names. No other
assumptions are made about the inputs, so an input string " abc "
will get formatted and passed though as "Abc".
Parameters
----------
month_names : iter
An iterable of strings representing the input month names.
Month names can be unformatted and contain 3-letter month
abbreviations.
Returns
-------
:obj:`list`
A list of month names matching the formatting of
:obj:`calendar.month_name` (upper case, no extra whitespace).
Abbreviations are also converted to a full month name.
Examples
--------
>>> input_names = ['March', ' aprIl ', 'Jun', 'jul', ' abc ']
>>> convert_to_full_month_names(input_names)
['March', 'April', 'June', 'July', 'Abc']
"""
formatted_names = []
for name in month_names:
month_name = format_month_name(name)
month_name = full_month_name_from_abbr(month_name) or month_name
formatted_names.append(month_name)
return formatted_names
[docs]def filter_unknown_month_names(month_names):
"""Split the input into known and unknown month names.
Parameters
----------
month_names : iter
An iterable of strings representing the input month names. Month
names must match the formatting in :obj:`calendar.month_name`
(upper case, no extra whitespace), otherwise they will be placed
into the ``unknown_months`` return list.
Returns
-------
known_months : :obj:`list`
List of known month names.
unknown_months : :obj:`list`
List of unknown month names.
"""
known_months, unknown_months = [], []
for name in month_names:
if name in calendar.month_name:
known_months.append(name)
else:
unknown_months.append(name)
return known_months, unknown_months
[docs]def hourly_indices_for_months(month_names):
"""Convert month names into a list of hourly indices.
Given a list of month names, this function will return a list
of indices such that any index value corresponds to an hour within
the input months.
Parameters
----------
month_names : iter
An iterable of month names for the desired starting indices.
The month names must match the formatting in
:obj:`calendar.month_name` (upper case, no extra whitespace),
otherwise their hourly indices will not be included in the
output.
Returns
-------
:obj:`list`
A list of hourly index values such that any index corresponds to
an hour within the input months.
"""
indices = []
for ind in sorted(month_indices(month_names)):
start_index = FIRST_DAY_INDEX_OF_MONTH[ind] * 24
hours_in_month = DAYS_PER_MONTH[ind] * 24
indices += list(range(start_index, start_index + hours_in_month))
return indices
[docs]def month_indices(month_names):
"""Convert input month names to an indices (0-11) of the months.
Parameters
----------
month_names : iter
An iterable of month names for the desired starting indices.
The month names must match the formatting in
:obj:`calendar.month_name` (upper case, no extra whitespace),
otherwise their index will not be included in the output.
Returns
-------
:obj:`set`
A set of month indices for the input month names. Unknown
month indices (-1) are removed.
"""
return {month_index(name) for name in month_names} - {-1}
[docs]def month_index(month_name):
"""Convert a month name (as string) to an index (0-11) of the month.
Parameters
----------
month_name : str
Name of month to corresponding to desired index. This input
must match the formatting in :obj:`calendar.month_name`
(upper case, no extra whitespace).
Returns
-------
:obj:`int`
The 0-index of the month, or -1 if the month name is not
understood.
Examples
--------
>>> month_index("June")
5
>>> month_index("July")
6
>>> month_index("Jun")
-1
>>> month_index("july")
-1
"""
for month_ind in range(12):
if calendar.month_name[month_ind + 1] == month_name:
return month_ind
return -1
[docs]def full_month_name_from_abbr(month_name):
"""Convert a month abbreviation to a full month name.
Parameters
----------
month_name : str
Abbreviated month name. Must be one of:
- "Jan"
- "Feb"
- "Mar"
- "Apr"
- "May"
- "Jun"
- "Jul"
- "Aug"
- "Sep"
- "Oct"
- "Nov"
- "Dec"
If the input does not match one of these, this function returns
:obj:`None`.
Returns
-------
:obj:`str` | :obj:`None`
Unabbreviated month name, or :obj:`None` if input abbreviation
is not understood.
Examples
--------
>>> full_month_name_from_abbr("Jun")
"June"
>>> full_month_name_from_abbr("June") is None
True
>>> full_month_name_from_abbr('Abcdef') is None
True
"""
for month_index in range(1, 13):
if calendar.month_abbr[month_index] == month_name:
return calendar.month_name[month_index]
def _validate_arrays_not_empty(obj, array_names=None):
"""Validate that the input data arrays are not empty. """
array_names = array_names or []
for name in array_names:
try:
arr = getattr(obj, name)
except AttributeError:
continue
if not arr.size:
msg = "Invalid {} input: Array is empty! - {}"
msg = msg.format(name.replace('_', ' '), arr)
logger.error(msg)
raise reVLossesValueError(msg)