From 62c10fff68eaded0e04c395e1b58faf92f17cb44 Mon Sep 17 00:00:00 2001 From: kedder Date: Tue, 22 Apr 2003 20:53:55 +0000 Subject: [PATCH] extended date syntax to make range searches even more useful git-svn-id: http://svn.roundup-tracker.org/svnroot/roundup/trunk@1671 57a73879-2fb5-44c3-a270-3262357dd7e2 --- CHANGES.txt | 1 + doc/user_guide.txt | 56 ++++++++++++++++++-------------- roundup/backends/back_anydbm.py | 6 ++-- roundup/backends/rdbms_common.py | 10 +++--- roundup/date.py | 31 +++++++++++------- test/test_dates.py | 6 +++- test/test_db.py | 5 +-- 7 files changed, 68 insertions(+), 47 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 81cd0e5..7cb8e42 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -63,6 +63,7 @@ Feature: - HTML templating files now have a .html extension - Roundup templates are now distributed much more sanely, allowing for 3rd-party templates. +- extended date syntax to make range searches even more useful Fixed: - applied unicode patch. All data is stored in utf-8. Incoming messages diff --git a/doc/user_guide.txt b/doc/user_guide.txt index 8d94530..b40b39f 100644 --- a/doc/user_guide.txt +++ b/doc/user_guide.txt @@ -2,7 +2,7 @@ User Guide ========== -:Version: $Revision: 1.20 $ +:Version: $Revision: 1.21 $ .. contents:: @@ -99,13 +99,13 @@ of dates in one of two formats: 1. English syntax:: - [[From] ][ To ] + [From ][To ] Keywords "From" and "To" are case insensitive. Keyword "From" is optional. 2. "Geek" syntax:: - [][; ] + [];[] Either first or second ```` can be omitted in both syntaxes. @@ -117,24 +117,30 @@ active between period of time since 2 months up-till month ago. Other possible examples (consider local time is Sat Mar 8 22:07:48 2003):: - >>> Range("from 2-12 to 4-2") - - - >>> Range("18:00 TO +2m") - - - >>> Range("12:00") - - - >>> Range("tO +3d") - - - >>> Range("2002-11-10; 2002-12-12") - - - >>> Range("; 20:00 +1d") - + >>> Range("from 2-12 to 4-2") + + + >>> Range("FROM 18:00 TO +2m") + + + >>> Range("12:00;") + + + >>> Range("tO +3d") + + + >>> Range("2002-11-10; 2002-12-12") + + >>> Range("; 20:00 +1d") + + + >>> Range("2003") + + + >>> Range("2003-04") + + Interval properties ~~~~~~~~~~~~~~~~~~~ @@ -486,10 +492,10 @@ The basic usage is:: -u -- the user[:password] to use for commands -d -- print full designators not just class id numbers -c -- when outputting lists of data, comma-separate them. - Same as '-S ","'. + Same as '-S ","'. -S -- when outputting lists of data, string-separate them -s -- when outputting lists of data, space-separate them. - Same as '-S " "'. + Same as '-S " "'. Only one of -s, -c or -S can be specified. @@ -549,6 +555,8 @@ printed results: "11-07.09:32:43" yyyy-11-07.14:32:43 "14:25" yyyy-mm-dd.19:25:00 "8:47:11" yyyy-mm-dd.13:47:11 + "2003" 2003-01-01.00:00:00 + "2003-04" 2003-04-01.00:00:00 "." "right now" - Link values are printed as item designators. When given as an argument, @@ -625,7 +633,7 @@ You can also display issue contents:: or status:: shell% roundup-admin get name `/tools/roundup/bin/roundup-admin \ - -dc -i /var/roundup/sysadmin get status issue3,issue1` + -dc -i /var/roundup/sysadmin get status issue3,issue1` unread deferred @@ -644,7 +652,7 @@ which is the same as:: Also the tautological:: shell% roundup-admin get name \ - `roundup-admin -dc get status \`roundup-admin -dc find issue \ + `roundup-admin -dc get status \`roundup-admin -dc find issue \ status=chatting\`` chatting chatting diff --git a/roundup/backends/back_anydbm.py b/roundup/backends/back_anydbm.py index 670a0af..8299415 100644 --- a/roundup/backends/back_anydbm.py +++ b/roundup/backends/back_anydbm.py @@ -15,7 +15,7 @@ # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. # -#$Id: back_anydbm.py,v 1.119 2003-04-20 11:58:45 kedder Exp $ +#$Id: back_anydbm.py,v 1.120 2003-04-22 20:53:54 kedder Exp $ ''' This module defines a backend that saves the hyperdatabase in a database chosen by anydbm. It is guaranteed to always be available in python @@ -1767,10 +1767,10 @@ class Class(hyperdb.Class): elif t == DATE or t == INTERVAL: if node[k] is None: break if v.to_value: - if not (v.from_value < node[k] and v.to_value > node[k]): + if not (v.from_value <= node[k] and v.to_value >= node[k]): break else: - if not (v.from_value < node[k]): + if not (v.from_value <= node[k]): break elif t == OTHER: # straight value comparison for the other types diff --git a/roundup/backends/rdbms_common.py b/roundup/backends/rdbms_common.py index 454fe3e..2d4a67a 100644 --- a/roundup/backends/rdbms_common.py +++ b/roundup/backends/rdbms_common.py @@ -1,4 +1,4 @@ -# $Id: rdbms_common.py,v 1.54 2003-04-20 11:58:45 kedder Exp $ +# $Id: rdbms_common.py,v 1.55 2003-04-22 20:53:54 kedder Exp $ ''' Relational database (SQL) backend common code. Basics: @@ -1852,10 +1852,10 @@ class Class(hyperdb.Class): # Try to filter on range of dates date_rng = Range(v, date.Date, offset=timezone) if (date_rng.from_value): - where.append('_%s > %s'%(k, a)) + where.append('_%s >= %s'%(k, a)) args.append(date_rng.from_value.serialise()) if (date_rng.to_value): - where.append('_%s < %s'%(k, a)) + where.append('_%s <= %s'%(k, a)) args.append(date_rng.to_value.serialise()) except ValueError: # If range creation fails - ignore that search parameter @@ -1870,10 +1870,10 @@ class Class(hyperdb.Class): # Try to filter on range of intervals date_rng = Range(v, date.Interval) if (date_rng.from_value): - where.append('_%s > %s'%(k, a)) + where.append('_%s >= %s'%(k, a)) args.append(date_rng.from_value.serialise()) if (date_rng.to_value): - where.append('_%s < %s'%(k, a)) + where.append('_%s <= %s'%(k, a)) args.append(date_rng.to_value.serialise()) except ValueError: # If range creation fails - ignore that search parameter diff --git a/roundup/date.py b/roundup/date.py index 95777a3..2a543cd 100644 --- a/roundup/date.py +++ b/roundup/date.py @@ -15,7 +15,7 @@ # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. # -# $Id: date.py,v 1.52 2003-04-21 14:29:39 kedder Exp $ +# $Id: date.py,v 1.53 2003-04-22 20:53:54 kedder Exp $ __doc__ = """ Date, time and time interval handling. @@ -59,6 +59,8 @@ class Date: "11-07.09:32:43" means "14:25" means "8:47:11" means + "2003" means + "2003-06" means "." means "right now" The Date class should understand simple date expressions of the form @@ -89,6 +91,7 @@ class Date: minute, second) is the serialisation format returned by the serialise() method, and is accepted as an argument on instatiation. ''' + def __init__(self, spec='.', offset=0, add_granularity=0): """Construct a date given a specification and a time zone offset. @@ -104,8 +107,10 @@ class Date: self.year, self.month, self.day, self.hour, self.minute, \ self.second, x, x, x = time.gmtime(ts) + usagespec='[yyyy]-[mm]-[dd].[H]H:MM[:SS][offset]' def set(self, spec, offset=0, date_re=re.compile(r''' - (((?P\d\d\d\d)[/-])?(?P\d\d?)?[/-](?P\d\d?))? # [yyyy-]mm-dd + ((?P\d\d\d\d)([/-](?P\d\d?)([/-](?P\d\d?))?)? # yyyy[-mm[-dd]] + |(?P\d\d?)[/-](?P\d\d?))? # or mm-dd (?P\.)? # . (((?P\d?\d):(?P\d\d))?(:(?P\d\d))?)? # hh:mm:ss (?P.+)? # offset @@ -125,24 +130,27 @@ class Date: # not serialised data, try usual format m = date_re.match(spec) if m is None: - raise ValueError, _('Not a date spec: [[yyyy-]mm-dd].' - '[[h]h:mm[:ss]][offset]') + raise ValueError, _('Not a date spec: %s' % self.usagespec) info = m.groupdict() if add_granularity: - _add_granularity(info, 'SMHdmy') + _add_granularity(info, 'SMHdmyab') # get the current date as our default y,m,d,H,M,S,x,x,x = time.gmtime(time.time()) - # override year, month, day parts - if info['m'] is not None and info['d'] is not None: - m = int(info['m']) - d = int(info['d']) + if info['y'] is not None or info['a'] is not None: if info['y'] is not None: y = int(info['y']) - # time defaults to 00:00:00 GMT - offset (local midnight) + m,d = (1,1) + if info['m'] is not None: + m = int(info['m']) + if info['d'] is not None: + d = int(info['d']) + if info['a'] is not None: + m = int(info['a']) + d = int(info['b']) H = -offset M = S = 0 @@ -165,8 +173,7 @@ class Date: try: self.applyInterval(Interval(info['o'], allowdate=0)) except ValueError: - raise ValueError, _('Not a date spec: [[yyyy-]mm-dd].' - '[[h]h:mm[:ss]][offset]') + raise ValueError, _('Not a date spec: %s' % self.usagespec) def addInterval(self, interval): ''' Add the interval to this date, returning the date tuple diff --git a/test/test_dates.py b/test/test_dates.py index 54a4857..69c1f4e 100644 --- a/test/test_dates.py +++ b/test/test_dates.py @@ -15,7 +15,7 @@ # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. # -# $Id: test_dates.py,v 1.23 2003-04-21 14:29:40 kedder Exp $ +# $Id: test_dates.py,v 1.24 2003-04-22 20:53:54 kedder Exp $ import unittest, time @@ -56,6 +56,8 @@ class DateTestCase(unittest.TestCase): ae(str(date), '%s-%02d-%02d.14:25:00'%(y, m, d)) date = Date("8:47:11") ae(str(date), '%s-%02d-%02d.08:47:11'%(y, m, d)) + ae(str(Date('2003')), '2003-01-01.00:00:00') + ae(str(Date('2004-06')), '2004-06-01.00:00:00') def testDateError(self): self.assertRaises(ValueError, Date, "12") @@ -249,6 +251,8 @@ class DateTestCase(unittest.TestCase): ae = self.assertEqual ae(str(Date('2003-2-12', add_granularity=1)), '2003-02-12.23:59:59') ae(str(Date('2003-1-1.23:00', add_granularity=1)), '2003-01-01.23:00:59') + ae(str(Date('2003', add_granularity=1)), '2003-12-31.23:59:59') + ae(str(Date('2003-5', add_granularity=1)), '2003-05-31.23:59:59') ae(str(Interval('+1w', add_granularity=1)), '+ 14d') ae(str(Interval('-2m 3w', add_granularity=1)), '- 2m 14d') diff --git a/test/test_db.py b/test/test_db.py index 8fc18d4..da9f3c7 100644 --- a/test/test_db.py +++ b/test/test_db.py @@ -15,7 +15,7 @@ # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. # -# $Id: test_db.py,v 1.87 2003-04-21 22:38:48 richard Exp $ +# $Id: test_db.py,v 1.88 2003-04-22 20:53:55 kedder Exp $ import unittest, os, shutil, time @@ -703,6 +703,7 @@ class anydbmDBTestCase(MyTestCase): ae(filt(None, {'deadline': 'from 2003-02-16'}), ['2', '3', '4']) ae(filt(None, {'deadline': '2003-02-16;'}), ['2', '3', '4']) # year and month granularity + ae(filt(None, {'deadline': '2002'}), []) ae(filt(None, {'deadline': '2003'}), ['1', '2', '3']) ae(filt(None, {'deadline': '2004'}), ['4']) ae(filt(None, {'deadline': '2003-02'}), ['2', '3']) @@ -713,7 +714,7 @@ class anydbmDBTestCase(MyTestCase): ae(filt(None, {'foo': 'from 0:50 to 2:00'}), ['1']) ae(filt(None, {'foo': 'from 0:50 to 1d 2:00'}), ['1', '2']) ae(filt(None, {'foo': 'from 5:50'}), ['2']) - ae(filt(None, {'foo': 'to 0:50'}), []) + ae(filt(None, {'foo': 'to 0:05'}), []) def testFilteringIntervalSort(self): ae, filt = self.filteringSetup() -- 2.30.2