Skip to content

Commit 01db6fb

Browse files
committed
- issue1714899: Feature Request: Optional Change Note. Added a new
quiet=True/False option for all property types. When quiet=True changes to the property will not be displayed in the:: confirmation banner (shown in green) when a change is made property change section of change note (nosy emails) web history display for an item. Note that this may confuse users if used on a property that is meant to be changed by a user. It is most useful on administrative properties that are changed by an auditor as part of a user generated change. Original patch by Daniel Diniz (ajaksu2) discussed also at: http://psf.upfronthosting.co.za/roundup/meta/issue249 Support for setting quiet when calling the class specifiers: E.G. prop=String(quiet=True) rather than:: prop=String() prop.quiet=True support for anydb backend, added tests, doc updates, support for ignoring quiet setting using showall=True in call to history() function in templates by John Rouillard. In addition to documenting quiet, I also documented required and default_value additions to the hyperdb property classes. Only place I could find is design.txt. Note tests for history in web interface are not done. It was manually checked but there are no automated tests. The template for setup is in db_test_base.py::testQuietJournal but it has no asserts. I need access to template.py::_HTMLItem::history() and I don't know how to do that. test_templates.py isn't helping me any at all and I want to get this patch in because it handles nicely an issue I have in the design of my own tracker. The issue is: The properties of an issue are displayed in framesets/subframes. The user can roll up the frameset leaving only the title bar. When the user saves the changes, the current state of the framesets (collapsed/uncollapsed) is saved to a property in the user's object. However there is no reason the user should see that this is updated since it's an administrative detail. Similarly, you could count the number of times an issue is reopened or reassigned. Updates to properties that are an indirect result of a user's change should not be displayed to the user as they can be confusing and distracting.
1 parent f2f7cf4 commit 01db6fb

File tree

8 files changed

+217
-20
lines changed

8 files changed

+217
-20
lines changed

CHANGES.txt

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,25 @@ Features:
8787
3) setting it to a fixed address (like [email protected])
8888
Done by John Rouillard from proposal by Peter Funk (pefu)
8989
in discussion with Tom Ekberg (tekberg). See doc/upgrading.txt.
90+
- issue1714899: Feature Request: Optional Change Note. Added a new
91+
quiet=True/False option for all property types. When quiet=True
92+
changes to the property will not be displayed in the::
93+
confirmation banner (shown in green) when a change is made
94+
property change section of change note (nosy emails)
95+
web history display for an item.
96+
Note that this may confuse users if used on a property that is
97+
meant to be changed by a user. It is most useful on administrative
98+
properties that are changed by an auditor as part of a user
99+
generated change. Original patch by Daniel Diniz (ajaksu2)
100+
discussed also at:
101+
http://psf.upfronthosting.co.za/roundup/meta/issue249
102+
Support for setting quiet when calling the class specifiers:
103+
E.G. prop=String(quiet=True) rather than::
104+
prop=String()
105+
prop.quiet=True
106+
support for anydb backend, added tests, doc updates, support for
107+
ignoring quiet setting using showall=True in call to history()
108+
function in templates by John Rouillard.
90109

91110
Fixed:
92111

doc/design.txt

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -254,8 +254,44 @@ Hyperdb Interface Specification
254254
TODO: replace the Interface Specifications with links to the pydoc
255255

256256
The hyperdb module provides property objects to designate the different
257-
kinds of properties. These objects are used when specifying what
258-
properties belong in classes::
257+
kinds of properties.
258+
259+
All property objects support the following settings:
260+
261+
quiet=False:
262+
if set to True, changes to the property will not be shown to the
263+
user. This can be used for adminstrative properties that are
264+
automatically updated when a user makes some other change. This
265+
reduces confusion by the user and clutter in the display.
266+
The property change will not be shown in:
267+
268+
- the change confirmation message when a change is entered in the web interface
269+
- the property change section of the change note email ("nosy email")
270+
- the web history shown at the bottom of an item page
271+
272+
required=False:
273+
if set to True, the property name is returned when calling
274+
get_required_props(self, propnames = []). Any additional props
275+
specified in propnames is merged with the required props.
276+
277+
default_value=None or [] depending on object type:
278+
this sets the default value if none is specified. The default
279+
value can be retrieved by calling the get_default_value()
280+
method on the property object.
281+
282+
E.G. assuming title is part of an Issue::
283+
284+
title=String(required=True, default_value="not set",quiet=True)
285+
286+
will create a property called ``title`` that will be included in the
287+
get_required_props() output. Calling
288+
db.issue.properties['title'].get_default_value() will return "not set".
289+
Changes to the property will not be displayed in emailed change notes,
290+
the history at the end of the item pages in the web interface and will
291+
be suppressed in the confirmation notice (displayed as a green banner)
292+
shown on changes.
293+
294+
These objects are used when specifying what properties belong in classes::
259295

260296
class String:
261297
def __init__(self, indexme='no'):
@@ -1652,6 +1688,7 @@ and Dean Tribble for their assistance with the first-round submission.
16521688
Changes to this document
16531689
------------------------
16541690

1691+
- Added docs for quiet, default_value and required arguments for properties.
16551692
- Added Boolean, Integer and Number types
16561693
- Added section Hyperdatabase Implementations
16571694
- "Item" has been renamed to "Issue" to account for the more specific

roundup/backends/back_anydbm.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1199,6 +1199,9 @@ def set_inner(self, nodeid, **propvalues):
11991199
# if the journal value is to be different, store it in here
12001200
journalvalues = {}
12011201

1202+
# omit quiet properties from history/changelog
1203+
quiet_props = []
1204+
12021205
# list() propvalues 'cos it might be modified by the loop
12031206
for propname, value in list(propvalues.items()):
12041207
# check to make sure we're not duplicating an existing key
@@ -1374,6 +1377,10 @@ def set_inner(self, nodeid, **propvalues):
13741377

13751378
node[propname] = value
13761379

1380+
# record quiet properties to omit from history/changelog
1381+
if prop.quiet:
1382+
quiet_props.append(propname)
1383+
13771384
# nothing to do?
13781385
if not propvalues:
13791386
return propvalues
@@ -1388,6 +1395,11 @@ def set_inner(self, nodeid, **propvalues):
13881395
if self.do_journal:
13891396
self.db.addjournal(self.classname, nodeid, 'set', journalvalues)
13901397

1398+
# remove quiet properties from output
1399+
for propname in quiet_props:
1400+
if propname in propvalues:
1401+
del propvalues[propname]
1402+
13911403
return propvalues
13921404

13931405
def retire(self, nodeid):

roundup/backends/rdbms_common.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1783,6 +1783,9 @@ def set_inner(self, nodeid, **propvalues):
17831783
# for the Database layer to do its stuff
17841784
multilink_changes = {}
17851785

1786+
# omit quiet properties from history/changelog
1787+
quiet_props = []
1788+
17861789
for propname, value in list(propvalues.items()):
17871790
# check to make sure we're not duplicating an existing key
17881791
if propname == self.key and node[propname] != value:
@@ -1958,6 +1961,10 @@ def set_inner(self, nodeid, **propvalues):
19581961
except ValueError:
19591962
raise TypeError('new property "%s" not boolean'%propname)
19601963

1964+
# record quiet properties to omit from history/changelog
1965+
if prop.quiet:
1966+
quiet_props.append(propname)
1967+
19611968
# nothing to do?
19621969
if not propvalues:
19631970
return propvalues
@@ -1977,6 +1984,11 @@ def set_inner(self, nodeid, **propvalues):
19771984
if self.do_journal:
19781985
self.db.addjournal(self.classname, nodeid, ''"set", journalvalues)
19791986

1987+
# remove quiet properties from output
1988+
for propname in quiet_props:
1989+
if propname in propvalues:
1990+
del propvalues[propname]
1991+
19801992
return propvalues
19811993

19821994
def retire(self, nodeid):

roundup/cgi/templating.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -870,7 +870,13 @@ def journal(self, direction='descending'):
870870
return []
871871

872872
def history(self, direction='descending', dre=re.compile('^\d+$'),
873-
limit=None):
873+
limit=None, showall=False ):
874+
"""Create an html view of the journal for the item.
875+
876+
Display property changes for all properties that does not have quiet set.
877+
If showall=True then all properties regardless of quiet setting will be
878+
shown.
879+
"""
874880
if not self.is_view_ok():
875881
return self._('[hidden]')
876882

@@ -965,7 +971,10 @@ def history(self, direction='descending', dre=re.compile('^\d+$'),
965971
except NoTemplate:
966972
hrefable = 0
967973

968-
if isinstance(prop, hyperdb.Multilink) and args[k]:
974+
if (not showall) and prop.quiet:
975+
# Omit quiet properties from history/changelog
976+
continue
977+
elif isinstance(prop, hyperdb.Multilink) and args[k]:
969978
ml = []
970979
for linkid in args[k]:
971980
if isinstance(linkid, type(())):

roundup/hyperdb.py

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,10 @@
3535
#
3636
class _Type(object):
3737
"""A roundup property type."""
38-
def __init__(self, required=False, default_value = None):
38+
def __init__(self, required=False, default_value = None, quiet=False):
3939
self.required = required
4040
self.__default_value = default_value
41+
self.quiet = quiet
4142
def __repr__(self):
4243
' more useful for dumps '
4344
return '<%s.%s>'%(self.__class__.__module__, self.__class__.__name__)
@@ -54,8 +55,8 @@ def sort_repr (self, cls, val, name):
5455

5556
class String(_Type):
5657
"""An object designating a String property."""
57-
def __init__(self, indexme='no', required=False, default_value = ""):
58-
super(String, self).__init__(required, default_value)
58+
def __init__(self, indexme='no', required=False, default_value = "", quiet=False):
59+
super(String, self).__init__(required, default_value, quiet)
5960
self.indexme = indexme == 'yes'
6061
def from_raw(self, value, propname='', **kw):
6162
"""fix the CRLF/CR -> LF stuff"""
@@ -73,8 +74,8 @@ def sort_repr (self, cls, val, name):
7374

7475
class Password(_Type):
7576
"""An object designating a Password property."""
76-
def __init__(self, scheme=None, required=False, default_value = None):
77-
super(Password, self).__init__(required, default_value)
77+
def __init__(self, scheme=None, required=False, default_value = None, quiet=False):
78+
super(Password, self).__init__(required, default_value, quiet)
7879
self.scheme = scheme
7980

8081
def from_raw(self, value, **kw):
@@ -93,9 +94,10 @@ def sort_repr (self, cls, val, name):
9394

9495
class Date(_Type):
9596
"""An object designating a Date property."""
96-
def __init__(self, offset=None, required=False, default_value = None):
97+
def __init__(self, offset=None, required=False, default_value = None, quiet=False):
9798
super(Date, self).__init__(required = required,
98-
default_value = default_value)
99+
default_value = default_value,
100+
quiet=quiet)
99101
self._offset = offset
100102
def offset(self, db):
101103
if self._offset is not None:
@@ -135,7 +137,7 @@ class _Pointer(_Type):
135137
to a node in a specified class."""
136138
def __init__(self, classname, do_journal='yes', try_id_parsing='yes',
137139
required=False, default_value=None,
138-
msg_header_property = None):
140+
msg_header_property = None, quiet=False):
139141
""" Default is to journal link and unlink events.
140142
When try_id_parsing is false, we don't allow IDs in input
141143
fields (the key of the Link or Multilink property must be
@@ -154,7 +156,7 @@ def __init__(self, classname, do_journal='yes', try_id_parsing='yes',
154156
property will generated message headers of the form:
155157
'X-Roundup-issue-assigned_to: joe_user'.
156158
"""
157-
super(_Pointer, self).__init__(required, default_value)
159+
super(_Pointer, self).__init__(required, default_value, quiet)
158160
self.classname = classname
159161
self.do_journal = do_journal == 'yes'
160162
self.try_id_parsing = try_id_parsing == 'yes'
@@ -196,12 +198,12 @@ class Multilink(_Pointer):
196198
'link' and 'unlink' events placed in their journal
197199
"""
198200

199-
def __init__(self, classname, do_journal = 'yes', required = False):
201+
def __init__(self, classname, do_journal = 'yes', required = False, quiet=False):
200202

201203
super(Multilink, self).__init__(classname,
202204
do_journal,
203205
required = required,
204-
default_value = [])
206+
default_value = [], quiet=quiet)
205207

206208
def from_raw(self, value, db, klass, propname, itemid, **kw):
207209
if not value:

roundup/roundupdb.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -703,6 +703,9 @@ def generateCreateNote(self, issueid):
703703
prop_items = props.items()
704704
prop_items.sort()
705705
for propname, prop in prop_items:
706+
# Omit quiet properties from history/changelog
707+
if prop.quiet:
708+
continue
706709
value = cl.get(issueid, propname, None)
707710
# skip boring entries
708711
if not value:
@@ -775,6 +778,9 @@ def generateChangeNote(self, issueid, oldvalues):
775778
changed_items.sort()
776779
for propname, oldvalue in changed_items:
777780
prop = props[propname]
781+
# Omit quiet properties from history/changelog
782+
if prop.quiet:
783+
continue
778784
value = cl.get(issueid, propname, None)
779785
if isinstance(prop, hyperdb.Link):
780786
link = self.db.classes[prop.classname]

0 commit comments

Comments
 (0)