From 84d18eea9877f527596f816dc428c26aff67da52 Mon Sep 17 00:00:00 2001 From: Khadim Fall Date: Wed, 23 Aug 2023 11:35:57 +0200 Subject: [PATCH 01/77] started wit hrefactor of course management --- web/ts/api/admin-lecture-list.ts | 97 +++++++++++++++++++++++++ web/ts/data-store/admin-lecture-list.ts | 42 +++++++++++ web/ts/edit-course.ts | 5 +- 3 files changed, 142 insertions(+), 2 deletions(-) create mode 100644 web/ts/api/admin-lecture-list.ts create mode 100644 web/ts/data-store/admin-lecture-list.ts diff --git a/web/ts/api/admin-lecture-list.ts b/web/ts/api/admin-lecture-list.ts new file mode 100644 index 000000000..66960adab --- /dev/null +++ b/web/ts/api/admin-lecture-list.ts @@ -0,0 +1,97 @@ +import { del, get, post, put } from "../utilities/fetch-wrappers"; +import {StatusCodes} from "http-status-codes"; +import {patchData, postData} from "../global"; + +export type UpdateLectureMetaRequest = { + name?: string; + description?: string; + lectureHallId?: number; + isChatEnabled?: boolean; +} + +export type CreateNewLectureRequest = { + title: "", + lectureHallId: 0, + start: "", + end: "", + isChatEnabled: false, + duration: 0, // Duration in Minutes + formatedDuration: "", // Duration in Minutes + premiere: false, + vodup: false, + adHoc: false, + recurring: false, + recurringInterval: "weekly", + eventsCount: 10, + recurringDates: [], + combFile: [], + presFile: [], + camFile: [], +} + +export type Lecture = { + ID?: number; + courseId: number; + courseSlug: string; + lectureId: number; + streamKey: string; + seriesIdentifier: string; + color: string; + vodViews: number; + start: Date; + end: Date; + isLiveNow: boolean; + isConverting: boolean; + isRecording: boolean; + isPast: boolean; + hasStats: boolean; + name: string; + description: string; + lectureHallId: string; + lectureHallName: string; + isChatEnabled: false; +}; + +/** + * REST API Wrapper for /api/stream/:id/sections + */ +export const AdminLectureList = { + get: async function (courseId: number): Promise { + return get(`/api/stream/${courseId}/sections`); + }, + + add: async function (courseId: number, request: object) { + return post(`/api/stream/${courseId}/sections`, request); + }, + + update: async function (courseId: number, lectureId: number, request: UpdateLectureMetaRequest) { + const promises = []; + if (request.name !== null) { + promises.push(postData("/api/course/" + courseId + "/renameLecture/" + lectureId, { name: request.name })); + } + + if (request.description !== null) { + promises.push(postData("/api/course/" + courseId + "/updateDescription/" + lectureId, { name: request.description })); + } + + if (request.lectureHallId !== null) { + promises.push(postData("/api/setLectureHall", { streamIds: [lectureId], lectureHall: request.lectureHallId })); + } + + if (request.isChatEnabled !== null) { + promises.push(patchData("/api/stream/" + lectureId + "/chat/enabled", { lectureId, isChatEnabled: request.isChatEnabled })); + } + + const errors = (await Promise.all(promises)).filter((res) => res.status !== StatusCodes.OK); + if (errors.length > 0) { + console.error(errors); + throw Error("Failed to update all data."); + } + }, + + delete: async function (courseId: number, lectureId: number): Promise { + return await postData(`/api/course/${courseId}/deleteLectures`, { + streamIDs: [lectureId], + }); + }, +}; diff --git a/web/ts/data-store/admin-lecture-list.ts b/web/ts/data-store/admin-lecture-list.ts new file mode 100644 index 000000000..945cd7a64 --- /dev/null +++ b/web/ts/data-store/admin-lecture-list.ts @@ -0,0 +1,42 @@ +import { StreamableMapProvider } from "./provider"; +import {AdminLectureList, Lecture, UpdateLectureMetaRequest} from "../api/admin-lecture-list"; + +export class AdminLectureListProvider extends StreamableMapProvider { + protected async fetcher(courseId: number): Promise { + const result = await AdminLectureList.get(courseId); + return result; + } + + async add(courseId: number, lecture: Lecture): Promise { + await AdminLectureList.add(courseId, lecture); + await this.fetch(courseId, true); + await this.triggerUpdate(courseId); + } + + async delete(courseId: number, streamId: number) { + await AdminLectureList.delete(courseId, streamId); + this.data[courseId] = (await this.getData(courseId)).filter((s) => s.ID !== streamId); + } + + async updateMeta(courseId: number, streamId: number, request: UpdateLectureMetaRequest) { + await AdminLectureList.update(courseId, streamId, request); + + this.data[courseId] = (await this.getData(courseId)).map((s) => { + if (s.ID === streamId) { + s = { + ...s, + }; + + // Path updated keys in local data + for (const requestKey in request) { + const val = request[requestKey]; + if (val !== null) { + s[requestKey] = val; + } + } + } + return s; + }); + await this.triggerUpdate(streamId); + } +} diff --git a/web/ts/edit-course.ts b/web/ts/edit-course.ts index 0b1e65ab7..9606367a8 100644 --- a/web/ts/edit-course.ts +++ b/web/ts/edit-course.ts @@ -1077,10 +1077,11 @@ export function createLectureForm(args: { s: [] }) { postData("/api/course/" + this.courseID + "/createLecture", payload) .then(async (res) => { const { ids } = await res.json(); - const url = new URL(window.location.href); + /*const url = new URL(window.location.href); url.hash = `lectures:${ids.join(",")}`; window.location.assign(url); - window.location.reload(); + window.location.reload();*/ + }) .catch((e) => { console.log(e); From 429ad671bd309e025325bc98c23afc66433c6379 Mon Sep 17 00:00:00 2001 From: Khadim Fall Date: Mon, 28 Aug 2023 14:01:44 +0200 Subject: [PATCH 02/77] add dao & route --- api/courses.go | 22 +++++++++- dao/streams.go | 8 ++++ mock_dao/streams.go | 15 +++++++ .../manage/course-lecture-management.gohtml | 5 +-- web/ts/api/admin-lecture-list.ts | 13 +++--- web/ts/data-store/admin-lecture-list.ts | 9 ++-- web/ts/data-store/data-store.ts | 4 ++ web/ts/edit-course.ts | 41 +++++++++++-------- 8 files changed, 86 insertions(+), 31 deletions(-) diff --git a/api/courses.go b/api/courses.go index 2ae7e1ef7..998556872 100644 --- a/api/courses.go +++ b/api/courses.go @@ -6,12 +6,12 @@ import ( "errors" "fmt" "github.com/RBG-TUM/commons" - "github.com/getsentry/sentry-go" - "github.com/gin-gonic/gin" "github.com/TUM-Dev/gocast/dao" "github.com/TUM-Dev/gocast/model" "github.com/TUM-Dev/gocast/tools" "github.com/TUM-Dev/gocast/tools/tum" + "github.com/getsentry/sentry-go" + "github.com/gin-gonic/gin" "github.com/meilisearch/meilisearch-go" uuid "github.com/satori/go.uuid" log "github.com/sirupsen/logrus" @@ -61,6 +61,7 @@ func configGinCourseRouter(router *gin.Engine, daoWrapper dao.DaoWrapper) { courses.Use(tools.InitCourse(daoWrapper)) courses.Use(tools.AdminOfCourse) courses.DELETE("/", routes.deleteCourse) + courses.GET("/", routes.fetchLectures) courses.POST("/createVOD", routes.createVOD) courses.POST("/uploadVODMedia", routes.uploadVODMedia) courses.POST("/copy", routes.copyCourse) @@ -1038,6 +1039,23 @@ func (r coursesRoutes) renameLecture(c *gin.Context) { } } +func (r coursesRoutes) fetchLectures(c *gin.Context) { + tlctx := c.MustGet("TUMLiveContext").(tools.TUMLiveContext) + streams, err := r.StreamsDao.GetAllStreamsForCourse(tlctx.Course.ID) + if err != nil { + _ = c.Error(tools.RequestError{ + Status: http.StatusNotFound, + CustomMessage: "can not find streams", + Err: err, + }) + return + } + + c.JSON(http.StatusOK, gin.H{ + "streams": streams, + }) +} + func (r coursesRoutes) updateLectureSeries(c *gin.Context) { stream, err := r.StreamsDao.GetStreamByID(context.Background(), c.Param("streamID")) if err != nil { diff --git a/dao/streams.go b/dao/streams.go index b6da68ba5..b70c003a7 100755 --- a/dao/streams.go +++ b/dao/streams.go @@ -26,6 +26,7 @@ type StreamsDao interface { GetStreamByID(ctx context.Context, id string) (stream model.Stream, err error) GetWorkersForStream(stream model.Stream) ([]model.Worker, error) GetAllStreams() ([]model.Stream, error) + GetAllStreamsForCourse(courseID uint) ([]model.Stream, error) ExecAllStreamsWithCoursesAndSubtitles(f func([]StreamWithCourseAndSubtitles)) GetCurrentLive(ctx context.Context) (currentLive []model.Stream, err error) GetCurrentLiveNonHidden(ctx context.Context) (currentLive []model.Stream, err error) @@ -210,6 +211,13 @@ func (d streamsDao) GetAllStreams() ([]model.Stream, error) { return res, err } +// GetAllStreamsForCourse returns all streams of a course +func (d streamsDao) GetAllStreamsForCourse(courseID uint) ([]model.Stream, error) { + var res []model.Stream + err := DB.Where("course_id = ?", courseID).Find(&res).Error + return res, err +} + type StreamWithCourseAndSubtitles struct { Name, Description, TeachingTerm, CourseName, Subtitles string ID, CourseID uint diff --git a/mock_dao/streams.go b/mock_dao/streams.go index f5b32c272..098f3cd4d 100644 --- a/mock_dao/streams.go +++ b/mock_dao/streams.go @@ -164,12 +164,27 @@ func (m *MockStreamsDao) GetAllStreams() ([]model.Stream, error) { return ret0, ret1 } +// GetAllStreamsForCourse mocks base method. +func (m MockStreamsDao) GetAllStreamsForCourse(courseID uint) ([]model.Stream, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetAllStreamsForCourse", courseID) + ret0, _ := ret[0].([]model.Stream) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + // GetAllStreams indicates an expected call of GetAllStreams. func (mr *MockStreamsDaoMockRecorder) GetAllStreams() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAllStreams", reflect.TypeOf((*MockStreamsDao)(nil).GetAllStreams)) } +// GetAllStreamsForCourse indicates an expected call of GetAllStreamsForCourse. +func (mr *MockStreamsDaoMockRecorder) GetAllStreamsForCourse(courseID uint) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAllStreamsForCourse", reflect.TypeOf((*MockStreamsDao)(nil).GetAllStreamsForCourse), courseID) +} + // GetCurrentLive mocks base method. func (m *MockStreamsDao) GetCurrentLive(ctx context.Context) ([]model.Stream, error) { m.ctrl.T.Helper() diff --git a/web/template/partial/course/manage/course-lecture-management.gohtml b/web/template/partial/course/manage/course-lecture-management.gohtml index 268dc1e33..1c0c38e1c 100644 --- a/web/template/partial/course/manage/course-lecture-management.gohtml +++ b/web/template/partial/course/manage/course-lecture-management.gohtml @@ -59,9 +59,8 @@ {{- /* streams: */ -}} -
+