Code

searching on ranges of intervals is implemented
authorkedder <kedder@57a73879-2fb5-44c3-a270-3262357dd7e2>
Sun, 20 Apr 2003 11:58:45 +0000 (11:58 +0000)
committerkedder <kedder@57a73879-2fb5-44c3-a270-3262357dd7e2>
Sun, 20 Apr 2003 11:58:45 +0000 (11:58 +0000)
git-svn-id: http://svn.roundup-tracker.org/svnroot/roundup/trunk@1668 57a73879-2fb5-44c3-a270-3262357dd7e2

CHANGES.txt
doc/upgrading.txt
doc/user_guide.txt
roundup/backends/back_anydbm.py
roundup/backends/back_metakit.py
roundup/backends/rdbms_common.py
test/test_db.py

index 2a93850b12edca41161e11636d5fa156b3418021..81cd0e5d9b51c46f1535b1bfe9dae53019e967ea 100644 (file)
@@ -47,8 +47,9 @@ Feature:
 - added Node.get() method
 - nicer page titles (sf feature 65197)
 - relaxed CVS importing (sf feature 693277)
-- added support for searching on ranges of dates (see doc/user_guide.txt in
-  chapter "Searching Page" for details) (closes sf feature 700178)
+- added support for searching on ranges of dates and intervals (see
+  doc/user_guide.txt in chapter "Searching Page" for details) (closes sf
+  feature 700178)
 - role names made case insensitive
 - added ability to restore retired nodes
 - more lenient date input and addition Interval input support (sf bug 677764)
@@ -56,7 +57,7 @@ Feature:
 - implemented ability to search for multilink properties with no value
 - Class.find() may now find unset Links (sf bug 700620)
 - more flexibility in classhelp link labelling (sf feature 608204)
-- added command-line functionality for roundup-adming (sf feature 687664)
+- added command-line functionality for roundup-admin (sf feature 687664)
 - added nicer popup windows for topic, nosy, etc (has add/remove buttons)
   thanks Gus Gollings
 - HTML templating files now have a .html extension
index a3344700a7a860b2852853b949d4c9503a9eb2db..742dabffb896cf82640da08370c02c43ed77766e 100644 (file)
@@ -70,7 +70,7 @@ name:confirm -> :confirm:name
   timezone if he/she has provided timezone information. To make it possible
   some modification to tracker's schema and HTML templates are required.
   First you should add string property 'timezone' to user class in dbinit.py
-  like this:
+  like this::
   
     user = Class(db, "user", 
                     username=String(),   password=Password(),
@@ -98,6 +98,25 @@ name:confirm -> :confirm:name
   However you are not forced to make these modifications. By default roundup
   will assume timezone=0 and will work as previous versions did.
 
+
+0.6.0 Notes for metakit backend users
+-------------------------------------
+
+- Roundup 0.6.0 introduced searching on ranges of dates and intervals. To
+  support it, some modifications to interval storing routine were made. So if
+  your tracker uses metakit backend and your db schema contains intervals
+  property, searches on that property will not be accurate for db items that
+  was stored before roundup' upgrade. However all new records should be
+  searchable on intervals.
+
+  It is possible to convert your database to new format: you can export and
+  import back all your data (consult "Migrating backends" in "Maintenance"
+  documentation). After this operation all your interval properties should
+  become searchable.
+
+  Users of backends others than metakit should not worry about this issue.
+
+
 Migrating from 0.4.x to 0.5.0
 =============================
 
index 42bb37981afe87079e81f442306821225923cec3..8d945309daa5264e664f21f3d2d1e11a9391aada 100644 (file)
@@ -2,7 +2,7 @@
 User Guide
 ==========
 
-:Version: $Revision: 1.19 $
+:Version: $Revision: 1.20 $
 
 .. contents::
 
@@ -141,6 +141,8 @@ Interval properties
 
 XXX explain...
 
+When searching on interval properties use the same syntax as for dates.
+
 
 Web Interface
 =============
index bed71b8324b108c8fa7b09c6a4ee7361e3222efd..670a0aff0267b61202f62a0d22b85b4e3686c257 100644 (file)
@@ -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.118 2003-03-26 11:19:28 richard Exp $
+#$Id: back_anydbm.py,v 1.119 2003-04-20 11:58:45 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
@@ -1639,6 +1639,7 @@ class Class(hyperdb.Class):
         MULTILINK = 1
         STRING = 2
         DATE = 3
+        INTERVAL = 4
         OTHER = 6
         
         timezone = self.db.getUserTimezone()
@@ -1695,18 +1696,21 @@ class Class(hyperdb.Class):
                     l.append((DATE, k, date_rng))
                 except ValueError:
                     # If range creation fails - ignore that search parameter
-                    pass                            
+                    pass
+            elif isinstance(propclass, Interval):
+                try:
+                    intv_rng = Range(v, date.Interval)
+                    l.append((INTERVAL, k, intv_rng))
+                except ValueError:
+                    # If range creation fails - ignore that search parameter
+                    pass
+                
             elif isinstance(propclass, Boolean):
                 if type(v) is type(''):
                     bv = v.lower() in ('yes', 'true', 'on', '1')
                 else:
                     bv = v
                 l.append((OTHER, k, bv))
-            # kedder: dates are filtered by ranges
-            #elif isinstance(propclass, Date):
-            #    l.append((OTHER, k, date.Date(v)))
-            elif isinstance(propclass, Interval):
-                l.append((OTHER, k, date.Interval(v)))
             elif isinstance(propclass, Number):
                 l.append((OTHER, k, int(v)))
             else:
@@ -1760,7 +1764,7 @@ class Class(hyperdb.Class):
                         # RE search
                         if node[k] is None or not v.search(node[k]):
                             break
-                    elif t == DATE:
+                    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]):
index 16ac60c04a757d153c07226ae679ce95ab9a6b23..7b94ac847e8b384601485db7e672e448f7f448ea 100755 (executable)
@@ -1,4 +1,4 @@
-# $Id: back_metakit.py,v 1.45 2003-03-26 10:44:00 richard Exp $
+# $Id: back_metakit.py,v 1.46 2003-04-20 11:58:45 kedder Exp $
 '''
    Metakit backend for Roundup, originally by Gordon McMillan.
 
@@ -603,10 +603,11 @@ class Class:
                 if value is None:
                     setattr(row, key, '')
                 else:
-                    setattr(row, key, str(value))
+                    # kedder: we should store interval values serialized
+                    setattr(row, key, value.serialise())
                 changes[key] = str(oldvalue)
                 propvalues[key] = str(value)
-                
             elif isinstance(prop, hyperdb.Number):
                 if value is None:
                     value = 0
@@ -963,6 +964,10 @@ class Class:
                     if date_rng.from_value:
                         t = date_rng.from_value.get_tuple()
                         where[propname] = int(calendar.timegm(t))
+                    else:
+                        # use minimum possible value to exclude items without
+                        # 'prop' property
+                        where[propname] = 0
                     if date_rng.to_value:
                         t = date_rng.to_value.get_tuple()
                         wherehigh[propname] = int(calendar.timegm(t))
@@ -972,7 +977,24 @@ class Class:
                     # If range creation fails - ignore that search parameter
                     pass                        
             elif isinstance(prop, hyperdb.Interval):
-                where[propname] = str(date.Interval(value))
+                try:
+                    # Try to filter on range of intervals
+                    date_rng = Range(value, date.Interval)
+                    if date_rng.from_value:
+                        #t = date_rng.from_value.get_tuple()
+                        where[propname] = date_rng.from_value.serialise()
+                    else:
+                        # use minimum possible value to exclude items without
+                        # 'prop' property
+                        where[propname] = '-99999999999999'
+                    if date_rng.to_value:
+                        #t = date_rng.to_value.get_tuple()
+                        wherehigh[propname] = date_rng.to_value.serialise()
+                    else:
+                        wherehigh[propname] = None
+                except ValueError:
+                    # If range creation fails - ignore that search parameter
+                    pass                        
             elif isinstance(prop, hyperdb.Number):
                 where[propname] = int(value)
             else:
@@ -1187,7 +1209,7 @@ class Class:
             if isinstance(prop, hyperdb.Date):
                 value = int(calendar.timegm(value))
             elif isinstance(prop, hyperdb.Interval):
-                value = str(date.Interval(value))
+                value = date.Interval(value).serialise()
             elif isinstance(prop, hyperdb.Number):
                 value = int(value)
             elif isinstance(prop, hyperdb.Boolean):
index ffc9391af7b65ab7b0d3c40e70072106a313be8f..454fe3e3acd459bd906cc441ceb4c67bdbc87cba 100644 (file)
@@ -1,4 +1,4 @@
-# $Id: rdbms_common.py,v 1.53 2003-04-08 06:41:48 richard Exp $
+# $Id: rdbms_common.py,v 1.54 2003-04-20 11:58:45 kedder Exp $
 ''' Relational database (SQL) backend common code.
 
 Basics:
@@ -1866,8 +1866,20 @@ class Class(hyperdb.Class):
                     where.append('_%s in (%s)'%(k, s))
                     args = args + [date.Interval(x).serialise() for x in v]
                 else:
-                    where.append('_%s=%s'%(k, a))
-                    args.append(date.Interval(v).serialise())
+                    try:
+                        # Try to filter on range of intervals
+                        date_rng = Range(v, date.Interval)
+                        if (date_rng.from_value):
+                            where.append('_%s > %s'%(k, a))                            
+                            args.append(date_rng.from_value.serialise())
+                        if (date_rng.to_value):
+                            where.append('_%s < %s'%(k, a))
+                            args.append(date_rng.to_value.serialise())
+                    except ValueError:
+                        # If range creation fails - ignore that search parameter
+                        pass                        
+                    #where.append('_%s=%s'%(k, a))
+                    #args.append(date.Interval(v).serialise())
             else:
                 if isinstance(v, type([])):
                     s = ','.join([a for x in v])
index 613b0c901aaa60f2d02b679e76dde8d6d3ff97f9..8d0a49e5c715fbc2cdfaecc8c0afefc8f549b6d3 100644 (file)
@@ -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.84 2003-03-26 11:19:28 richard Exp $ 
+# $Id: test_db.py,v 1.85 2003-04-20 11:58:45 kedder Exp $ 
 
 import unittest, os, shutil, time
 
@@ -691,6 +691,7 @@ class anydbmDBTestCase(MyTestCase):
 
     def testFilteringRange(self):
         ae, filt = self.filteringSetup()
+        # Date ranges
         ae(filt(None, {'deadline': 'from 2003-02-10 to 2003-02-23'}), ['2'])
         ae(filt(None, {'deadline': '2003-02-10; 2003-02-23'}), ['2'])
         ae(filt(None, {'deadline': '; 2003-02-16'}), ['1'])
@@ -698,6 +699,11 @@ class anydbmDBTestCase(MyTestCase):
         # may fail :)
         ae(filt(None, {'deadline': 'from 2003-02-16'}), ['2', '3'])
         ae(filt(None, {'deadline': '2003-02-16'}), ['2', '3'])
+        # Interval ranges
+        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'}), [])
 
     def testFilteringIntervalSort(self):
         ae, filt = self.filteringSetup()