feat: zjazd (meeting) on readings and materials
The Pulpit hero lists what to read for the current or next zjazd, so readings and materials get a nullable meeting_id (FK to jz_bm_meetings, ON DELETE SET NULL, indexed). The reading admin form gets a "Na zjazd" picker, the student reading JSON carries meeting_id, and the management /readings records filter and validate it.
This commit is contained in:
@@ -173,6 +173,7 @@ type readingJSON struct {
|
||||
ID uint `json:"id"`
|
||||
CourseID uint `json:"course_id"`
|
||||
Course *courseRef `json:"course"`
|
||||
MeetingID *uint `json:"meeting_id"`
|
||||
Title string `json:"title"`
|
||||
Author *string `json:"author"`
|
||||
Source *string `json:"source"`
|
||||
|
||||
28
controllers/api/json_test.go
Normal file
28
controllers/api/json_test.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestReadingJSONCarriesMeetingID checks that meeting_id is always present
|
||||
// in a reading, as a number or as null.
|
||||
func TestReadingJSONCarriesMeetingID(t *testing.T) {
|
||||
id := uint(4)
|
||||
for _, tc := range []struct {
|
||||
meeting *uint
|
||||
want string
|
||||
}{
|
||||
{&id, `"meeting_id":4`},
|
||||
{nil, `"meeting_id":null`},
|
||||
} {
|
||||
b, err := json.Marshal(readingJSON{ID: 1, Title: "Lektura", MeetingID: tc.meeting})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !strings.Contains(string(b), tc.want) {
|
||||
t.Errorf("reading JSON %s does not contain %s", b, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -42,6 +42,7 @@ func listReadings(db *gorm.DB, userID, courseID uint) ([]readingJSON, error) {
|
||||
ID: row.ID,
|
||||
CourseID: idOf(row.CourseID),
|
||||
Course: newCourseRef(courses[idOf(row.CourseID)]),
|
||||
MeetingID: row.MeetingID,
|
||||
Title: row.Title,
|
||||
Author: row.Author,
|
||||
Source: row.Source,
|
||||
|
||||
@@ -33,13 +33,7 @@ func (classSessionsAdmin) RequiredPermissions() []string {
|
||||
func (classSessionsAdmin) AdminFieldRelations() []cabana.FieldRelationContract {
|
||||
return []cabana.FieldRelationContract{
|
||||
courseRelation("course_id"),
|
||||
{
|
||||
Field: "meeting",
|
||||
Kind: "belongsTo",
|
||||
NewRelated: func() any { return &models.Meeting{} },
|
||||
ForeignKey: "meeting_id",
|
||||
LabelColumn: "number",
|
||||
},
|
||||
meetingRelation(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -54,3 +54,14 @@ func courseRelation(foreignKey string) cabana.FieldRelationContract {
|
||||
LabelColumn: "name",
|
||||
}
|
||||
}
|
||||
|
||||
// meetingRelation is the belongsTo picker of a meeting_id column (zjazd).
|
||||
func meetingRelation() cabana.FieldRelationContract {
|
||||
return cabana.FieldRelationContract{
|
||||
Field: "meeting",
|
||||
Kind: "belongsTo",
|
||||
NewRelated: func() any { return &models.Meeting{} },
|
||||
ForeignKey: "meeting_id",
|
||||
LabelColumn: "number",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,9 +29,9 @@ func (readingsAdmin) RequiredPermissions() []string {
|
||||
return []string{PermCourses}
|
||||
}
|
||||
|
||||
// AdminFieldRelations binds the course picker.
|
||||
// AdminFieldRelations binds the course and meeting pickers.
|
||||
func (readingsAdmin) AdminFieldRelations() []cabana.FieldRelationContract {
|
||||
return []cabana.FieldRelationContract{courseRelation("course_id")}
|
||||
return []cabana.FieldRelationContract{courseRelation("course_id"), meetingRelation()}
|
||||
}
|
||||
|
||||
// ReadingsController returns the jz.bm.readings admin controller.
|
||||
|
||||
Reference in New Issue
Block a user