Templating System (Developer Guide)

This document explains how the templating system works end-to-end for developers: data model, server endpoints, client flows, and expected copy semantics.


Concept

A template is a first-class course record with template = true. It acts as a blueprint to create new courses. Creating from a template performs a deep copy of the original course’s configuration.


Data Model and Constraints

  • course.template (boolean) marks a course as a template.

  • Migrations support template courses: e.g., 0015_check_dates_template.up.sql allows end_date to be null for template courses.

  • Name/SemesterTag validation on the client prevents - in either field.

  • ECTS is fixed for some course types (Seminar = 5, Practical = 10) via UI rules; Lecture requires manual input.


Server (Core) Endpoints

All routes below are in servers/core and use Gin with permission middleware.

  • GET /api/courses/template β†’ List template courses

    • Implementation: course/router.go#getTemplateCourses β†’ course/service.go#GetTemplateCourses

    • Admins get all template courses; Lecturers see only templates they have roles for (restricted query).

  • GET /api/courses/:uuid/template β†’ Check if a course is a template

    • Implementation: course/router.go#checkCourseTemplateStatus β†’ service.CheckCourseTemplateStatus

  • PUT /api/courses/:uuid/template β†’ Mark/unmark a course as a template

    • Implementation: course/router.go#updateCourseTemplateStatus β†’ service.UpdateCourseTemplateStatus

    • DB: MarkCourseAsTemplate / UnmarkCourseAsTemplate in db/query/template.sql

  • POST /api/courses/:uuid/copy β†’ Copy a course (optionally creating a template)

    • Implementation: course/copy/router.go#copyCourse β†’ copy.copyCourseInternal

    • See Copy Semantics below.

  • GET /api/courses/:uuid/copyable β†’ Verify all phases expose /copy endpoint and are reachable.

Copy Semantics (servers/core/course/copy/copy_course.go)

When copying with Template = false:

  • Creates a new course with given Name, SemesterTag, StartDate, EndDate

  • Copies:

    • Course phases and their order (phase graph)

    • Phase/data/participation graphs

    • DTO mappings and meta graphs

    • Application form if both source and target course have an application phase

    • Student-readable and restricted metadata

    • CourseType and ECTS

  • Creates Keycloak roles/groups for the new course

When copying with Template = true:

  • Same as above, but:

    • StartDate and EndDate are omitted (NULL dates)

    • The new course is marked as template (MarkCourseAsTemplate)

Error handling: Runs within a DB transaction; partial failures roll back. Some operations (e.g., phase configuration copy) are executed after commit and may still return errors up the call chain.


Server (Assessment) β€” Assessment Templates

Separate from course templates, the assessment service exposes assessment template management:

  • Router: servers/assessment/assessmentTemplates/router.go

  • Service: servers/assessment/assessmentTemplates/service.go

  • DTOs: assessmentTemplateDTO/*

Endpoints under /assessment-template (Prompt Admin for mutating routes):

  • GET /assessment-template β†’ List

  • GET /assessment-template/:templateID β†’ Get by ID

  • POST /assessment-template β†’ Create

  • PUT /assessment-template/:templateID β†’ Update

  • DELETE /assessment-template/:templateID β†’ Delete

Use GetCoursePhasesByAssessmentTemplate to find course phases using a given assessment template.


Client (Core) Flow

Key files under clients/core:

  • Course creation choices UI: managementConsole/courseOverview/AddingCourse/components/CourseCreationChoiceDialog.tsx

    • Offers: New Course, Create New Template, Use Template

  • Create Template from scratch: managementConsole/courseOverview/AddTemplateDialog.tsx

    • Properties form: AddTemplateProperties.tsx with templateFormSchema

    • Creates course via POST /api/courses/ with template: true

  • Make Template from existing course: managementConsole/courseSettings/components/CourseTemplateToggle.tsx

    • Opens CopyCourseDialog with createTemplate=true

    • useTemplateForm β†’ validates name/semester tag

    • Backend call: POST /api/courses/:id/copy with Template = true

  • Use Template to create course: TemplateSelectionDialog.tsx

    • Queries templates with getTemplateCourses (GET /api/courses/template)

    • On select β†’ opens CopyCourseDialog (useTemplateCopy = true, createTemplate = false)

    • Backend call: POST /api/courses/:templateId/copy with Template = false

Networking helpers:

  • src/network/queries/getTemplateCourses.ts

  • src/network/queries/checkCourseTemplateStatus.ts

  • src/network/mutations/updateCourseTemplateStatus.ts

  • src/network/hooks/useCopyCourse.ts

  • src/network/hooks/useTemplateForm.ts

Validations:

  • src/validations/template.ts and makeTemplateCourse.ts enforce name and semester tag rules and ECTS input constraints.


Permissions

  • Listing templates: Prompt Admin, Course Lecturer

  • Creating templates (from scratch or from existing course): Prompt Admin, Course Lecturer

  • Using templates to create a course: Prompt Admin, Course Lecturer

The server additionally filters template visibility for non-admins based on the user’s course roles.


Edge Cases and Notes

  • Name/SemesterTag uniqueness and format are enforced downstream; UI prevents hyphens and empty values.

  • If the source course lacks an application phase, the application form is not copied (no-op).

  • Copyability check: /copyable verifies all phases implement copy; UI shows a confirmation step.

  • After creating a course (or template), the client refreshes Keycloak token and refetches courses to ensure new roles are loaded for navigation.


Extending the System

  • To add fields that should be copied, update copy_course.go and the DTO/metadata conversion utilities.

  • To expose new template kinds (e.g., per-phase templates), add appropriate endpoints in the respective service and mirror the client flows.