Source code for merlin.dates

from cytoolz import first
from cytoolz import reduce
from cytoolz import second
from cytoolz import thread_first
from dateutil import parser
from merlin import chips
from merlin import functions as f
from operator import eq
from operator import or_
import re


[docs]def to_ordinal(datestring): """Extract an ordinal date from a date string Args: datestring (str): date value Returns: int: ordinal date """ return parser.parse(datestring).toordinal()
[docs]def startdate(acquired): """Returns the startdate from an acquired date string Args: acquired (str): / separated date range in iso8601 format Returns: str: Start date """ return acquired.split('/')[0]
[docs]def enddate(acquired): """Returns the enddate from an acquired date string Args: acquired (str): / separated date range in iso8601 format Returns: str: End date """ return acquired.split('/')[1]
[docs]def is_acquired(acquired): """Is the date string a / separated date range in iso8601 format? Args: acquired: A date string Returns: bool: True or False """ # 1980-01-01/2015-12-31 regex = '^[0-9]{4}-[0-9]{2}-[0-9]{2}\/[0-9]{4}-[0-9]{2}-[0-9]{2}$' return bool(re.match(regex, acquired))
[docs]def mapped(chipmap): """Transform a dict of chips into a dict of datestrings Args: chipmap (dict): {k: [chips]} Returns: dict: {k: [datestring2, datestring1, datestring3]} """ return {k: chips.dates(v) for k, v in chipmap.items()}
[docs]def minmax(dates): """Returns an iso8601 daterange string that represents the min and max datemap.values(). Args: datestrings: [d1, d2, d3,] Returns: ['min_date/max_date',] Example: >>> minmax(['2008-01-01', '2010-01-01', '2009-01-01']) "2008-01-01/2010-01-01" """ return '{}/{}'.format(min(dates), max(dates)) if dates else '/'
[docs]def symmetric(datemap): """Returns a sequence of dates that are common to all map values if all datemap values are represented, else Exception. Args: datemap: {key: [datestrings,]} Returns: Sequence of date strings or Exception Example: >>> symmetric({"reds": [ds3, ds1, ds2], "blues": [ds2, ds3, ds1]}) [ds2, ds3, ds1] >>> >>> symmetric({"reds": [ds3, ds1], "blues": [ds2, ds3, ds1]}) Exception: assymetric dates detected - {'reds':[ds3, ds1]} != {'blues':[ds2, ds3, ds1]} """ def check(a, b): """Reducer for efficiently comparing two unordered sequences. Executes in linear(On) time. Args: a: {k:[datestring1, datestring2...]} b: {k:[datestring2, datestring1...]} Returns: b if a == b, else Exception with details """ if f.seqeq(second(a), second(b)): return b else: msg = ('assymetric dates detected - {} != {}' .format(first(a), first(b))) msga = '{}{}'.format(first(a), second(a)) msgb = '{}{}'.format(first(b), second(b)) raise Exception('\n\n'.join([msg, msga, msgb])) return second(reduce(check, datemap.items()))
[docs]def single(datemap): """Returns a sequence of iso8601 daterange strings if each datemap.values() is length <= 1, else Exception. Args: datemap: {key: [datestring,], key2: [datestring,], ...} Returns: ['ds2', 'ds3', 'ds1'] or Exception Example: >>> single({"nlcd": [ds3,], "dem": [ds2,]}) [ds3, ds2] >>> >>> single({"nlcd": [ds3, ds1], "dem": [ds2,]}) Exception: assymetric dates detected - {'nlcd':[ds3, ds1], 'dem':[ds2]} """ if or_(all(map(lambda a: eq(1, len(a)), datemap.values())), all(map(lambda a: eq(0, len(a)), datemap.values()))): return list(f.flatten(datemap.values())) else: raise Exception('assymetric dates detected - {}'.format(datemap))
[docs]def rsort(dateseq): """ Reverse sorts a sequence of dates. Args: dateseq: sequence of dates Returns: sequence: reverse sorted sequence of dates """ return f.rsort(dateseq)