comparison roundup/hyperdb.py @ 3384:6d6d7e331c54 maint-0.8

merge from HEAD
author Richard Jones <richard@users.sourceforge.net>
date Tue, 12 Jul 2005 01:43:17 +0000
parents ed97e2a85576
children 876cda688636
comparison
equal deleted inserted replaced
3381:e012231982e9 3384:6d6d7e331c54
13 # BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 13 # BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
14 # FOR A PARTICULAR PURPOSE. THE CODE PROVIDED HEREUNDER IS ON AN "AS IS" 14 # FOR A PARTICULAR PURPOSE. THE CODE PROVIDED HEREUNDER IS ON AN "AS IS"
15 # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, 15 # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
16 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 16 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
17 # 17 #
18 # $Id: hyperdb.py,v 1.107.2.1 2005-03-03 22:12:35 richard Exp $ 18 # $Id: hyperdb.py,v 1.107.2.2 2005-07-12 01:43:17 richard Exp $
19 19
20 """Hyperdatabase implementation, especially field types. 20 """Hyperdatabase implementation, especially field types.
21 """ 21 """
22 __docformat__ = 'restructuredtext' 22 __docformat__ = 'restructuredtext'
23 23
35 def __init__(self, indexme='no'): 35 def __init__(self, indexme='no'):
36 self.indexme = indexme == 'yes' 36 self.indexme = indexme == 'yes'
37 def __repr__(self): 37 def __repr__(self):
38 ' more useful for dumps ' 38 ' more useful for dumps '
39 return '<%s>'%self.__class__ 39 return '<%s>'%self.__class__
40 def from_raw(self, value, **kw):
41 """fix the CRLF/CR -> LF stuff"""
42 return fixNewlines(value)
40 43
41 class Password: 44 class Password:
42 """An object designating a Password property.""" 45 """An object designating a Password property."""
43 def __repr__(self): 46 def __repr__(self):
44 ' more useful for dumps ' 47 ' more useful for dumps '
45 return '<%s>'%self.__class__ 48 return '<%s>'%self.__class__
49 def from_raw(self, value, **kw):
50 m = password.Passowrd.pwre.match(value)
51 if m:
52 # password is being given to us encrypted
53 p = password.Password()
54 p.scheme = m.group(1)
55 if p.scheme not in 'SHA crypt plaintext'.split():
56 raise HyperdbValueError, 'property %s: unknown encryption '\
57 'scheme %r'%(propname, p.scheme)
58 p.password = m.group(2)
59 value = p
60 else:
61 try:
62 value = password.Password(value)
63 except password.PasswordValueError, message:
64 raise HyperdbValueError, 'property %s: %s'%(propname, message)
65 return value
46 66
47 class Date: 67 class Date:
48 """An object designating a Date property.""" 68 """An object designating a Date property."""
69 def __init__(self, offset = None):
70 self._offset = offset
49 def __repr__(self): 71 def __repr__(self):
50 ' more useful for dumps ' 72 ' more useful for dumps '
51 return '<%s>'%self.__class__ 73 return '<%s>'%self.__class__
74 def offset (self, db) :
75 if self._offset is not None :
76 return self._offset
77 return db.getUserTimezone ()
78 def from_raw(self, value, db, **kw):
79 try:
80 value = date.Date(value).local(-self.offset(db))
81 except ValueError, message:
82 raise HyperdbValueError, 'property %s: %r is an invalid '\
83 'date (%s)'%(propname, value, message)
84 return value
85 def range_from_raw (self, value, db):
86 """return Range value from given raw value with offset correction"""
87 return date.Range(value, date.Date, offset=self.offset (db))
52 88
53 class Interval: 89 class Interval:
54 """An object designating an Interval property.""" 90 """An object designating an Interval property."""
55 def __repr__(self): 91 def __repr__(self):
56 ' more useful for dumps ' 92 ' more useful for dumps '
57 return '<%s>'%self.__class__ 93 return '<%s>'%self.__class__
94 def from_raw(self, value, **kw):
95 try:
96 value = date.Interval(value)
97 except ValueError, message:
98 raise HyperdbValueError, 'property %s: %r is an invalid '\
99 'date interval (%s)'%(propname, value, message)
100 return value
58 101
59 class Link: 102 class Link:
60 """An object designating a Link property that links to a 103 """An object designating a Link property that links to a
61 node in a specified class.""" 104 node in a specified class."""
62 def __init__(self, classname, do_journal='yes'): 105 def __init__(self, classname, do_journal='yes'):
65 self.classname = classname 108 self.classname = classname
66 self.do_journal = do_journal == 'yes' 109 self.do_journal = do_journal == 'yes'
67 def __repr__(self): 110 def __repr__(self):
68 ' more useful for dumps ' 111 ' more useful for dumps '
69 return '<%s to "%s">'%(self.__class__, self.classname) 112 return '<%s to "%s">'%(self.__class__, self.classname)
113 def from_raw(self, value, db, propname, **kw):
114 if value == '-1' or not value:
115 value = None
116 else:
117 value = convertLinkValue(db, propname, self, value)
118 return value
70 119
71 class Multilink: 120 class Multilink:
72 """An object designating a Multilink property that links 121 """An object designating a Multilink property that links
73 to nodes in a specified class. 122 to nodes in a specified class.
74 123
83 self.classname = classname 132 self.classname = classname
84 self.do_journal = do_journal == 'yes' 133 self.do_journal = do_journal == 'yes'
85 def __repr__(self): 134 def __repr__(self):
86 ' more useful for dumps ' 135 ' more useful for dumps '
87 return '<%s to "%s">'%(self.__class__, self.classname) 136 return '<%s to "%s">'%(self.__class__, self.classname)
137 def from_raw(self, value, db, klass, propname, itemid, **kw):
138 # get the current item value if it's not a new item
139 if itemid and not itemid.startswith('-'):
140 curvalue = klass.get(itemid, propname)
141 else:
142 curvalue = []
143
144 # if the value is a comma-separated string then split it now
145 if isinstance(value, type('')):
146 value = value.split(',')
147
148 # handle each add/remove in turn
149 # keep an extra list for all items that are
150 # definitely in the new list (in case of e.g.
151 # <propname>=A,+B, which should replace the old
152 # list with A,B)
153 set = 1
154 newvalue = []
155 for item in value:
156 item = item.strip()
157
158 # skip blanks
159 if not item: continue
160
161 # handle +/-
162 remove = 0
163 if item.startswith('-'):
164 remove = 1
165 item = item[1:]
166 set = 0
167 elif item.startswith('+'):
168 item = item[1:]
169 set = 0
170
171 # look up the value
172 itemid = convertLinkValue(db, propname, proptype, item)
173
174 # perform the add/remove
175 if remove:
176 try:
177 curvalue.remove(itemid)
178 except ValueError:
179 raise HyperdbValueError, 'property %s: %r is not ' \
180 'currently an element'%(propname, item)
181 else:
182 newvalue.append(itemid)
183 if itemid not in curvalue:
184 curvalue.append(itemid)
185
186 # that's it, set the new Multilink property value,
187 # or overwrite it completely
188 if set:
189 value = newvalue
190 else:
191 value = curvalue
192
193 # TODO: one day, we'll switch to numeric ids and this will be
194 # unnecessary :(
195 value = [int(x) for x in value]
196 value.sort()
197 value = [str(x) for x in value]
198 return value
88 199
89 class Boolean: 200 class Boolean:
90 """An object designating a boolean property""" 201 """An object designating a boolean property"""
91 def __repr__(self): 202 def __repr__(self):
92 'more useful for dumps' 203 'more useful for dumps'
93 return '<%s>' % self.__class__ 204 return '<%s>' % self.__class__
205 def from_raw(self, value, **kw):
206 value = value.strip()
207 # checked is a common HTML checkbox value
208 value = value.lower() in ('checked', 'yes', 'true', 'on', '1')
209 return value
94 210
95 class Number: 211 class Number:
96 """An object designating a numeric property""" 212 """An object designating a numeric property"""
97 def __repr__(self): 213 def __repr__(self):
98 'more useful for dumps' 214 'more useful for dumps'
99 return '<%s>' % self.__class__ 215 return '<%s>' % self.__class__
216 def from_raw(self, value, **kw):
217 value = value.strip()
218 try:
219 value = float(value)
220 except ValueError:
221 raise HyperdbValueError, 'property %s: %r is not a number'%(
222 propname, value)
223 return value
100 # 224 #
101 # Support for splitting designators 225 # Support for splitting designators
102 # 226 #
103 class DesignatorError(ValueError): 227 class DesignatorError(ValueError):
104 pass 228 pass
595 endings. Our solution is to convert all line endings to LF. 719 endings. Our solution is to convert all line endings to LF.
596 """ 720 """
597 text = text.replace('\r\n', '\n') 721 text = text.replace('\r\n', '\n')
598 return text.replace('\r', '\n') 722 return text.replace('\r', '\n')
599 723
600 def rawToHyperdb(db, klass, itemid, propname, value, 724 def rawToHyperdb(db, klass, itemid, propname, value, **kw):
601 pwre=re.compile(r'{(\w+)}(.+)')):
602 ''' Convert the raw (user-input) value to a hyperdb-storable value. The 725 ''' Convert the raw (user-input) value to a hyperdb-storable value. The
603 value is for the "propname" property on itemid (may be None for a 726 value is for the "propname" property on itemid (may be None for a
604 new item) of "klass" in "db". 727 new item) of "klass" in "db".
605 728
606 The value is usually a string, but in the case of multilink inputs 729 The value is usually a string, but in the case of multilink inputs
618 klass.classname) 741 klass.classname)
619 742
620 # if we got a string, strip it now 743 # if we got a string, strip it now
621 if isinstance(value, type('')): 744 if isinstance(value, type('')):
622 value = value.strip() 745 value = value.strip()
623
624 # convert the input value to a real property value 746 # convert the input value to a real property value
625 if isinstance(proptype, String): 747 value = proptype.from_raw \
626 # fix the CRLF/CR -> LF stuff 748 ( value
627 value = fixNewlines(value) 749 , db = db
628 if isinstance(proptype, Password): 750 , klass = klass
629 m = pwre.match(value) 751 , propname = propname
630 if m: 752 , itemid = itemid
631 # password is being given to us encrypted 753 , **kw
632 p = password.Password() 754 )
633 p.scheme = m.group(1)
634 if p.scheme not in 'SHA crypt plaintext'.split():
635 raise HyperdbValueError, 'property %s: unknown encryption '\
636 'scheme %r'%(propname, p.scheme)
637 p.password = m.group(2)
638 value = p
639 else:
640 try:
641 value = password.Password(value)
642 except password.PasswordValueError, message:
643 raise HyperdbValueError, 'property %s: %s'%(propname, message)
644 elif isinstance(proptype, Date):
645 try:
646 tz = db.getUserTimezone()
647 value = date.Date(value).local(-tz)
648 except ValueError, message:
649 raise HyperdbValueError, 'property %s: %r is an invalid '\
650 'date (%s)'%(propname, value, message)
651 elif isinstance(proptype, Interval):
652 try:
653 value = date.Interval(value)
654 except ValueError, message:
655 raise HyperdbValueError, 'property %s: %r is an invalid '\
656 'date interval (%s)'%(propname, value, message)
657 elif isinstance(proptype, Link):
658 if value == '-1' or not value:
659 value = None
660 else:
661 value = convertLinkValue(db, propname, proptype, value)
662
663 elif isinstance(proptype, Multilink):
664 # get the current item value if it's not a new item
665 if itemid and not itemid.startswith('-'):
666 curvalue = klass.get(itemid, propname)
667 else:
668 curvalue = []
669
670 # if the value is a comma-separated string then split it now
671 if isinstance(value, type('')):
672 value = value.split(',')
673
674 # handle each add/remove in turn
675 # keep an extra list for all items that are
676 # definitely in the new list (in case of e.g.
677 # <propname>=A,+B, which should replace the old
678 # list with A,B)
679 set = 1
680 newvalue = []
681 for item in value:
682 item = item.strip()
683
684 # skip blanks
685 if not item: continue
686
687 # handle +/-
688 remove = 0
689 if item.startswith('-'):
690 remove = 1
691 item = item[1:]
692 set = 0
693 elif item.startswith('+'):
694 item = item[1:]
695 set = 0
696
697 # look up the value
698 itemid = convertLinkValue(db, propname, proptype, item)
699
700 # perform the add/remove
701 if remove:
702 try:
703 curvalue.remove(itemid)
704 except ValueError:
705 raise HyperdbValueError, 'property %s: %r is not ' \
706 'currently an element'%(propname, item)
707 else:
708 newvalue.append(itemid)
709 if itemid not in curvalue:
710 curvalue.append(itemid)
711
712 # that's it, set the new Multilink property value,
713 # or overwrite it completely
714 if set:
715 value = newvalue
716 else:
717 value = curvalue
718
719 # TODO: one day, we'll switch to numeric ids and this will be
720 # unnecessary :(
721 value = [int(x) for x in value]
722 value.sort()
723 value = [str(x) for x in value]
724 elif isinstance(proptype, Boolean):
725 value = value.strip()
726 # checked is a common HTML checkbox value
727 value = value.lower() in ('checked', 'yes', 'true', 'on', '1')
728 elif isinstance(proptype, Number):
729 value = value.strip()
730 try:
731 value = float(value)
732 except ValueError:
733 raise HyperdbValueError, 'property %s: %r is not a number'%(
734 propname, value)
735 return value 755 return value
736 756
737 class FileClass: 757 class FileClass:
738 ''' A class that requires the "content" property and stores it on 758 ''' A class that requires the "content" property and stores it on
739 disk. 759 disk.

Roundup Issue Tracker: http://roundup-tracker.org/