The schedule showed a lecturer only where the session notes named one (Globalizacja), so the student plan had nobody under the other courses. The session JSON now carries the course lecturer in its course ref and an optional per-session lecturer (new nullable column, admin field, seed key) for courses shared between two people. The student app shows the session's own lecturer or, failing that, the course's.
255 lines
7.1 KiB
Go
255 lines
7.1 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"`
|
|
Lecturer *string `json:"lecturer"`
|
|
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, Lecturer: c.Lecturer, 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"`
|
|
Lecturer *string `json:"lecturer"`
|
|
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,
|
|
Lecturer: s.Lecturer,
|
|
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"`
|
|
}
|