Skip to content

How we implement groups functionality

Jamie Folsom edited this page Sep 18, 2013 · 22 revisions

Annotation Studio allows users to view both documents and annotations through membership in groups.

We implement the groups feature in three elements:

  • Annotator. We add several fields to the annotator data schema.
  • Annotation Studio. User and Document objects can both be associated with any number of tags, each representing a group.
  • MIT Annotation Data Store supports queries that limit results to annotations to matching values in their groups fields.

###Annotator

Here is our modified annotation data model. Note the two additions to the schema: groups and subgroups:

{
    "id": "xyz",
    "quote": "outward-bound",
    "text": "",
    "uri": "http://annotationstudio.mit.edu/documents/billy-budd",
    "user": "[email protected]",
    "username": "HyperStudio",
    "uuid": "1B9D142B",
    "groups": ["21L.705", "CMS.350"], // group students by class, control visibility of documents
    "subgroups": ["geography", "character development"], // sets of students within classes, for small group work
    "permissions": {
        "delete": ["[email protected]"], // who can delete (only authors)
        "update": ["[email protected]"], // who can edit (only authors)
        "admin": ["[email protected]"], // who can change permissions (only authors)
        "read": [] // who can view, in this case anyone who matches the group(s) queried.
    },
    "tags": [],
    "ranges": [{
        "start": "/div[1]/p[6]",
        "startOffset": 604,
        "end": "/div[1]/p[6]",
        "endOffset": 617,
    }],
    "updated": "2013-09-16T19:45:39.267Z",
    "created": "2013-09-16T19:45:13.012Z",
    "annotator_schema_version": "1.0"
}

Those group values are stored per-user, and are added automatically to all of each user's annotations.

###Annotation Studio

In Annotation Studio, in addition to the users and documents tables, we have a tags table

ans_dev=# select * from tags;
 id |            name
----+----------------------------
  1 | Partners
  2 | Hyperstudio
  3 | Instructors
  4 | Developers
  5 | Melville
  6 | Darwin

and a taggings mapping table

ans_dev=# select id, tag_id, taggable_id, taggable_type, context from taggings;
 id  | tag_id | taggable_id | taggable_type |   context
-----+--------+-------------+---------------+--------------
 523 |     73 |         172 | User          | rep_subgroup
 525 |     44 |          87 | Document      | rep_group
 526 |     45 |          87 | Document      | rep_group
 527 |     75 |          87 | Document      | rep_group
 528 |     71 |         174 | User          | rep_group
 529 |     75 |         174 | User          | rep_group
 530 |     76 |         174 | User          | rep_subgroup
 531 |     77 |         174 | User          | rep_subgroup
 532 |     71 |         171 | User          | rep_group
 534 |     76 |         175 | User          | rep_subgroup

This is the structure set up by the excellent acts_as_taggable gem, which is incorporated as part of our Repertoire Groups gem. Repertoire Groups also provides user roles (like administrator, editor, etc). Tags can be associated with User objects and with Document objects, and can in turn be subgrouped into contexts, in our case, rep_group, and rep_subgroup.

###MIT Annotation Data Store

Finally, on the MIT Annotation Data Store, in addition to storing annotations, we support queries against those groups and subgroups values.

app.get('/api/search', tokenOK, function (req, res) {
    var query;

    //...other query options...

    switch (req.query.mode) {
    case 'user':
       query.where('user').equals(req.query.user);
       break;
    case 'group':
       query.where('subgroups').in(req.query.subgroups);
       break;
    case 'class':
       query.where('groups').in(req.query.groups);
       break;
    case 'admin':
        // Admins can see everything, so don't limit
        // results by group and subgroup values in query
       break;
    }

    //...query execution...

}

This separation of concerns seems to be a good one -- Annotator is agnostic about the user and groups implementation in Annotation Studio, we simply pass group values into the annotation object before storing it, and we use those values in both the front end, and in the data store/API.

It would be easy in this architecture to implement other schemes, say with three kinds of groups, or with just one, or none. That's what's suggested by the Annotator wiki, in fact, although we circumvented that approach with our own, since it was not clear to me how to make that one work.

Clone this wiki locally