Code

Handles new node display now.
[roundup.git] / roundup / hyperdb.py
index f9f3a2651f5dca7bb257480298963fbdf324a73a..252194c0c93636a041f3a316d733841848c0f9a8 100644 (file)
@@ -1,4 +1,4 @@
-# $Id: hyperdb.py,v 1.2 2001-07-22 12:09:32 richard Exp $
+# $Id: hyperdb.py,v 1.9 2001-07-29 09:28:23 richard Exp $
 
 # standard python modules
 import cPickle, re, string
@@ -98,8 +98,13 @@ class Class:
         If an id in a link or multilink property does not refer to a valid
         node, an IndexError is raised.
         """
+        if propvalues.has_key('id'):
+            raise KeyError, '"id" is reserved'
+
         if self.db.journaltag is None:
             raise DatabaseError, 'Database open read-only'
+
+        # new node's id
         newid = str(self.count() + 1)
 
         # validate propvalues
@@ -116,17 +121,20 @@ class Class:
             prop = self.properties[key]
 
             if prop.isLinkType:
-                value = str(value)
+                if type(value) != type(''):
+                    raise ValueError, 'link value must be String'
+#                value = str(value)
                 link_class = self.properties[key].classname
+                # if it isn't a number, it's a key
                 if not num_re.match(value):
                     try:
                         value = self.db.classes[link_class].lookup(value)
                     except:
-                        raise ValueError, 'new property "%s": %s not a %s'%(
+                        raise IndexError, 'new property "%s": %s not a %s'%(
                             key, value, self.properties[key].classname)
                 propvalues[key] = value
                 if not self.db.hasnode(link_class, value):
-                    raise ValueError, '%s has no node %s'%(link_class, value)
+                    raise IndexError, '%s has no node %s'%(link_class, value)
 
                 # register the link with the newly linked node
                 self.db.addjournal(link_class, value, 'link',
@@ -137,12 +145,15 @@ class Class:
                     raise TypeError, 'new property "%s" not a list of ids'%key
                 link_class = self.properties[key].classname
                 l = []
-                for entry in map(str, value):
+                for entry in value:
+                    if type(entry) != type(''):
+                        raise ValueError, 'link value must be String'
+                    # if it isn't a number, it's a key
                     if not num_re.match(entry):
                         try:
                             entry = self.db.classes[link_class].lookup(entry)
                         except:
-                            raise ValueError, 'new property "%s": %s not a %s'%(
+                            raise IndexError, 'new property "%s": %s not a %s'%(
                                 key, entry, self.properties[key].classname)
                     l.append(entry)
                 value = l
@@ -151,7 +162,7 @@ class Class:
                 # handle additions
                 for id in value:
                     if not self.db.hasnode(link_class, id):
-                        raise ValueError, '%s has no node %s'%(link_class, id)
+                        raise IndexError, '%s has no node %s'%(link_class, id)
                     # register the link with the newly linked node
                     self.db.addjournal(link_class, id, 'link',
                         (self.classname, newid, key))
@@ -168,8 +179,8 @@ class Class:
                 if not hasattr(value, 'isInterval'):
                     raise TypeError, 'new property "%s" not an Interval'% key
 
-        for key,prop in self.properties.items():
-            if propvalues.has_key(str(key)):
+        for key, prop in self.properties.items():
+            if propvalues.has_key(key):
                 continue
             if prop.isMultilinkType:
                 propvalues[key] = []
@@ -188,7 +199,10 @@ class Class:
         IndexError is raised.  'propname' must be the name of a property
         of this class or a KeyError is raised.
         """
-        d = self.db.getnode(self.classname, str(nodeid))
+        if propname == 'id':
+            return nodeid
+#        nodeid = str(nodeid)
+        d = self.db.getnode(self.classname, nodeid)
         return d[propname]
 
     # XXX not in spec
@@ -217,9 +231,14 @@ class Class:
         """
         if not propvalues:
             return
+
+        if propvalues.has_key('id'):
+            raise KeyError, '"id" is reserved'
+
         if self.db.journaltag is None:
             raise DatabaseError, 'Database open read-only'
-        nodeid = str(nodeid)
+
+#        nodeid = str(nodeid)
         node = self.db.getnode(self.classname, nodeid)
         if node.has_key(self.db.RETIRED_FLAG):
             raise IndexError
@@ -239,17 +258,20 @@ class Class:
             prop = self.properties[key]
 
             if prop.isLinkType:
-                value = str(value)
+#                value = str(value)
                 link_class = self.properties[key].classname
+                # if it isn't a number, it's a key
+                if type(value) != type(''):
+                    raise ValueError, 'link value must be String'
                 if not num_re.match(value):
                     try:
                         value = self.db.classes[link_class].lookup(value)
                     except:
-                        raise ValueError, 'new property "%s": %s not a %s'%(
+                        raise IndexError, 'new property "%s": %s not a %s'%(
                             key, value, self.properties[key].classname)
 
                 if not self.db.hasnode(link_class, value):
-                    raise ValueError, '%s has no node %s'%(link_class, value)
+                    raise IndexError, '%s has no node %s'%(link_class, value)
 
                 # register the unlink with the old linked node
                 if node[key] is not None:
@@ -266,12 +288,15 @@ class Class:
                     raise TypeError, 'new property "%s" not a list of ids'%key
                 link_class = self.properties[key].classname
                 l = []
-                for entry in map(str, value):
+                for entry in value:
+                    # if it isn't a number, it's a key
+                    if type(entry) != type(''):
+                        raise ValueError, 'link value must be String'
                     if not num_re.match(entry):
                         try:
                             entry = self.db.classes[link_class].lookup(entry)
                         except:
-                            raise ValueError, 'new property "%s": %s not a %s'%(
+                            raise IndexError, 'new property "%s": %s not a %s'%(
                                 key, entry, self.properties[key].classname)
                     l.append(entry)
                 value = l
@@ -290,7 +315,7 @@ class Class:
                 # handle additions
                 for id in value:
                     if not self.db.hasnode(link_class, id):
-                        raise ValueError, '%s has no node %s'%(link_class, id)
+                        raise IndexError, '%s has no node %s'%(link_class, id)
                     if id in l:
                         continue
                     # register the link with the newly linked node
@@ -324,7 +349,7 @@ class Class:
         Retired nodes are not returned by the find(), list(), or lookup()
         methods, and other nodes may reuse the values of their key properties.
         """
-        nodeid = str(nodeid)
+#        nodeid = str(nodeid)
         if self.db.journaltag is None:
             raise DatabaseError, 'Database open read-only'
         node = self.db.getnode(self.classname, nodeid)
@@ -362,6 +387,28 @@ class Class:
         """Return the name of the key property for this class or None."""
         return self.key
 
+    def labelprop(self):
+        ''' Return the property name for a label for the given node.
+
+        This method attempts to generate a consistent label for the node.
+        It tries the following in order:
+            1. key property
+            2. "name" property
+            3. "title" property
+            4. first property from the sorted property name list
+        '''
+        k = self.getkey()
+        if  k:
+            return k
+        props = self.getprops()
+        if props.has_key('name'):
+            return 'name'
+        elif props.has_key('title'):
+            return 'title'
+        props = props.keys()
+        props.sort()
+        return props[0]
+
     # TODO: set up a separate index db file for this? profile?
     def lookup(self, keyvalue):
         """Locate a particular node by its key property and return its id.
@@ -395,7 +442,7 @@ class Class:
         """
         propspec = propspec.items()
         for propname, nodeid in propspec:
-            nodeid = str(nodeid)
+#            nodeid = str(nodeid)
             # check the prop is OK
             prop = self.properties[propname]
             if not prop.isLinkType and not prop.isMultilinkType:
@@ -411,7 +458,7 @@ class Class:
             if node.has_key(self.db.RETIRED_FLAG):
                 continue
             for propname, nodeid in propspec:
-                nodeid = str(nodeid)
+#                nodeid = str(nodeid)
                 property = node[propname]
                 if prop.isLinkType and nodeid == property:
                     l.append(id)
@@ -504,7 +551,6 @@ class Class:
                     u.append(entry)
                 l.append((1, k, u))
             elif propclass.isStringType:
-                v = v[0]
                 if '*' in v or '?' in v:
                     # simple glob searching
                     v = v.replace('?', '.')
@@ -587,12 +633,12 @@ class Class:
             else:
                 m.append((entry[0], entry[1:]))
         group = m
-
         # now, sort the result
         def sortfun(a, b, sort=sort, group=group, properties=self.getprops(),
                 db = self.db, cl=self):
             a_id, an = a
             b_id, bn = b
+            # sort by group and then sort
             for list in group, sort:
                 for dir, prop in list:
                     # handle the properties that might be "faked"
@@ -627,6 +673,9 @@ class Class:
                     # nodes; or finally on  the node ids.
                     elif propclass.isLinkType:
                         link = db.classes[propclass.classname]
+                        if av is None and bv is not None: return -1
+                        if av is not None and bv is None: return 1
+                        if av is None and bv is None: return 0
                         if link.getprops().has_key('order'):
                             if dir == '+':
                                 r = cmp(link.get(av, 'order'),
@@ -661,7 +710,11 @@ class Class:
                         elif dir == '-':
                             r = cmp(len(bv), len(av))
                             if r != 0: return r
+                # end for dir, prop in list:
+            # end for list in sort, group:
+            # if all else fails, compare the ids
             return cmp(a[0], b[0])
+
         l.sort(sortfun)
         return [i[0] for i in l]
 
@@ -678,7 +731,9 @@ class Class:
 
     def getprops(self):
         """Return a dictionary mapping property names to property objects."""
-        return self.properties
+        d = self.properties.copy()
+        d['id'] = String()
+        return d
 
     def addprop(self, **properties):
         """Add properties to this class.
@@ -735,6 +790,32 @@ def Choice(name, *options):
 
 #
 # $Log: not supported by cvs2svn $
+# Revision 1.8  2001/07/29 08:27:40  richard
+# Fixed handling of passed-in values in form elements (ie. during a
+# drill-down)
+#
+# Revision 1.7  2001/07/29 07:01:39  richard
+# Added vim command to all source so that we don't get no steenkin' tabs :)
+#
+# Revision 1.6  2001/07/29 05:36:14  richard
+# Cleanup of the link label generation.
+#
+# Revision 1.5  2001/07/29 04:05:37  richard
+# Added the fabricated property "id".
+#
+# Revision 1.4  2001/07/27 06:25:35  richard
+# Fixed some of the exceptions so they're the right type.
+# Removed the str()-ification of node ids so we don't mask oopsy errors any
+# more.
+#
+# Revision 1.3  2001/07/27 05:17:14  richard
+# just some comments
+#
+# Revision 1.2  2001/07/22 12:09:32  richard
+# Final commit of Grande Splite
+#
 # Revision 1.1  2001/07/22 11:58:35  richard
 # More Grande Splite
 #
+#
+# vim: set filetype=python ts=4 sw=4 et si