[open-ils-commits] r849 - servres/branches/2010-02-campus-integration-reorg/conifer/integration (gfawcett)

svn at svn.open-ils.org svn at svn.open-ils.org
Fri Mar 26 23:32:18 EDT 2010


Author: gfawcett
Date: 2010-03-26 23:32:15 -0400 (Fri, 26 Mar 2010)
New Revision: 849

Added:
   servres/branches/2010-02-campus-integration-reorg/conifer/integration/.header.tex
Modified:
   servres/branches/2010-02-campus-integration-reorg/conifer/integration/campus-interface.md
Log:
more work on the campus-information interface spec.

Added: servres/branches/2010-02-campus-integration-reorg/conifer/integration/.header.tex
===================================================================
--- servres/branches/2010-02-campus-integration-reorg/conifer/integration/.header.tex	                        (rev 0)
+++ servres/branches/2010-02-campus-integration-reorg/conifer/integration/.header.tex	2010-03-27 03:32:15 UTC (rev 849)
@@ -0,0 +1,37 @@
+%% pandoc -s -N -t latex --template .header.tex < campus-interface.md > /tmp/campus.tex
+\documentclass[english]{article}
+\usepackage{charter}
+\usepackage[T1]{fontenc}
+\usepackage[latin9]{inputenc}
+\usepackage[letterpaper]{geometry}
+\geometry{verbose,tmargin=3cm,bmargin=3cm,lmargin=3cm,rmargin=3cm}
+\makeatletter
+\newcommand{\href}[2]{\textsc{#2}}
+\makeatother
+\usepackage{babel}
+
+$if(title)$
+\title{$title$}
+$endif$
+$if(author)$
+\author{$for(author)$$author$$sep$\\$endfor$}
+$endif$
+$if(date)$
+\date{$date$}
+$endif$
+
+\begin{document}
+$if(title)$
+\maketitle
+$endif$
+
+$for(include-before)$
+$include-before$
+
+$endfor$
+$if(toc)$
+\tableofcontents
+
+$endif$
+$body$
+\end{document}

Modified: servres/branches/2010-02-campus-integration-reorg/conifer/integration/campus-interface.md
===================================================================
--- servres/branches/2010-02-campus-integration-reorg/conifer/integration/campus-interface.md	2010-03-26 01:12:18 UTC (rev 848)
+++ servres/branches/2010-02-campus-integration-reorg/conifer/integration/campus-interface.md	2010-03-27 03:32:15 UTC (rev 849)
@@ -1,89 +1,118 @@
-<!-- -*- mode: markdown; indent-tabs-mode: nil; -*- -->
+% DRAFT: Interface specification for an OpenSRF campus-information service
+% Graham Fawcett
+% March 26, 2010
 
-# Course Information Service Interface
+# Introduction
 
-This is an early, early draft.
+This document specifies the interface for an OpenSRF-based service
+which gives OpenSRF applications access to *campus information*, such
+as the names of courses taught at a given university, who is teaching
+them, and which students are enrolled in them.
 
-## Introduction
+This service is designed to meet the needs of our reserves
+application, "Syrup." We hope the service will be useful in a wide
+range of library applications that could benefit from access to course-related
+information.
 
-This document specifies the interface for an OpenSRF service which
-gives access to course-related information, such as the names of
-courses taught at a given university, who is teaching them, and which
-students are enrolled in them. 
+This document specifies the OpenSRF interface of the campus
+information service, but it does not dictate how the service must be
+implemented. Each institution will need to implement the service,
+using the tools of their choice, in a way that makes local sense.
 
-This service is designed to meet the needs of a certain reserves
-application (Syrup). More broadly, we hope to design a service that
-will be useful in a wide range of applications that need to integrate
-library information with course-related information.
+# Design considerations
 
-We will specify the OpenSRF interface of the course-information
-service, without constraining how the service is implemented. Each
-institution will need to implement the service in a way that makes
-local sense.
+## Partial implementations
 
-## Design considerations
-   
-### Partial implementations
+In an ideal world, a library would have unlimited access to all the
+course-related information they wanted; but many libraries do not
+enjoy such access. Not all applications need the same types of
+information, and many applications can adapt to different levels of
+available campus information. Given this, it is acceptable to
+*partially* implement the campus-information service, skipping the
+parts that you cannot (or choose not to) implement.
 
-*write me.* Explain why partial implementations might be all you
-get. How to test the interface to see what it implements. See
-[Static informational methods].
+For example, if you don't have access to any class-list information,
+but you do have a machine-readable version of the academic calendar,
+you could implement the course-lookup and term-lookup parts of the
+interface, but skip the course-offering parts.
 
+An application must be able to determine what parts of the interface
+you have implemented. Therefore, you must implement the
+`methods-supported` method (see [Static informational methods]). Since
+this method-list is essentially static (it will change only if you
+modify your implementation), an application may test it infrequently,
+e.g. just once upon startup.
 
-### Caching
+## Caching
 
-*write me.* Explain that caching should be done in OpenSRF
-fashion. Applications should avoid caching themselves, and let the
-course-info service decide on how quickly course-info gets stale.
+OpenSRF provides a high-performance caching framework. You should
+consider using this framework when designing your
+implementation. 
 
-## Data types
+Applications are discouraged from caching campus information:
+especially information on people and course offerings, which both
+change relatively frequently. It makes more sense to centralize policy
+decisions about the lifespans of campus data at the service layer.  If
+applications must cache campus information (e.g. for demonstrated
+performance reasons), they are encouraged to keep the cache-lifetimes
+as short as possible.
 
-We specify all of the data types that should appear in a complete
-implementation of the interface. Since you are free to implement only
-parts of the interface (see [Partial implementations]), all of these
-data types might not apply in your case.
+# Data types
 
-### Identifier types
+All of these data types are needed in a complete implementation of the
+interface. Since you are free to implement only parts of the interface
+(see [Partial implementations]), all of these data types might not
+apply in your case.
 
+## Identifier types
+
     COURSE-ID   = string        (matching a local COURSE-ID-FORMAT)
     TERM-ID     = string        (matching a local TERM-ID-FORMAT)
     OFFERING-ID = string        (matching a local OFFERING-ID-FORMAT)
     PERSON-ID   = string        (matching a local PERSON-ID-FORMAT)
 
-These four identifier types are used respectively as unique keys for
+The four identifier types are used respectively as unique keys for
 courses, terms, course offerings, and people. (`String` is the
 primitive type of strings of Unicode characers.)
 
-People may have several unique identifiers (student numbers, SINs,
-etc.). Since the PERSON-ID may be exposed in reports and user
-interfaces, it should be a common public identifier (such as a
-'single-sign-on ID', 'email prefix', or 'campus username') that can be
-displayed beside the person's name without violating privacy
-regulations.
+Since the PERSON-ID may be exposed in reports and user interfaces, it
+should be a common public identifier (such as a 'single-sign-on ID',
+'email prefix', or 'campus username') that can be displayed beside the
+person's name without violating privacy regulations.
 
-**Section numbers:** Note that while we have an OFFERING-ID, we don't
-mention 'section numbers' at all, even though you might use them at
-your institution (to differentiate multiple instances of a course in
-the same term). Your offering ID `ENG100-2010W-03` might represent
-Section 3 of English 100 being taught in Winter 2010: here, the
-section number is embedded in the offering ID. There is no requirement
-that your offering ID is so structured, and nowhere in the interface
-do we use section numbers. But if you use section numbers at your
-school, consider embedding them in your offering ID (in case your
-end-users have to decode them).
+Your institution may use 'section numbers' to differentiate multiple
+offerings of a course in the same term. You may embed them in your
+identifiers: for example, the offering ID `ENG100-2010W-03` might
+represent Section 3 of English 100 being taught in Winter 2010. But it
+isn't required that your offering IDs are so structured.
 
-**Formats:** Each identifier type adheres to a respective,
+**Formats:** Each type of identifier complies with a respective,
 locally-defined format. You should define a (private, internal)
 function for each format, that verifies whether a given string matches
 the format. For example, a Java implementation might define a
-function, `boolean isValidCourseID(String)`.  Consider using regular
+function, `boolean isValidCourseID(String)`. You might use regular
 expressions to define your formats, but it's not a requirement. At the
-very least your local formats should reject empty strings as IDs.
+very least, your local formats should reject empty strings as IDs. You
+may expose these functions for application use: see
+[Format-matching methods].
 
-### Record types
+## Record types
 
-We model the record types as associative arrays (lists of key/value
-pairs).
+Record types are modelled as associative arrays (sets of key/value
+pairs). \[Is this acceptable in OpenSRF? It's valid JSON, but I'm not clear on OpenSRF conventions.\]
+The following notation is used in the type definitions:
+
+             string            (a string primitive)
+             [string]*         (an unordered set of zero or more strings)
+             (string)?         (an optional string: it may be NULL.)
+
+Strictly, unordered sets *do* have an order, since they are
+implemented as JSON lists. But the specification does not guarantee
+that the order of the list is significant. 
+
+Missing optional values may be indicated in two equivalent ways:
+either include the key, and pair it with a `null` value (`{key: null,
+...}`), or simply omit the key/value pair from the record.
                
     COURSE = { id:    COURSE-ID, 
                title: string }
@@ -102,7 +131,7 @@
 A TERM record describes a typical period in which a course is offered
 (a 'term' or 'semester'). It must have a unique term-ID, a
 probably-unique name, and start and end dates. (`Date` is a primitive
-type, representing a year-month-day date.)
+type, representing a calendar date.)
 
     PERSON = { id:         PERSON-ID, 
                surname:    string, 
@@ -110,10 +139,8 @@
                email:      (string)? }
 
 A PERSON record describes a person! It must include a unique
-person-ID, a surname and given name. A value for `email` is optional
-(the `(string)?` notation indicates a value that may be omitted,
-i.e. it may be `null`).  You may also add other attributes as you see
-fit.
+person-ID, a surname and given name. A value for `email` is
+optional. You may also add other attributes as you see fit.
                  
     OFFERING = { id:            OFFERING-ID,
                  course:        COURSE-ID,
@@ -123,18 +150,23 @@
                  assistants:    [PERSON-ID]*,
                  instructors:   [PERSON-ID]* }
 
-A OFFERING record describes an instance of a course being offered:
-your institution might call this a 'class' or a 'section'.  It has
-specific start and and end dates (derived from its starting and ending
-terms: note that some institutions may have courses that span multiple
-terms). It refers to a single course, of which it is an instance (our
-specification punts on the issue of cross-listed offerings). It has
-sets of zero-or-more students, teaching assistants and instructors.
+An OFFERING record describes a course offering: your institution might
+call this a 'class' or a 'section'.  It has specific start- and and
+end-dates (derived from its starting and ending terms: some
+institutions have courses that span multiple terms). The `course`
+attribute refers to the single course of which it is an instance (our
+specification punts on the issue of cross-listed offerings).  It has
+unordered sets of zero-or-more students, teaching assistants and
+instructors.
 
-A given OFFERING record is a snapshot of a course offering at a given
-time. It is expected that people may join and leave the actual course
-offering at any point during the offering's duration.
+Each OFFERING record is a snapshot of a course offering at a given
+time. It is assumed that people may join or leave the course
+offering at any point during its duration.
 
+The set of "assistants" is loosely defined. It might include teaching
+assistants (TAs and GAs) but also technical assistants, departmental
+support staff, and other ancillary support staff.
+
     OFFERING-FLESHED = { id:            OFFERING-ID,
                          course:        COURSE,
                          starting-term: TERM,
@@ -143,101 +175,164 @@
                          assistants:    [PERSON]*,
                          instructors:   [PERSON]* }
 
-A OFFERING-FLESHED record is like a OFFERING record, except that the
+A OFFERING-FLESHED record is like an OFFERING record, except that the
 course, term, and people-set attributes have been 'fleshed out', so
 that they contain not codes, but actual copies of the COURSE, TERM and
 PERSON records.
 
-## Method signatures
+# Method signatures
 
-### Static informational methods
+The following notation is used for method signatures:
 
-    methods-supported:   void      -> [string]*
+    method-name:   arg1-type, ... argN-type -> result-type
+    
+The `void` type is used to express empty argument-lists.
 
-*write me.* This should tell the client which of the other methods are
-actually implemented (see [Partial implementations]). This one should
-be mandatory.
+## Static informational methods
 
-### Format-matching methods
+    methods-supported:   void      -> [string]*
 
-    resembles-course-id:   string    -> boolean
-    resembles-offering-id: string    -> boolean
-    resembles-term-id:     string    -> boolean
-    resembles-person-id:   string    -> boolean
+The `methods-supported` method is the only method that you *must*
+implement (see [Partial implementations]). It returns a list of the
+names of the methods for which you've provided
+implementations. Applications can use this list to determine the
+capabilities of your implementation.
     
-These can be used to implement data-input validation tests in user
-interfaces, primarily where lookups are not possible.  Applications
-can use them to determine whether a given string falls within the
-general guidelines of what your IDs are supposed to look like. At some
-institutions, this might be the best you can offer: you might not have
-access to databases in which you can look records up (see
-[Partial implementations]), but at least you can offer a means to
-avoid basic typographic errors.
+## Course methods
 
-You could implement these methods by exposing the functions you
-defined for your COURSE-ID-FORMAT, TERM-ID-FORMAT, OFFERING-ID-FORMAT
-and PERSON-ID-FORMAT tests (see [Identifier types]).
-
-You might choose to use implement these as lookup functions, returning
-`True` only if a matching record was found. For example, if your
-school offers only two courses (say `ENG100` and `ENG200`), you could
-choose to implement a `resembles-course-id` method that only returned
-a `true` value if the argument was exactly one of those two course
-codes. But keep in mind that the intent of the `resembles` methods is
-to offer some general guidance, not to restrict vocabularies.
-    
-### Course methods
-
     course-lookup:       COURSE-ID -> (COURSE)?
     course-id-list:      void      -> [COURSE-ID]*
     course-list:         void      -> [COURSE]*
-    course-id-example:   void      -> COURSE-ID
+    course-id-example:   void      -> (COURSE-ID)?
 
-*write me.*
+Given a COURSE-ID string, `course-lookup` will return the matching
+COURSE record, or `null` if no such course exists. 
 
-### Term methods
+The methods `course-id-list` and `course-list` return a list of the
+IDs (or records, respectively) of *all* known courses in the campus
+system. An application might use these to populate option-lists or
+report headings. The lists may be limited to the courses which are
+defined in the current academic calendar (that is, your implementation
+may omit obsolete course descriptions).
 
+The `course-id-example` method returns a course ID *example*. In
+user-interfaces where a course ID must be typed in, this example can
+be used to offer some guidance to the user. If the method returns
+`null`, or if the method is not implemented, an application should
+simply omit any example from the user interface.
+
+## Term methods
+
     term-lookup:         TERM-ID   -> (TERM)?
+    term-list:           void      -> [TERM]*
     terms-at-date:       date      -> [TERM]*
-    term-list:           void      -> [TERM]*
 
-*write me.*
+The `term-lookup` and `term-list` are analogous to the `course-lookup`
+and `course-list` methods. The `terms-at-date` method takes a date
+argument, and returns a list of all TERM records such that `term.start
+<= date <= term.finish`. (We do not specify that terms are
+non-overlapping.)
 
-### Person methods
+## Person methods
 
     person-lookup:       PERSON-ID   -> (PERSON)?
 
-*write me.*
+## Offering methods
 
-### Offering methods
+To describe the return-values of some of the Offering methods, we
+introduce the notation `MBR(X)` as an abbreviation for the type
+`([X]*, [X]*, [X]*)`, that is, a trio of sets representing the three
+membership groups associated with a course offering: teachers,
+assistants, and students. The types of elements contained in the sets
+is specified by the specializing type, `X`: so, `MBR(PERSON)` is a
+trio of sets of PERSON records.
 
+    MBR(TYPE) = ([TYPE]*,  # memberships as a teacher,
+                 [TYPE]*,  # as an assistant,
+                 [TYPE]*)  # as a student.
+
+
     course-term-offerings:         (COURSE-ID, TERM-ID) -> [OFFERING]*
     course-term-offerings-fleshed: (COURSE-ID, TERM-ID) -> [OFFERING-FLESHED]*
 
-                           # fixme, move to Data Types. A triple of lists:
-    MBR(TYPE) = ([TYPE]*,  # memberships as a teacher,
-                 [TYPE]*,  # as an assistant,
-                 [TYPE]*)  # as a student.
-    
+Given a COURSE-ID and a TERM-ID, these methods will return records for
+all course offerings for the course represented by COURSE-ID, whose
+`starting-term` *or* `ending-term` is equal to TERM-ID.
+
     memberships:         PERSON-ID    -> MBR(OFFERING)
     membership-ids:      PERSON-ID    -> MBR(OFFERING-ID)
     memberships-fleshed: PERSON-ID    -> MBR(OFFERING-FLESHED)
+    
+These methods take a PERSON-ID and return a trio of sets whose
+elements represent the course-offerings in which the person is
+(respectively) a teacher, assistant, or student. 
 
+Within a given course-offering, a person must belong to no more than
+one of the three sets. For example, it is not permitted to be both a
+teacher and student for the same offering. 
+
+If the PERSON-ID is invalid, or if the person is not a member of any
+offerings, the return value should be a trio of three empty sets --
+`[[], [], []]` -- *not* a `null` value or an error.
+
     member-ids:          OFFERING-ID -> MBR(PERSON-ID)
     members:             OFFERING-ID -> MBR(PERSON)
+    
+These methods take an OFFERING-ID and return a trio of sets whose
+elements represent (respectively) the teachers, assistants, and
+students in the offering. 
 
-    teacher-ids:         OFFERING-ID -> MBR(PERSON-ID)
-    teachers:            OFFERING-ID -> MBR(PERSON)
+If the OFFERING-ID is invalid, or if the offering is "empty", the
+return value should be a trio of three empty sets -- `[[], [], []]` --
+*not* a `null` value or an error.
 
-*write me.*
+    teacher-ids:         OFFERING-ID -> ([PERSON-ID]*, [PERSON-ID]*)
+    teachers:            OFFERING-ID -> ([PERSON]*, [PERSON]*)
 
-`teachers` is like `members` -- they both return a MBR triple of
-(teachers, assistants, students)) -- but in the `teachers` triple, the
-student list may be empty (even if there are students). It is an
-optimized version of `members`: if an application just needs the
-teaching team for an offering (typically a very small list), don't
-calculate the student list as well (typically 50-100 times larger).
 
+The `teacher` methods are identical to the `member` methods, except
+that the student set is omitted: the return-value is a *pair* of sets
+representing teachers and assistants. These are essentially optimized
+versions of the `members` methods for cases when you only need to know
+about the teaching and support teams (typically, very small groups)
+and can avoid the cost of calculating and transmitting the student list
+(typically, 10-100 times larger).
+
+## Format-matching methods
+
+    resembles-course-id:   string    -> boolean
+    resembles-offering-id: string    -> boolean
+    resembles-term-id:     string    -> boolean
+    resembles-person-id:   string    -> boolean
+    
+Applications can use these to implement data-input validation tests in
+user interfaces, primarily where lookups are not possible. They
+determine whether a given string falls within the general guidelines
+of what your IDs are supposed to look like. At some institutions, this
+might be the best you can offer: you might not have access to
+databases in which you can look records up, but at least you can offer
+a means to avoid basic typographic errors.
+
+You could implement these methods by exposing the functions you
+defined for your COURSE-ID-FORMAT, TERM-ID-FORMAT, OFFERING-ID-FORMAT
+and PERSON-ID-FORMAT tests (see [Identifier types]). At the least,
+these formats should ensure that empty strings are rejected.
+
+You might choose to use implement these as lookup functions, returning
+`true` only if a matching record was found. For example, if your
+school offers only two courses (say, `ENG100` and `ENG200`), you could
+choose to implement a `resembles-course-id` method that only returned
+`true` if the argument was exactly one of those two course codes.  No
+matter how you implement it, the intent of the `resembles` methods is
+to help avoid typographic errors, not to act as a membership test.
+
 [Partial implementations]: #partial-implementations
 [Static informational methods]: #static-informational-methods
 [Identifier types]: #identifier-types
+[Format-matching methods]: #format-matching-methods
+
+<!-- 
+Local Variables:
+mode: markdown
+End: 
+-->



More information about the open-ils-commits mailing list