From e7165b7f340f1b82689fe49993c4c1dae12497db Mon Sep 17 00:00:00 2001 From: Tyler Hill Date: Wed, 5 Feb 2025 16:38:55 -0600 Subject: [PATCH 1/6] Events route --- api/controllers/events.go | 92 ++++++++++++++++++++++++++++++++ api/controllers/rooms.go | 14 +++++ api/responses/events_response.go | 15 ++++++ api/responses/rooms_response.go | 9 ++++ api/routes/events.go | 16 ++++++ api/routes/rooms.go | 15 ++++++ api/schema/objects.go | 19 +++++++ api/server.go | 2 + 8 files changed, 182 insertions(+) create mode 100644 api/controllers/events.go create mode 100644 api/controllers/rooms.go create mode 100644 api/responses/events_response.go create mode 100644 api/responses/rooms_response.go create mode 100644 api/routes/events.go create mode 100644 api/routes/rooms.go diff --git a/api/controllers/events.go b/api/controllers/events.go new file mode 100644 index 00000000..39803bc4 --- /dev/null +++ b/api/controllers/events.go @@ -0,0 +1,92 @@ +package controllers + +import ( + "context" + "net/http" + "time" + + "github.com/UTDNebula/nebula-api/api/common/log" + "github.com/UTDNebula/nebula-api/api/configs" + "github.com/UTDNebula/nebula-api/api/responses" + "github.com/UTDNebula/nebula-api/api/schema" + + "github.com/gin-gonic/gin" + + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/mongo" +) + +var eventsCollection *mongo.Collection = configs.GetCollection("events") + +// @Id events +// @Router /events/{date} [get] +// @Description "Returns all sections with meetings on the specified date" +// @Produce json +// @Param date path string true "ISO date of the set of events to get" +// @Success 200 {array} schema.MultiBuildingEvents "All sections with meetings on the specified date" +func Events(c *gin.Context) { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + + date := c.Param("date") + + var events schema.MultiBuildingEvents + + defer cancel() + + // find and parse matching date + err := eventsCollection.FindOne(ctx, bson.M{"date": date}).Decode(&events) + if err != nil { + log.WriteError(err) + c.JSON(http.StatusInternalServerError, responses.ErrorResponse{Status: http.StatusInternalServerError, Message: "error", Data: err.Error()}) + return + } + + c.JSON(http.StatusOK, responses.MultiBuildingEventsResponse{Status: http.StatusOK, Message: "success", Data: events}) +} + +// @Id eventsByBuilding +// @Router /events/{date}/{building} [get] +// @Description "Returns all sections with meetings on the specified date in the specified building" +// @Produce json +// @Param date path string true "ISO date of the set of events to get" +// @Param building path string true "building abbreviation of event locations" +// @Success 200 {array} schema.SingleBuildingEvents "All sections with meetings on the specified date in the specified building" +func EventsByBuilding(c *gin.Context) { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + + date := c.Param("date") + building := c.Param("building") + + var events schema.MultiBuildingEvents + var eventsByBuilding schema.SingleBuildingEvents + + defer cancel() + + // find and parse matching date + err := eventsCollection.FindOne(ctx, bson.M{"date": date}).Decode(&events) + if err != nil { + log.WriteError(err) + c.JSON(http.StatusInternalServerError, responses.ErrorResponse{Status: http.StatusInternalServerError, Message: "error", Data: err.Error()}) + return + } + + // filter for the specified building + for _, b := range events.Buildings { + if b.Building == building { + eventsByBuilding = b + break + } + } + + // If no building is found, return an error + if eventsByBuilding.Building == "" { + c.JSON(http.StatusNotFound, responses.ErrorResponse{ + Status: http.StatusNotFound, + Message: "error", + Data: "No events found for the specified building", + }) + return + } + + c.JSON(http.StatusOK, responses.SingleBuildingEventsResponse{Status: http.StatusOK, Message: "success", Data: eventsByBuilding}) +} diff --git a/api/controllers/rooms.go b/api/controllers/rooms.go new file mode 100644 index 00000000..b9350709 --- /dev/null +++ b/api/controllers/rooms.go @@ -0,0 +1,14 @@ +package controllers + +import ( + "github.com/gin-gonic/gin" +) + +// @Id rooms +// @Router /rooms [get] +// @Description "Returns all classrooms being used in the current and futures semesters" +// @Produce json +// @Success 200 {array} schema.BuildingRooms "All classrooms being used in the current and futures semesters" +func Rooms(c *gin.Context) { + +} diff --git a/api/responses/events_response.go b/api/responses/events_response.go new file mode 100644 index 00000000..be1277f0 --- /dev/null +++ b/api/responses/events_response.go @@ -0,0 +1,15 @@ +package responses + +import "github.com/UTDNebula/nebula-api/api/schema" + +type MultiBuildingEventsResponse struct { + Status int `json:"status"` + Message string `json:"message"` + Data schema.MultiBuildingEvents `json:"data"` +} + +type SingleBuildingEventsResponse struct { + Status int `json:"status"` + Message string `json:"message"` + Data schema.SingleBuildingEvents `json:"data"` +} diff --git a/api/responses/rooms_response.go b/api/responses/rooms_response.go new file mode 100644 index 00000000..6453353d --- /dev/null +++ b/api/responses/rooms_response.go @@ -0,0 +1,9 @@ +package responses + +import "github.com/UTDNebula/nebula-api/api/schema" + +type RoomsResponse struct { + Status int `json:"status"` + Message string `json:"message"` + Data []schema.BuildingRooms `json:"data"` +} diff --git a/api/routes/events.go b/api/routes/events.go new file mode 100644 index 00000000..7efb100b --- /dev/null +++ b/api/routes/events.go @@ -0,0 +1,16 @@ +package routes + +import ( + "github.com/gin-gonic/gin" + + "github.com/UTDNebula/nebula-api/api/controllers" +) + +func EventsRoute(router *gin.Engine) { + // All routes related to sections come here + eventsGroup := router.Group("/events") + + eventsGroup.OPTIONS("", controllers.Preflight) + eventsGroup.GET(":date", controllers.Events) + eventsGroup.GET(":date/:building", controllers.EventsByBuilding) +} diff --git a/api/routes/rooms.go b/api/routes/rooms.go new file mode 100644 index 00000000..1a95f1bf --- /dev/null +++ b/api/routes/rooms.go @@ -0,0 +1,15 @@ +package routes + +import ( + "github.com/gin-gonic/gin" + + "github.com/UTDNebula/nebula-api/api/controllers" +) + +func RoomsRoute(router *gin.Engine) { + // All routes related to sections come here + roomsGroup := router.Group("/rooms") + + roomsGroup.OPTIONS("", controllers.Preflight) + roomsGroup.GET("", controllers.Rooms) +} diff --git a/api/schema/objects.go b/api/schema/objects.go index 7dda929b..ee1533ee 100644 --- a/api/schema/objects.go +++ b/api/schema/objects.go @@ -118,6 +118,25 @@ type Event struct { ContactPhoneNumber string `bson:"contact_phone_number" json:"contact_phone_number"` } +type MultiBuildingEvents struct { + Buildings []SingleBuildingEvents `bson:"buildings" json:"buildings"` +} + +type SingleBuildingEvents struct { + Building string `bson:"building" json:"building"` + Rooms []RoomEvents `bson:"rooms" json:"rooms"` +} + +type RoomEvents struct { + Room string `bson:"room" json:"room"` + Sections []primitive.ObjectID `bson:"sections" json:"sections"` +} + +type BuildingRooms struct { + Building string `bson:"building" json:"building"` + Rooms []string `bson:"rooms" json:"rooms"` +} + // 5 Level Likert Item scale for evaluation responses type EvaluationResponse int diff --git a/api/server.go b/api/server.go index cde5fd14..95fc2e59 100644 --- a/api/server.go +++ b/api/server.go @@ -60,6 +60,8 @@ func main() { routes.GradesRoute(router) routes.AutocompleteRoute(router) routes.StorageRoute(router) + routes.RoomsRoute(router) + routes.EventsRoute(router) // Retrieve the port string to serve traffic on portString := configs.GetPortString() From 4454c3e347e0173a856b5fc6d80983022dd42c3e Mon Sep 17 00:00:00 2001 From: Tyler Hill Date: Wed, 5 Feb 2025 17:49:58 -0600 Subject: [PATCH 2/6] Include start and end time in response --- api/schema/objects.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/api/schema/objects.go b/api/schema/objects.go index ee1533ee..b01b15de 100644 --- a/api/schema/objects.go +++ b/api/schema/objects.go @@ -128,8 +128,14 @@ type SingleBuildingEvents struct { } type RoomEvents struct { - Room string `bson:"room" json:"room"` - Sections []primitive.ObjectID `bson:"sections" json:"sections"` + Room string `bson:"room" json:"room"` + Sections []SectionWithTime `bson:"sections" json:"sections"` +} + +type SectionWithTime struct { + Section primitive.ObjectID `bson:"section" json:"section"` + StartTime string `bson:"start_time" json:"start_time"` + EndTime string `bson:"end_time" json:"end_time"` } type BuildingRooms struct { From f165307846e649d19e9f79b93cf32ca1d36e1232 Mon Sep 17 00:00:00 2001 From: Tyler Hill Date: Fri, 14 Feb 2025 22:06:10 -0600 Subject: [PATCH 3/6] Generic events hierarchy --- api/controllers/events.go | 14 +++++++------- api/responses/events_response.go | 16 ++++++++-------- api/schema/objects.go | 21 +++++++++++---------- 3 files changed, 26 insertions(+), 25 deletions(-) diff --git a/api/controllers/events.go b/api/controllers/events.go index 39803bc4..3eb6c84f 100644 --- a/api/controllers/events.go +++ b/api/controllers/events.go @@ -23,13 +23,13 @@ var eventsCollection *mongo.Collection = configs.GetCollection("events") // @Description "Returns all sections with meetings on the specified date" // @Produce json // @Param date path string true "ISO date of the set of events to get" -// @Success 200 {array} schema.MultiBuildingEvents "All sections with meetings on the specified date" +// @Success 200 {array} schema.MultiBuildingEvents[schema.SectionWithTime] "All sections with meetings on the specified date" func Events(c *gin.Context) { ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) date := c.Param("date") - var events schema.MultiBuildingEvents + var events schema.MultiBuildingEvents[schema.SectionWithTime] defer cancel() @@ -41,7 +41,7 @@ func Events(c *gin.Context) { return } - c.JSON(http.StatusOK, responses.MultiBuildingEventsResponse{Status: http.StatusOK, Message: "success", Data: events}) + c.JSON(http.StatusOK, responses.MultiBuildingEventsResponse[schema.SectionWithTime]{Status: http.StatusOK, Message: "success", Data: events}) } // @Id eventsByBuilding @@ -50,15 +50,15 @@ func Events(c *gin.Context) { // @Produce json // @Param date path string true "ISO date of the set of events to get" // @Param building path string true "building abbreviation of event locations" -// @Success 200 {array} schema.SingleBuildingEvents "All sections with meetings on the specified date in the specified building" +// @Success 200 {array} schema.SingleBuildingEvents[schema.SectionWithTime] "All sections with meetings on the specified date in the specified building" func EventsByBuilding(c *gin.Context) { ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) date := c.Param("date") building := c.Param("building") - var events schema.MultiBuildingEvents - var eventsByBuilding schema.SingleBuildingEvents + var events schema.MultiBuildingEvents[schema.SectionWithTime] + var eventsByBuilding schema.SingleBuildingEvents[schema.SectionWithTime] defer cancel() @@ -88,5 +88,5 @@ func EventsByBuilding(c *gin.Context) { return } - c.JSON(http.StatusOK, responses.SingleBuildingEventsResponse{Status: http.StatusOK, Message: "success", Data: eventsByBuilding}) + c.JSON(http.StatusOK, responses.SingleBuildingEventsResponse[schema.SectionWithTime]{Status: http.StatusOK, Message: "success", Data: eventsByBuilding}) } diff --git a/api/responses/events_response.go b/api/responses/events_response.go index be1277f0..a9a80c12 100644 --- a/api/responses/events_response.go +++ b/api/responses/events_response.go @@ -2,14 +2,14 @@ package responses import "github.com/UTDNebula/nebula-api/api/schema" -type MultiBuildingEventsResponse struct { - Status int `json:"status"` - Message string `json:"message"` - Data schema.MultiBuildingEvents `json:"data"` +type MultiBuildingEventsResponse[T any] struct { + Status int `json:"status"` + Message string `json:"message"` + Data schema.MultiBuildingEvents[T] `json:"data"` } -type SingleBuildingEventsResponse struct { - Status int `json:"status"` - Message string `json:"message"` - Data schema.SingleBuildingEvents `json:"data"` +type SingleBuildingEventsResponse[T any] struct { + Status int `json:"status"` + Message string `json:"message"` + Data schema.SingleBuildingEvents[T] `json:"data"` } diff --git a/api/schema/objects.go b/api/schema/objects.go index b01b15de..6c9719a4 100644 --- a/api/schema/objects.go +++ b/api/schema/objects.go @@ -118,26 +118,27 @@ type Event struct { ContactPhoneNumber string `bson:"contact_phone_number" json:"contact_phone_number"` } -type MultiBuildingEvents struct { - Buildings []SingleBuildingEvents `bson:"buildings" json:"buildings"` +// Event hierarchy +type MultiBuildingEvents[T any] struct { + Buildings []SingleBuildingEvents[T] `bson:"buildings" json:"buildings"` } - -type SingleBuildingEvents struct { - Building string `bson:"building" json:"building"` - Rooms []RoomEvents `bson:"rooms" json:"rooms"` +type SingleBuildingEvents[T any] struct { + Building string `bson:"building" json:"building"` + Rooms []RoomEvents[T] `bson:"rooms" json:"rooms"` } - -type RoomEvents struct { - Room string `bson:"room" json:"room"` - Sections []SectionWithTime `bson:"sections" json:"sections"` +type RoomEvents[T any] struct { + Room string `bson:"room" json:"room"` + Sections []T `bson:"events" json:"events"` } +// Event types type SectionWithTime struct { Section primitive.ObjectID `bson:"section" json:"section"` StartTime string `bson:"start_time" json:"start_time"` EndTime string `bson:"end_time" json:"end_time"` } +// Rooms type type BuildingRooms struct { Building string `bson:"building" json:"building"` Rooms []string `bson:"rooms" json:"rooms"` From ac422baf0d13a3810316fa6ed2f8cbf1f36c07ab Mon Sep 17 00:00:00 2001 From: Tyler Hill Date: Fri, 14 Feb 2025 22:08:24 -0600 Subject: [PATCH 4/6] Forgot a rename --- api/schema/objects.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/schema/objects.go b/api/schema/objects.go index 6c9719a4..f5325a6b 100644 --- a/api/schema/objects.go +++ b/api/schema/objects.go @@ -127,8 +127,8 @@ type SingleBuildingEvents[T any] struct { Rooms []RoomEvents[T] `bson:"rooms" json:"rooms"` } type RoomEvents[T any] struct { - Room string `bson:"room" json:"room"` - Sections []T `bson:"events" json:"events"` + Room string `bson:"room" json:"room"` + Events []T `bson:"events" json:"events"` } // Event types From 095c440301bd3f016a1fd32d41db16b1c618f22b Mon Sep 17 00:00:00 2001 From: recursiveTurtle Date: Tue, 18 Feb 2025 11:27:01 -0600 Subject: [PATCH 5/6] rooms controller --- api/controllers/rooms.go | 41 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/api/controllers/rooms.go b/api/controllers/rooms.go index b9350709..f82cf964 100644 --- a/api/controllers/rooms.go +++ b/api/controllers/rooms.go @@ -1,14 +1,55 @@ package controllers import ( + "context" + "net/http" + "time" + "github.com/gin-gonic/gin" + + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/mongo" + + "github.com/UTDNebula/nebula-api/api/configs" + "github.com/UTDNebula/nebula-api/api/responses" + "github.com/UTDNebula/nebula-api/api/schema" ) +// var roomsCollection *mongo.Collection = configs.GetCollection("rooms") +var buildingCollection *mongo.Collection = configs.GetCollection("building") + // @Id rooms // @Router /rooms [get] // @Description "Returns all classrooms being used in the current and futures semesters" // @Produce json // @Success 200 {array} schema.BuildingRooms "All classrooms being used in the current and futures semesters" func Rooms(c *gin.Context) { + //gin context has info about request and allows converting to json format + + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() //make resources available if Rooms returns before timeout + + var buildingRooms []schema.BuildingRooms //buildings and rooms to be returned + + //cursor is the pointer for the returned documents + cursor, err := buildingCollection.Find(ctx, bson.M{}) + + //if there is an error + if err != nil { + //serialize ErrorResponse struct data into JSON format + c.JSON(http.StatusInternalServerError, responses.ErrorResponse{Status: http.StatusInternalServerError, Message: "error", Data: err.Error()}) + return + } + + //use cursor to fill rooms slice to return + err = cursor.All(ctx, &buildingRooms) + + //if there is an error filling rooms slice + if err != nil { + c.JSON(http.StatusInternalServerError, responses.ErrorResponse{Status: http.StatusInternalServerError, Message: "error", Data: err.Error()}) + return + } + //serialize RoomsResponse struct data into JSON format + c.JSON(http.StatusOK, responses.RoomsResponse{Status: http.StatusOK, Message: "success", Data: buildingRooms}) } From 17aab80d33c187321903c5c2c2827c82e033d56b Mon Sep 17 00:00:00 2001 From: Tyler Hill Date: Tue, 18 Feb 2025 13:47:20 -0600 Subject: [PATCH 6/6] change collection name --- api/controllers/rooms.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/api/controllers/rooms.go b/api/controllers/rooms.go index f82cf964..8a2d286b 100644 --- a/api/controllers/rooms.go +++ b/api/controllers/rooms.go @@ -15,8 +15,7 @@ import ( "github.com/UTDNebula/nebula-api/api/schema" ) -// var roomsCollection *mongo.Collection = configs.GetCollection("rooms") -var buildingCollection *mongo.Collection = configs.GetCollection("building") +var buildingCollection *mongo.Collection = configs.GetCollection("rooms") // @Id rooms // @Router /rooms [get]