Files
sm-bm-plugin/controllers/api/json.go
Jakub Zych 71b848a376 Adopt SummerCMS v0.1.1 admin features
Datepickers on native time.Time / lagoon.Date fields, file uploads on
materials, readings and class sessions managed from the course form
(course_id made nullable).
2026-10-03 06:03:34 +02:00

252 lines
7.0 KiB
Go

package api
import "git.golem15.com/jakub/sm-bm-plugin/models"
// idOf is the value of an optional id, 0 when it is nil. The API serves only
// sessions and readings that have a course, so their course_id is set.
func idOf(id *uint) uint {
if id == nil {
return 0
}
return *id
}
// courseRef is the short form of a course embedded in other records.
type courseRef struct {
ID uint `json:"id"`
Code *string `json:"code"`
Name string `json:"name"`
Form string `json:"form"`
Semester int `json:"semester"`
}
func newCourseRef(c *models.Course) *courseRef {
if c == nil {
return nil
}
return &courseRef{ID: c.ID, Code: c.Code, Name: c.Name, Form: c.Form, Semester: c.Semester}
}
// courseJSON is a course's own fields.
type courseJSON struct {
ID uint `json:"id"`
Code *string `json:"code"`
Name string `json:"name"`
Form string `json:"form"`
Lecturer *string `json:"lecturer"`
Semester int `json:"semester"`
ECTS *float64 `json:"ects"`
Assessment *string `json:"assessment"`
ElectiveGroup *string `json:"elective_group"`
Description *string `json:"description"`
SyllabusURL *string `json:"syllabus_url"`
SortOrder int `json:"sort_order"`
}
func newCourseJSON(c models.Course) courseJSON {
return courseJSON{
ID: c.ID,
Code: c.Code,
Name: c.Name,
Form: c.Form,
Lecturer: c.Lecturer,
Semester: c.Semester,
ECTS: c.ECTS,
Assessment: c.Assessment,
ElectiveGroup: c.ElectiveGroup,
Description: c.Description,
SyllabusURL: c.SyllabusURL,
SortOrder: c.SortOrder,
}
}
// courseCounts are the per-course counters of /courses and /courses/{id}.
type courseCounts struct {
Sessions int64 `json:"sessions"`
Readings int64 `json:"readings"`
ReadingsRead int64 `json:"readings_read"`
Materials int64 `json:"materials"`
}
type courseListJSON struct {
courseJSON
Counts courseCounts `json:"counts"`
}
type courseDetailJSON struct {
courseJSON
Counts courseCounts `json:"counts"`
Sessions []sessionJSON `json:"sessions"`
Readings []readingJSON `json:"readings"`
Materials []materialListJSON `json:"materials"`
}
// meetingJSON is one zjazd.
type meetingJSON struct {
ID uint `json:"id"`
Number int `json:"number"`
StartsOn string `json:"starts_on"`
EndsOn string `json:"ends_on"`
Notes *string `json:"notes"`
SessionsCount int64 `json:"sessions_count"`
}
func newMeetingJSON(m models.Meeting, sessions int64) meetingJSON {
return meetingJSON{ID: m.ID, Number: m.Number, StartsOn: m.StartsOn.String(), EndsOn: m.EndsOn.String(), Notes: m.Notes, SessionsCount: sessions}
}
// sessionJSON is one class session with its course.
type sessionJSON struct {
ID uint `json:"id"`
CourseID uint `json:"course_id"`
MeetingID *uint `json:"meeting_id"`
MeetingNumber *int `json:"meeting_number"`
StartsAt string `json:"starts_at"`
EndsAt string `json:"ends_at"`
Room *string `json:"room"`
Kind *string `json:"kind"`
Notes *string `json:"notes"`
IsOnline bool `json:"is_online"`
Course *courseRef `json:"course"`
}
func newSessionJSON(s models.ClassSession) sessionJSON {
out := sessionJSON{
ID: s.ID,
CourseID: idOf(s.CourseID),
MeetingID: s.MeetingID,
StartsAt: models.FormatDateTime(s.StartsAt),
EndsAt: models.FormatDateTime(s.EndsAt),
Room: s.Room,
Kind: s.Kind,
Notes: s.Notes,
IsOnline: s.IsOnline,
Course: newCourseRef(s.Course),
}
if s.Meeting != nil {
n := s.Meeting.Number
out.MeetingNumber = &n
}
return out
}
func newSessionsJSON(rows []models.ClassSession) []sessionJSON {
out := make([]sessionJSON, 0, len(rows))
for _, s := range rows {
out = append(out, newSessionJSON(s))
}
return out
}
// deadlineJSON is one terminarz entry.
type deadlineJSON struct {
ID uint `json:"id"`
Title string `json:"title"`
DueAt string `json:"due_at"`
Kind string `json:"kind"`
Description *string `json:"description"`
CourseID *uint `json:"course_id"`
Course *courseRef `json:"course"`
}
func newDeadlinesJSON(rows []models.Deadline) []deadlineJSON {
out := make([]deadlineJSON, 0, len(rows))
for _, d := range rows {
out = append(out, deadlineJSON{
ID: d.ID,
Title: d.Title,
DueAt: models.FormatDateTime(d.DueAt),
Kind: d.Kind,
Description: d.Description,
CourseID: d.CourseID,
Course: newCourseRef(d.Course),
})
}
return out
}
// readingJSON is one reading with the caller's read state.
type readingJSON struct {
ID uint `json:"id"`
CourseID uint `json:"course_id"`
Course *courseRef `json:"course"`
Title string `json:"title"`
Author *string `json:"author"`
Source *string `json:"source"`
URL *string `json:"url"`
IsRequired bool `json:"is_required"`
SortOrder int `json:"sort_order"`
ReadAt *string `json:"read_at"`
}
// tagJSON is one material tag.
type tagJSON struct {
ID uint `json:"id"`
Name string `json:"name"`
Slug string `json:"slug"`
}
func newTagsJSON(tags []models.Tag) []tagJSON {
out := make([]tagJSON, 0, len(tags))
for _, t := range tags {
out = append(out, tagJSON{ID: t.ID, Name: t.Name, Slug: t.Slug})
}
return out
}
// materialListJSON is a published material in a list.
type materialListJSON struct {
ID uint `json:"id"`
CourseID *uint `json:"course_id"`
Course *courseRef `json:"course"`
Title string `json:"title"`
Type string `json:"type"`
Source *string `json:"source"`
URL *string `json:"url"`
PublishedAt string `json:"published_at"`
Tags []tagJSON `json:"tags"`
FilesCount int64 `json:"files_count"`
}
func newMaterialListJSON(m models.Material, files int64) materialListJSON {
out := materialListJSON{
ID: m.ID,
CourseID: m.CourseID,
Course: newCourseRef(m.Course),
Title: m.Title,
Type: m.Type,
Source: m.Source,
URL: m.URL,
Tags: newTagsJSON(m.Tags),
FilesCount: files,
}
if m.PublishedAt != nil {
out.PublishedAt = models.FormatDateTime(*m.PublishedAt)
}
return out
}
// materialDetailJSON is a published material with its body and files.
type materialDetailJSON struct {
materialListJSON
Body *string `json:"body"`
Files []fileJSON `json:"files"`
}
// fileJSON is one attached file.
type fileJSON struct {
ID uint `json:"id"`
URL string `json:"url"`
Name string `json:"name"`
Size int64 `json:"size"`
Mime string `json:"mime"`
}
// readingsSummary is the reading progress block of /dashboard.
type readingsSummary struct {
Total int64 `json:"total"`
Read int64 `json:"read"`
RequiredTotal int64 `json:"required_total"`
RequiredRead int64 `json:"required_read"`
}