Skip to content

Commit

Permalink
Add courses aggregate endpoint to professor endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
mikehquan19 committed Oct 31, 2024
1 parent 966e2cb commit b21fa85
Show file tree
Hide file tree
Showing 2 changed files with 134 additions and 0 deletions.
130 changes: 130 additions & 0 deletions api/controllers/professor.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,3 +143,133 @@ func ProfessorAll(c *gin.Context) {
// return result
c.JSON(http.StatusOK, responses.MultiProfessorResponse{Status: http.StatusOK, Message: "success", Data: professors})
}

// @Id professorCourseSearch
// @Router /professor [get]
// @Description "Returns all of the courses of all the professors matching the query's string-typed key-value pairs"
// @Produce json
// @Param first_name query string false "The professor's first name"
// @Param last_name query string false "The professor's last name"
// @Param titles query string false "One of the professor's title"
// @Param email query string false "The professor's email address"
// @Param phone_number query string false "The professor's phone number"
// @Param office.building query string false "The building of the location of the professor's office"
// @Param office.room query string false "The room of the location of the professor's office"
// @Param office.map_uri query string false "A hyperlink to the UTD room locator of the professor's office"
// @Param profile_uri query string false "A hyperlink pointing to the professor's official university profile"
// @Param image_uri query string false "A link to the image used for the professor on the professor's official university profile"
// @Param office_hours.start_date query string false "The start date of one of the office hours meetings of the professor"
// @Param office_hours.end_date query string false "The end date of one of the office hours meetings of the professor"
// @Param office_hours.meeting_days query string false "One of the days that one of the office hours meetings of the professor"
// @Param office_hours.start_time query string false "The time one of the office hours meetings of the professor starts"
// @Param office_hours.end_time query string false "The time one of the office hours meetings of the professor ends"
// @Param office_hours.modality query string false "The modality of one of the office hours meetings of the professor"
// @Param office_hours.location.building query string false "The building of one of the office hours meetings of the professor"
// @Param office_hours.location.room query string false "The room of one of the office hours meetings of the professor"
// @Param office_hours.location.map_uri query string false "A hyperlink to the UTD room locator of one of the office hours meetings of the professor"
// @Param sections query string false "The _id of one of the sections the professor teaches"
// @Success 200 {array} schema.Course "A list of Courses"
func ProfessorCourseSearch() gin.HandlerFunc {
// Wrapper of professorCourse() with flag of Search
return func(c *gin.Context) {
professorCourse("Search", c)
}
}

// @Id professorCourseById
// @Router /professor/{id} [get]
// @Description "Returns all the courses taught by the professor with given ID"
// @Produce json
// @Param id path string true "ID of the professor to get"
// @Success 200 {array} schema.Course "A list of courses"
func ProfessorCourseById() gin.HandlerFunc {
// Essentially wrapper of professorCourse() with flag of ById
return func(c *gin.Context) {
professorCourse("ById", c)
}
}

// Get all of the courses of the professors depending on the type of flag
func professorCourse(flag string, c *gin.Context) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)

var professorCourses []schema.Course // array of courses of the professors (or single professor with Id)
var professorQuery bson.M // query filter the professor
var err error

defer cancel()

if flag == "Search" {
// if the flag is Search, filter professors based on query parameters
professorQuery, err = schema.FilterQuery[schema.Professor](c)
if err != nil {
c.JSON(http.StatusBadRequest, responses.ErrorResponse{Status: http.StatusBadRequest, Message: "schema validation error", Data: err.Error()})
return
}
} else if flag == "ById" {
// if the flag is ById, filter that single professor based on their _id
// parse the ObjectId
professorId := c.Param("id")
professorObjId, err := primitive.ObjectIDFromHex(professorId)
if err != nil {
log.WriteError(err)
c.JSON(http.StatusBadRequest, responses.ErrorResponse{Status: http.StatusBadRequest, Message: "error", Data: err.Error()})
return
}
professorQuery = bson.M{"_id": professorObjId}
} else {
// something wrong that messed up the server
c.JSON(http.StatusInternalServerError, responses.ErrorResponse{Status: http.StatusInternalServerError, Message: "error", Data: "Endpoint broken"})
return
}

// TODO: ASK QUESTION ABOUT THE OPTION LIMIT

// Pipeline to query the courses from the filtered professors (or a single professor)
professorCoursePipeline := mongo.Pipeline{
// filter the professors
bson.D{{Key: "$match", Value: professorQuery}},

// lookup the array of sections from sections collection
bson.D{{Key: "$lookup", Value: bson.D{
{Key: "from", Value: "sections"},
{Key: "localField", Value: "sections"},
{Key: "foreignField", Value: "_id"},
{Key: "as", Value: "sections"},
}}},

// project the courses referenced by each section in the array
bson.D{{Key: "$project", Value: bson.D{{Key: "courses", Value: "$sections.course_reference"}}}},

// lookup the array of courses from coures collection
bson.D{{Key: "$lookup", Value: bson.D{
{Key: "from", Value: "courses"},
{Key: "localField", Value: "courses"},
{Key: "foreignField", Value: "_id"},
{Key: "as", Value: "courses"},
}}},

// unwind the courses
bson.D{{Key: "$unwind", Value: bson.D{
{Key: "path", Value: "$courses"},
{Key: "preserveNullAndEmptyArrays", Value: true},
}}},

// replace the combination of ids and courses with the courses entirely
bson.D{{Key: "$replaceWith", Value: "$courses"}},
}

// Perform aggreration on the pipeline
cursor, err := professorCollection.Aggregate(ctx, professorCoursePipeline)
if err != nil {
// return the error with there's something wrong with the aggregation
log.WriteError(err)
c.JSON(http.StatusInternalServerError, responses.ErrorResponse{Status: http.StatusInternalServerError, Message: "error", Data: err.Error()})
return
}
// Parse the array of courses from these professors
if err = cursor.All(ctx, &professorCourses); err != nil {
panic(err)
}
c.JSON(http.StatusOK, responses.MultiCourseResponse{Status: http.StatusOK, Message: "success", Data: professorCourses})
}
4 changes: 4 additions & 0 deletions api/routes/professor.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,8 @@ func ProfessorRoute(router *gin.Engine) {
professorGroup.GET("", controllers.ProfessorSearch)
professorGroup.GET(":id", controllers.ProfessorById)
professorGroup.GET("all", controllers.ProfessorAll)

// Endpoints to get the courses of the professors
professorGroup.GET("course", controllers.ProfessorCourseSearch())
professorGroup.GET(":id/course", controllers.ProfessorCourseById())
}

0 comments on commit b21fa85

Please sign in to comment.