Example #1
0
def get_application_periods_by_pattern(start_date, end_date, weekly_pattern, time_slots, time_zone):
    result = []
    for time_slot in time_slots:
        begin_time = parse_time(time_slot['begin']).replace(tzinfo=None)
        end_time = parse_time(time_slot['end']).replace(tzinfo=None)
        temp_date = start_date
        while temp_date <= end_date:
            period = get_one_period(temp_date, weekly_pattern, begin_time, end_time, time_zone)
            if period:
                result.append(period)
            temp_date += timedelta(days=1)
    return result
Example #2
0
File: utils.py Project: dvdn/Chaos
def get_application_periods_by_pattern(start_date, end_date, weekly_pattern, time_slots, time_zone):
    result = []
    for time_slot in time_slots:
        begin_time = parse_time(time_slot['begin']).replace(tzinfo=None)
        end_time = parse_time(time_slot['end']).replace(tzinfo=None)
        temp_date = start_date
        while temp_date <= end_date:
            period = get_one_period(temp_date, weekly_pattern, begin_time, end_time, time_zone)
            if period:
                result.append(period)
            temp_date += timedelta(days=1)
    return result
Example #3
0
    def parse_timestamp(self, ts, method):
        ret = parse_time(ts)

        if not self._synced_timestamp:
            # The first timestamp we parse requires syncing the time
            # (in case _current_date is greater than the start date)
            if self._current_date is not None:
                self._current_date = self._current_date.replace(
                    hour=ret.hour,
                    minute=ret.minute,
                    second=ret.second,
                    microsecond=ret.microsecond,
                )
            # Only do it once per parse tree
            self._synced_timestamp = True

        # Logs don't have dates :(
        if self._current_date is None:
            # No starting date is available. Return just the time.
            return ret

        ret = datetime.combine(self._current_date, ret)
        ret = ret.replace(tzinfo=self._current_date.tzinfo)
        if ret < self._current_date:
            # If the new date falls before the last saved date, that
            # means we rolled over and need to increment the day by 1.
            ret += timedelta(days=1)
        self._current_date = ret
        return ret
Example #4
0
	def parse_timestamp(self, ts, method):
		ret = parse_time(ts)

		if not self._synced_timestamp:
			# The first timestamp we parse requires syncing the time
			# (in case _current_date is greater than the start date)
			if self._current_date is not None:
				self._current_date = self._current_date.replace(
					hour=ret.hour,
					minute=ret.minute,
					second=ret.second,
					microsecond=ret.microsecond,
				)
			# Only do it once per parse tree
			self._synced_timestamp = True

		# Logs don't have dates :(
		if self._current_date is None:
			# No starting date is available. Return just the time.
			return ret

		ret = datetime.combine(self._current_date, ret)
		ret = ret.replace(tzinfo=self._current_date.tzinfo)
		if ret < self._current_date:
			# If the new date falls before the last saved date, that
			# means we rolled over and need to increment the day by 1.
			ret += timedelta(days=1)
		self._current_date = ret
		return ret
Example #5
0
 def parse_value(cls, value):
     try:
         if isinstance(value, datetime.time):
             return value
         elif isinstance(value, string_types):
             return parse_time(value)
     except ValueError:
         return None
Example #6
0
def convert_date_time_iso8601(d, t):
    # in the hurricane data date and time are 2 seperate fields,
    # formatted <YYYYMMDD> and <HHMM>
    # this function returns a string in ISO8601 format:
    # <YYYY>-<MM>-<DD>T<HH>:<MM>:<SS>
    dt = datetime.datetime.combine(aniso8601.parse_date(d),
                                   aniso8601.parse_time(t))
    return dt.isoformat()
Example #7
0
 def parse_value(cls, value):
     try:
         if isinstance(value, datetime.time):
             return value
         elif isinstance(value, str):
             return parse_time(value)
     except ValueError:
         return INVALID
Example #8
0
 def __call__(self, item, field, value):
     if value:
         setattr(
             item,
             self.attribute,
             parse_time(value).replace(tzinfo=None)
         )
     else:
         setattr(item, self.attribute, None)
Example #9
0
 def _to_python(self):
     # don't parse data that is already native
     if isinstance(self.data, datetime.datetime):
         return self.data
     elif self.format is None:
         # parse as iso8601
         return aniso8601.parse_time(self.data)
     else:
         return datetime.datetime.strptime(self.data, self.format).time()
Example #10
0
 def __call__(self, item, field, value):
     if value:
         setattr(
             item,
             self.attribute,
             parse_time(value).replace(tzinfo=None)
         )
     else:
         setattr(item, self.attribute, None)
Example #11
0
 def parse_value(cls, value):
     if isinstance(value, datetime.time):
         return value
     if not isinstance(value, str):
         raise GraphQLError(
             f"Time cannot represent non-string value: {repr(value)}")
     try:
         return parse_time(value)
     except ValueError:
         raise GraphQLError(f"Time cannot represent value: {repr(value)}")
Example #12
0
def _get_schedule_timefield(p_schedule_obj):
    """
    """
    l_timefield = p_schedule_obj.Time.lower()
    try:
        # l_time = dparser.parse(l_timefield, fuzzy=True) # Worked ok in Python2
        l_time = aniso8601.parse_time(l_timefield)
    except ValueError:
        l_time = datetime.time(0)
    return l_time, l_timefield
Example #13
0
def to_time(amazon_time):
    if amazon_time == "AM":
        return time(hour=0)
    if amazon_time == "PM":
        return time(hour=12)
    try:
        return aniso8601.parse_time(amazon_time)
    except ValueError as e:
        logger.warn("ValueError for amazon_time '{}'.".format(amazon_time))
        logger.warn("ValueError message: {}".format(e.message))
        return None
Example #14
0
    def clean(self, data):
        time_string = super(DateTimeField, self).clean(data)

        try:
            value = aniso8601.parse_time(time_string)
        except (ValueError, NotImplementedError):
            raise self.error('invalid_format')

        if self.timezone_required and value.tzinfo is None:
            raise self.error('no_timezone')

        return value
Example #15
0
    def test_import(self):
        #Just some tests repeated from other places to make sure the
        #imports work
        time = aniso8601.parse_time('01:23:45')
        self.assertEqual(time.hour, 1)
        self.assertEqual(time.minute, 23)
        self.assertEqual(time.second, 45)

        resultdatetime = aniso8601.parse_datetime(
            '1981-04-05T23:21:28.512400Z')
        self.assertEqual(resultdatetime.year, 1981)
        self.assertEqual(resultdatetime.month, 4)
        self.assertEqual(resultdatetime.day, 5)
        self.assertEqual(resultdatetime.hour, 23)
        self.assertEqual(resultdatetime.minute, 21)
        self.assertEqual(resultdatetime.second, 28)
        self.assertEqual(resultdatetime.microsecond, 512400)
        tzinfoobject = resultdatetime.tzinfo
        self.assertEqual(tzinfoobject.utcoffset(None),
                         datetime.timedelta(hours=0))
        self.assertEqual(tzinfoobject.tzname(None), 'UTC')

        date = aniso8601.parse_date('19810405')
        self.assertEqual(date.year, 1981)
        self.assertEqual(date.month, 4)
        self.assertEqual(date.day, 5)

        resultduration = aniso8601.parse_duration('P1Y2M3DT4H54M6S')
        self.assertEqual(resultduration.days, 428)
        self.assertEqual(resultduration.seconds, 17646)

        resultinterval = aniso8601.parse_interval(
            '1980-03-05T01:01:00/1981-04-05T01:01:00')
        self.assertEqual(
            resultinterval[0],
            datetime.datetime(year=1980, month=3, day=5, hour=1, minute=1))
        self.assertEqual(
            resultinterval[1],
            datetime.datetime(year=1981, month=4, day=5, hour=1, minute=1))

        results = list(aniso8601.parse_repeating_interval('R3/1981-04-05/P1D'))
        self.assertEqual(results[0], datetime.date(year=1981, month=4, day=5))
        self.assertEqual(results[1], datetime.date(year=1981, month=4, day=6))
        self.assertEqual(results[2], datetime.date(year=1981, month=4, day=7))
Example #16
0
def to_time(amazon_time):
    if amazon_time == "AM":
        return time(hour=0)
    if amazon_time == "PM":
        return time(hour=12)
    if amazon_time == "MO":
        return time(hour=5)
    if amazon_time == "AF":
        return time(hour=12)
    if amazon_time == "EV":
        return time(hour=17)
    if amazon_time == "NI":
        return time(hour=21)
    try:
        return aniso8601.parse_time(amazon_time)
    except ValueError as e:
        logger.warn("ValueError for amazon_time '{}'.".format(amazon_time))
        logger.warn("ValueError message: {}".format(str(e)))
        return None
Example #17
0
def parse_time(time_string):
    t, resolution = (
        iso8601.parse_time(time_string),
        iso8601.get_time_resolution(time_string),
    )
    tMin = t
    if resolution == TimeResolution.Hours:
        tMax = t.replace(
            minute=time.max.minute,
            second=time.max.second,
            microsecond=time.max.microsecond,
        )
    elif resolution == TimeResolution.Minutes:
        tMax = t.replace(second=time.max.second, microsecond=time.max.microsecond)
    elif resolution == TimeResolution.Seconds:
        if t.microsecond:
            tMax = t
        else:
            tMax = t.replace(microsecond=time.max.microsecond)
    else:
        raise NotImplementedError(f'Unable to deal with time input "{time_string}".')
    return tMin, tMax
    def test_import(self):
        #Just some tests repeated from other places to make sure the
        #imports work
        time = aniso8601.parse_time('01:23:45')
        self.assertEqual(time.hour, 1)
        self.assertEqual(time.minute, 23)
        self.assertEqual(time.second, 45)

        resultdatetime = aniso8601.parse_datetime('1981-04-05T23:21:28.512400Z')
        self.assertEqual(resultdatetime.year, 1981)
        self.assertEqual(resultdatetime.month, 4)
        self.assertEqual(resultdatetime.day, 5)
        self.assertEqual(resultdatetime.hour, 23)
        self.assertEqual(resultdatetime.minute, 21)
        self.assertEqual(resultdatetime.second, 28)
        self.assertEqual(resultdatetime.microsecond, 512400)
        tzinfoobject = resultdatetime.tzinfo
        self.assertEqual(tzinfoobject.utcoffset(None), datetime.timedelta(hours=0))
        self.assertEqual(tzinfoobject.tzname(None), 'UTC')

        date = aniso8601.parse_date('19810405')
        self.assertEqual(date.year, 1981)
        self.assertEqual(date.month, 4)
        self.assertEqual(date.day, 5)

        resultduration = aniso8601.parse_duration('P1Y2M3DT4H54M6S')
        self.assertEqual(resultduration.days, 428)
        self.assertEqual(resultduration.seconds, 17646)

        resultinterval = aniso8601.parse_interval('1980-03-05T01:01:00/1981-04-05T01:01:00')
        self.assertEqual(resultinterval[0], datetime.datetime(year=1980, month=3, day=5, hour=1, minute=1))
        self.assertEqual(resultinterval[1], datetime.datetime(year=1981, month=4, day=5, hour=1, minute=1))

        results = list(aniso8601.parse_repeating_interval('R3/1981-04-05/P1D'))
        self.assertEqual(results[0], datetime.date(year=1981, month=4, day=5))
        self.assertEqual(results[1], datetime.date(year=1981, month=4, day=6))
        self.assertEqual(results[2], datetime.date(year=1981, month=4, day=7))
Example #19
0
 def parse_value(cls, value):
     try:
         return parse_time(value)
     except ValueError:
         return None
Example #20
0
def parse_time(amztime):
    if amztime in TIME_DICT:
        return tuple(datetime.time(*tt) for tt in TIME_DICT[amztime])
    return aniso8601.parse_time(amztime)
Example #21
0
    def _extract_schedule_time(p_schedule_obj, p_rise_set):
        """ Find the number of minutes from midnight until the schedule time for action.

        Possible valid formats are:
            hh:mm:ss
            hh:mm
            sunrise
            sunrise + hh:mm
            sunrise - hh:mm
        @return: the number of minutes
        """
        l_timefield = p_schedule_obj.Time.lower()
        if 'dawn' in l_timefield:
            # print('Dawn - {}'.format(l_timefield))
            l_base = to_minutes(p_rise_set.Dawn)
            l_timefield = l_timefield[4:]
        elif 'sunrise' in l_timefield:
            # print('SunRise - {}'.format(l_timefield))
            l_base = to_minutes(p_rise_set.SunRise)
            l_timefield = l_timefield[7:]
        elif 'noon' in l_timefield:
            # print('Noon - {}'.format(l_timefield))
            l_base = to_minutes(p_rise_set.Noon)
            l_timefield = l_timefield[4:]
        elif 'sunset' in l_timefield:
            # print('SunSet - {}'.format(l_timefield))
            l_base = to_minutes(p_rise_set.SunSet)
            l_timefield = l_timefield[6:]
        elif 'dusk' in l_timefield:
            # print('Dusk - {}'.format(l_timefield))
            l_base = to_minutes(p_rise_set.Dusk)
            l_timefield = l_timefield[4:]
        else:
            l_base = 0

        l_timefield = l_timefield.strip()
        # print('==time== - {}'.format(l_timefield))

        l_subflag = False
        if '-' in l_timefield:
            # print(" found - ")
            l_subflag = True
            l_timefield = l_timefield[1:]
        elif '+' in l_timefield:
            # print(" found + ")
            l_subflag = False
            l_timefield = l_timefield[1:]
        l_timefield = l_timefield.strip()

        try:
            # l_time = dparser.parse(l_timefield, fuzzy=True)
            l_time = aniso8601.parse_time(l_timefield)
            # print('Parsable time field "{}" = "{}"'.format(l_timefield, l_time))
        except ValueError:
            # print('Unparsable time field "{}"'.format(l_timefield))
            l_time = datetime.time(0)

        l_offset = to_minutes(l_time)
        #
        #
        if l_subflag:
            l_minutes = l_base - l_offset
        else:
            l_minutes = l_base + l_offset
        #
        return l_minutes
Example #22
0
 def f(v):    
     try:
         return aniso8601.parse_time(v)
     except:
         raise good.Invalid('invalid time {}'.format(v))
Example #23
0
File: extra.py Project: rshk/pyql
def time_parse_value(cls, value):
    try:
        return aniso8601.parse_time(value)
    except ValueError:
        return None
 def _parse_time(value):
     t = aniso8601.parse_time(value)
     d = datetime.datetime.combine(datetime.datetime.now().date(), t)
     return apply_timezone(d)
Example #25
0
def parse_literal(ast_node, _variables=None):
    if isinstance(ast_node, ast.StringValueNode):
        return aniso8601.parse_time(ast_node.value)
    return graphql.INVALID
Example #26
0
def coerce(value: str):
    return aniso8601.parse_time(value)
Example #27
0
def get_time_from_json_attr(json, attr):
    return parse_time(json[attr])
Example #28
0
def add_diaper(name, kind, date, time):
    if name is not None:
        if SESSION_KIND not in session.attributes or session.attributes[SESSION_KIND] is None:
            return _save_and_prompt("kind",
                                    **{SESSION_NAME: name, SESSION_DATE: date, SESSION_TIME: time})
        elif SESSION_DATE not in session.attributes or session.attributes[SESSION_DATE] is None:
            return _save_and_prompt("date",
                                    **{SESSION_NAME: name, SESSION_SIDE: side, SESSION_TIME: time})
        elif SESSION_TIME not in session.attributes or session.attributes[SESSION_TIME] is None:
            return _save_and_prompt("time",
                                    **{SESSION_NAME: name, SESSION_SIDE: side, SESSION_DATE: date})
    elif kind is not None:
        if SESSION_NAME not in session.attributes or session.attributes[SESSION_NAME] is None:
            return _save_and_prompt("name",
                                    **{SESSION_KIND: kind, SESSION_DATE: date, SESSION_TIME: time})
        elif SESSION_DATE not in session.attributes or session.attributes[SESSION_DATE] is None:
            return _save_and_prompt("date",
                                    **{SESSION_NAME: name, SESSION_SIDE: side, SESSION_TIME: time})
        elif SESSION_TIME not in session.attributes or session.attributes[SESSION_TIME] is None:
            return _save_and_prompt("time",
                                    **{SESSION_NAME: name, SESSION_SIDE: side, SESSION_DATE: date})
    elif date is not None:
        if SESSION_NAME not in session.attributes or session.attributes[SESSION_NAME] is None:
            return _save_and_prompt("name",
                                    **{SESSION_KIND: kind, SESSION_DATE: date, SESSION_TIME: time})
        elif SESSION_KIND not in session.attributes or session.attributes[SESSION_KIND] is None:
            return _save_and_prompt("kind",
                                    **{SESSION_NAME: name, SESSION_DATE: date, SESSION_TIME: time})
        elif SESSION_TIME not in session.attributes or session.attributes[SESSION_TIME] is None:
            return _save_and_prompt("time",
                                    **{SESSION_NAME: name, SESSION_SIDE: side, SESSION_DATE: date})
    elif time is not None:
        if SESSION_NAME not in session.attributes or session.attributes[SESSION_NAME] is None:
            return _save_and_prompt("name",
                                    **{SESSION_KIND: kind, SESSION_DATE: date, SESSION_TIME: time})
        elif SESSION_KIND not in session.attributes or session.attributes[SESSION_KIND] is None:
            return _save_and_prompt("kind",
                                    **{SESSION_NAME: name, SESSION_DATE: date, SESSION_TIME: time})
        elif SESSION_DATE not in session.attributes or session.attributes[SESSION_DATE] is None:
            return _save_and_prompt("date",
                                    **{SESSION_NAME: name, SESSION_SIDE: side, SESSION_TIME: time})
    else:
        return _gather("name")
    # the first 4 blocks will always fall through to here while the else will not
    endpoint = "/api/diaper"
    user_token = session.user.accessToken
    child = name_to_id(name, user_token)
    if "children" in child:
        text = render_template("multiple_name")
        return statement(text).simple_card(text)
    child_id = child["_id"]
    kind = session.attributes[SESSION_KIND]
    date = aniso8601.parse_date(session.attributes[SESSION_DATE])
    time = aniso8601.parse_time(session.attributes[SESSION_TIME])
    dt = datetime.datime.combine(date, time)
    r = requests.post(BASE+endpoint,
                      headers={'Authorization': 'Basic %s' % b64encode(bytes(user_token+":"))},
                      data={'child_id': child_id, 'kind': kind, 'datetime': to_rfc822(dt)})
    if r.status_code != 200:
        return statement(render_template("failed_request"))  # TODO handle 400
    return statement("Diaper added.").simple_card("Diaper added")
Example #29
0
 def parse_value(cls, value):
     try:
         return parse_time(value)
     except ValueError:
         return None
Example #30
0
 def f(v):    
     try:
         return aniso8601.parse_time(v)
     except:
         raise good.Invalid('invalid time {}'.format(v))
Example #31
0
def add_feeding(name, side, date, time, length):
    if name is not None:
        if SESSION_SIDE not in session.attributes or session.attributes[SESSION_SIDE] is None:
            return _save_and_prompt("side",
                                    **{SESSION_NAME: name, SESSION_DATE: date, SESSION_TIME: time, SESSION_LENGTH: length})
        elif SESSION_DATE not in session.attributes or session.attributes[SESSION_DATE] is None:
            return _save_and_prompt("date",
                                    **{SESSION_NAME: name, SESSION_SIDE: side, SESSION_TIME: time, SESSION_LENGTH: length})
        elif SESSION_TIME not in session.attributes or session.attributes[SESSION_TIME] is None:
            return _save_and_prompt("time",
                                    **{SESSION_NAME: name, SESSION_SIDE: side, SESSION_DATE: date, SESSION_LENGTH: length})
        elif SESSION_LENGTH not in session.attributes or session.attributes[SESSION_LENGTH] is None:
            return _save_and_prompt("length",
                                    **{SESSION_NAME: name, SESSION_SIDE: side, SESSION_DATE: date, SESSION_TIME: time})
    elif side is not None:
        if SESSION_NAME not in session.attributes or session.attributes[SESSION_NAME] is None:
            return _save_and_prompt("name",
                                    **{SESSION_TIME: time, SESSION_SIDE: side, SESSION_DATE: date, SESSION_LENGTH: length})
        elif SESSION_DATE not in session.attributes or session.attributes[SESSION_DATE] is None:
            return _save_and_prompt("date",
                                    **{SESSION_NAME: name, SESSION_SIDE: side, SESSION_TIME: time, SESSION_LENGTH: length})
        elif SESSION_TIME not in session.attributes or session.attributes[SESSION_TIME] is None:
            return _save_and_prompt("time",
                                    **{SESSION_NAME: name, SESSION_SIDE: side, SESSION_DATE: date, SESSION_LENGTH: length})
        elif SESSION_LENGTH not in session.attributes or session.attributes[SESSION_LENGTH] is None:
            return _save_and_prompt("length",
                                    **{SESSION_NAME: name, SESSION_SIDE: side, SESSION_DATE: date, SESSION_TIME: time})
    elif date is not None:
        if SESSION_SIDE not in session.attributes or session.attributes[SESSION_SIDE] is None:
            return _save_and_prompt("side",
                                    **{SESSION_NAME: name, SESSION_DATE: date, SESSION_TIME: time, SESSION_LENGTH: length})
        elif SESSION_NAME not in session.attributes or session.attributes[SESSION_NAME] is None:
            return _save_and_prompt("name",
                                    **{SESSION_TIME: time, SESSION_SIDE: side, SESSION_DATE: date, SESSION_LENGTH: length})
        elif SESSION_TIME not in session.attributes or session.attributes[SESSION_TIME] is None:
            return _save_and_prompt("time",
                                    **{SESSION_NAME: name, SESSION_SIDE: side, SESSION_DATE: date, SESSION_LENGTH: length})
        elif SESSION_LENGTH not in session.attributes or session.attributes[SESSION_LENGTH] is None:
            return _save_and_prompt("length",
                                    **{SESSION_NAME: name, SESSION_SIDE: side, SESSION_DATE: date, SESSION_TIME: time})
    elif time is not None:
        if SESSION_SIDE not in session.attributes or session.attributes[SESSION_SIDE] is None:
            return _save_and_prompt("side",
                                    **{SESSION_NAME: name, SESSION_DATE: date, SESSION_TIME: time, SESSION_LENGTH: length})
        elif SESSION_DATE not in session.attributes or session.attributes[SESSION_DATE] is None:
            return _save_and_prompt("date",
                                    **{SESSION_NAME: name, SESSION_SIDE: side, SESSION_TIME: time, SESSION_LENGTH: length})
        elif SESSION_NAME not in session.attributes or session.attributes[SESSION_NAME] is None:
            return _save_and_prompt("name",
                                    **{SESSION_TIME: time, SESSION_SIDE: side, SESSION_DATE: date, SESSION_LENGTH: length})
        elif SESSION_LENGTH not in session.attributes or session.attributes[SESSION_LENGTH] is None:
            return _save_and_prompt("length",
                                    **{SESSION_NAME: name, SESSION_SIDE: side, SESSION_DATE: date, SESSION_TIME: time})
    elif length is not None:
        if SESSION_SIDE not in session.attributes or session.attributes[SESSION_SIDE] is None:
            return _save_and_prompt("side",
                                    **{SESSION_NAME: name, SESSION_DATE: date, SESSION_TIME: time, SESSION_LENGTH: length})
        elif SESSION_DATE not in session.attributes or session.attributes[SESSION_DATE] is None:
            return _save_and_prompt("date",
                                    **{SESSION_NAME: name, SESSION_SIDE: side, SESSION_TIME: time, SESSION_LENGTH: length})
        elif SESSION_NAME not in session.attributes or session.attributes[SESSION_NAME] is None:
            return _save_and_prompt("name",
                                    **{SESSION_TIME: time, SESSION_SIDE: side, SESSION_DATE: date, SESSION_LENGTH: length})
        elif SESSION_TIME not in session.attributes or session.attributes[SESSION_TIME] is None:
            return _save_and_prompt("time",
                                    **{SESSION_NAME: name, SESSION_SIDE: side, SESSION_DATE: date, SESSION_LENGTH: length})
    else:
        return _gather("name")
    # the first 5 blocks will always fall through to here while the else will not
    endpoint = "/api/feeding"
    user_token = session.user.accessToken
    if name is None:
        name = session.attributes[SESSION_NAME]
    child = name_to_id(name, user_token)
    if "children" in child:
        text = render_template("multiple_name")
        return statement(text).simple_card(text)
    child_id = child["_id"]
    side = session.attributes[SESSION_SIDE]
    length = session.attributes[SESSION_LENGTH]
    date = aniso8601.parse_date(session.attributes[SESSION_DATE])
    time = aniso8601.parse_time(session.attributes[SESSION_TIME])
    dt = datetime.datime.combine(date, time)
    r = requests.post(BASE+endpoint,
                      headers={'Authorization': 'Basic %s' % b64encode(bytes(user_token+":"))},
                      data={'child_id': child_id, 'side': side, 'datetime': to_rfc822(dt), 'duration': length})
    if r.status_code != 200:
        return statement(render_template("failed_request"))  # TODO handle 400
    return statement("Feeding added.").simple_card("Feeding added")