diff --git a/api/controllers/budget.go b/api/controllers/budget.go new file mode 100644 index 0000000..e7e184d --- /dev/null +++ b/api/controllers/budget.go @@ -0,0 +1,51 @@ +package controllers + +import ( + "context" + "errors" + "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/schema" +) + +var budgetCollection *mongo.Collection = configs.GetCollection("budgets") + +// @Id Budget +// @Router /budget/{year} [get] +// @Tags Other +// @Description "Returns discal year Budget based on the input year" +// @Produce json +// @Param year path string true "year to retrieve budget for" +// @Success 200 {object} schema.APIResponse[schema.Budget] "Single budget from the given fiscal year" +// @Failure 500 {object} schema.APIResponse[string] "A string describing the error" +// @Failure 404 {object} schema.APIResponse[string] "A string describing the error" +func Budget(c *gin.Context) { + ctx, cancel := context.WithTimeout(c.Request.Context(), 10*time.Second) + defer cancel() + + year := c.Param("year") + + var budget schema.Budget + + // Find budget given date + err := budgetCollection.FindOne(ctx, bson.M{"_id": year}).Decode(&budget) + if err != nil { + if errors.Is(err, mongo.ErrNoDocuments) { + respond(c, http.StatusNotFound, "error", "No budgets found for the specified fiscal year") + return + } else { + respondWithInternalError(c, err) + return + } + } + + respond(c, http.StatusOK, "success", budget) +} diff --git a/api/docs/docs.go b/api/docs/docs.go index 10b92f1..2305274 100644 --- a/api/docs/docs.go +++ b/api/docs/docs.go @@ -179,6 +179,47 @@ const docTemplate = `{ } } }, + "/budget/{year}": { + "get": { + "description": "\"Returns discal year Budget based on the input year\"", + "produces": [ + "application/json" + ], + "tags": [ + "Other" + ], + "operationId": "Budget", + "parameters": [ + { + "type": "string", + "description": "year to retrieve budget for", + "name": "year", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "Single budget from the given fiscal year", + "schema": { + "$ref": "#/definitions/schema.APIResponse-schema_Budget" + } + }, + "404": { + "description": "A string describing the error", + "schema": { + "$ref": "#/definitions/schema.APIResponse-string" + } + }, + "500": { + "description": "A string describing the error", + "schema": { + "$ref": "#/definitions/schema.APIResponse-string" + } + } + } + } + }, "/calendar/{date}": { "get": { "description": "\"Returns CometCalendarEvent based on the input date\"", @@ -317,9 +358,6 @@ const docTemplate = `{ } } }, - "/combined/sections/trends": { - "get": { - "description": "\"Returns sections matching both course and professor criteria from the combined trends collection. Specialized high-speed convenience endpoint for UTD Trends internal use; limited query flexibility.\"", "/club/search": { "get": { "description": "\"Returns list of clubs matching the search string\"", @@ -327,36 +365,6 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Combined" - ], - "operationId": "trendsCombinedSectionSearch", - "parameters": [ - { - "type": "string", - "description": "The course's official number", - "name": "course_number", - "in": "query", - "required": true - }, - { - "type": "string", - "description": "The course's subject prefix", - "name": "subject_prefix", - "in": "query", - "required": true - }, - { - "type": "string", - "description": "The professor's first name", - "name": "first_name", - "in": "query", - "required": true - }, - { - "type": "string", - "description": "The professor's last name", - "name": "last_name", - "in": "query", "Clubs" ], "operationId": "clubSearch", @@ -412,9 +420,6 @@ const docTemplate = `{ ], "responses": { "200": { - "description": "A list of Sections", - "schema": { - "$ref": "#/definitions/schema.APIResponse-array_schema_Section" "description": "A club", "schema": { "$ref": "#/definitions/schema.APIResponse-schema_Club" @@ -435,6 +440,62 @@ const docTemplate = `{ } } }, + "/combined/sections/trends": { + "get": { + "description": "\"Returns sections matching both course and professor criteria from the combined trends collection. Specialized high-speed convenience endpoint for UTD Trends internal use; limited query flexibility.\"", + "produces": [ + "application/json" + ], + "tags": [ + "Combined" + ], + "operationId": "trendsCombinedSectionSearch", + "parameters": [ + { + "type": "string", + "description": "The course's official number", + "name": "course_number", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "The course's subject prefix", + "name": "subject_prefix", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "The professor's first name", + "name": "first_name", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "The professor's last name", + "name": "last_name", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "A list of Sections", + "schema": { + "$ref": "#/definitions/schema.APIResponse-array_schema_Section" + } + }, + "500": { + "description": "A string describing the error", + "schema": { + "$ref": "#/definitions/schema.APIResponse-string" + } + } + } + } + }, "/course": { "get": { "description": "\"Returns paginated list of courses matching the query's string-typed key-value pairs. See offset for more details on pagination.\"", @@ -3626,6 +3687,20 @@ const docTemplate = `{ } } }, + "schema.APIResponse-schema_Budget": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/schema.Budget" + }, + "message": { + "type": "string" + }, + "status": { + "type": "integer" + } + } + }, "schema.APIResponse-schema_Club": { "type": "object", "properties": { @@ -3892,6 +3967,26 @@ const docTemplate = `{ } } }, + "schema.AnnualFinancialReport": { + "type": "object", + "properties": { + "beginning_net_position": { + "type": "number" + }, + "ending_net_position": { + "type": "number" + }, + "nonoperating_revenues": { + "$ref": "#/definitions/schema.Table-float64" + }, + "operating_expenses": { + "$ref": "#/definitions/schema.Table-float64" + }, + "operating_revenues": { + "$ref": "#/definitions/schema.Table-float64" + } + } + }, "schema.Assistant": { "type": "object", "properties": { @@ -3952,6 +4047,23 @@ const docTemplate = `{ } } }, + "schema.AuxiliaryExpensesValues": { + "type": "object", + "properties": { + "budgeted_expenses": { + "type": "number" + }, + "debt_service": { + "type": "number" + }, + "estimated_income": { + "type": "number" + }, + "other": { + "type": "number" + } + } + }, "schema.BasicCourse": { "type": "object", "properties": { @@ -4030,6 +4142,20 @@ const docTemplate = `{ } } }, + "schema.Budget": { + "type": "object", + "properties": { + "_id": { + "type": "string" + }, + "annual_financial_report": { + "$ref": "#/definitions/schema.AnnualFinancialReport" + }, + "operating_budget": { + "$ref": "#/definitions/schema.OperatingBudget" + } + } + }, "schema.BuildingRooms": { "type": "object", "properties": { @@ -4361,6 +4487,17 @@ const docTemplate = `{ } } }, + "schema.FundsValues": { + "type": "object", + "properties": { + "budgeted_expenses": { + "type": "number" + }, + "estimated_income": { + "type": "number" + } + } + }, "schema.GradeData": { "type": "object", "properties": { @@ -4564,6 +4701,38 @@ const docTemplate = `{ } } }, + "schema.OperatingBudget": { + "type": "object", + "properties": { + "auxiliary_expenses": { + "$ref": "#/definitions/schema.Table2-schema_AuxiliaryExpensesValues" + }, + "budgeted_nonoperating_revenues": { + "$ref": "#/definitions/schema.Table-float64" + }, + "budgeted_tuition_and_student_fees": { + "$ref": "#/definitions/schema.Table2-float64" + }, + "designated_funds": { + "$ref": "#/definitions/schema.Table2-schema_FundsValues" + }, + "operating_expenses": { + "$ref": "#/definitions/schema.Table-float64" + }, + "operating_revenues": { + "$ref": "#/definitions/schema.Table-float64" + }, + "restricted_funds": { + "$ref": "#/definitions/schema.Table2-schema_FundsValues" + }, + "salaries_doe_and_instructional_admin": { + "$ref": "#/definitions/schema.Table-schema_SalariesDoeAndInstructionalAdminValues" + }, + "service_departments_funds": { + "$ref": "#/definitions/schema.Table2-schema_FundsValues" + } + } + }, "schema.Professor": { "type": "object", "properties": { @@ -4692,6 +4861,67 @@ const docTemplate = `{ } } }, + "schema.Row-float64": { + "type": "object", + "properties": { + "label": { + "type": "string" + }, + "value": { + "type": "number" + } + } + }, + "schema.Row-schema_AuxiliaryExpensesValues": { + "type": "object", + "properties": { + "label": { + "type": "string" + }, + "value": { + "$ref": "#/definitions/schema.AuxiliaryExpensesValues" + } + } + }, + "schema.Row-schema_FundsValues": { + "type": "object", + "properties": { + "label": { + "type": "string" + }, + "value": { + "$ref": "#/definitions/schema.FundsValues" + } + } + }, + "schema.Row-schema_SalariesDoeAndInstructionalAdminValues": { + "type": "object", + "properties": { + "label": { + "type": "string" + }, + "value": { + "$ref": "#/definitions/schema.SalariesDoeAndInstructionalAdminValues" + } + } + }, + "schema.SalariesDoeAndInstructionalAdminValues": { + "type": "object", + "properties": { + "departmental_operating_expenses": { + "type": "number" + }, + "faculty_salaries": { + "type": "number" + }, + "instructional_administration": { + "type": "number" + }, + "total": { + "type": "number" + } + } + }, "schema.Section": { "type": "object", "properties": { @@ -4872,6 +5102,125 @@ const docTemplate = `{ } } }, + "schema.Table-float64": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "rows": { + "type": "array", + "items": { + "$ref": "#/definitions/schema.Row-float64" + } + }, + "total": { + "type": "number" + } + } + }, + "schema.Table-schema_AuxiliaryExpensesValues": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "rows": { + "type": "array", + "items": { + "$ref": "#/definitions/schema.Row-schema_AuxiliaryExpensesValues" + } + }, + "total": { + "$ref": "#/definitions/schema.AuxiliaryExpensesValues" + } + } + }, + "schema.Table-schema_FundsValues": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "rows": { + "type": "array", + "items": { + "$ref": "#/definitions/schema.Row-schema_FundsValues" + } + }, + "total": { + "$ref": "#/definitions/schema.FundsValues" + } + } + }, + "schema.Table-schema_SalariesDoeAndInstructionalAdminValues": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "rows": { + "type": "array", + "items": { + "$ref": "#/definitions/schema.Row-schema_SalariesDoeAndInstructionalAdminValues" + } + }, + "total": { + "$ref": "#/definitions/schema.SalariesDoeAndInstructionalAdminValues" + } + } + }, + "schema.Table2-float64": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "rows": { + "type": "array", + "items": { + "$ref": "#/definitions/schema.Table-float64" + } + }, + "total": { + "type": "number" + } + } + }, + "schema.Table2-schema_AuxiliaryExpensesValues": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "rows": { + "type": "array", + "items": { + "$ref": "#/definitions/schema.Table-schema_AuxiliaryExpensesValues" + } + }, + "total": { + "$ref": "#/definitions/schema.AuxiliaryExpensesValues" + } + } + }, + "schema.Table2-schema_FundsValues": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "rows": { + "type": "array", + "items": { + "$ref": "#/definitions/schema.Table-schema_FundsValues" + } + }, + "total": { + "$ref": "#/definitions/schema.FundsValues" + } + } + }, "schema.TypedGradeData": { "type": "object", "properties": { diff --git a/api/docs/swagger.yaml b/api/docs/swagger.yaml index d7a34d4..261100b 100644 --- a/api/docs/swagger.yaml +++ b/api/docs/swagger.yaml @@ -138,6 +138,15 @@ definitions: status: type: integer type: object + schema.APIResponse-schema_Budget: + properties: + data: + $ref: '#/definitions/schema.Budget' + message: + type: string + status: + type: integer + type: object schema.APIResponse-schema_Club: properties: data: @@ -309,6 +318,19 @@ definitions: $ref: '#/definitions/schema.SectionNumberProfessors' type: array type: object + schema.AnnualFinancialReport: + properties: + beginning_net_position: + type: number + ending_net_position: + type: number + nonoperating_revenues: + $ref: '#/definitions/schema.Table-float64' + operating_expenses: + $ref: '#/definitions/schema.Table-float64' + operating_revenues: + $ref: '#/definitions/schema.Table-float64' + type: object schema.Assistant: properties: email: @@ -348,6 +370,17 @@ definitions: subject_prefix: type: string type: object + schema.AuxiliaryExpensesValues: + properties: + budgeted_expenses: + type: number + debt_service: + type: number + estimated_income: + type: number + other: + type: number + type: object schema.BasicCourse: properties: _id: @@ -399,6 +432,15 @@ definitions: updated: type: string type: object + schema.Budget: + properties: + _id: + type: string + annual_financial_report: + $ref: '#/definitions/schema.AnnualFinancialReport' + operating_budget: + $ref: '#/definitions/schema.OperatingBudget' + type: object schema.BuildingRooms: properties: building: @@ -618,6 +660,13 @@ definitions: type: string type: array type: object + schema.FundsValues: + properties: + budgeted_expenses: + type: number + estimated_income: + type: number + type: object schema.GradeData: properties: _id: @@ -751,6 +800,27 @@ definitions: description: method to be used with signed URL. For example, PUT type: string type: object + schema.OperatingBudget: + properties: + auxiliary_expenses: + $ref: '#/definitions/schema.Table2-schema_AuxiliaryExpensesValues' + budgeted_nonoperating_revenues: + $ref: '#/definitions/schema.Table-float64' + budgeted_tuition_and_student_fees: + $ref: '#/definitions/schema.Table2-float64' + designated_funds: + $ref: '#/definitions/schema.Table2-schema_FundsValues' + operating_expenses: + $ref: '#/definitions/schema.Table-float64' + operating_revenues: + $ref: '#/definitions/schema.Table-float64' + restricted_funds: + $ref: '#/definitions/schema.Table2-schema_FundsValues' + salaries_doe_and_instructional_admin: + $ref: '#/definitions/schema.Table-schema_SalariesDoeAndInstructionalAdminValues' + service_departments_funds: + $ref: '#/definitions/schema.Table2-schema_FundsValues' + type: object schema.Professor: properties: _id: @@ -834,6 +904,45 @@ definitions: room: type: string type: object + schema.Row-float64: + properties: + label: + type: string + value: + type: number + type: object + schema.Row-schema_AuxiliaryExpensesValues: + properties: + label: + type: string + value: + $ref: '#/definitions/schema.AuxiliaryExpensesValues' + type: object + schema.Row-schema_FundsValues: + properties: + label: + type: string + value: + $ref: '#/definitions/schema.FundsValues' + type: object + schema.Row-schema_SalariesDoeAndInstructionalAdminValues: + properties: + label: + type: string + value: + $ref: '#/definitions/schema.SalariesDoeAndInstructionalAdminValues' + type: object + schema.SalariesDoeAndInstructionalAdminValues: + properties: + departmental_operating_expenses: + type: number + faculty_salaries: + type: number + instructional_administration: + type: number + total: + type: number + type: object schema.Section: properties: _id: @@ -952,6 +1061,83 @@ definitions: $ref: '#/definitions/schema.RoomEvents-schema_SectionWithTime' type: array type: object + schema.Table-float64: + properties: + name: + type: string + rows: + items: + $ref: '#/definitions/schema.Row-float64' + type: array + total: + type: number + type: object + schema.Table-schema_AuxiliaryExpensesValues: + properties: + name: + type: string + rows: + items: + $ref: '#/definitions/schema.Row-schema_AuxiliaryExpensesValues' + type: array + total: + $ref: '#/definitions/schema.AuxiliaryExpensesValues' + type: object + schema.Table-schema_FundsValues: + properties: + name: + type: string + rows: + items: + $ref: '#/definitions/schema.Row-schema_FundsValues' + type: array + total: + $ref: '#/definitions/schema.FundsValues' + type: object + schema.Table-schema_SalariesDoeAndInstructionalAdminValues: + properties: + name: + type: string + rows: + items: + $ref: '#/definitions/schema.Row-schema_SalariesDoeAndInstructionalAdminValues' + type: array + total: + $ref: '#/definitions/schema.SalariesDoeAndInstructionalAdminValues' + type: object + schema.Table2-float64: + properties: + name: + type: string + rows: + items: + $ref: '#/definitions/schema.Table-float64' + type: array + total: + type: number + type: object + schema.Table2-schema_AuxiliaryExpensesValues: + properties: + name: + type: string + rows: + items: + $ref: '#/definitions/schema.Table-schema_AuxiliaryExpensesValues' + type: array + total: + $ref: '#/definitions/schema.AuxiliaryExpensesValues' + type: object + schema.Table2-schema_FundsValues: + properties: + name: + type: string + rows: + items: + $ref: '#/definitions/schema.Table-schema_FundsValues' + type: array + total: + $ref: '#/definitions/schema.FundsValues' + type: object schema.TypedGradeData: properties: _id: @@ -1089,6 +1275,33 @@ paths: $ref: '#/definitions/schema.APIResponse-string' tags: - Other + /budget/{year}: + get: + description: '"Returns discal year Budget based on the input year"' + operationId: Budget + parameters: + - description: year to retrieve budget for + in: path + name: year + required: true + type: string + produces: + - application/json + responses: + "200": + description: Single budget from the given fiscal year + schema: + $ref: '#/definitions/schema.APIResponse-schema_Budget' + "404": + description: A string describing the error + schema: + $ref: '#/definitions/schema.APIResponse-string' + "500": + description: A string describing the error + schema: + $ref: '#/definitions/schema.APIResponse-string' + tags: + - Other /calendar/{date}: get: description: '"Returns CometCalendarEvent based on the input date"' @@ -1184,31 +1397,6 @@ paths: $ref: '#/definitions/schema.APIResponse-string' tags: - Events - /combined/sections/trends: - get: - description: '"Returns sections matching both course and professor criteria - from the combined trends collection. Specialized high-speed convenience endpoint - for UTD Trends internal use; limited query flexibility."' - operationId: trendsCombinedSectionSearch - parameters: - - description: The course's official number - in: query - name: course_number - required: true - type: string - - description: The course's subject prefix - in: query - name: subject_prefix - required: true - type: string - - description: The professor's first name - in: query - name: first_name - required: true - type: string - - description: The professor's last name - in: query - name: last_name /club/{id}: get: description: '"Returns the directory info for given club."' @@ -1250,9 +1438,6 @@ paths: - application/json responses: "200": - description: A list of Sections - schema: - $ref: '#/definitions/schema.APIResponse-array_schema_Section' description: List of matching clubs schema: $ref: '#/definitions/schema.APIResponse-array_schema_Club' @@ -1265,8 +1450,47 @@ paths: schema: $ref: '#/definitions/schema.APIResponse-string' tags: - - Combined - Clubs + /combined/sections/trends: + get: + description: '"Returns sections matching both course and professor criteria + from the combined trends collection. Specialized high-speed convenience endpoint + for UTD Trends internal use; limited query flexibility."' + operationId: trendsCombinedSectionSearch + parameters: + - description: The course's official number + in: query + name: course_number + required: true + type: string + - description: The course's subject prefix + in: query + name: subject_prefix + required: true + type: string + - description: The professor's first name + in: query + name: first_name + required: true + type: string + - description: The professor's last name + in: query + name: last_name + required: true + type: string + produces: + - application/json + responses: + "200": + description: A list of Sections + schema: + $ref: '#/definitions/schema.APIResponse-array_schema_Section' + "500": + description: A string describing the error + schema: + $ref: '#/definitions/schema.APIResponse-string' + tags: + - Combined /course: get: description: '"Returns paginated list of courses matching the query''s string-typed diff --git a/api/routes/budget.go b/api/routes/budget.go new file mode 100644 index 0000000..1dbce53 --- /dev/null +++ b/api/routes/budget.go @@ -0,0 +1,14 @@ +package routes + +import ( + "github.com/UTDNebula/nebula-api/api/controllers" + "github.com/gin-gonic/gin" +) + +func BudgetRoute(router *gin.Engine) { + //All routes related to budgets come here + budgetGroup := router.Group("/budget") + + budgetGroup.OPTIONS("", controllers.Preflight) + budgetGroup.GET(":year", controllers.Budget) +} diff --git a/api/server.go b/api/server.go index daee36e..6d70f98 100644 --- a/api/server.go +++ b/api/server.go @@ -90,6 +90,7 @@ func main() { routes.ClubRoute(router) routes.DiscountRoutes(router) routes.EmailRoute(router) + routes.BudgetRoute(router) // Retrieve the port string to serve traffic on portString := configs.GetPortString()