Skip to content

feat: Add comment to student grade #180

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jan 18, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 14 additions & 6 deletions __tests__/integration/grades_test.go
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@ import (
"github.com/stretchr/testify/require"
)

func TestSetCriteriaToStudentSubmission(t *testing.T) {
func TestGradeStudentSubmission(t *testing.T) {
c := require.New(t)

// ## Test preparation
@@ -78,18 +78,26 @@ func TestSetCriteriaToStudentSubmission(t *testing.T) {
firstStudent := enrolledStudents[0].(map[string]interface{})
studentUUID := firstStudent["uuid"].(string)

// ## Test execution
// ## Test: Set criteria to student grade
// Select the criteria for the student grade
_, code := SetCriteriaToStudentGrade(&SetCriteriaToStudentGradeUtilsDTO{
LaboratoryUUID: laboratoryUUID,
RubricUUID: rubricUUID,
StudentUUID: studentUUID,
ObjectiveUUID: objectiveUUID,
CriteriaUUID: criteriaUUID,
}, cookie)
c.Equal(http.StatusNoContent, code)

// Get all the grades of students in the laboratory
// ## Test: Set comment to student grade
comment := "Set criteria to student grade test - comment"
_, code = SetCommentToStudentGrade(&SetCommentToStudentGradeUtilsDTO{
LaboratoryUUID: laboratoryUUID,
StudentUUID: studentUUID,
Comment: comment,
}, cookie)
c.Equal(http.StatusNoContent, code)

// ## Test: Get all the grades of students in the laboratory
studentGradeResponse, code := GetSummarizedGrades(laboratoryUUID, cookie)
c.Equal(http.StatusOK, code)

@@ -100,7 +108,7 @@ func TestSetCriteriaToStudentSubmission(t *testing.T) {
c.Equal(studentUUID, firstStudentGrade["student_uuid"].(string))
c.Equal(criteriaWeight, firstStudentGrade["grade"].(float64))

// Get the grade of the student in the laboratory
// ## Test: Get the grade of the student in the laboratory
studentGradeResponse, code = GetStudentGrade(&GetStudentGradeUtilsDTO{
LaboratoryUUID: laboratoryUUID,
RubricUUID: rubricUUID,
@@ -112,7 +120,7 @@ func TestSetCriteriaToStudentSubmission(t *testing.T) {
c.Equal(criteriaWeight, studentGrade)

gradeComment := studentGradeResponse["comment"].(string)
c.Equal("", gradeComment)
c.Equal(comment, gradeComment)

selectedCriteriaList := studentGradeResponse["selected_criteria"].([]interface{})
c.Equal(1, len(selectedCriteriaList))
25 changes: 23 additions & 2 deletions __tests__/integration/grades_utils_test.go
Original file line number Diff line number Diff line change
@@ -17,7 +17,6 @@ func GetSummarizedGrades(laboratoryUUID string, cookie *http.Cookie) (response m

type SetCriteriaToStudentGradeUtilsDTO struct {
LaboratoryUUID string
RubricUUID string
StudentUUID string
ObjectiveUUID string
CriteriaUUID string
@@ -28,7 +27,6 @@ func SetCriteriaToStudentGrade(dto *SetCriteriaToStudentGradeUtilsDTO, cookie *h
w, r := PrepareRequest("PUT", endpoint, map[string]interface{}{
"objective_uuid": dto.ObjectiveUUID,
"criteria_uuid": dto.CriteriaUUID,
"rubric_uuid": dto.RubricUUID,
})
r.AddCookie(cookie)
router.ServeHTTP(w, r)
@@ -58,3 +56,26 @@ func GetStudentGrade(dto *GetStudentGradeUtilsDTO, cookie *http.Cookie) (respons
jsonResponse := ParseJsonResponse(w.Body)
return jsonResponse, w.Code
}

type SetCommentToStudentGradeUtilsDTO struct {
LaboratoryUUID string
StudentUUID string
Comment string
}

func SetCommentToStudentGrade(dto *SetCommentToStudentGradeUtilsDTO, cookie *http.Cookie) (response map[string]interface{}, statusCode int) {
endpoint := fmt.Sprintf(
"/api/v1/grades/laboratories/%s/students/%s/comment",
dto.LaboratoryUUID,
dto.StudentUUID,
)

w, r := PrepareRequest("PUT", endpoint, map[string]interface{}{
"comment": dto.Comment,
})
r.AddCookie(cookie)
router.ServeHTTP(w, r)

jsonResponse := ParseJsonResponse(w.Body)
return jsonResponse, w.Code
}
17 changes: 17 additions & 0 deletions docs/bruno/grades/set-comment-to-student-grade.bru
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
meta {
name: set-comment-to-student-grade
type: http
seq: 4
}

put {
url: {{BASE_URL}}/grades/laboratories/d0ce7e95-59b4-4ac1-9238-461e9a47ce1d/students/3fd0671b-8673-4282-94f5-9bd6300d943f/comment
body: json
auth: none
}

body:json {
{
"comment": "Good job"
}
}
57 changes: 51 additions & 6 deletions docs/openapi/spec.openapi.yaml
Original file line number Diff line number Diff line change
@@ -2176,7 +2176,7 @@ paths:
schema:
$ref: "#/components/schemas/default_error_response"

/grades/laboratory/{laboratory_uuid}:
/grades/laboratories/{laboratory_uuid}:
get:
tags:
- Grades
@@ -2216,6 +2216,56 @@ paths:
schema:
$ref: "#/components/schemas/default_error_response"

/grades/laboratories/{laboratory_uuid}/students/{student_uuid}/comment:
put:
tags:
- Grades
security:
- cookieAuth: []
parameters:
- in: path
name: student_uuid
schema:
type: string
example: "b0c553b3-ddb2-4392-9d94-b31d8c9c4a84"
required: true
- in: path
name: laboratory_uuid
schema:
type: string
example: "a9be2f1e-e0e9-4b8d-9f72-6ed55ea5b1b8"
required: true
description: Add / update the comment given by the teacher to the given student in the given laboratory.
requestBody:
content:
application/json:
schema:
type: object
properties:
comment:
type: string
example: "This is a comment made by the teacher :D"
responses:
"204":
description: The comment was updated.
"400":
description: Required fields were missed or doesn't fulfill the required format.
content:
application/json:
schema:
$ref: "#/components/schemas/default_error_response"
"403":
description: The session token isn't valid or the user doesn't have enough permissions.
content:
application/json:
schema:
$ref: "#/components/schemas/default_error_response"
"500":
description: There was an unexpected error in the server side.
content:
application/json:
schema:
$ref: "#/components/schemas/default_error_response"

components:
securitySchemes:
@@ -2357,11 +2407,6 @@ components:
select_criteria_to_grade_req:
allOf:
- $ref: "#/components/schemas/selected_criteria_in_grade"
type: object
properties:
rubric_uuid:
type: string
example: "3fc29baf-9517-430c-9048-0f85599b61b7"

# Responses
default_error_response:
Original file line number Diff line number Diff line change
@@ -3,7 +3,6 @@ package implementations
import (
"context"
"database/sql"
"fmt"
"log"
"time"

@@ -480,7 +479,6 @@ func (repository *BlocksPostgresRepository) SwapBlocks(firstBlockUUID, secondBlo
}

// Run the query
fmt.Println("Swapping blocks", firstBlockUUID, secondBlockUUID)
_, err = tx.ExecContext(ctx, query, firstBlockUUID, secondBlockUUID)
if err != nil {
return err
40 changes: 36 additions & 4 deletions src/grades/application/use_cases.go
Original file line number Diff line number Diff line change
@@ -74,10 +74,8 @@ func (useCases *GradesUseCases) SetCriteriaToGrade(dto *dtos.SetCriteriaToGradeD
return gradesErrors.LaboratoryDoesNotHaveRubricError{}
}

// Validate the rubric UUID
if *rubricUUID != dto.RubricUUID {
return gradesErrors.RubricDoesNotMatchLaboratoryError{}
}
// Set the rubric UUID
dto.RubricUUID = *rubricUUID

// Validate the objective belongs to the rubric
objectiveBelongsToRubric, err := useCases.RubricsRepository.DoesRubricHaveObjective(
@@ -134,3 +132,37 @@ func (useCases *GradesUseCases) GetStudentGradeInLaboratoryWithRubric(dto *dtos.
grade, err := useCases.GradesRepository.GetStudentGradeInLaboratoryWithRubric(dto)
return grade, err
}

// SetCommentToGrade sets a comment to an student's grade
func (useCases *GradesUseCases) SetCommentToGrade(dto *dtos.SetCommentToGradeDTO) error {
// Validate the teacher owns the laboratory
teacherOwnsLaboratory, err := useCases.LaboratoriesRepository.DoesTeacherOwnLaboratory(
dto.TeacherUUID,
dto.LaboratoryUUID,
)
if err != nil {
return err
}
if !teacherOwnsLaboratory {
return laboratoriesErrors.TeacherDoesNotOwnLaboratoryError{}
}

// Get the UUID of the current rubric of the laboratory
laboratoryUUID := dto.LaboratoryUUID
laboratoryInformation, err := useCases.LaboratoriesRepository.GetLaboratoryInformationByUUID(laboratoryUUID)
if err != nil {
return err
}

// Return an error if the laboratory does not have a rubric
rubricUUID := laboratoryInformation.RubricUUID
if rubricUUID == nil {
return gradesErrors.LaboratoryDoesNotHaveRubricError{}
}

// Set the rubric UUID
dto.RubricUUID = *rubricUUID

// Set the comment to the student's grade
return useCases.GradesRepository.SetCommentToGrade(dto)
}
1 change: 1 addition & 0 deletions src/grades/domain/definitions/grades_repository.go
Original file line number Diff line number Diff line change
@@ -11,6 +11,7 @@ type GradesRepository interface {
[]*dtos.SummarizedStudentGradeDTO, error,
)
SetCriteriaToGrade(dto *dtos.SetCriteriaToGradeDTO) error
SetCommentToGrade(dto *dtos.SetCommentToGradeDTO) error
GetStudentGradeInLaboratoryWithRubric(
dto *dtos.GetStudentGradeInLaboratoryWithRubricDTO,
) (
9 changes: 9 additions & 0 deletions src/grades/domain/dtos/grades_dtos.go
Original file line number Diff line number Diff line change
@@ -58,3 +58,12 @@ type SelectedCriteriaInStudentGradeDTO struct {
ObjectiveUUID string `json:"objective_uuid"`
CriteriaUUID string `json:"criteria_uuid"`
}

// SetCommentToGradeDTO data transfer object to parse the request of the endpoint
type SetCommentToGradeDTO struct {
TeacherUUID string
LaboratoryUUID string
RubricUUID string
StudentUUID string
Comment string
}
56 changes: 54 additions & 2 deletions src/grades/infrastructure/http/controllers.go
Original file line number Diff line number Diff line change
@@ -79,12 +79,11 @@ func (controller *GradesController) HandleSetCriteriaGrade(c *gin.Context) {
return
}

// Create DTO
// Create DTO, note that the rubric field will be populated in the use case
dto := &dtos.SetCriteriaToGradeDTO{
TeacherUUID: teacherUUID,
LaboratoryUUID: laboratoryUUID,
StudentUUID: studentUUID,
RubricUUID: request.RubricUUID,
ObjectiveUUID: request.ObjectiveUUID,
CriteriaUUID: request.CriteriaUUID,
}
@@ -137,3 +136,56 @@ func (controller *GradesController) HandleGetStudentGradeInLaboratoryWithRubric(

c.JSON(http.StatusOK, grade)
}

// HandleSetCommentToGrade controller to set a comment to a student's grade
func (controller *GradesController) HandleSetCommentToGrade(c *gin.Context) {
teacherUUID := c.GetString("session_uuid")
laboratoryUUID := c.Param("laboratoryUUID")
studentUUID := c.Param("studentUUID")

// Validate UUIDs
requestUUIDs := requests.SetCommentToGradeRequestUUIDs{
StudentUUID: studentUUID,
LaboratoryUUID: laboratoryUUID,
}
if err := sharedInfrastructure.GetValidator().Struct(requestUUIDs); err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"message": "Please, make sure the provided UUIDs are valid",
})
return
}

// Parse the request body
var request requests.SetCommentToGradeRequest
if err := c.ShouldBindJSON(&request); err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"message": "Request body is not valid",
})
return
}

// Validate the request body
if err := sharedInfrastructure.GetValidator().Struct(request); err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"message": "Validation error",
"errors": err.Error(),
})
return
}

// Create DTO. Note that the rubric field will be populated in the use case
dto := &dtos.SetCommentToGradeDTO{
TeacherUUID: teacherUUID,
LaboratoryUUID: laboratoryUUID,
StudentUUID: studentUUID,
Comment: request.Comment,
}

err := controller.UseCases.SetCommentToGrade(dto)
if err != nil {
c.Error(err)
return
}

c.Status(http.StatusNoContent)
}
7 changes: 7 additions & 0 deletions src/grades/infrastructure/http/routes.go
Original file line number Diff line number Diff line change
@@ -42,4 +42,11 @@ func StartGradesRoutes(g *gin.RouterGroup) {
sharedInfrastructure.WithAuthorizationMiddleware([]string{"teacher"}),
controller.HandleSetCriteriaGrade,
)

gradesGroup.PUT(
"/laboratories/:laboratoryUUID/students/:studentUUID/comment",
sharedInfrastructure.WithAuthenticationMiddleware(),
sharedInfrastructure.WithAuthorizationMiddleware([]string{"teacher"}),
controller.HandleSetCommentToGrade,
)
}
Loading