def test_valid(self): ref = time.mktime(time.strptime("2004", "%Y")) self.assertEqual(util.parse_date("2004"), ref) self.assertEqual(util.parse_date("2004-01-01"), ref) self.assertEqual(util.parse_date("2004-1-1"), ref) self.assertTrue( util.parse_date("2004-01-01") < util.parse_date("2004-01-02"))
def test_date_op(self): o, v = map_numeric_op("lastplayed", "=", "2004") self.assertTrue(o(parse_date("2004"), v)) o, v = map_numeric_op("lastplayed", "<", "2004") self.assertTrue(o(parse_date("2003"), v)) o, v = map_numeric_op("lastplayed", ">", "2004") self.assertFalse(o(parse_date("2003"), v))
def test_date_tag(self): song = AudioFile({'date': '2012-11-09'}) self.failUnless(NumexprTag('date').evaluate(song, 0, True) == parse_date('2012-11-09')) self.failUnless(NumexprTag('date').evaluate(song, 424242, True) == parse_date('2012-11-09')) self.failUnless(NumexprTag('date').evaluate(song, 0, True) > parse_date('2012-11-08')) self.failUnless(NumexprTag('date').evaluate(song, 0, True) < parse_date('2012-11-10'))
def __init__(self, date): self.date = parse_date(date) parts = date.split('-') self.number = int(parts[0]) if len(parts) > 1: self.number -= int(parts[1]) if len(parts) > 2: self.number -= int(parts[2])
def evaluate(self, data, time, use_date): if self.__tag == 'date': date = data('date') if not date: return None try: num = parse_date(date) except ValueError: return None else: num = data(self.__ftag, None) if num is not None: if self.__tag in TIME_KEYS: num = time - num return round(num, 2) return None
def evaluate(self, data, time, use_date): if self._tag == 'date': date = data('date') if not date: return None try: num = parse_date(date) except ValueError: return None else: num = data(self._ftag, None) if num is not None: if self._ftag in TIME_TAGS: num = time - num return round(num, 2) return None
def evaluate(self, data, time, use_date): if self._tag == 'date': date = data('date') if not date: return None try: num = parse_date(date) except ValueError: return None else: num = data(self._ftag, None) if num is not None: # Strip aggregate function from tag func_start = self._ftag.find(":") tag = self._ftag[:func_start] if func_start >= 0 else self._ftag if tag in TIME_TAGS: num = time - num return round(num, 2) return None
def map_numeric_op(tag, op, value, time_=None): """Maps a human readable numeric comparison to something we can use. Handles cases like '< 3 days', '>5MB' etc.. If parsing fails, raises a ParseError. Takes a tag, an operator string and and a value string: op, v = map_numeric_op("added", "<", "today") Returns an (operator function, numeric value) tuple: if op(v, song("~#added")): ... (time_ is only used for testing) """ operators = { "<": operator.lt, "<=": operator.le, ">": operator.gt, ">=": operator.ge, "=": operator.eq, "!=": operator.ne, } if op not in operators: raise ParseError("Unknown operator %s" % op) inv_op = op.replace(">", "<") if op[0] == ">" else op.replace("<", ">") inv_op = operators[inv_op] op = operators[op] value = value.lower().strip() if tag == "date": if not validate_query_date(value): raise ParseError("Invalid date %r" % value) return (op, date_key(value)) if tag in TIME_KEYS: try: value = parse_date(value) except ValueError: pass else: return (op, value) if value == "now": value = (time_ or time.time()) return (inv_op, value) if value == "today": value = (time_ or time.time()) - 24 * 60 * 60 return (inv_op, value) # check for time formats: "5:30" # TODO: handle "5:30 ago" try: hms = map(int, value.split(":")) except ValueError: pass else: if len(hms) > 1: value = 0 for t in hms: value *= 60 value += t if tag in TIME_KEYS: value = (time_ or time.time()) - value return (inv_op, value) return (op, value) # get the biggest float/int max_val = "" for i in xrange(len(value) + 1, 0, -1): part = value[:i] try: float(part) except ValueError: pass else: max_val = part break else: raise ParseError("No numeric value %r" % value) unit = value[len(max_val):].strip() try: value = int(max_val) except ValueError: value = float(max_val) if tag in TIME_KEYS: part = (unit.split() or [""])[0].rstrip("s") if part.startswith("second"): value = value elif part == "minute": value *= 60 elif part == "hour": value *= 60 * 60 elif part == "day": value *= 24 * 60 * 60 elif part == "week": value *= 7 * 24 * 60 * 60 elif part == "month": value *= 30 * 24 * 60 * 60 elif part == "year": value *= 365 * 24 * 60 * 60 elif unit: raise ParseError("No time unit: %r" % unit) else: # don't allow raw seconds since epoch. It's not that usefull # and overlaps with the date parsing # (10 would be 10 seconds, 1970 would be 0) raise ParseError("No valid time format") value = int((time_ or time.time()) - value) return (inv_op, value) if tag in SIZE_KEYS: if unit.startswith("g"): value *= 1024**3 elif unit.startswith("m"): value *= 1024**2 elif unit.startswith("k"): value *= 1024 elif unit.startswith("b"): pass elif unit: raise ParseError("No size unit: %r" % unit) elif unit: raise ParseError("Tag %r does not support units (%r)" % (tag, unit)) return (op, value)
def map_numeric_op(tag, op, value, time_=None): """Maps a human readable numeric comparison to something we can use. Handles cases like '< 3 days', '>5MB' etc.. If parsing fails, raises a ParseError. Takes a tag, an operator string and and a value string: op, v = map_numeric_op("added", "<", "today") Returns an (operator function, numeric value) tuple: if op(v, song("~#added")): ... (time_ is only used for testing) """ operators = { "<": operator.lt, "<=": operator.le, ">": operator.gt, ">=": operator.ge, "=": operator.eq, "!=": operator.ne, } if op not in operators: raise ParseError("Unknown operator %s" % op) inv_op = op.replace(">", "<") if op[0] == ">" else op.replace("<", ">") inv_op = operators[inv_op] op = operators[op] value = value.lower().strip() if tag == "date": if not validate_query_date(value): raise ParseError("Invalid date %r" % value) return (op, date_key(value)) if tag in TIME_KEYS: try: value = parse_date(value) except ValueError: pass else: return (op, value) if value == "now": value = (time_ or time.time()) return (inv_op, value) if value == "today": value = (time_ or time.time()) - 24 * 60 * 60 return (inv_op, value) # check for time formats: "5:30" # TODO: handle "5:30 ago" try: hms = map(int, value.split(":")) except ValueError: pass else: if len(hms) > 1: value = 0 for t in hms: value *= 60 value += t if tag in TIME_KEYS: value = (time_ or time.time()) - value return (inv_op, value) return (op, value) # get the biggest float/int max_val = "" for i in xrange(len(value) + 1, 1, -1): part = value[:i] try: float(part) except ValueError: pass else: max_val = part break else: raise ParseError("No numeric value %r" % value) unit = value[len(max_val):].strip() try: value = int(max_val) except ValueError: value = float(max_val) if tag in TIME_KEYS: part = (unit.split() or [""])[0].rstrip("s") if part.startswith("second"): value = value elif part == "minute": value *= 60 elif part == "hour": value *= 60 * 60 elif part == "day": value *= 24 * 60 * 60 elif part == "week": value *= 7 * 24 * 60 * 60 elif part == "month": value *= 30 * 24 * 60 * 60 elif part == "year": value *= 365 * 24 * 60 * 60 elif unit: raise ParseError("No time unit: %r" % unit) else: # don't allow raw seconds since epoch. It's not that usefull # and overlaps with the date parsing # (10 would be 10 seconds, 1970 would be 0) raise ParseError("No valid time format") value = int((time_ or time.time()) - value) return (inv_op, value) if tag in SIZE_KEYS: if unit.startswith("g"): value *= 1024 ** 3 elif unit.startswith("m"): value *= 1024 ** 2 elif unit.startswith("k"): value *= 1024 elif unit.startswith("b"): pass elif unit: raise ParseError("No size unit: %r" % unit) elif unit: raise ParseError("Tag %r does not support units (%r)" % (tag, unit)) return (op, value)