diff --git a/doc/design.txt b/doc/design.txt
index 8856d37f532804838e10e7f33b0d95c414224244..0748fa29183de290805d49dcb868ba150f0cd292 100644 (file)
--- a/doc/design.txt
+++ b/doc/design.txt
Roundup - An Issue-Tracking System for Knowledge Workers
========================================================
-:Authors: Ka-Ping Yee (original__), Richard Jones (implementation)
-
-__ spec.html
+:Authors: Ka-Ping Yee (original), Richard Jones (implementation)
.. contents::
This document presents a description of the components of the Roundup
system and specifies their interfaces and behaviour in sufficient detail
to guide an implementation. For the philosophy and rationale behind the
-Roundup design, see the first-round Software Carpentry submission for
-Roundup. This document fleshes out that design as well as specifying
+Roundup design, see the first-round Software Carpentry `submission for
+Roundup`__. This document fleshes out that design as well as specifying
interfaces so that the components can be developed separately.
+__ spec.html
+
The Layer Cake
-----------------
-------------
The lowest-level component to be implemented is the hyperdatabase. The
-hyperdatabase is intended to be a flexible data store that can hold
-configurable data in records which we call items.
+hyperdatabase is a flexible data store that can hold configurable data
+in records which we call items.
The hyperdatabase is implemented on top of the storage layer, an
-external module for storing its data. The storage layer could be a
-third-party RDBMS; for a "batteries-included" distribution, implementing
-the hyperdatabase on the standard bsddb module is suggested.
+external module for storing its data. The "batteries-includes" distribution
+implements the hyperdatabase on the standard anydbm module. The storage
+layer could be a third-party RDBMS; for a low-maintenance solution,
+implementing the hyperdatabase on the SQLite RDBMS is suggested.
+
Dates and Date Arithmetic
~~~~~~~~~~~~~~~~~~~~~~~~~
id is returned; otherwise a KeyError is raised.
"""
- def find(self, propname, itemid):
+ def find(self, **propspec):
"""Get the ids of items in this class which link to the
given items.
- 'propspec' consists of keyword args propname={itemid:1,}
- 'propname' must be the name of a property in this class, or
- a KeyError is raised. That property must be a Link or
- Multilink property, or a TypeError is raised.
+ 'propspec' consists of keyword args propname=itemid or
+ propname={<itemid 1>:1, <itemid 2>: 1, ...}
+ 'propname' must be the name of a property in this class,
+ or a KeyError is raised. That property must
+ be a Link or Multilink property, or a TypeError
+ is raised.
Any item in this class whose 'propname' property links to
- any of the itemids will be returned. Used by the full text
- indexing, which knows that "foo" occurs in msg1, msg3 and
- file7, so we have hits on these issues:
+ any of the itemids will be returned. Examples::
+ db.issue.find(messages='1')
db.issue.find(messages={'1':1,'3':1}, files={'7':1})
"""
def filter(self, search_matches, filterspec, sort, group):
- """ Return a list of the ids of the active items in this
- class that match the 'filter' spec, sorted by the group spec
- and then the sort spec.
+ """Return a list of the ids of the active nodes in this class that
+ match the 'filter' spec, sorted by the group spec and then the
+ sort spec.
+
+ "search_matches" is a container type
+
+ "filterspec" is {propname: value(s)}
+
+ "sort" and "group" are [(dir, prop), ...] where dir is '+', '-'
+ or None and prop is a prop name or None. Note that for
+ backward-compatibility reasons a single (dir, prop) tuple is
+ also allowed.
+
+ The filter must match all properties specificed. If the property
+ value to match is a list:
+
+ 1. String properties must match all elements in the list, and
+ 2. Other properties must match any of the elements in the list.
+
+ The propname in filterspec and prop in a sort/group spec may be
+ transitive, i.e., it may contain properties of the form
+ link.link.link.name, e.g. you can search for all issues where
+ a message was added by a certain user in the last week with a
+ filterspec of
+ {'messages.author' : '42', 'messages.creation' : '.-1w;'}
"""
def list(self):
4
>>> db.issue.create(title="abuse", status=1)
5
- >>> hyperdb.Class(db, "user", username=hyperdb.Key(),
+ >>> hyperdb.Class(db, "user", username=hyperdb.String(),
... password=hyperdb.String())
<hyperdb.Class "user">
>>> db.issue.addprop(fixer=hyperdb.Link("user"))
properties or "actor" cause a KeyError.
"""
- # New methods:
-
- def audit(self, event, detector):
- def react(self, event, detector):
- """Register a detector (see below for more details)."""
-
class IssueClass(Class):
# Overridden methods:
Class(db, "keyword", name=hyperdb.String())
Class(db, "issue", fixer=hyperdb.Multilink("user"),
- topic=hyperdb.Multilink("keyword"),
+ keyword=hyperdb.Multilink("keyword"),
priority=hyperdb.Link("priority"),
status=hyperdb.Link("status"))
class of items::
class Class:
- def audit(self, event, detector):
+ def audit(self, event, detector, priority=100):
"""Register an auditor on this class.
'event' should be one of "create", "set", "retire", or
"restore". 'detector' should be a function accepting four
- arguments.
+ arguments. Detectors are called in priority order, execution
+ order is undefined for detectors with the same priority.
"""
- def react(self, event, detector):
+ def react(self, event, detector, priority=100):
"""Register a reactor on this class.
'event' should be one of "create", "set", "retire", or
"restore". 'detector' should be a function accepting four
- arguments.
+ arguments. Detectors are called in priority order, execution
+ order is undefined for detectors with the same priority.
"""
Auditors are called with the arguments::
Command Interface Specification
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-A single command, roundup, provides basic access to the hyperdatabase
+A single command, ``roundup-admin``, provides basic access to the hyperdatabase
from the command line::
roundup-admin help
are both accepted; an empty string, a single item, or a list of items
joined by commas is accepted.
-When multiple items are specified to the roundup get or roundup set
+When multiple items are specified to the roundup-admin get or roundup-admin set
commands, the specified properties are retrieved or set on all the
listed items.
-When multiple results are returned by the roundup get or roundup find
+When multiple results are returned by the roundup-admin get or
+roundup-admin find
commands, they are printed one per line (default) or joined by commas
(with the -list) option.
"spam", for example, you could execute the following command from the
directory where the database dumps its files::
- shell% for issue in `roundup find issue status=in-progress`; do
- > grep -l spam `roundup get $issue messages`
+ shell% for issue in `roundup-admin find issue status=in-progress`; do
+ > grep -l spam `roundup-admin get $issue messages`
> done
msg23
msg49
Or, using the -list option, this can be written as a single command::
- shell% grep -l spam `roundup get \
- \`roundup find -list issue status=in-progress\` messages`
+ shell% grep -l spam `roundup-admin get \
+ \`roundup-admin find -list issue status=in-progress\` messages`
msg23
msg49
msg50
The e-mail interface also provides a simple way to set properties on
issues. At the end of the subject line, ``propname=value`` pairs can be
specified in square brackets, using the same conventions as for the
-roundup ``set`` shell command.
+roundup-admin ``set`` shell command.
Web User Interface
clarity)::
/issue?status=unread,in-progress,resolved&
- topic=security,ui&
- :group=priority&
+ keyword=security,ui&
+ :group=priority,-status&
:sort=-activity&
- :filters=status,topic&
+ :filters=status,keyword&
:columns=title,status,fixer
The example specifies an index of "issue" items. Only issues with a
"status" of either "unread" or "in-progres" or "resolved" are displayed,
-and only issues with "topic" values including both "security" and "ui"
-are displayed. The issues are grouped by priority, arranged in
-ascending order; and within groups, sorted by activity, arranged in
-descending order. The filter section shows filters for the "status" and
-"topic" properties, and the table includes columns for the "title",
-"status", and "fixer" properties.
+and only issues with "keyword" values including both "security" and "ui"
+are displayed. The items are grouped by priority arranged in ascending
+order and in descending order by status; and within groups, sorted by
+activity, arranged in descending order. The filter section shows
+filters for the "status" and "keyword" properties, and the table includes
+columns for the "title", "status", and "fixer" properties.
Associated with each issue class is a default layout specifier. The
layout specifier in the above example is the default layout to be
multiple Permissions to Roles. These definitions are not persistent -
they're defined when the application initialises.
-There will be two levels of Permission. The Class level permissions
+There will be three levels of Permission. The Class level permissions
define logical permissions associated with all items of a particular
class (or all classes). The Item level permissions define logical
permissions associated with specific items by way of their user-linked
-properties.
+properties. The Property level permissions define logical permissions
+associated with a specific property of an item.
Access Control Interface Specification
- name
- description
- klass (optional)
+ - properties (optional)
+ - check function (optional)
The klass may be unset, indicating that this permission is
not locked to a particular hyperdb class. There may be
multiple Permissions for the same name for different
classes.
+
+ If property names are set, permission is restricted to those
+ properties only.
+
+ If check function is set, permission is granted only when
+ the function returns value interpreted as boolean true.
+ The function is called with arguments db, userid, itemid.
'''
class Role:
the base roles (for admin user).
'''
- def getPermission(self, permission, classname=None):
- ''' Find the Permission matching the name and for the class,
- if the classname is specified.
+ def getPermission(self, permission, classname=None, properties=None,
+ check=None):
+ ''' Find the Permission exactly matching the name, class,
+ properties list and check function.
Raise ValueError if there is no exact match.
'''
- def hasPermission(self, permission, userid, classname=None):
+ def hasPermission(self, permission, userid, classname=None,
+ property=None, itemid=None):
''' Look through all the Roles, and hence Permissions, and
- see if "permission" is there for the specified
- classname.
- '''
+ see if "permission" exists given the constraints of
+ classname, property and itemid.
- def hasItemPermission(self, classname, itemid, **propspec):
- ''' Check the named properties of the given item to see if
- the userid appears in them. If it does, then the user is
- granted this permission check.
+ If classname is specified (and only classname) then the
+ search will match if there is *any* Permission for that
+ classname, even if the Permission has additional
+ constraints.
- 'propspec' consists of a set of properties and values
- that must be present on the given item for access to be
- granted.
+ If property is specified, the Permission matched must have
+ either no properties listed or the property must appear in
+ the list.
- If a property is a Link, the value must match the
- property value. If a property is a Multilink, the value
- must appear in the Multilink list.
+ If itemid is specified, the Permission matched must have
+ either no check function defined or the check function,
+ when invoked, must return a True value.
+
+ Note that this functionality is actually implemented by the
+ Permission.test() method.
'''
def addPermission(self, **propspec):
''' Create a new Permission with the properties defined in
- 'propspec'
+ 'propspec'. See the Permission class for the possible
+ keyword args.
'''
def addRole(self, **propspec):
- Access Controls
- Added "actor" property
-------------------
-
-Back to `Table of Contents`_
-
-.. _`Table of Contents`: index.html
.. _customisation: customizing.html