Premiere version : mise en route du suivi.
[auf_roundup.git] / doc / .svn / text-base / spec.html.svn-base
1 <html>
2 <head>
3 <title>Software Carpentry Track: Roundup</title>
4 </head>
5 <body bgcolor=white>
6
7 <table width="100%">
8 <tr>
9
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>
14
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>
27
28 </tr>
29 </table>
30
31 <hr><p>
32
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>
38
39 <h2>Contents</h2>
40
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>
103
104 <p><hr>
105 <h2>1. Introduction</h2>
106
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.
114
115 <p><hr>
116 <h2>2. The Layer Cake</h2>
117
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.
122
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>
185
186 <p>The colourful parts of the cake are part of our system;
187 the faint grey parts of the cake are external components.
188
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.
192
193 <p><hr>
194 <h2>3. Hyperdatabase</h2>
195
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>.
200
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.
206
207 <h3>3.1. Dates and Date Arithmetic</h3>
208
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.
214
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.
227
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.
236
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>
249
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).
254
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>
264
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).
273
274 <p>Here is an outline of the Date and Interval classes.
275
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.
280
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         """
285
286     def <strong>__add__</strong>(self, interval):
287         """Add an interval to this date to produce another date."""
288
289     def <strong>__sub__</strong>(self, interval):
290         """Subtract an interval from this date to produce another date."""
291
292     def <strong>__cmp__</strong>(self, other):
293         """Compare this date to another date."""
294
295     def <strong>__str__</strong>(self):
296         """Return this date as a string in the yyyy-mm-dd.hh:mm:ss format."""
297
298     def <strong>local</strong>(self, offset):
299         """Return this date as yyyy-mm-dd.hh:mm:ss in a local time zone."""
300
301 class <strong>Interval</strong>:
302     def <strong>__init__</strong>(self, spec):
303         """Construct an interval given a specification."""
304
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>
312
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.
316
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>
337
338 <h3>3.2. Items and Classes</h3>
339
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.
345
346 <h3>3.3. Identifiers and Designators</h3>
347
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.
355
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.
364
365 <h3>3.4. Property Names and Types</h3>
366
367 <p>Property names must begin with a letter.
368
369 <p>A property may be one of five <em>basic types</em>:
370
371 <ul>
372 <li><em>String</em> properties are for storing arbitrary-length
373 strings.
374
375 <li><em>Date</em> properties store date-and-time stamps.
376 Their values are Timestamp objects.
377
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.
381
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>
385
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.
390
391 <h3>3.5. Interface Specification</h3>
392
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.
396
397 <blockquote><pre><small
398 >class <strong>String</strong>:
399     def <strong>__init__</strong>(self):
400         """An object designating a String property."""
401
402 class <strong>Date</strong>:
403     def <strong>__init__</strong>(self):
404         """An object designating a Date property."""
405
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."""
410
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>
416
417 <p>Here is the interface provided by the hyperdatabase.
418
419 <blockquote><pre><small
420 >class <strong>Database</strong>:
421     """A database for storing records containing flexible data types."""
422
423     def <strong>__init__</strong>(self, storagelocator, journaltag):
424         """Open a hyperdatabase given a specifier to some storage.
425
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.
430
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         """
436
437     def <strong>__getattr__</strong>(self, classname):
438         """A convenient way of calling self.getclass(classname)."""
439
440     def <strong>getclasses</strong>(self):
441         """Return a list of the names of all existing classes."""
442
443     def <strong>getclass</strong>(self, classname):
444         """Get the Class object representing a particular class.
445
446         If 'classname' is not a valid class name, a KeyError is raised.
447         """
448
449 class <strong>Class</strong>:
450     """The handle to a particular class of items in a hyperdatabase."""
451
452     def <strong>__init__</strong>(self, db, classname, **properties):
453         """Create a new class with a given name and property specification.
454
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         """
459
460     # Editing items:
461
462     def <strong>create</strong>(self, **propvalues):
463         """Create a new item of this class and return its id.
464
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         """
474
475     def <strong>get</strong>(self, itemid, propname):
476         """Get the value of a property on an existing item of this class.
477
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         """
482
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         """
495
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         """
504
505     def <strong>history</strong>(self, itemid):
506         """Retrieve the journal of edits on a particular item.
507
508         'itemid' must be the id of an existing item of this class or an
509         IndexError is raised.
510
511         The returned list contains tuples of the form
512
513             (date, tag, action, params)
514
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:
518
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         """
523
524     # Locating items:
525
526     def <strong>setkey</strong>(self, propname):
527         """Select a String property of this class to be the key property.
528
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         """
533
534     def <strong>getkey</strong>(self):
535         """Return the name of the key property for this class or None."""
536
537     def <strong>lookup</strong>(self, keyvalue):
538         """Locate a particular item by its key property and return its id.
539
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         """
545
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         """
555
556     def <strong>list</strong>(self):
557         """Return a list of the ids of the active items in this class."""
558
559     def <strong>count</strong>(self):
560         """Get the number of items in this class.
561
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         """
566
567     # Manipulating properties:
568
569     def <strong>getprops</strong>(self):
570         """Return a dictionary mapping property names to property objects."""
571
572     def <strong>addprop</strong>(self, **properties):
573         """Add properties to this class.
574
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>
580
581 <h3>3.6. Application Example</h3>
582
583 <p>Here is an example of how the hyperdatabase module would work in practice.
584
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>
650
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.
658
659 <p><hr>
660 <h2>4. Roundup Database</h2>
661
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.
668
669 <h3>4.1. Reserved Classes</h3>
670
671 <p>Internal to this layer we reserve three special classes
672 of items that are not items.
673
674 <h4>4.1.1. Users</h4>
675
676 <p>Users are stored in the hyperdatabase as items of
677 class "user".  The "user" class has the definition:
678
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>
684
685 <h4>4.1.2. Messages</h4>
686
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:
696
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>
704
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.
710
711 <h4>4.1.3. Files</h4>
712
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:
718
719 <blockquote><pre><small
720 >hyperdb.Class(db, "file", user=hyperdb.Link("user"),
721                           name=hyperdb.String(),
722                           type=hyperdb.String())</small></pre></blockquote>
723
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.
727
728 <h3>4.2. Item Classes</h3>
729
730 <p>All items have the following standard properties:
731
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>
738
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).
748
749 <h3>4.3. Interface Specification</h3>
750
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.
754
755 <blockquote><pre><small
756 >class <strong>Database</strong>:
757     # Overridden methods:
758
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."""
762
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."""
766
767     # New methods:
768
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."""
772
773 class <strong>Class</strong>:
774     # Overridden methods:
775
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         """
782
783     # New methods:
784
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)."""
788
789 class <strong>ItemClass</strong>(Class):
790     # Overridden methods:
791
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."""
797
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."""
802
803     # New methods:
804
805     def <strong>addmessage</strong>(self, itemid, summary, text):
806         """Add a message to an item's mail spool.
807
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         """
815
816     def <strong>nosymessage</strong>(self, itemid, msgid):
817         """Send a message to the members of an item's nosy list.
818
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>
824
825 <h3>4.4. Default Schema</h3>
826
827 <p>The default schema included with Roundup turns it into a
828 typical software bug tracker.  The database is set up like this:
829
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")
838
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")
849
850 Class(db, "keyword", name=hyperdb.String())
851
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>
857
858 <p>(The "order" property hasn't been explained yet.  It
859 gets used by the Web user interface for sorting.)
860
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:
865
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>
873
874 <p><hr>
875 <h2>5. Detector Interface</h2>
876
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.
884
885 <p>There are two kinds of detectors:
886
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>
891
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.
898
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.
903
904 <h3>5.1. Interface Specification</h3>
905
906 <p>The <tt>audit()</tt> and <tt>react()</tt> methods
907 register detectors on a given class of items.
908
909 <blockquote><pre><small
910 >class Class:
911     def <strong>audit</strong>(self, event, detector):
912         """Register an auditor on this class.
913
914         'event' should be one of "create", "set", or "retire".
915         'detector' should be a function accepting four arguments.
916         """
917
918     def <strong>react</strong>(self, event, detector):
919         """Register a reactor on this class.
920
921         'event' should be one of "create", "set", or "retire".
922         'detector' should be a function accepting four arguments.
923         """
924 </small></pre></blockquote>
925
926 <p>Auditors are called with the arguments:
927
928 <blockquote><pre><small
929 >audit(db, cl, itemid, newdata)</small></pre></blockquote>
930
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.
934
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.
939
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.
943
944 For a <tt>retire()</tt> operation, <tt>newdata</tt> is <tt>None</tt>.
945
946 <p>Reactors are called with the arguments:
947
948 <blockquote><pre><small
949 >react(db, cl, itemid, olddata)</small></pre></blockquote>
950
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.
954
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.
958
959 For a <tt>set()</tt> operation, <tt>olddata</tt>
960 contains the names and previous values of properties that were changed.
961
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>.
964
965 <h3>5.2. Detector Example</h3>
966
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.
971
972 <blockquote><pre><small
973 ># Permit users only to add themselves to the "approvals" list.
974
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."
990
991 # When three people have approved a project, change its
992 # status from "pending" to "approved".
993
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"))
998
999 def init(db):
1000     db.project.audit("set", check_approval)
1001     db.project.react("set", approve_project)</small
1002 ></pre></blockquote>    
1003
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".
1010
1011 <blockquote><pre><small
1012 ># Only accept attempts to create new patches that come with patch files.
1013
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."
1021
1022 # When the status is changed from "approved" to "applied", apply the patch.
1023
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...
1028
1029 def init(db):
1030     db.patch.audit("create", check_new_patch)
1031     db.patch.react("set", apply_patch)</small
1032 ></pre></blockquote>
1033
1034 <p><hr>
1035 <h2>6. Command Interface</h2>
1036
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.)
1041
1042 <h3>6.1. Interface Specification</h3>
1043
1044 <p>A single command, <tt>roundup</tt>, provides basic access to
1045 the hyperdatabase from the command line.
1046
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>
1057
1058 <p>Property values are represented as strings in command arguments
1059 and in the printed results:
1060
1061 <ul>
1062 <li>Strings are, well, strings.
1063
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.
1067
1068 <li>Link values are printed as item designators.  When given as
1069 an argument, item designators and key strings are both accepted.
1070
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>
1076
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.
1081
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.
1085
1086 <h3>6.2. Usage Example</h3>
1087
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:
1092
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>
1102
1103 <p>Or, using the <tt>-list</tt> option, this can be written as a single command:
1104
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>
1116
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).
1121
1122 <h3>7.1. Message Processing</h3>
1123
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.
1134
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.
1142
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.
1157
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).
1163
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.
1168
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.
1173
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.
1180
1181 <h3>7.2. Nosy Lists</h3>
1182
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.
1192
1193 <h3>7.3. Setting Properties</h3>
1194
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.
1200
1201 <p><hr>
1202 <h2>8. Web User Interface</h2>
1203
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.
1209
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.
1214
1215 <h3>8.1. Views and View Specifiers</h3>
1216
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.
1222
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.
1231
1232 <h3>8.2. Displaying Properties</h3>
1233
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.
1240
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.
1245
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
1251
1252 <blockquote><pre><small
1253 >    &lt;display call="plain('status', max=30)"&gt;
1254 </small></pre></blockquote>
1255
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>
1261
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.
1266
1267 <p>Some of the standard displayer functions include:
1268
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)
1275
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
1279
1280 <li><strong>menu</strong>: for a Link property, display
1281 a menu of the available choices
1282
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
1286
1287 <li><strong>count</strong>: for a Multilink property, display
1288 a count of the number of links in the list
1289
1290 <li><strong>reldate</strong>: display a Date property in terms
1291 of an interval relative to the current date (e.g. "+ 3w", "- 2d").
1292
1293 <li><strong>download</strong>: show a Link("file") or Multilink("file")
1294 property using links that allow you to download files
1295
1296 <li><strong>checklist</strong>: for a Link or Multilink property,
1297 display checkboxes for the available choices to permit filtering
1298 </ul>
1299
1300 <h3>8.3. Index Views</h3>
1301
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.
1307
1308 <h4>8.3.1. Index View Specifiers</h4>
1309
1310 <p>An index view specifier looks like this (whitespace
1311 has been added for clarity):
1312
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>
1321
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.
1330
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.
1335
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.
1341
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.
1353
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.
1359
1360 <h4>8.3.2. Filter Section</h4>
1361
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.
1367
1368 <p>Here's a simple example of a filter template.
1369
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>
1382
1383 <h4>8.3.3. Index Section</h4>
1384
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.
1392
1393 <p>Here's a simple example of an index template.
1394
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>
1407
1408 <h4>8.3.4. Sorting</h4>
1409
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.
1416
1417 <h3>8.4. Item Views</h3>
1418
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.
1422
1423 <h4>8.4.1. Item View Specifiers</h4>
1424
1425 <p>An item view specifier is simply the item's designator:
1426
1427 <blockquote><pre><small
1428 >/patch23
1429 </small></pre></blockquote>
1430
1431 <h4>8.4.2. Editor Section</h4>
1432
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.
1436
1437 <p>Here's an example of a basic editor template.
1438
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>
1469
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.
1473
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:
1479
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>
1487
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).
1492
1493 <h4>8.4.3. Spool Section</h4>
1494
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.
1499
1500 <p><hr>
1501 <h2>9. Deployment Scenarios</h2>
1502
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.
1510
1511 <p><hr>
1512 <h2>10. Acknowledgements</h2>
1513
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>
1523
1524 <p>
1525
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>
1542
1543 <p><hr>
1544 <center>Last modified 2001/04/06 11:50:59.9063 US/Mountain</center>
1545 </BODY>
1546 </HTML>