Dashboard, courses with counters, schedule, meetings, deadlines, readings with per-user progress (mark and unmark) and paginated published materials with tags and files, behind golem15.user's jwt.auth. Responses are built field by field with Warsaw RFC 3339 timestamps; bad query parameters answer 422 in Laravel's shape.
243 lines
6.7 KiB
Go
243 lines
6.7 KiB
Go
package api
|
|
|
|
import "git.golem15.com/jakub/sm-bm-plugin/models"
|
|
|
|
// 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: string(m.StartsOn), EndsOn: string(m.EndsOn), 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: s.CourseID,
|
|
MeetingID: s.MeetingID,
|
|
StartsAt: string(s.StartsAt),
|
|
EndsAt: string(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: string(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 = string(*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"`
|
|
}
|