• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In
Build has been canceled!

classconnect-grupo3 / courses-service / 15892480990

26 Jun 2025 03:46AM UTC coverage: 74.755% (-2.0%) from 76.8%
15892480990

push

github

web-flow
Merge pull request #47 from classconnect-grupo3/features/log-aux-teachers-activity

Features/log aux teachers activity

133 of 299 new or added lines in 10 files covered. (44.48%)

10 existing lines in 3 files now uncovered.

4051 of 5419 relevant lines covered (74.76%)

0.83 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

84.39
/src/controller/courses_controller.go
1
package controller
2

3
import (
4
        "fmt"
5
        "log/slog"
6
        "net/http"
7
        "slices"
8

9
        "courses-service/src/ai"
10
        "courses-service/src/model"
11
        "courses-service/src/schemas"
12
        "courses-service/src/service"
13

14
        "github.com/gin-gonic/gin"
15
)
16

17
type CourseController struct {
18
        service         service.CourseServiceInterface
19
        aiClient        *ai.AiClient
20
        activityService service.TeacherActivityServiceInterface
21
}
22

23
func NewCourseController(service service.CourseServiceInterface, aiClient *ai.AiClient, activityService service.TeacherActivityServiceInterface) *CourseController {
1✔
24
        return &CourseController{
1✔
25
                service:         service,
1✔
26
                aiClient:        aiClient,
1✔
27
                activityService: activityService,
1✔
28
        }
1✔
29
}
1✔
30

31
// @Summary Get all courses
32
// @Description Get all courses available in the database
33
// @Tags courses
34
// @Accept json
35
// @Produce json
36
// @Success 200 {array} model.Course
37
// @Router /courses [get]
38
func (c *CourseController) GetCourses(ctx *gin.Context) {
1✔
39
        slog.Debug("Getting courses")
1✔
40

1✔
41
        courses, err := c.service.GetCourses()
1✔
42
        if err != nil {
2✔
43
                slog.Error("Error getting courses", "error", err)
1✔
44
                ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
1✔
45
                return
1✔
46
        }
1✔
47
        slog.Debug("Courses retrieved", "courses", courses)
1✔
48
        ctx.JSON(http.StatusOK, courses)
1✔
49
}
50

51
// @Summary Course creation
52
// @Description Create a new course
53
// @Tags courses
54
// @Accept json
55
// @Produce json
56
// @Param course body schemas.CreateCourseRequest true "Course to create"
57
// @Success 201 {object} model.Course
58
// @Router /courses [post]
59
func (c *CourseController) CreateCourse(ctx *gin.Context) {
1✔
60
        slog.Debug("Creating course")
1✔
61

1✔
62
        var course schemas.CreateCourseRequest
1✔
63
        if err := ctx.ShouldBindJSON(&course); err != nil {
2✔
64
                slog.Error("Error binding JSON", "error", err)
1✔
65
                ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
1✔
66
                return
1✔
67
        }
1✔
68

69
        createdCourse, err := c.service.CreateCourse(course)
1✔
70
        if err != nil {
2✔
71
                slog.Error("Error creating course", "error", err)
1✔
72
                ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
1✔
73
                return
1✔
74
        }
1✔
75
        slog.Debug("Course created", "course", createdCourse)
1✔
76
        ctx.JSON(http.StatusCreated, createdCourse)
1✔
77
}
78

79
// @Summary Get a course by ID
80
// @Description Get a course by ID
81
// @Tags courses
82
// @Accept json
83
// @Produce json
84
// @Param id path string true "Course ID"
85
// @Success 200 {object} model.Course
86
// @Router /courses/{id} [get]
87
func (c *CourseController) GetCourseById(ctx *gin.Context) {
1✔
88
        slog.Debug("Getting course by ID")
1✔
89

1✔
90
        id := ctx.Param("id")
1✔
91
        course, err := c.service.GetCourseById(id)
1✔
92
        if err != nil {
2✔
93
                slog.Error("Error getting course by ID", "error", err)
1✔
94
                ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
1✔
95
                return
1✔
96
        }
1✔
97
        slog.Debug("Course retrieved", "course", course)
1✔
98
        ctx.JSON(http.StatusOK, course)
1✔
99
}
100

101
// @Summary Delete a course
102
// @Description Delete a course by ID
103
// @Tags courses
104
// @Accept json
105
// @Produce json
106
// @Param id path string true "Course ID"
107
// @Param teacherId query string true "Teacher ID"
108
// @Success 200 {object} schemas.DeleteCourseResponse
109
// @Router /courses/{id} [delete]
110
func (c *CourseController) DeleteCourse(ctx *gin.Context) {
1✔
111
        slog.Debug("Deleting course")
1✔
112
        id := ctx.Param("id")
1✔
113
        if id == "" {
1✔
114
                slog.Error("Course ID is required")
×
115
                ctx.JSON(http.StatusBadRequest, gin.H{"error": "Course ID is required"})
×
116
                return
×
117
        }
×
118

119
        teacherId := ctx.Query("teacherId")
1✔
120
        if teacherId == "" {
2✔
121
                slog.Error("Teacher ID is required")
1✔
122
                ctx.JSON(http.StatusBadRequest, gin.H{"error": "Teacher ID is required"})
1✔
123
                return
1✔
124
        }
1✔
125

126
        err := c.service.DeleteCourse(id, teacherId)
1✔
127
        if err != nil {
2✔
128
                slog.Error("Error deleting course", "error", err)
1✔
129
                ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
1✔
130
                return
1✔
131
        }
1✔
132
        slog.Debug("Course deleted", "id", id)
1✔
133
        ctx.JSON(http.StatusOK, gin.H{"message": "Course deleted successfully"})
1✔
134
}
135

136
// @Summary Get a course by teacher ID
137
// @Description Get a course by teacher ID
138
// @Tags courses
139
// @Accept json
140
// @Produce json
141
// @Param teacherId path string true "Teacher ID"
142
// @Success 200 {array} model.Course
143
// @Router /courses/teacher/{teacherId} [get]
144
func (c *CourseController) GetCourseByTeacherId(ctx *gin.Context) {
1✔
145
        slog.Debug("Getting course by teacher ID")
1✔
146
        teacherId := ctx.Param("teacherId")
1✔
147
        course, err := c.service.GetCourseByTeacherId(teacherId)
1✔
148
        if err != nil {
2✔
149
                slog.Error("Error getting course by teacher ID", "error", err)
1✔
150
                ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
1✔
151
                return
1✔
152
        }
1✔
153
        slog.Debug("Course retrieved", "course", course)
1✔
154
        ctx.JSON(http.StatusOK, course)
1✔
155
}
156

157
// @Summary Get a course by title
158
// @Description Get a course by title
159
// @Tags courses
160
// @Accept json
161
// @Produce json
162
// @Param title path string true "Course title"
163
// @Success 200 {array} model.Course
164
// @Router /courses/title/{title} [get]
165
func (c *CourseController) GetCourseByTitle(ctx *gin.Context) {
1✔
166
        slog.Debug("Getting course by title")
1✔
167
        title := ctx.Param("title")
1✔
168
        course, err := c.service.GetCourseByTitle(title)
1✔
169
        if err != nil {
1✔
170
                slog.Error("Error getting course by title", "error", err)
×
171
                ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
×
172
                return
×
173
        }
×
174
        if len(course) == 0 {
2✔
175
                slog.Error("Course not found")
1✔
176
                ctx.JSON(http.StatusNotFound, gin.H{"error": "Course not found"})
1✔
177
                return
1✔
178
        }
1✔
179
        slog.Debug("Course retrieved", "course", course)
×
180
        ctx.JSON(http.StatusOK, course)
×
181
}
182

183
// @Summary Update a course
184
// @Description Update a course by ID
185
// @Tags courses
186
// @Accept json
187
// @Produce json
188
// @Param id path string true "Course ID"
189
// @Param course body schemas.UpdateCourseRequest true "Course to update"
190
// @Success 200 {object} model.Course
191
// @Router /courses/{id} [put]
192
func (c *CourseController) UpdateCourse(ctx *gin.Context) {
1✔
193
        slog.Debug("Updating course")
1✔
194
        id := ctx.Param("id")
1✔
195

1✔
196
        var updateCourseRequest schemas.UpdateCourseRequest
1✔
197
        if err := ctx.ShouldBindJSON(&updateCourseRequest); err != nil {
2✔
198
                slog.Error("Error binding JSON", "error", err)
1✔
199
                ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
1✔
200
                return
1✔
201
        }
1✔
202

203
        updatedCourse, err := c.service.UpdateCourse(id, updateCourseRequest)
1✔
204
        if err != nil {
2✔
205
                slog.Error("Error updating course", "error", err)
1✔
206
                ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
1✔
207
                return
1✔
208
        }
1✔
209
        slog.Debug("Course updated", "course", updatedCourse)
1✔
210
        ctx.JSON(http.StatusOK, updatedCourse)
1✔
211
}
212

213
// @Summary Get courses by student ID
214
// @Description Get courses by student ID
215
// @Tags courses
216
// @Accept json
217
// @Produce json
218
// @Param studentId path string true "Student ID"
219
// @Success 200 {array} model.Course
220
// @Router /courses/student/{studentId} [get]
221
func (c *CourseController) GetCoursesByStudentId(ctx *gin.Context) {
1✔
222
        slog.Debug("Getting courses by student ID")
1✔
223
        studentId := ctx.Param("studentId")
1✔
224
        courses, err := c.service.GetCoursesByStudentId(studentId)
1✔
225
        if err != nil {
2✔
226
                slog.Error("Error getting courses by student ID", "error", err)
1✔
227
                ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
1✔
228
                return
1✔
229
        }
1✔
230
        slog.Debug("Courses retrieved", "courses", courses)
1✔
231
        ctx.JSON(http.StatusOK, courses)
1✔
232
}
233

234
// @Summary Get courses by user ID
235
// @Description Get courses by user ID
236
// @Tags courses
237
// @Accept json
238
// @Produce json
239
// @Param userId path string true "User ID"
240
// @Success 200 {array} model.Course
241
// @Router /courses/user/{userId} [get]
242
func (c *CourseController) GetCoursesByUserId(ctx *gin.Context) {
1✔
243
        slog.Debug("Getting courses by user ID")
1✔
244
        userId := ctx.Param("userId")
1✔
245
        courses, err := c.service.GetCoursesByUserId(userId)
1✔
246
        if err != nil {
2✔
247
                slog.Error("Error getting courses by user ID", "error", err)
1✔
248
                ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
1✔
249
                return
1✔
250
        }
1✔
251
        slog.Debug("Courses retrieved", "courses", courses)
1✔
252
        ctx.JSON(http.StatusOK, courses)
1✔
253
}
254

255
// @Summary Add an aux teacher to a course
256
// @Description Add an aux teacher to a course by ID
257
// @Tags courses
258
// @Accept json
259
// @Produce json
260
// @Param id path string true "Course ID"
261
func (c *CourseController) AddAuxTeacherToCourse(ctx *gin.Context) {
1✔
262
        slog.Debug("Adding aux teacher to course")
1✔
263
        id := ctx.Param("id")
1✔
264
        if id == "" {
2✔
265
                slog.Error("Course ID is required")
1✔
266
                ctx.JSON(http.StatusBadRequest, gin.H{"error": "Course ID is required"})
1✔
267
                return
1✔
268
        }
1✔
269

270
        var auxTeacherRequest schemas.AddAuxTeacherToCourseRequest
1✔
271
        if err := ctx.ShouldBindJSON(&auxTeacherRequest); err != nil {
2✔
272
                slog.Error("Error binding JSON", "error", err)
1✔
273
                ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
1✔
274
                return
1✔
275
        }
1✔
276

277
        teacherId := auxTeacherRequest.TeacherID
1✔
278
        auxTeacherId := auxTeacherRequest.AuxTeacherID
1✔
279
        course, err := c.service.AddAuxTeacherToCourse(id, teacherId, auxTeacherId)
1✔
280
        if err != nil {
2✔
281
                slog.Error("Error adding aux teacher to course", "error", err)
1✔
282
                ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
1✔
283
                return
1✔
284
        }
1✔
285
        slog.Debug("Aux teacher added to course", "course", course)
1✔
286
        ctx.JSON(http.StatusOK, course)
1✔
287
}
288

289
// @Summary Remove an aux teacher from a course
290
// @Description Remove an aux teacher from a course by ID
291
// @Tags courses
292
// @Accept json
293
// @Produce json
294
// @Param id path string true "Course ID"
295
// @Param teacherId query string true "Teacher ID"
296
// @Param auxTeacherId query string true "Aux teacher ID"
297
// @Success 200 {object} model.Course
298
// @Router /courses/{id}/aux-teacher/remove [delete]
299
func (c *CourseController) RemoveAuxTeacherFromCourse(ctx *gin.Context) {
1✔
300
        slog.Debug("Removing aux teacher from course")
1✔
301
        id := ctx.Param("id")
1✔
302
        if id == "" {
2✔
303
                slog.Error("Course ID is required")
1✔
304
                ctx.JSON(http.StatusBadRequest, gin.H{"error": "Course ID is required"})
1✔
305
                return
1✔
306
        }
1✔
307

308
        teacherId := ctx.Query("teacherId")
1✔
309
        auxTeacherId := ctx.Query("auxTeacherId")
1✔
310

1✔
311
        if teacherId == "" || auxTeacherId == "" {
2✔
312
                slog.Error("Teacher ID and aux teacher ID are required")
1✔
313
                ctx.JSON(http.StatusBadRequest, gin.H{"error": "Teacher ID and aux teacher ID are required"})
1✔
314
                return
1✔
315
        }
1✔
316

317
        course, err := c.service.RemoveAuxTeacherFromCourse(id, teacherId, auxTeacherId)
1✔
318
        if err != nil {
2✔
319
                slog.Error("Error removing aux teacher from course", "error", err)
1✔
320
                ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
1✔
321
                return
1✔
322
        }
1✔
323
        slog.Debug("Aux teacher removed from course", "course", course)
1✔
324
        ctx.JSON(http.StatusOK, course)
1✔
325
}
326

327
// @Summary Get favourite courses
328
// @Description Get favourite courses by student ID
329
// @Tags courses
330
// @Accept json
331
// @Produce json
332
// @Param studentId path string true "Student ID"
333
// @Success 200 {array} model.Course
334
// @Router /courses/favourite/{studentId} [get]
335
func (c *CourseController) GetFavouriteCourses(ctx *gin.Context) {
1✔
336
        slog.Debug("Getting favourite courses")
1✔
337
        studentId := ctx.Param("studentId")
1✔
338
        if studentId == "" {
2✔
339
                slog.Error("Student ID is required")
1✔
340
                ctx.JSON(http.StatusBadRequest, gin.H{"error": "Student ID is required"})
1✔
341
                return
1✔
342
        }
1✔
343

344
        courses, err := c.service.GetFavouriteCourses(studentId)
1✔
345
        if err != nil {
2✔
346
                slog.Error("Error getting favourite courses", "error", err)
1✔
347
                ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
1✔
348
                return
1✔
349
        }
1✔
350
        slog.Debug("Favourite courses retrieved", "courses", courses)
1✔
351
        ctx.JSON(http.StatusOK, courses)
1✔
352
}
353

354
// @Summary Create course feedback
355
// @Description Create course feedback by course ID
356
// @Tags courses
357
// @Accept json
358
// @Produce json
359
// @Param id path string true "Course ID"
360
// @Param feedback body schemas.CreateCourseFeedbackRequest true "Course feedback"
361
// @Success 200 {object} model.CourseFeedback
362
// @Router /courses/{id}/feedback [post]
363
func (c *CourseController) CreateCourseFeedback(ctx *gin.Context) {
1✔
364
        slog.Debug("Creating course feedback")
1✔
365
        courseId := ctx.Param("id")
1✔
366
        if courseId == "" {
2✔
367
                slog.Error("Course ID is required")
1✔
368
                ctx.JSON(http.StatusBadRequest, gin.H{"error": "Course ID is required"})
1✔
369
                return
1✔
370
        }
1✔
371

372
        var feedback schemas.CreateCourseFeedbackRequest
1✔
373
        if err := ctx.ShouldBindJSON(&feedback); err != nil {
2✔
374
                slog.Error("Error binding create course feedback request", "error", err)
1✔
375
                ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
1✔
376
                return
1✔
377
        }
1✔
378

379
        if !slices.Contains(model.FeedbackTypes, feedback.FeedbackType) {
2✔
380
                slog.Error("Invalid feedback type")
1✔
381
                ctx.JSON(http.StatusBadRequest, gin.H{"error": "Invalid feedback type"})
1✔
382
                return
1✔
383
        }
1✔
384

385
        feedbackModel, err := c.service.CreateCourseFeedback(courseId, feedback)
1✔
386
        if err != nil {
2✔
387
                slog.Error("Error creating course feedback", "error", err)
1✔
388
                ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
1✔
389
                return
1✔
390
        }
1✔
391

392
        // Log activity if teacher is auxiliary
393
        teacherUUID := ctx.GetHeader("X-Teacher-UUID")
1✔
394
        if teacherUUID != "" {
1✔
NEW
395
                c.activityService.LogActivityIfAuxTeacher(
×
NEW
396
                        courseId,
×
NEW
397
                        teacherUUID,
×
NEW
398
                        "CREATE_COURSE_FEEDBACK",
×
NEW
399
                        fmt.Sprintf("Created course feedback of type: %s", feedback.FeedbackType),
×
NEW
400
                )
×
NEW
401
        }
×
402

403
        slog.Debug("Course feedback created", "feedback", feedbackModel)
1✔
404
        ctx.JSON(http.StatusOK, feedbackModel)
1✔
405
}
406

407
// @Summary Get course feedback
408
// @Description Get course feedback by course ID
409
// @Tags courses
410
// @Accept json
411
// @Produce json
412
// @Param id path string true "Course ID"
413
// @Param getCourseFeedbackRequest body schemas.GetCourseFeedbackRequest true "Get course feedback request"
414
// @Success 200 {array} model.CourseFeedback
415
// @Router /courses/{id}/feedback [put]
416
func (c *CourseController) GetCourseFeedback(ctx *gin.Context) {
1✔
417
        slog.Debug("Getting course feedback")
1✔
418
        courseId := ctx.Param("id")
1✔
419
        if courseId == "" {
2✔
420
                slog.Error("Course ID is required")
1✔
421
                ctx.JSON(http.StatusBadRequest, gin.H{"error": "Course ID is required"})
1✔
422
                return
1✔
423
        }
1✔
424

425
        var getCourseFeedbackRequest schemas.GetCourseFeedbackRequest
1✔
426
        if err := ctx.ShouldBindJSON(&getCourseFeedbackRequest); err != nil {
2✔
427
                slog.Error("Error binding get course feedback request", "error", err)
1✔
428
                ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
1✔
429
                return
1✔
430
        }
1✔
431

432
        feedback, err := c.service.GetCourseFeedback(courseId, getCourseFeedbackRequest)
1✔
433
        if err != nil {
2✔
434
                slog.Error("Error getting course feedback", "error", err)
1✔
435
                ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
1✔
436
                return
1✔
437
        }
1✔
438

439
        slog.Debug("Course feedback retrieved", "feedback", feedback)
1✔
440
        ctx.JSON(http.StatusOK, feedback)
1✔
441
}
442

443
// @Summary Get course feedback summary
444
// @Description Get course feedback summary by course ID
445
// @Tags courses
446
// @Accept json
447
// @Produce json
448
// @Param id path string true "Course ID"
449
// @Success 200 {object} schemas.AiSummaryResponse
450
// @Router /courses/{id}/feedback/summary [get]
451
func (c *CourseController) GetCourseFeedbackSummary(ctx *gin.Context) {
×
452
        slog.Debug("Getting course feedback summary")
×
453
        courseId := ctx.Param("id")
×
454
        if courseId == "" {
×
455
                slog.Error("Course ID is required")
×
456
                ctx.JSON(http.StatusBadRequest, gin.H{"error": "Course ID is required"})
×
457
                return
×
458
        }
×
459

460
        feedbacks, err := c.service.GetCourseFeedback(courseId, schemas.GetCourseFeedbackRequest{})
×
461
        if err != nil {
×
462
                slog.Error("Error getting course feedback", "error", err)
×
463
                ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
×
464
                return
×
465
        }
×
466

467
        if len(feedbacks) == 0 {
×
468
                slog.Error("No feedbacks found")
×
469
                ctx.JSON(http.StatusNotFound, gin.H{"error": "No feedbacks found"})
×
470
                return
×
471
        }
×
472

473
        summary, err := c.aiClient.SummarizeCourseFeedbacks(feedbacks)
×
474
        if err != nil {
×
475
                slog.Error("Error getting course feedback summary", "error", err)
×
476
                ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
×
477
                return
×
478
        }
×
479

480
        slog.Debug("Course feedback summary retrieved", "summary", summary)
×
481
        ctx.JSON(http.StatusOK, schemas.AiSummaryResponse{Summary: summary})
×
482
}
483

484
// @Summary Get course members
485
// @Description Get all members of a course (teacher, aux teachers, and students)
486
// @Tags courses
487
// @Accept json
488
// @Produce json
489
// @Param id path string true "Course ID"
490
// @Success 200 {object} schemas.CourseMembersResponse
491
// @Failure 400 {object} schemas.ErrorResponse
492
// @Failure 404 {object} schemas.ErrorResponse
493
// @Failure 500 {object} schemas.ErrorResponse
494
// @Router /courses/{id}/members [get]
495
func (c *CourseController) GetCourseMembers(ctx *gin.Context) {
1✔
496
        slog.Debug("Getting course members")
1✔
497

1✔
498
        courseId := ctx.Param("id")
1✔
499
        if courseId == "" {
1✔
500
                ctx.JSON(http.StatusBadRequest, schemas.ErrorResponse{Error: "Course ID is required"})
×
501
                return
×
502
        }
×
503

504
        members, err := c.service.GetCourseMembers(courseId)
1✔
505
        if err != nil {
2✔
506
                slog.Error("Error getting course members", "error", err)
1✔
507
                ctx.JSON(http.StatusInternalServerError, schemas.ErrorResponse{Error: err.Error()})
1✔
508
                return
1✔
509
        }
1✔
510

511
        slog.Debug("Course members retrieved", "course_id", courseId)
1✔
512
        ctx.JSON(http.StatusOK, members)
1✔
513
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc