Code

When debugging mail (debug = <filename> setting in [mail] section of
[roundup.git] / doc / spec.html
1 <html>
2 <head>
3 <title>Software Carpentry Track: Roundup</title>
4 </head>
5 <body bgcolor=white>
7 <table width="100%">
8 <tr>
10 <td align="left">
11 <a href="http://www.software-carpentry.com"><img
12 src="images/logo-software-carpentry-standard.png" alt="[Software Carpentry logo]" border="0"></a>
13 </td>
15 <td align="right">
16 <table>
17 <tr><td>
18 <a href="http://www.acl.lanl.gov"><img src="images//logo-acl-medium.png" alt="[ACL Logo]" border="0"></a>
19 </td></tr>
20 <tr><td><hr></td></tr>
21 <tr><td>
22 <a href="http://www.codesourcery.com"><img
23 src="images/logo-codesourcery-medium.png" alt="[CodeSourcery Logo]" border="0"></a>
24 </td></tr>
25 </table>
26 </td>
28 </tr>
29 </table>
31 <hr><p>
33 <h1 align=center>Roundup</h1>
34 <h3 align=center>An Issue-Tracking System for Knowledge Workers</h3>
35 <h4 align=center><a href="http://www.lfw.org/ping/">Ka-Ping Yee</a><br>
36 <a href="mailto:ping@lfw.org">ping@lfw.org</a></h4>
37 <h3 align=center>Implementation Guide</h3>
39 <h2>Contents</h2>
41 <ol>
42 <li>Introduction
43 <li>The Layer Cake
44 <li>Hyperdatabase
45     <ol>
46     <li>Dates and Date Arithmetic
47     <li>Items and Classes
48     <li>Identifiers and Designators
49     <li>Property Names and Types
50     <li>Interface Specification
51     <li>Application Example
52     </ol>
53 <li>Roundup Database
54     <ol>
55     <li>Reserved Classes
56         <ol>
57         <li>Users
58         <li>Messages
59         <li>Files
60         </ol>
61     <li>Item Classes
62     <li>Interface Specification
63     <li>Default Schema
64     </ol>
65 <li>Detector Interface
66     <ol>
67     <li>Interface Specification
68     <li>Detector Example
69     </ol>
70 <li>Command Interface
71     <ol>
72     <li>Interface Specification
73     <li>Usage Example
74     </ol>
75 <li>E-mail User Interface
76     <ol>
77     <li>Message Processing
78     <li>Nosy Lists
79     <li>Setting Properties
80     <li>Workflow Example
81     </ol>
82 <li>Web User Interface
83     <ol>
84     <li>Views and View Specifiers
85     <li>Displaying Properties
86     <li>Index Views
87         <ol>
88         <li>Index View Specifiers
89         <li>Filter Section
90         <li>Index Section
91         <li>Sorting
92         </ol>
93     <li>Item Views
94         <ol>
95         <li>Item View Specifiers
96         <li>Editor Section
97         <li>Spool Section
98         </ol>
99     </ol>
100 <li>Deployment Scenarios
101 <li>Acknowledgements
102 </ol>
104 <p><hr>
105 <h2>1. Introduction</h2>
107 <p>This document presents a description of the components
108 of the Roundup system and specifies their interfaces and
109 behaviour in sufficient detail to guide an implementation.
110 For the philosophy and rationale behind the Roundup design,
111 see the first-round Software Carpentry submission for Roundup.
112 This document fleshes out that design as well as specifying
113 interfaces so that the components can be developed separately.
115 <p><hr>
116 <h2>2. The Layer Cake</h2>
118 <p>Lots of software design documents come with a picture of
119 a cake.  Everybody seems to like them.  I also like cakes
120 (i think they are tasty).  So i, too, shall include
121 a picture of a cake here.
123 <p align=center><table cellspacing=0 cellpadding=10 border=0 align=center>
124 <tr>
125 <td bgcolor="#e8e8e8" align=center>
126 <p><font face="helvetica, arial"><small>
127 E-mail Client
128 </small></font>
129 </td>
130 <td bgcolor="#e0e0e0" align="center">
131 <p><font face="helvetica, arial"><small>
132 Web Browser
133 </small></font>
134 </td>
135 <td bgcolor="#e8e8e8" align=center>
136 <p><font face="helvetica, arial"><small>
137 Detector Scripts
138 </small></font>
139 </td>
140 <td bgcolor="#e0e0e0" align="center">
141 <p><font face="helvetica, arial"><small>
142 Shell
143 </small></font>
144 </td>
145 <tr>
146 <td bgcolor="#d0d0f0" align=center>
147 <p><font face="helvetica, arial"><small>
148 E-mail User Interface
149 </small></font>
150 </td>
151 <td bgcolor="#f0d0d0" align=center>
152 <p><font face="helvetica, arial"><small>
153 Web User Interface
154 </small></font>
155 </td>
156 <td bgcolor="#d0f0d0" align=center>
157 <p><font face="helvetica, arial"><small>
158 Detector Interface
159 </small></font>
160 </td>
161 <td bgcolor="#f0d0f0" align=center>
162 <p><font face="helvetica, arial"><small>
163 Command Interface
164 </small></font>
165 </td>
166 <tr>
167 <td bgcolor="#f0f0d0" colspan=4 align=center>
168 <p><font face="helvetica, arial"><small>
169 Roundup Database Layer
170 </small></font>
171 </td>
172 <tr>
173 <td bgcolor="#d0f0f0" colspan=4 align=center>
174 <p><font face="helvetica, arial"><small>
175 Hyperdatabase Layer
176 </small></font>
177 </td>
178 <tr>
179 <td bgcolor="#e8e8e8" colspan=4 align=center>
180 <p><font face="helvetica, arial"><small>
181 Storage Layer
182 </small></font>
183 </td>
184 </table>
186 <p>The colourful parts of the cake are part of our system;
187 the faint grey parts of the cake are external components.
189 <p>I will now proceed to forgo all table manners and
190 eat from the bottom of the cake to the top.  You may want
191 to stand back a bit so you don't get covered in crumbs.
193 <p><hr>
194 <h2>3. Hyperdatabase</h2>
196 <p>The lowest-level component to be implemented is the hyperdatabase.
197 The hyperdatabase is intended to be
198 a flexible data store that can hold configurable data in
199 records which we call <em>items</em>.
201 <p>The hyperdatabase is implemented on top of the storage layer,
202 an external module for storing its data.  The storage layer could
203 be a third-party RDBMS; for a "batteries-included" distribution,
204 implementing the hyperdatabase on the standard <tt>bsddb</tt>
205 module is suggested.
207 <h3>3.1. Dates and Date Arithmetic</h3>
209 <p>Before we get into the hyperdatabase itself, we need a
210 way of handling dates.  The hyperdatabase module provides
211 Timestamp objects for
212 representing date-and-time stamps and Interval objects for
213 representing date-and-time intervals.
215 <p>As strings, date-and-time stamps are specified with
216 the date in international standard format
217 (<em>yyyy</em>-<em>mm</em>-<em>dd</em>)
218 joined to the time (<em>hh</em>:<em>mm</em>:<em>ss</em>)
219 by a period (".").  Dates in
220 this form can be easily compared and are fairly readable
221 when printed.  An example of a valid stamp is
222 "<strong>2000-06-24.13:03:59</strong>".
223 We'll call this the "full date format".  When Timestamp objects are
224 printed as strings, they appear in the full date format with
225 the time always given in GMT.  The full date format is always
226 exactly 19 characters long.
228 <p>For user input, some partial forms are also permitted:
229 the whole time or just the seconds may be omitted; and the whole date
230 may be omitted or just the year may be omitted.  If the time is given,
231 the time is interpreted in the user's local time zone.
232 The <tt>Date</tt> constructor takes care of these conversions.
233 In the following examples, suppose that <em>yyyy</em> is the current year,
234 <em>mm</em> is the current month, and <em>dd</em> is the current
235 day of the month; and suppose that the user is on Eastern Standard Time.
237 <ul>
238 <li>"<strong>2000-04-17</strong>" means &lt;Date 2000-04-17.00:00:00&gt;
239 <li>"<strong>01-25</strong>" means &lt;Date <em>yyyy</em>-01-25.00:00:00&gt;
240 <li>"<strong>2000-04-17.03:45</strong>" means &lt;Date 2000-04-17.08:45:00&gt;
241 <li>"<strong>08-13.22:13</strong>" means &lt;Date <em>yyyy</em>-08-14.03:13:00&gt;
242 <li>"<strong>11-07.09:32:43</strong>" means &lt;Date <em>yyyy</em>-11-07.14:32:43&gt;
243 <li>"<strong>14:25</strong>" means
244 &lt;Date <em>yyyy</em>-<em>mm</em>-<em>dd</em>.19:25:00&gt;
245 <li>"<strong>8:47:11</strong>" means
246 &lt;Date <em>yyyy</em>-<em>mm</em>-<em>dd</em>.13:47:11&gt;
247 <li>the special date "<strong>.</strong>" means "right now"
248 </ul>
250 <p>Date intervals are specified using the suffixes
251 "y", "m", and "d".  The suffix "w" (for "week") means 7 days.
252 Time intervals are specified in hh:mm:ss format (the seconds
253 may be omitted, but the hours and minutes may not).
255 <ul>
256 <li>"<strong>3y</strong>" means three years
257 <li>"<strong>2y 1m</strong>" means two years and one month
258 <li>"<strong>1m 25d</strong>" means one month and 25 days
259 <li>"<strong>2w 3d</strong>" means two weeks and three days
260 <li>"<strong>1d 2:50</strong>" means one day, two hours, and 50 minutes
261 <li>"<strong>14:00</strong>" means 14 hours
262 <li>"<strong>0:04:33</strong>" means four minutes and 33 seconds
263 </ul>
265 <p>The Date class should understand simple date expressions of the form 
266 <em>stamp</em> + <em>interval</em> and <em>stamp</em> - <em>interval</em>.
267 When adding or subtracting intervals involving months or years, the
268 components are handled separately.  For example, when evaluating
269 "<strong>2000-06-25 + 1m 10d</strong>", we first add one month to
270 get <strong>2000-07-25</strong>, then add 10 days to get
271 <strong>2000-08-04</strong> (rather than trying to decide whether
272 <strong>1m 10d</strong> means 38 or 40 or 41 days).
274 <p>Here is an outline of the Date and Interval classes.
276 <blockquote>
277 <pre><small>class <strong>Date</strong>:
278     def <strong>__init__</strong>(self, spec, offset):
279         """Construct a date given a specification and a time zone offset.
281         'spec' is a full date or a partial form, with an optional
282         added or subtracted interval.  'offset' is the local time
283         zone offset from GMT in hours.
284         """
286     def <strong>__add__</strong>(self, interval):
287         """Add an interval to this date to produce another date."""
289     def <strong>__sub__</strong>(self, interval):
290         """Subtract an interval from this date to produce another date."""
292     def <strong>__cmp__</strong>(self, other):
293         """Compare this date to another date."""
295     def <strong>__str__</strong>(self):
296         """Return this date as a string in the yyyy-mm-dd.hh:mm:ss format."""
298     def <strong>local</strong>(self, offset):
299         """Return this date as yyyy-mm-dd.hh:mm:ss in a local time zone."""
301 class <strong>Interval</strong>:
302     def <strong>__init__</strong>(self, spec):
303         """Construct an interval given a specification."""
305     def <strong>__cmp__</strong>(self, other):
306         """Compare this interval to another interval."""
307         
308     def <strong>__str__</strong>(self):
309         """Return this interval as a string."""
310 </small></pre>
311 </blockquote>
313 <p>Here are some examples of how these classes would behave in practice.
314 For the following examples, assume that we are on Eastern Standard
315 Time and the current local time is 19:34:02 on 25 June 2000.
317 <blockquote><pre><small
318 >&gt;&gt;&gt; <span class="input">Date(".")</span>
319 <span class="output">&lt;Date 2000-06-26.00:34:02&gt;</span>
320 &gt;&gt;&gt; <span class="input">_.local(-5)</span>
321 <span class="output">"2000-06-25.19:34:02"</span>
322 &gt;&gt;&gt; <span class="input">Date(". + 2d")</span>
323 <span class="output">&lt;Date 2000-06-28.00:34:02&gt;</span>
324 &gt;&gt;&gt; <span class="input">Date("1997-04-17", -5)</span>
325 <span class="output">&lt;Date 1997-04-17.00:00:00&gt;</span>
326 &gt;&gt;&gt; <span class="input">Date("01-25", -5)</span>
327 <span class="output">&lt;Date 2000-01-25.00:00:00&gt;</span>
328 &gt;&gt;&gt; <span class="input">Date("08-13.22:13", -5)</span>
329 <span class="output">&lt;Date 2000-08-14.03:13:00&gt;</span>
330 &gt;&gt;&gt; <span class="input">Date("14:25", -5)</span>
331 <span class="output">&lt;Date 2000-06-25.19:25:00&gt;</span>
332 &gt;&gt;&gt; <span class="input">Interval("  3w  1  d  2:00")</span>
333 <span class="output">&lt;Interval 22d 2:00&gt;</span>
334 &gt;&gt;&gt; <span class="input">Date(". + 2d") - Interval("3w")</span>
335 <span class="output">&lt;Date 2000-06-07.00:34:02&gt;</span
336 ></small></pre></blockquote>
338 <h3>3.2. Items and Classes</h3>
340 <p>Items contain data in <em>properties</em>.  To Python, these
341 properties are presented as the key-value pairs of a dictionary.
342 Each item belongs to a <em>class</em> which defines the names
343 and types of its properties.  The database permits the creation
344 and modification of classes as well as items.
346 <h3>3.3. Identifiers and Designators</h3>
348 <p>Each item has a numeric identifier which is unique among
349 items in its class.  The items are numbered sequentially
350 within each class in order of creation, starting from 1.
351 The <em>designator</em>
352 for an item is a way to identify an item in the database, and
353 consists of the name of the item's class concatenated with
354 the item's numeric identifier.
356 <p>For example, if "spam" and "eggs" are classes, the first
357 item created in class "spam" has id 1 and designator "spam1".
358 The first item created in class "eggs" also has id 1 but has
359 the distinct designator "eggs1".  Item designators are
360 conventionally enclosed in square brackets when mentioned
361 in plain text.  This permits a casual mention of, say,
362 "[patch37]" in an e-mail message to be turned into an active
363 hyperlink.
365 <h3>3.4. Property Names and Types</h3>
367 <p>Property names must begin with a letter.
369 <p>A property may be one of five <em>basic types</em>:
371 <ul>
372 <li><em>String</em> properties are for storing arbitrary-length
373 strings.
375 <li><em>Date</em> properties store date-and-time stamps.
376 Their values are Timestamp objects.
378 <li>A <em>Link</em> property refers to a single other item
379 selected from a specified class.  The class is part of the property;
380 the value is an integer, the id of the chosen item.
382 <li>A <em>Multilink</em> property refers to possibly many items
383 in a specified class.  The value is a list of integers.
384 </ul>
386 <p><tt>None</tt> is also a permitted value for any of these property
387 types.  An attempt to store <tt>None</tt> into a String property
388 stores the empty string; an attempt to store <tt>None</tt>
389 into a Multilink property stores an empty list.
391 <h3>3.5. Interface Specification</h3>
393 <p>The hyperdb module provides property objects to designate
394 the different kinds of properties.  These objects are used when
395 specifying what properties belong in classes.
397 <blockquote><pre><small
398 >class <strong>String</strong>:
399     def <strong>__init__</strong>(self):
400         """An object designating a String property."""
402 class <strong>Date</strong>:
403     def <strong>__init__</strong>(self):
404         """An object designating a Date property."""
406 class <strong>Link</strong>:
407     def <strong>__init__</strong>(self, classname):
408         """An object designating a Link property that links to
409         items in a specified class."""
411 class <strong>Multilink</strong>:
412     def <strong>__init__</strong>(self, classname):
413         """An object designating a Multilink property that links
414         to items in a specified class."""
415 </small></pre></blockquote>
417 <p>Here is the interface provided by the hyperdatabase.
419 <blockquote><pre><small
420 >class <strong>Database</strong>:
421     """A database for storing records containing flexible data types."""
423     def <strong>__init__</strong>(self, storagelocator, journaltag):
424         """Open a hyperdatabase given a specifier to some storage.
426         The meaning of 'storagelocator' depends on the particular
427         implementation of the hyperdatabase.  It could be a file name,
428         a directory path, a socket descriptor for a connection to a
429         database over the network, etc.
431         The 'journaltag' is a token that will be attached to the journal
432         entries for any edits done on the database.  If 'journaltag' is
433         None, the database is opened in read-only mode: the Class.create(),
434         Class.set(), and Class.retire() methods are disabled.
435         """
437     def <strong>__getattr__</strong>(self, classname):
438         """A convenient way of calling self.getclass(classname)."""
440     def <strong>getclasses</strong>(self):
441         """Return a list of the names of all existing classes."""
443     def <strong>getclass</strong>(self, classname):
444         """Get the Class object representing a particular class.
446         If 'classname' is not a valid class name, a KeyError is raised.
447         """
449 class <strong>Class</strong>:
450     """The handle to a particular class of items in a hyperdatabase."""
452     def <strong>__init__</strong>(self, db, classname, **properties):
453         """Create a new class with a given name and property specification.
455         'classname' must not collide with the name of an existing class,
456         or a ValueError is raised.  The keyword arguments in 'properties'
457         must map names to property objects, or a TypeError is raised.
458         """
460     # Editing items:
462     def <strong>create</strong>(self, **propvalues):
463         """Create a new item of this class and return its id.
465         The keyword arguments in 'propvalues' map property names to values.
466         The values of arguments must be acceptable for the types of their
467         corresponding properties or a TypeError is raised.  If this class
468         has a key property, it must be present and its value must not
469         collide with other key strings or a ValueError is raised.  Any other
470         properties on this class that are missing from the 'propvalues'
471         dictionary are set to None.  If an id in a link or multilink
472         property does not refer to a valid item, an IndexError is raised.
473         """
475     def <strong>get</strong>(self, itemid, propname):
476         """Get the value of a property on an existing item of this class.
478         'itemid' must be the id of an existing item of this class or an
479         IndexError is raised.  'propname' must be the name of a property
480         of this class or a KeyError is raised.
481         """
483     def <strong>set</strong>(self, itemid, **propvalues):
484         """Modify a property on an existing item of this class.
485         
486         'itemid' must be the id of an existing item of this class or an
487         IndexError is raised.  Each key in 'propvalues' must be the name
488         of a property of this class or a KeyError is raised.  All values
489         in 'propvalues' must be acceptable types for their corresponding
490         properties or a TypeError is raised.  If the value of the key
491         property is set, it must not collide with other key strings or a
492         ValueError is raised.  If the value of a Link or Multilink
493         property contains an invalid item id, a ValueError is raised.
494         """
496     def <strong>retire</strong>(self, itemid):
497         """Retire an item.
498         
499         The properties on the item remain available from the get() method,
500         and the item's id is never reused.  Retired items are not returned
501         by the find(), list(), or lookup() methods, and other items may
502         reuse the values of their key properties.
503         """
505     def <strong>history</strong>(self, itemid):
506         """Retrieve the journal of edits on a particular item.
508         'itemid' must be the id of an existing item of this class or an
509         IndexError is raised.
511         The returned list contains tuples of the form
513             (date, tag, action, params)
515         'date' is a Timestamp object specifying the time of the change and
516         'tag' is the journaltag specified when the database was opened.
517         'action' may be:
519             'create' or 'set' -- 'params' is a dictionary of property values
520             'link' or 'unlink' -- 'params' is (classname, itemid, propname)
521             'retire' -- 'params' is None
522         """
524     # Locating items:
526     def <strong>setkey</strong>(self, propname):
527         """Select a String property of this class to be the key property.
529         'propname' must be the name of a String property of this class or
530         None, or a TypeError is raised.  The values of the key property on
531         all existing items must be unique or a ValueError is raised.
532         """
534     def <strong>getkey</strong>(self):
535         """Return the name of the key property for this class or None."""
537     def <strong>lookup</strong>(self, keyvalue):
538         """Locate a particular item by its key property and return its id.
540         If this class has no key property, a TypeError is raised.  If the
541         'keyvalue' matches one of the values for the key property among
542         the items in this class, the matching item's id is returned;
543         otherwise a KeyError is raised.
544         """
546     def <strong>find</strong>(self, propname, itemid):
547         """Get the ids of items in this class which link to a given item.
548         
549         'propname' must be the name of a property in this class, or a
550         KeyError is raised.  That property must be a Link or Multilink
551         property, or a TypeError is raised.  'itemid' must be the id of
552         an existing item in the class linked to by the given property,
553         or an IndexError is raised.
554         """
556     def <strong>list</strong>(self):
557         """Return a list of the ids of the active items in this class."""
559     def <strong>count</strong>(self):
560         """Get the number of items in this class.
562         If the returned integer is 'numitems', the ids of all the items
563         in this class run from 1 to numitems, and numitems+1 will be the
564         id of the next item to be created in this class.
565         """
567     # Manipulating properties:
569     def <strong>getprops</strong>(self):
570         """Return a dictionary mapping property names to property objects."""
572     def <strong>addprop</strong>(self, **properties):
573         """Add properties to this class.
575         The keyword arguments in 'properties' must map names to property
576         objects, or a TypeError is raised.  None of the keys in 'properties'
577         may collide with the names of existing properties, or a ValueError
578         is raised before any properties have been added.
579         """</small></pre></blockquote>
581 <h3>3.6. Application Example</h3>
583 <p>Here is an example of how the hyperdatabase module would work in practice.
585 <blockquote><pre><small
586 >&gt;&gt;&gt; <span class="input">import hyperdb</span>
587 &gt;&gt;&gt; <span class="input">db = hyperdb.Database("foo.db", "ping")</span>
588 &gt;&gt;&gt; <span class="input">db</span>
589 <span class="output">&lt;hyperdb.Database "foo.db" opened by "ping"&gt;</span>
590 &gt;&gt;&gt; <span class="input">hyperdb.Class(db, "status", name=hyperdb.String())</span>
591 <span class="output">&lt;hyperdb.Class "status"&gt;</span>
592 &gt;&gt;&gt; <span class="input">_.setkey("name")</span>
593 &gt;&gt;&gt; <span class="input">db.status.create(name="unread")</span>
594 <span class="output">1</span>
595 &gt;&gt;&gt; <span class="input">db.status.create(name="in-progress")</span>
596 <span class="output">2</span>
597 &gt;&gt;&gt; <span class="input">db.status.create(name="testing")</span>
598 <span class="output">3</span>
599 &gt;&gt;&gt; <span class="input">db.status.create(name="resolved")</span>
600 <span class="output">4</span>
601 &gt;&gt;&gt; <span class="input">db.status.count()</span>
602 <span class="output">4</span>
603 &gt;&gt;&gt; <span class="input">db.status.list()</span>
604 <span class="output">[1, 2, 3, 4]</span>
605 &gt;&gt;&gt; <span class="input">db.status.lookup("in-progress")</span>
606 <span class="output">2</span>
607 &gt;&gt;&gt; <span class="input">db.status.retire(3)</span>
608 &gt;&gt;&gt; <span class="input">db.status.list()</span>
609 <span class="output">[1, 2, 4]</span>
610 &gt;&gt;&gt; <span class="input">hyperdb.Class(db, "issue", title=hyperdb.String(), status=hyperdb.Link("status"))</span>
611 <span class="output">&lt;hyperdb.Class "issue"&gt;</span>
612 &gt;&gt;&gt; <span class="input">db.issue.create(title="spam", status=1)</span>
613 <span class="output">1</span>
614 &gt;&gt;&gt; <span class="input">db.issue.create(title="eggs", status=2)</span>
615 <span class="output">2</span>
616 &gt;&gt;&gt; <span class="input">db.issue.create(title="ham", status=4)</span>
617 <span class="output">3</span>
618 &gt;&gt;&gt; <span class="input">db.issue.create(title="arguments", status=2)</span>
619 <span class="output">4</span>
620 &gt;&gt;&gt; <span class="input">db.issue.create(title="abuse", status=1)</span>
621 <span class="output">5</span>
622 &gt;&gt;&gt; <span class="input">hyperdb.Class(db, "user", username=hyperdb.Key(), password=hyperdb.String())</span>
623 <span class="output">&lt;hyperdb.Class "user"&gt;</span>
624 &gt;&gt;&gt; <span class="input">db.issue.addprop(fixer=hyperdb.Link("user"))</span>
625 &gt;&gt;&gt; <span class="input">db.issue.getprops()</span>
626 <span class="output"
627 >{"title": &lt;hyperdb.String&gt;, "status": &lt;hyperdb.Link to "status"&gt;,
628  "user": &lt;hyperdb.Link to "user"&gt;}</span>
629 &gt;&gt;&gt; <span class="input">db.issue.set(5, status=2)</span>
630 &gt;&gt;&gt; <span class="input">db.issue.get(5, "status")</span>
631 <span class="output">2</span>
632 &gt;&gt;&gt; <span class="input">db.status.get(2, "name")</span>
633 <span class="output">"in-progress"</span>
634 &gt;&gt;&gt; <span class="input">db.issue.get(5, "title")</span>
635 <span class="output">"abuse"</span>
636 &gt;&gt;&gt; <span class="input">db.issue.find("status", db.status.lookup("in-progress"))</span>
637 <span class="output">[2, 4, 5]</span>
638 &gt;&gt;&gt; <span class="input">db.issue.history(5)</span>
639 <span class="output"
640 >[(&lt;Date 2000-06-28.19:09:43&gt;, "ping", "create", {"title": "abuse", "status": 1}),
641  (&lt;Date 2000-06-28.19:11:04&gt;, "ping", "set", {"status": 2})]</span>
642 &gt;&gt;&gt; <span class="input">db.status.history(1)</span>
643 <span class="output"
644 >[(&lt;Date 2000-06-28.19:09:43&gt;, "ping", "link", ("issue", 5, "status")),
645  (&lt;Date 2000-06-28.19:11:04&gt;, "ping", "unlink", ("issue", 5, "status"))]</span>
646 &gt;&gt;&gt; <span class="input">db.status.history(2)</span>
647 <span class="output"
648 >[(&lt;Date 2000-06-28.19:11:04&gt;, "ping", "link", ("issue", 5, "status"))]</span>
649 </small></pre></blockquote>
651 <p>For the purposes of journalling, when a Multilink property is
652 set to a new list of items, the hyperdatabase compares the old
653 list to the new list.
654 The journal records "unlink" events for all the items that appear
655 in the old list but not the new list,
656 and "link" events for
657 all the items that appear in the new list but not in the old list.
659 <p><hr>
660 <h2>4. Roundup Database</h2>
662 <p>The Roundup database layer is implemented on top of the
663 hyperdatabase and mediates calls to the database.
664 Some of the classes in the Roundup database are considered
665 <em>item classes</em>.
666 The Roundup database layer adds detectors and user items,
667 and on items it provides mail spools, nosy lists, and superseders.
669 <h3>4.1. Reserved Classes</h3>
671 <p>Internal to this layer we reserve three special classes
672 of items that are not items.
674 <h4>4.1.1. Users</h4>
676 <p>Users are stored in the hyperdatabase as items of
677 class "user".  The "user" class has the definition:
679 <blockquote><pre><small
680 >hyperdb.Class(db, "user", username=hyperdb.String(),
681                           password=hyperdb.String(),
682                           address=hyperdb.String())
683 db.user.setkey("username")</small></pre></blockquote>
685 <h4>4.1.2. Messages</h4>
687 <p>E-mail messages are represented by hyperdatabase items of class "msg".
688 The actual text content of the messages is stored in separate files.
689 (There's no advantage to be gained by stuffing them into the
690 hyperdatabase, and if messages are stored in ordinary text files,
691 they can be grepped from the command line.)  The text of a message is
692 saved in a file named after the message item designator (e.g. "msg23")
693 for the sake of the command interface (see below).  Attachments are
694 stored separately and associated with "file" items.
695 The "msg" class has the definition:
697 <blockquote><pre><small
698 >hyperdb.Class(db, "msg", author=hyperdb.Link("user"),
699                          recipients=hyperdb.Multilink("user"),
700                          date=hyperdb.Date(),
701                          summary=hyperdb.String(),
702                          files=hyperdb.Multilink("file"))</small
703 ></pre></blockquote>
705 <p>The "author" property indicates the author of the message
706 (a "user" item must exist in the hyperdatabase for any messages
707 that are stored in the system).
708 The "summary" property contains a summary of the message for display
709 in a message index.
711 <h4>4.1.3. Files</h4>
713 <p>Submitted files are represented by hyperdatabase
714 items of class "file".  Like e-mail messages, the file content
715 is stored in files outside the database,
716 named after the file item designator (e.g. "file17").
717 The "file" class has the definition:
719 <blockquote><pre><small
720 >hyperdb.Class(db, "file", user=hyperdb.Link("user"),
721                           name=hyperdb.String(),
722                           type=hyperdb.String())</small></pre></blockquote>
724 <p>The "user" property indicates the user who submitted the
725 file, the "name" property holds the original name of the file,
726 and the "type" property holds the MIME type of the file as received.
728 <h3>4.2. Item Classes</h3>
730 <p>All items have the following standard properties:
732 <blockquote><pre><small
733 >title=hyperdb.String()
734 messages=hyperdb.Multilink("msg")
735 files=hyperdb.Multilink("file")
736 nosy=hyperdb.Multilink("user")
737 superseder=hyperdb.Multilink("item")</small></pre></blockquote>
739 <p>Also, two Date properties named "creation" and "activity" are
740 fabricated by the Roundup database layer.  By "fabricated" we
741 mean that no such properties are actually stored in the
742 hyperdatabase, but when properties on items are requested, the
743 "creation" and "activity" properties are made available.
744 The value of the "creation" property is the date when an item was
745 created, and the value of the "activity" property is the
746 date when any property on the item was last edited (equivalently,
747 these are the dates on the first and last records in the item's journal).
749 <h3>4.3. Interface Specification</h3>
751 <p>The interface to a Roundup database delegates most method
752 calls to the hyperdatabase, except for the following
753 changes and additional methods.
755 <blockquote><pre><small
756 >class <strong>Database</strong>:
757     # Overridden methods:
759     def <strong>__init__</strong>(self, storagelocator, journaltag):
760         """When the Roundup database is opened by a particular user,
761         the 'journaltag' is the id of the user's "user" item."""
763     def <strong>getclass</strong>(self, classname):
764         """This method now returns an instance of either Class or
765         ItemClass depending on whether an item class is specified."""
767     # New methods:
769     def <strong>getuid</strong>(self):
770         """Return the id of the "user" item associated with the user
771         that owns this connection to the hyperdatabase."""
773 class <strong>Class</strong>:
774     # Overridden methods:
776     def <strong>create</strong>(self, **propvalues):
777     def <strong>set</strong>(self, **propvalues):
778     def <strong>retire</strong>(self, itemid):
779         """These operations trigger detectors and can be vetoed.  Attempts
780         to modify the "creation" or "activity" properties cause a KeyError.
781         """
783     # New methods:
785     def <strong>audit</strong>(self, event, detector):
786     def <strong>react</strong>(self, event, detector):
787         """Register a detector (see below for more details)."""
789 class <strong>ItemClass</strong>(Class):
790     # Overridden methods:
792     def <strong>__init__</strong>(self, db, classname, **properties):
793         """The newly-created class automatically includes the "messages",
794         "files", "nosy", and "superseder" properties.  If the 'properties'
795         dictionary attempts to specify any of these properties or a
796         "creation" or "activity" property, a ValueError is raised."""
798     def <strong>get</strong>(self, itemid, propname):
799     def <strong>getprops</strong>(self):
800         """In addition to the actual properties on the item, these
801         methods provide the "creation" and "activity" properties."""
803     # New methods:
805     def <strong>addmessage</strong>(self, itemid, summary, text):
806         """Add a message to an item's mail spool.
808         A new "msg" item is constructed using the current date, the
809         user that owns the database connection as the author, and
810         the specified summary text.  The "files" and "recipients"
811         fields are left empty.  The given text is saved as the body
812         of the message and the item is appended to the "messages"
813         field of the specified item.
814         """
816     def <strong>nosymessage</strong>(self, itemid, msgid):
817         """Send a message to the members of an item's nosy list.
819         The message is sent only to users on the nosy list who are not
820         already on the "recipients" list for the message.  These users
821         are then added to the message's "recipients" list.
822         """
823 </small></pre></blockquote>
825 <h3>4.4. Default Schema</h3>
827 <p>The default schema included with Roundup turns it into a
828 typical software bug tracker.  The database is set up like this:
830 <blockquote><pre><small
831 >pri = Class(db, "priority", name=hyperdb.String(), order=hyperdb.String())
832 pri.setkey("name")
833 pri.create(name="critical", order="1")
834 pri.create(name="urgent", order="2")
835 pri.create(name="bug", order="3")
836 pri.create(name="feature", order="4")
837 pri.create(name="wish", order="5")
839 stat = Class(db, "status", name=hyperdb.String(), order=hyperdb.String())
840 stat.setkey("name")
841 stat.create(name="unread", order="1")
842 stat.create(name="deferred", order="2")
843 stat.create(name="chatting", order="3")
844 stat.create(name="need-eg", order="4")
845 stat.create(name="in-progress", order="5")
846 stat.create(name="testing", order="6")
847 stat.create(name="done-cbb", order="7")
848 stat.create(name="resolved", order="8")
850 Class(db, "keyword", name=hyperdb.String())
852 Class(db, "issue", fixer=hyperdb.Multilink("user"),
853                    topic=hyperdb.Multilink("keyword"),
854                    priority=hyperdb.Link("priority"),
855                    status=hyperdb.Link("status"))
856 </small></pre></blockquote>
858 <p>(The "order" property hasn't been explained yet.  It
859 gets used by the Web user interface for sorting.)
861 <p>The above isn't as pretty-looking as the schema specification
862 in the first-stage submission, but it could be made just as easy
863 with the addition of a convenience function like <tt>Choice</tt>
864 for setting up the "priority" and "status" classes:
866 <blockquote><pre><small
867 >def Choice(name, *options):
868     cl = Class(db, name, name=hyperdb.String(), order=hyperdb.String())
869     for i in range(len(options)):
870         cl.create(name=option[i], order=i)
871     return hyperdb.Link(name)
872 </small></pre></blockquote>
874 <p><hr>
875 <h2>5. Detector Interface</h2>
877 <p>Detectors are Python functions that are triggered on certain
878 kinds of events.  The definitions of the
879 functions live in Python modules placed in a directory set aside
880 for this purpose.  Importing the Roundup database module also
881 imports all the modules in this directory, and the <tt>init()</tt>
882 function of each module is called when a database is opened to
883 provide it a chance to register its detectors.
885 <p>There are two kinds of detectors:
887 <ul>
888 <li>an <em>auditor</em> is triggered just before modifying an item
889 <li>a <em>reactor</em> is triggered just after an item has been modified
890 </ul>
892 <p>When the Roundup database is about to perform a
893 <tt>create()</tt>, <tt>set()</tt>, or <tt>retire()</tt>
894 operation, it first calls any auditors that
895 have been registered for that operation on that class.
896 Any auditor may raise a <tt>Reject</tt> exception
897 to abort the operation.
899 <p>If none of the auditors raises an exception, the database
900 proceeds to carry out the operation.  After it's done, it
901 then calls all of the reactors that have been registered
902 for the operation.
904 <h3>5.1. Interface Specification</h3>
906 <p>The <tt>audit()</tt> and <tt>react()</tt> methods
907 register detectors on a given class of items.
909 <blockquote><pre><small
910 >class Class:
911     def <strong>audit</strong>(self, event, detector):
912         """Register an auditor on this class.
914         'event' should be one of "create", "set", or "retire".
915         'detector' should be a function accepting four arguments.
916         """
918     def <strong>react</strong>(self, event, detector):
919         """Register a reactor on this class.
921         'event' should be one of "create", "set", or "retire".
922         'detector' should be a function accepting four arguments.
923         """
924 </small></pre></blockquote>
926 <p>Auditors are called with the arguments:
928 <blockquote><pre><small
929 >audit(db, cl, itemid, newdata)</small></pre></blockquote>
931 where <tt>db</tt> is the database, <tt>cl</tt> is an
932 instance of Class or ItemClass within the database, and <tt>newdata</tt>
933 is a dictionary mapping property names to values.
935 For a <tt>create()</tt>
936 operation, the <tt>itemid</tt> argument is <tt>None</tt> and <tt>newdata</tt>
937 contains all of the initial property values with which the item
938 is about to be created.
940 For a <tt>set()</tt> operation, <tt>newdata</tt>
941 contains only the names and values of properties that are about
942 to be changed.
944 For a <tt>retire()</tt> operation, <tt>newdata</tt> is <tt>None</tt>.
946 <p>Reactors are called with the arguments:
948 <blockquote><pre><small
949 >react(db, cl, itemid, olddata)</small></pre></blockquote>
951 where <tt>db</tt> is the database, <tt>cl</tt> is an
952 instance of Class or ItemClass within the database, and <tt>olddata</tt>
953 is a dictionary mapping property names to values.
955 For a <tt>create()</tt>
956 operation, the <tt>itemid</tt> argument is the id of the
957 newly-created item and <tt>olddata</tt> is None.
959 For a <tt>set()</tt> operation, <tt>olddata</tt>
960 contains the names and previous values of properties that were changed.
962 For a <tt>retire()</tt> operation, <tt>itemid</tt> is the
963 id of the retired item and <tt>olddata</tt> is <tt>None</tt>.
965 <h3>5.2. Detector Example</h3>
967 <p>Here is an example of detectors written for a hypothetical
968 project-management application, where users can signal approval
969 of a project by adding themselves to an "approvals" list, and
970 a project proceeds when it has three approvals.
972 <blockquote><pre><small
973 ># Permit users only to add themselves to the "approvals" list.
975 def check_approvals(db, cl, id, newdata):
976     if newdata.has_key("approvals"):
977         if cl.get(id, "status") == db.status.lookup("approved"):
978             raise Reject, "You can't modify the approvals list " \
979                           "for a project that has already been approved."
980         old = cl.get(id, "approvals")
981         new = newdata["approvals"]
982         for uid in old:
983             if uid not in new and uid != db.getuid():
984                 raise Reject, "You can't remove other users from the "
985                               "approvals list; you can only remove yourself."
986         for uid in new:
987             if uid not in old and uid != db.getuid():
988                 raise Reject, "You can't add other users to the approvals "
989                               "list; you can only add yourself."
991 # When three people have approved a project, change its
992 # status from "pending" to "approved".
994 def approve_project(db, cl, id, olddata):
995     if olddata.has_key("approvals") and len(cl.get(id, "approvals")) == 3:
996         if cl.get(id, "status") == db.status.lookup("pending"):
997             cl.set(id, status=db.status.lookup("approved"))
999 def init(db):
1000     db.project.audit("set", check_approval)
1001     db.project.react("set", approve_project)</small
1002 ></pre></blockquote>    
1004 <p>Here is another example of a detector that can allow or prevent
1005 the creation of new items.  In this scenario, patches for a software
1006 project are submitted by sending in e-mail with an attached file,
1007 and we want to ensure that there are <tt>text/plain</tt> attachments on
1008 the message.  The maintainer of the package can then apply the
1009 patch by setting its status to "applied".
1011 <blockquote><pre><small
1012 ># Only accept attempts to create new patches that come with patch files.
1014 def check_new_patch(db, cl, id, newdata):
1015     if not newdata["files"]:
1016         raise Reject, "You can't submit a new patch without " \
1017                       "attaching a patch file."
1018     for fileid in newdata["files"]:
1019         if db.file.get(fileid, "type") != "text/plain":
1020             raise Reject, "Submitted patch files must be text/plain."
1022 # When the status is changed from "approved" to "applied", apply the patch.
1024 def apply_patch(db, cl, id, olddata):
1025     if cl.get(id, "status") == db.status.lookup("applied") and \
1026         olddata["status"] == db.status.lookup("approved"):
1027         # ...apply the patch...
1029 def init(db):
1030     db.patch.audit("create", check_new_patch)
1031     db.patch.react("set", apply_patch)</small
1032 ></pre></blockquote>
1034 <p><hr>
1035 <h2>6. Command Interface</h2>
1037 <p>The command interface is a very simple and minimal interface,
1038 intended only for quick searches and checks from the shell prompt.
1039 (Anything more interesting can simply be written in Python using
1040 the Roundup database module.)
1042 <h3>6.1. Interface Specification</h3>
1044 <p>A single command, <tt>roundup</tt>, provides basic access to
1045 the hyperdatabase from the command line.
1047 <ul>
1048 <li><tt>roundup&nbsp;get&nbsp;</tt>[<tt>-list</tt>]<tt>&nbsp;</tt
1049 ><em>designator</em>[<tt>,</tt
1050 ><em>designator</em><tt>,</tt>...]<tt>&nbsp;</tt><em>propname</em>
1051 <li><tt>roundup&nbsp;set&nbsp;</tt><em>designator</em>[<tt>,</tt
1052 ><em>designator</em><tt>,</tt>...]<tt>&nbsp;</tt><em>propname</em
1053 ><tt>=</tt><em>value</em> ...
1054 <li><tt>roundup&nbsp;find&nbsp;</tt>[<tt>-list</tt>]<tt>&nbsp;</tt
1055 ><em>classname</em><tt>&nbsp;</tt><em>propname</em>=<em>value</em> ...
1056 </ul>
1058 <p>Property values are represented as strings in command arguments
1059 and in the printed results:
1061 <ul>
1062 <li>Strings are, well, strings.
1064 <li>Date values are printed in the full date format in the local
1065 time zone, and accepted in the full format or any of the partial
1066 formats explained above.
1068 <li>Link values are printed as item designators.  When given as
1069 an argument, item designators and key strings are both accepted.
1071 <li>Multilink values are printed as lists of item designators
1072 joined by commas.  When given as an argument, item designators
1073 and key strings are both accepted; an empty string, a single item,
1074 or a list of items joined by commas is accepted.
1075 </ul>
1077 <p>When multiple items are specified to the
1078 <tt>roundup&nbsp;get</tt> or <tt>roundup&nbsp;set</tt>
1079 commands, the specified properties are retrieved or set
1080 on all the listed items.
1082 <p>When multiple results are returned by the <tt>roundup&nbsp;get</tt>
1083 or <tt>roundup&nbsp;find</tt> commands, they are printed one per
1084 line (default) or joined by commas (with the <tt>-list</tt>) option.
1086 <h3>6.2. Usage Example</h3>
1088 <p>To find all messages regarding in-progress issues that
1089 contain the word "spam", for example, you could execute the
1090 following command from the directory where the database
1091 dumps its files:
1093 <blockquote><pre><small
1094 >shell% <span class="input">for issue in `roundup find issue status=in-progress`; do</span>
1095 &gt; <span class="input">grep -l spam `roundup get $issue messages`</span>
1096 &gt; <span class="input">done</span>
1097 <span class="output">msg23
1098 msg49
1099 msg50
1100 msg61</span>
1101 shell%</small></pre></blockquote>
1103 <p>Or, using the <tt>-list</tt> option, this can be written as a single command:
1105 <blockquote><pre><small
1106 >shell% <span class="input">grep -l spam `roundup get \
1107     \`roundup find -list issue status=in-progress\` messages`</span>
1108 <span class="output">msg23
1109 msg49
1110 msg50
1111 msg61</span>
1112 shell%</small></pre></blockquote>
1113     
1114 <p><hr>
1115 <h2>7. E-mail User Interface</h2>
1117 <p>The Roundup system must be assigned an e-mail address
1118 at which to receive mail.  Messages should be piped to
1119 the Roundup mail-handling script by the mail delivery
1120 system (e.g. using an alias beginning with "|" for sendmail).
1122 <h3>7.1. Message Processing</h3>
1124 <p>Incoming messages are examined for multiple parts.
1125 In a <tt>multipart/mixed</tt> message or part, each subpart is
1126 extracted and examined.  In a <tt>multipart/alternative</tt>
1127 message or part, we look for a <tt>text/plain</tt> subpart and
1128 ignore the other parts.  The <tt>text/plain</tt> subparts are
1129 assembled to form the textual body of the message, to
1130 be stored in the file associated with a "msg" class item.
1131 Any parts of other types are each stored in separate
1132 files and given "file" class items that are linked to
1133 the "msg" item.
1135 <p>The "summary" property on message items is taken from
1136 the first non-quoting section in the message body.
1137 The message body is divided into sections by blank lines.
1138 Sections where the second and all subsequent lines begin
1139 with a "&gt;" or "|" character are considered "quoting
1140 sections".  The first line of the first non-quoting 
1141 section becomes the summary of the message.
1143 <p>All of the addresses in the To: and Cc: headers of the
1144 incoming message are looked up among the user items, and
1145 the corresponding users are placed in the "recipients"
1146 property on the new "msg" item.  The address in the From:
1147 header similarly determines the "author" property of the
1148 new "msg" item.
1149 The default handling for
1150 addresses that don't have corresponding users is to create
1151 new users with no passwords and a username equal to the
1152 address.  (The web interface does not permit logins for
1153 users with no passwords.)  If we prefer to reject mail from
1154 outside sources, we can simply register an auditor on the
1155 "user" class that prevents the creation of user items with
1156 no passwords.
1158 <p>The subject line of the incoming message is examined to
1159 determine whether the message is an attempt to create a new
1160 item or to discuss an existing item.  A designator enclosed
1161 in square brackets is sought as the first thing on the
1162 subject line (after skipping any "Fwd:" or "Re:" prefixes).
1164 <p>If an item designator (class name and id number) is found
1165 there, the newly created "msg" item is added to the "messages"
1166 property for that item, and any new "file" items are added to
1167 the "files" property for the item.
1169 <p>If just an item class name is found there, we attempt to
1170 create a new item of that class with its "messages" property
1171 initialized to contain the new "msg" item and its "files"
1172 property initialized to contain any new "file" items.
1174 <p>Both cases may trigger detectors (in the first case we
1175 are calling the <tt>set()</tt> method to add the message to the
1176 item's spool; in the second case we are calling the
1177 <tt>create()</tt> method to create a new item).  If an auditor
1178 raises an exception, the original message is bounced back to
1179 the sender with the explanatory message given in the exception.
1181 <h3>7.2. Nosy Lists</h3>
1183 <p>A standard detector is provided that watches for additions
1184 to the "messages" property.  When a new message is added, the
1185 detector sends it to all the users on the "nosy" list for the
1186 item that are not already on the "recipients" list of the
1187 message.  Those users are then appended to the "recipients"
1188 property on the message, so multiple copies of a message
1189 are never sent to the same user.  The journal recorded by
1190 the hyperdatabase on the "recipients" property then provides
1191 a log of when the message was sent to whom.
1193 <h3>7.3. Setting Properties</h3>
1195 <p>The e-mail interface also provides a simple way to set
1196 properties on items.  At the end of the subject line,
1197 <em>propname</em><tt>=</tt><em>value</em> pairs can be
1198 specified in square brackets, using the same conventions
1199 as for the <tt>roundup&nbsp;set</tt> shell command.
1201 <p><hr>
1202 <h2>8. Web User Interface</h2>
1204 <p>The web interface is provided by a CGI script that can be
1205 run under any web server.  A simple web server can easily be
1206 built on the standard <tt>CGIHTTPServer</tt> module, and
1207 should also be included in the distribution for quick
1208 out-of-the-box deployment.
1210 <p>The user interface is constructed from a number of template
1211 files containing mostly HTML.  Among the HTML tags in templates
1212 are interspersed some nonstandard tags, which we use as
1213 placeholders to be replaced by properties and their values.
1215 <h3>8.1. Views and View Specifiers</h3>
1217 <p>There are two main kinds of views: index views and item views.
1218 An index view displays a list of items of a particular class,
1219 optionally sorted and filtered as requested.  An item view
1220 presents the properties of a particular item for editing
1221 and displays the message spool for the item.
1223 <p>A <em>view specifier</em> is a string that specifies
1224 all the options needed to construct a particular view.
1225 It goes after the URL to the Roundup CGI script or the
1226 web server to form the complete URL to a view.  When the
1227 result of selecting a link or submitting a form takes
1228 the user to a new view, the Web browser should be redirected
1229 to a canonical location containing a complete view specifier
1230 so that the view can be bookmarked.
1232 <h3>8.2. Displaying Properties</h3>
1234 <p>Properties appear in the user interface in three contexts:
1235 in indices, in editors, and as filters.  For each type of
1236 property, there are several display possibilities.  For example,
1237 in an index view, a string property may just be printed as
1238 a plain string, but in an editor view, that property should
1239 be displayed in an editable field.
1241 <p>The display of a property is handled by functions in
1242 a <tt>displayers</tt> module.  Each function accepts at
1243 least three standard arguments -- the database, class name,
1244 and item id -- and returns a chunk of HTML.
1246 <p>Displayer functions are triggered by <tt>&lt;display&gt;</tt>
1247 tags in templates.  The <tt>call</tt> attribute of the tag
1248 provides a Python expression for calling the displayer
1249 function.  The three standard arguments are inserted in
1250 front of the arguments given.  For example, the occurrence of
1252 <blockquote><pre><small
1253 >    &lt;display call="plain('status', max=30)"&gt;
1254 </small></pre></blockquote>
1256 in a template triggers a call to
1257     
1258 <blockquote><pre><small
1259 >    plain(db, "issue", 13, "status", max=30)
1260 </small></pre></blockquote>
1262 when displaying item 13 in the "issue" class.  The displayer
1263 functions can accept extra arguments to further specify
1264 details about the widgets that should be generated.  By defining new
1265 displayer functions, the user interface can be highly customized.
1267 <p>Some of the standard displayer functions include:
1269 <ul>
1270 <li><strong>plain</strong>: display a String property directly;
1271 display a Date property in a specified time zone with an option
1272 to omit the time from the date stamp; for a Link or Multilink
1273 property, display the key strings of the linked items (or the
1274 ids if the linked class has no key property)
1276 <li><strong>field</strong>: display a property like the
1277 <strong>plain</strong> displayer above, but in a text field
1278 to be edited
1280 <li><strong>menu</strong>: for a Link property, display
1281 a menu of the available choices
1283 <li><strong>link</strong>: for a Link or Multilink property,
1284 display the names of the linked items, hyperlinked to the
1285 item views on those items
1287 <li><strong>count</strong>: for a Multilink property, display
1288 a count of the number of links in the list
1290 <li><strong>reldate</strong>: display a Date property in terms
1291 of an interval relative to the current date (e.g. "+ 3w", "- 2d").
1293 <li><strong>download</strong>: show a Link("file") or Multilink("file")
1294 property using links that allow you to download files
1296 <li><strong>checklist</strong>: for a Link or Multilink property,
1297 display checkboxes for the available choices to permit filtering
1298 </ul>
1300 <h3>8.3. Index Views</h3>
1302 <p>An index view contains two sections: a filter section
1303 and an index section.
1304 The filter section provides some widgets for selecting
1305 which items appear in the index.  The index section is
1306 a table of items.
1308 <h4>8.3.1. Index View Specifiers</h4>
1310 <p>An index view specifier looks like this (whitespace
1311 has been added for clarity):
1313 <blockquote><pre><small
1314 >/issue?status=unread,in-progress,resolved&amp;
1315         topic=security,ui&amp;
1316         :group=+priority&amp;
1317         :sort=-activity&amp;
1318         :filters=status,topic&amp;
1319         :columns=title,status,fixer
1320 </small></pre></blockquote>
1322 <p>The index view is determined by two parts of the
1323 specifier: the layout part and the filter part.
1324 The layout part consists of the query parameters that
1325 begin with colons, and it determines the way that the
1326 properties of selected items are displayed.
1327 The filter part consists of all the other query parameters,
1328 and it determines the criteria by which items 
1329 are selected for display.
1331 <p>The filter part is interactively manipulated with
1332 the form widgets displayed in the filter section.  The
1333 layout part is interactively manipulated by clicking
1334 on the column headings in the table.
1336 <p>The filter part selects the <em>union</em> of the
1337 sets of items with values matching any specified Link
1338 properties and the <em>intersection</em> of the sets
1339 of items with values matching any specified Multilink
1340 properties.
1342 <p>The example specifies an index of "issue" items.
1343 Only items with a "status" of <em>either</em>
1344 "unread" or "in-progres" or "resolved" are displayed,
1345 and only items with "topic" values including <em>both</em>
1346 "security" <em>and</em> "ui" are displayed.  The items
1347 are grouped by priority, arranged in ascending order;
1348 and within groups, sorted by activity, arranged in
1349 descending order.  The filter section shows filters
1350 for the "status" and "topic" properties, and the
1351 table includes columns for the "title", "status", and
1352 "fixer" properties.
1354 <p>Associated with each item class is a default
1355 layout specifier.  The layout specifier in the above
1356 example is the default layout to be provided with
1357 the default bug-tracker schema described above in
1358 section 4.4.
1360 <h4>8.3.2. Filter Section</h4>
1362 <p>The template for a filter section provides the
1363 filtering widgets at the top of the index view.
1364 Fragments enclosed in <tt>&lt;property&gt;</tt>...<tt>&lt;/property&gt;</tt>
1365 tags are included or omitted depending on whether the
1366 view specifier requests a filter for a particular property.
1368 <p>Here's a simple example of a filter template.
1370 <blockquote><pre><small
1371 >&lt;property name=status&gt;
1372     &lt;display call="checklist('status')"&gt;
1373 &lt;/property&gt;
1374 &lt;br&gt;
1375 &lt;property name=priority&gt;
1376     &lt;display call="checklist('priority')"&gt;
1377 &lt;/property&gt;
1378 &lt;br&gt;
1379 &lt;property name=fixer&gt;
1380     &lt;display call="menu('fixer')"&gt;
1381 &lt;/property&gt;</small></pre></blockquote>
1383 <h4>8.3.3. Index Section</h4>
1385 <p>The template for an index section describes one row of
1386 the index table.
1387 Fragments enclosed in <tt>&lt;property&gt;</tt>...<tt>&lt;/property&gt;</tt>
1388 tags are included or omitted depending on whether the
1389 view specifier requests a column for a particular property.
1390 The table cells should contain <tt>&lt;display&gt;</tt> tags
1391 to display the values of the item's properties.
1393 <p>Here's a simple example of an index template.
1395 <blockquote><pre><small
1396 >&lt;tr&gt;
1397     &lt;property name=title&gt;
1398         &lt;td&gt;&lt;display call="plain('title', max=50)"&gt;&lt;/td&gt;
1399     &lt;/property&gt;
1400     &lt;property name=status&gt;
1401         &lt;td&gt;&lt;display call="plain('status')"&gt;&lt;/td&gt;
1402     &lt;/property&gt;
1403     &lt;property name=fixer&gt;
1404         &lt;td&gt;&lt;display call="plain('fixer')"&gt;&lt;/td&gt;
1405     &lt;/property&gt;
1406 &lt;/tr&gt;</small></pre></blockquote>
1408 <h4>8.3.4. Sorting</h4>
1410 <p>String and Date values are sorted in the natural way.
1411 Link properties are sorted according to the value of the
1412 "order" property on the linked items if it is present; or
1413 otherwise on the key string of the linked items; or
1414 finally on the item ids.  Multilink properties are
1415 sorted according to how many links are present.
1417 <h3>8.4. Item Views</h3>
1419 <p>An item view contains an editor section and a spool section.
1420 At the top of an item view, links to superseding and superseded
1421 items are always displayed.
1423 <h4>8.4.1. Item View Specifiers</h4>
1425 <p>An item view specifier is simply the item's designator:
1427 <blockquote><pre><small
1428 >/patch23
1429 </small></pre></blockquote>
1431 <h4>8.4.2. Editor Section</h4>
1433 <p>The editor section is generated from a template
1434 containing <tt>&lt;display&gt;</tt> tags to insert
1435 the appropriate widgets for editing properties.
1437 <p>Here's an example of a basic editor template.
1439 <blockquote><pre><small
1440 >&lt;table&gt;
1441 &lt;tr&gt;
1442     &lt;td colspan=2&gt;
1443         &lt;display call="field('title', size=60)"&gt;
1444     &lt;/td&gt;
1445 &lt;/tr&gt;
1446 &lt;tr&gt;
1447     &lt;td&gt;
1448         &lt;display call="field('fixer', size=30)"&gt;
1449     &lt;/td&gt;
1450     &lt;td&gt;
1451         &lt;display call="menu('status')&gt;
1452     &lt;/td&gt;
1453 &lt;/tr&gt;
1454 &lt;tr&gt;
1455     &lt;td&gt;
1456         &lt;display call="field('nosy', size=30)"&gt;
1457     &lt;/td&gt;
1458     &lt;td&gt;
1459         &lt;display call="menu('priority')&gt;
1460     &lt;/td&gt;
1461 &lt;/tr&gt;
1462 &lt;tr&gt;
1463     &lt;td colspan=2&gt;
1464         &lt;display call="note()"&gt;
1465     &lt;/td&gt;
1466 &lt;/tr&gt;
1467 &lt;/table&gt;
1468 </small></pre></blockquote>
1470 <p>As shown in the example, the editor template can also
1471 request the display of a "note" field, which is a
1472 text area for entering a note to go along with a change.
1474 <p>When a change is submitted, the system automatically
1475 generates a message describing the changed properties.
1476 The message displays all of the property values on the
1477 item and indicates which ones have changed.
1478 An example of such a message might be this:
1480 <blockquote><pre><small
1481 >title: Polly Parrot is dead
1482 priority: critical
1483 status: unread -&gt; in-progress
1484 fixer: (none)
1485 keywords: parrot,plumage,perch,nailed,dead
1486 </small></pre></blockquote>
1488 <p>If a note is given in the "note" field, the note is
1489 appended to the description.  The message is then added
1490 to the item's message spool (thus triggering the standard
1491 detector to react by sending out this message to the nosy list).
1493 <h4>8.4.3. Spool Section</h4>
1495 <p>The spool section lists messages in the item's "messages"
1496 property.  The index of messages displays the "date", "author",
1497 and "summary" properties on the message items, and selecting a
1498 message takes you to its content.
1500 <p><hr>
1501 <h2>9. Deployment Scenarios</h2>
1503 <p>The design described above should be general enough
1504 to permit the use of Roundup for bug tracking, managing
1505 projects, managing patches, or holding discussions.  By
1506 using items of multiple types, one could deploy a system
1507 that maintains requirement specifications, catalogs bugs,
1508 and manages submitted patches, where patches could be
1509 linked to the bugs and requirements they address.
1511 <p><hr>
1512 <h2>10. Acknowledgements</h2>
1514 <p>My thanks are due to Christy Heyl for 
1515 reviewing and contributing suggestions to this paper
1516 and motivating me to get it done, and to
1517 Jesse Vincent, Mark Miller, Christopher Simons,
1518 Jeff Dunmall, Wayne Gramlich, and Dean Tribble for
1519 their assistance with the first-round submission.
1520 </td>
1521 </tr>
1522 </table>
1524 <p>
1526 <center>
1527 <table>
1528 <tr>
1529 <td>&nbsp;&nbsp;&nbsp;<a href="http://www.software-carpentry.com/index.html"><b>[Home]</b></a>&nbsp;&nbsp;&nbsp;</td>
1530 <td>&nbsp;&nbsp;&nbsp;<a href="http://www.software-carpentry.com/faq.html"><b>[FAQ]</b></a>&nbsp;&nbsp;&nbsp;</td>
1531 <td>&nbsp;&nbsp;&nbsp;<a href="http://www.software-carpentry.com/license.html"><b>[License]</b></a>&nbsp;&nbsp;&nbsp;</td>
1532 <td>&nbsp;&nbsp;&nbsp;<a href="http://www.software-carpentry.com/contest-rules.html"><b>[Rules]</b></a>&nbsp;&nbsp;&nbsp;</td>
1533 <td>&nbsp;&nbsp;&nbsp;<a href="http://www.software-carpentry.com/sc_config/"><b>[Configure]</b></a>&nbsp;&nbsp;&nbsp;</td>
1534 <td>&nbsp;&nbsp;&nbsp;<a href="http://www.software-carpentry.com/sc_build/"><b>[Build]</b></a>&nbsp;&nbsp;&nbsp;</td>
1535 <td>&nbsp;&nbsp;&nbsp;<a href="http://www.software-carpentry.com/sc_test/"><b>[Test]</b></a>&nbsp;&nbsp;&nbsp;</td>
1536 <td>&nbsp;&nbsp;&nbsp;<a href="http://www.software-carpentry.com/sc_track/"><b>[Track]</b></a>&nbsp;&nbsp;&nbsp;</td>
1537 <td>&nbsp;&nbsp;&nbsp;<a href="http://www.software-carpentry.com/biblio.html"><b>[Resources]</b></a>&nbsp;&nbsp;&nbsp;</td>
1538 <td>&nbsp;&nbsp;&nbsp;<a href="http://www.software-carpentry.com/lists/"><b>[Archives]</b></a>&nbsp;&nbsp;&nbsp;</td>
1539 </tr>
1540 </table>
1541 </center>
1543 <p><hr>
1544 <center>Last modified 2001/04/06 11:50:59.9063 US/Mountain</center>
1545 </BODY>
1546 </HTML>