===========================
SCHOOLTOOL DEVELOPER MANUAL
===========================

This document describes Schooltool milestone 8.


1. Application Architecture
===========================

Schooltool is a client-server application.  The server and client use
HTTP to communicate and follow the REST architectural style.  The
Relax NG schemas of the XML documents the client and server use to
communicate are in the src/schooltool/schema directory.

There are several clients:

   * a GUI client using the wxPython library (deprecated)
   * a scriptable command line client for debugging and functional
     testing
   * an auxiliary client to import initial data from CSV files.


2. Server Architecture
======================

The server was designed for high flexibility and configurability by
use of such concepts as facets, relationships, and events.

The overview of these APIs can be found in the schooltool.interfaces
module.

Most important global helper functions (including pseudo-adapters) can
be found in the schooltool.component module.


Application objects
-------------------

There are several kinds of first-class objects (called application
objects) in SchoolTool: persons, groups, and resources.  In addition,
there are notes and residences.

Application objects live in application object containers, which serve
as factories and containers for application objects.  When initializing
the application, application object containers for the first-class
objects are created: 'persons', 'groups', 'resources', 'residences' and
'notes'.

All people, including pupils, teachers, and administrators, are
organized into a tree of groups.


Paths
-----

All objects that need to be traversable through the ReSTive or the web
interface need to have a path.  That is, they need to implement the
ILocation interface (a part of the Zope3 location architecture).  That
interface specifies two attributes: a __name__ and a __parent__.  The
parent must be an ILocation object.


Views
-----

Most objects have auxiliary classes called views.  Views are responsible
for representation of the corresponding objects, and parsing data provided
by the user and applying changes to the objects.

There are currently two types of views: ReSTive (see package
schooltool.rest) and browser views (see package schooltool.browser).  The
ReSTive views are responsible for publishing and parsing XML
representations of objects.  Browser views deal with web browsers -- they
usually render page templates to present data to the user and handle POST
requests to parse data provided in web page forms.

Usually the view classes take an instance of an object published (called
context) as the first argument of the constructor.

Views are Resources in terms of twisted.web.

The browser view hierarchy is explicitly created by View._traverse()
methods.  When a request to render an object is sent, a browser view for
each component in the given path is invoked, and that view's
_traverse(key) method is called with the name of the child object.  This
is repeated until the view for the last component is retrieved, whose
do_GET or do_POST is invoked.

On the other hand, ReSTive views are also use the view registry which is a
mapping from interfaces to view classes (e.g., IResource -> ResourceView).
See schooltool.interfaces.component.IViewAPI for more information.


Facets
------

The groups and persons can exhibit certain polymorphic behavior.  For
instance, the same person might be a pupil and later become a teacher.
Or, more commonly, a teacher might periodically become an administrator.

There might be some data pertinent to a teacher, which is not relevant
when a person is not a teacher.  In order to accommodate these cases,
the concept of a facet is introduced.

A facet is a persistent auxiliary object that is stored in the
__facets__ attribute of an object and implies certain semantics.

The facets are accessed using the schooltool.component.FacetManager
adapter.  Facets must implement the schooltool.interfaces.IFacet
interface.

The facets which are registered to be available for addition to any
application object through the RESTive API are called Free Floating
Facets.  For example, the schooltool.teaching.TeacherGroupFacet is a
Free Floating Facet, but the facet that the Teacher group adds to all
its members (the schooltool.teaching.TeacherFacet) is not; same with
schooltool.absence.AbsenceTrackerFacet.


URIs
----

Relationships and roles are uniquely identified by URIs represented in the
application as objects providing schooltool.interfaces.IURIObject.  They
are represented in XML data as simple URIs.  There is a global registry
that lets us get a corresponding URI object (that has a friendly name)
from a URI string.

As a convention, the names of URIs have the prefix 'URI':  URIMembership,
URITeaching, etc.

A set of "standard" URIs is defined in the schooltool.uris module.


Relationships and valencies
---------------------------

There is infrastructure for persistently storing relationships
between objects.  Relationships are stored in the __links__
attribute of objects, but are accessed using the API defined in
schooltool.interface.relationship.

The relationships are implemented as three auxiliary objects:

  +---+    +---------+   +---------------+    +--------+   +---+
  | A |----| Link A  |---| Relationship  |----| Link B |---| B |
  +---+    +---------+   +---------------+    +--------+   +---+

The link objects implement the public schooltool.interfaces.ILink API,
and the Relationship object always remains behind the scenes.

To facilitate the creation of these complex structures, several levels
of helpers are provided.  The relationships are normally created by
invoking a relationship schema, and passing the two related objects as
arguments.  For example::

    from schooltool.membership import Membership
    Membership(group=group1, member=person1)

Relationship types are identified by URIs.  Also, both parties in a
relationship have roles, identified by URIs.  In the case of membership,
these are:

    from schooltool.uris import URIMembership, URIMember, URIGroup

Objects that can participate with a certain role in a certain
relationship can express that fact using hints called valencies.
For example, the schooltool.model.Group objects have a valency to
participate in Membership as groups.  This makes it possible to post
to the relationships view of a group and create a membership
relationship to another group, person, or resource from it.

Only relationships advertised as valencies can be created through the
RESTive interface.


Events
------

There is infrastructure for events.

The main idea is that an event is injected where it was originated, is
being resent to related objects according to the event routing tables
on objects, and keeps a list of objects it has visited so it does not
get sent to the same object twice.

Events are not persistent.

The logic of event propagation is shared among the events and the event
targets.  Events need to subclass EventMixin, and event targets need to
subclass EventTargetMixin.  The latter implements the IEventConfigurable
interface.  Event targets need to provide an event table, which is a
sequence of IEventActions.   There are several EventActions defined that
take care of further event routing, provide a way to call some method,
etc.

The application objects subclass the
schooltool.facet.FacetedEventTargetMixin, which generates the event table
of an object by summing up the event tables provided by the object itself
and all of its facets.  This way, facets can handle certain kinds of
events, or define behaviour for certain kinds of events.

All the dispatched events reach the global EventService.  It provides
an API to subscribe to certain kinds of events.

Events are also propagated into the Zope3 event framework.  You may
register event handlers by using Zope3 adapters, see schooltool.booking
for an example.

Currently, events are used for logging modifications of relationships
and absence tracking.



3. Domain specific functionality
================================


Attendance tracking
-------------------

All persons can be tracked for attendance.  Usually it is done by running
roll calls on groups.  When some person is not present, an absence is
created on that person.  An absence has several attributes, such as the
"resolved" status (whether an excuse has been presented), the expected
presence date and others (see schooltool.interfaces.app.IAbsence).  The
absence may contain several AbsenceComments.  There can be only one
unended absence for one person, so if a person is absent from several
consecutive roll calls, several comments get added to the same Absence
object.

When the absence is registered (an AbsenceComment is added to the
Absence), an AbsenceEvent is sent to the system.  This event propagates up
the membership hierarchy and is registered in all the AbsenceTrackers
along the way.  Finally, it arrives at the global AbsenceTrackerUtility,
which can be used to track all absences happening in the school.

Absence trackers can be added to the groups by adding the free floating
AbsenceTrackerFacet.


Timetabling
-----------

Every group or person can have a number of timetables.  First, the
timetables can vary in the timetable schema.  For instance, in a
school with a 4-day rotating timetable there might be some events
recurring weekly.  Second, there are separate timetables for different
time periods, such as semesters.

Every group or person can have a number of own timetables and a number
of composite timetables.  Own timetables consist of events private to a
group or person, and composite timetables consist of all events related
to a group or person.  For example, a composite timetable for a teacher
will contain all the lessons they are teaching, and a composite
timetable of a pupil will contain all the events from the private
timetables of the groups the're in, in other words -- all the lessons
they need to attend.

Objects that have a timetable should implement ITimetabled.  All of
the application objects are timetabled.

The model used for timetabling is that each set of pupils meeting
regularly with the same teacher for the same subject should be
represented by a separate group.  A group can have at most one
teacher.  So, if the same group of pupils go to Math, English, and
History, there will have to be three separate groups containing the
same pupils.

This model was chosen so that the composite timetables for teachers can
be derived by traversing their membership and teaching relationships,
and the composite timetables for groups and pupils can be derived by
traversing their membership relationship.

It should be noted that when an activity which uses some resource is
posted to a group's timetable, the same activity gets added to the
resource's timetable.

The timetabling code is located in schooltool.timetable module.  A
Timetable is conceptually a mapping of day ids to TimetableDays.
The ordered sequence of day ids can be found in the attribute day_ids.

TimetableDay is a mapping between period ids and sets of
TimetableActivities.  Usually these sets contain zero or one
TimetableActivities, but more activities happening at the same time are
also supported by this model.

TimetableActivities are immutable objects that have a title, the
owner, a reference to the timetable they are in, and an ImmutableSet of
resources.

Therefore, a Timetable is just a table in which each cell has a day id, a
period id and some activities associated with them.

Now, converting a Timetable to a Calendar involves several auxiliary
objects which keep different parts of the data needed for the process.

SchooldayModel is a calendar for a range of dates which can tell
whether each date within the range is a schoolday or not.  A
SchooldayTemplate is conceptually a mapping between period ids and
(start time, duration) of events.

A TimetableModel puts it all together. It knows the algorithm of mapping
dates to day ids and has a dictionary of days of week to
SchooldayTemplates.  TimetableModel.createCalendar, given a schoolday
model and a timetable, can generate a calendar.


Calendaring
-----------

Persons, groups and resources (and any other objects that implement
ICalendarOwner) have a private calendar which can be edited and a
composite calendar, which is composed from other calendars.  Composite
calendars are driven by the URICalendarSubscription relationship - that
is, an object's composite calendar consists of objects who are related to
it as URICalendarProviders.

Objects that implement ITimetabled also have a read-only composite
timetable calendar which is formed by composing the object's timetable
and all its parent groups' composite timetables.

The personal calendar has an iCalendar view, that can present the calendar
in the iCalendar format (RFC 2445) for third-party clients, and it can
parse calendars submitted by those clients and update the calendar.  A
read-only iCalendar view for the composite timetable calendar is also
available.

When a person books a resource, the same booking event is added to the
private calendars of that person and the resource.  The owner and context
attributes of the CalendarEvent then are, respectively, the person and the
resource.

The core calendaring code is located in the schooltool.cal module.  A
Calendar has a set of CalendarEvents and can be either iterated over or
queried for events that happened on a certain date.  A calendar may also
be merged with another calendar.

There are several subclasses of calendar event (TimetableCalendarEvent,
ExceptionalTTCalendarEvent, ExpandedCalendarEvent and
InheritedCalendarEvent), but they are for mostly internal use.  See their
docstrings for more information.

If you need a functionally defined calendar for internal use, you should
use ImmutableCalendar, so that you do not accidentally try to modify a
derived calendar instead of the original one, in which case the changes
would be lost.
