Skip to navigation

Curriculum structures

How a program's requirements are modelled, built and published
View as Markdown

A curriculum is a tree of containers. Each container states a requirement, such as “Core Courses”, “Prescribed Electives” or “Choose 12 UOC from the following”, and holds subject entries, child containers, or both.

Academic Structure (root container)
├── Core Courses creditPoints: 72
│ ├── COMP1511 (6 UOC)
│ └── COMP1521 (6 UOC)
└── Prescribed Electives creditPoints: 24, creditPointsMax: 36
├── Level 3 Electives (child container)
│ └── COMP3141 (6 UOC)
└── COMP2511 (6 UOC)

We don’t impose a shape on the tree. Depth, naming and how you divide requirements are yours to decide, so build whatever describes your program.

What a container carries

FieldMeaning
titleThe requirement’s name, e.g. “Core Courses”
descriptionThe requirement in prose, e.g. “Students must take 30 UOC of the following subjects.”
creditPointsUOC that must be taken from this container
creditPointsMaxUpper bound, where a range applies
dynamicRulesWildcard requirements (“any General Education subject”), for display only
itemsThe subject entries
childrenNested containers

dynamicRules exists so we can render handbook text faithfully. Nothing evaluates it, and no assessment reads it. If a requirement has to affect an outcome, it needs to be an explicit entry.

Draft first, then publish

Containers are created as DRAFT, which is what lets you assemble and review a whole tree before anything depends on it.

Three operations are allowed only while a container is a draft:

  • attaching it to a program or specialisation, with PUT /structures/{structureId}/owner
  • nesting it under another container, with PUT /structures/{structureId}/parent
  • deleting it

Publishing with PUT /structures/{structureId}/status sets the tree LIVE. Assessment reads live trees, so the shape stops moving at that point, though you can still add and remove subject entries.

To find work you started and left, GET /structures/drafts lists drafts that aren’t yet attached to anything.

Ownership

A container is owned by at most one program or specialisation, never both. While it is still a draft it can belong to neither, which is how you build a tree before deciding where it goes.

Ownership applies to the root of a tree. Child containers belong to their parent and inherit the owner from it.

Deleting a program or specialisation deletes its containers along with it. If you delete a specialisation that some other container references, that entry stays where it is with its link cleared, so you don’t silently remove someone else’s requirement.

Adding and removing subjects

POST /institution/catalogue/structures/{structureId}/subjects
DELETE /institution/catalogue/structures/{structureId}/subjects/{code}/{year}

A subject must already exist in your catalogue at that code and year before you can add it to a container. Create the subject first, then reference it.

Entries carry a resolved flag. When it’s false, the code isn’t in the catalogue. This only happens for entries that came from the original handbook import, never for anything you add through the API.

Entries have an itemType of Course, Major, Minor, Honours, Specialisation or Research. A subject entry reports itemType: "Course" even though the endpoints call it a subject. The stored value predates the rename, and changing it would rewrite tens of thousands of historical rows, so match on "Course" rather than "Subject".

Only Course entries can be added through the API. The rest came from the handbook import and are read-only.

Reading a curriculum

GET /institution/catalogue/programs/{code}/{year}/structure
GET /institution/catalogue/specialisations/{code}/{year}/structure
GET /institution/catalogue/structures/{structureId}

The first two return the whole tree, nested, with entries in sibling order. The third returns a single container and everything beneath it. Both produce the same shape, so one renderer will handle either.

A typical build

  1. POST /structures for the root, then again for each child. All of them start as DRAFT.
  2. PUT /structures/{id}/parent to nest the children.
  3. POST /structures/{id}/subjects to add entries.
  4. GET /structures/{rootId} to review the assembled tree.
  5. PUT /structures/{id}/owner to attach it to its program.
  6. PUT /structures/{id}/status to publish it LIVE.

Steps 1 to 4 are freely reversible. From step 5 onwards the shape is settled.