aboutsummaryrefslogtreecommitdiff
path: root/endpoint_newcourses.go
diff options
context:
space:
mode:
Diffstat (limited to 'endpoint_newcourses.go')
-rw-r--r--endpoint_newcourses.go217
1 files changed, 55 insertions, 162 deletions
diff --git a/endpoint_newcourses.go b/endpoint_newcourses.go
index 9f26b73..e11ed1e 100644
--- a/endpoint_newcourses.go
+++ b/endpoint_newcourses.go
@@ -33,84 +33,44 @@ import (
"github.com/jackc/pgx/v5"
)
-func handleNewCourses(w http.ResponseWriter, req *http.Request) {
+func handleNewCourses(w http.ResponseWriter, req *http.Request) (string, int, error) {
if req.Method != http.MethodPost {
- wstr(w, http.StatusMethodNotAllowed, "Only POST is allowed here")
- return
+ return "", http.StatusMethodNotAllowed, errPostOnly
}
_, _, department, err := getUserInfoFromRequest(req)
if err != nil {
- wstr(
- w,
- http.StatusInternalServerError,
- fmt.Sprintf("Error: %v", err),
- )
+ return "", -1, err
}
if department != staffDepartment {
- wstr(
- w,
- http.StatusForbidden,
- "You are not authorized to view this page",
- )
- return
+ return "", http.StatusForbidden, errStaffOnly
}
if atomic.LoadUint32(&state) != 0 {
- wstr(
- w,
- http.StatusBadRequest,
- "Uploading the course table is only supported when student-access is disabled",
- )
- return
+ return "", http.StatusBadRequest, errDisableStudentAccessFirst
}
/* TODO: Potential race. The global state may need to be write-locked. */
file, fileHeader, err := req.FormFile("coursecsv")
if err != nil {
- wstr(
- w,
- http.StatusBadRequest,
- "Failed loading file from request... did you select a file before hitting that red button?",
- )
- return
+ return "", http.StatusBadRequest, wrapError(errFormNoFile, err)
}
if fileHeader.Header.Get("Content-Type") != "text/csv" {
- wstr(
- w,
- http.StatusBadRequest,
- "Does not look like a proper CSV file",
- )
- return
+ return "", http.StatusBadRequest, errNotACSV
}
csvReader := csv.NewReader(file)
titleLine, err := csvReader.Read()
if err != nil {
- wstr(
- w,
- http.StatusBadRequest,
- "Error reading CSV",
- )
- return
+ return "", http.StatusBadRequest, wrapError(errCannotReadCSV, err)
}
if titleLine == nil {
- wstr(
- w,
- http.StatusInternalServerError,
- "Unexpected nil titleLine slice",
- )
- return
+ return "", -1, errUnexpectedNilCSVLine
}
if len(titleLine) != 8 {
- wstr(
- w,
- http.StatusBadRequest,
- "First line has more than 8 elements",
- )
- return
+ return "", -1, wrapAny(errBadCSVFormat, "expecting 8 fields on the first line")
}
var titleIndex, maxIndex, teacherIndex, locationIndex,
typeIndex, groupIndex, sectionIDIndex,
@@ -136,66 +96,41 @@ func handleNewCourses(w http.ResponseWriter, req *http.Request) {
}
}
- {
- check := func(indexName string, indexNum int) bool {
- if indexNum == -1 {
- wstr(
- w,
- http.StatusBadRequest,
- fmt.Sprintf(
- "Missing column \"%s\"",
- indexName,
- ),
- )
- return true
- }
- return false
- }
-
- if check("Title", titleIndex) {
- return
- }
- if check("Max", maxIndex) {
- return
- }
- if check("Teacher", teacherIndex) {
- return
- }
- if check("Location", locationIndex) {
- return
- }
- if check("Type", typeIndex) {
- return
- }
- if check("Group", groupIndex) {
- return
- }
- if check("Course ID", courseIDIndex) {
- return
- }
- if check("Section ID", sectionIDIndex) {
- return
- }
+ if titleIndex == -1 {
+ return "", http.StatusBadRequest, wrapAny(errMissingCSVColumn, "Title")
+ }
+ if maxIndex == -1 {
+ return "", http.StatusBadRequest, wrapAny(errMissingCSVColumn, "Max")
+ }
+ if teacherIndex == -1 {
+ return "", http.StatusBadRequest, wrapAny(errMissingCSVColumn, "Teacher")
+ }
+ if locationIndex == -1 {
+ return "", http.StatusBadRequest, wrapAny(errMissingCSVColumn, "Location")
+ }
+ if typeIndex == -1 {
+ return "", http.StatusBadRequest, wrapAny(errMissingCSVColumn, "Type")
+ }
+ if groupIndex == -1 {
+ return "", http.StatusBadRequest, wrapAny(errMissingCSVColumn, "Group")
+ }
+ if courseIDIndex == -1 {
+ return "", http.StatusBadRequest, wrapAny(errMissingCSVColumn, "Course ID")
+ }
+ if sectionIDIndex == -1 {
+ return "", http.StatusBadRequest, wrapAny(errMissingCSVColumn, "Section ID")
}
lineNumber := 1
- ok := func(ctx context.Context) bool {
+ ok, statusCode, err := func(ctx context.Context) (retBool bool, retStatus int, retErr error) {
tx, err := db.Begin(ctx)
if err != nil {
- wstr(
- w,
- http.StatusInternalServerError,
- "Unexpected database error",
- )
+ return false, -1, wrapError(errUnexpectedDBError, err)
}
defer func() {
err := tx.Rollback(ctx)
if err != nil && (!errors.Is(err, pgx.ErrTxClosed)) {
- wstr(
- w,
- http.StatusInternalServerError,
- "Unexpected database error",
- )
+ retBool, retStatus, retErr = false, -1, wrapError(errUnexpectedDBError, err)
return
}
}()
@@ -204,22 +139,14 @@ func handleNewCourses(w http.ResponseWriter, req *http.Request) {
"DELETE FROM choices",
)
if err != nil {
- wstr(
- w,
- http.StatusInternalServerError,
- "Unexpected database error",
- )
+ return false, -1, wrapError(errUnexpectedDBError, err)
}
_, err = tx.Exec(
ctx,
"DELETE FROM courses",
)
if err != nil {
- wstr(
- w,
- http.StatusInternalServerError,
- "Unexpected database error",
- )
+ return false, -1, wrapError(errUnexpectedDBError, err)
}
for {
@@ -229,57 +156,36 @@ func handleNewCourses(w http.ResponseWriter, req *http.Request) {
if errors.Is(err, io.EOF) {
break
}
- wstr(
- w,
- http.StatusInternalServerError,
- "Error reading CSV",
- )
- return false
+ return false, -1, wrapError(errCannotReadCSV, err)
}
if line == nil {
- wstr(
- w,
- http.StatusInternalServerError,
- "Unexpected nil line",
- )
- return false
+ return false, -1, wrapError(errCannotReadCSV, errUnexpectedNilCSVLine)
}
if len(line) != 8 {
- wstr(
- w,
- http.StatusBadRequest,
- fmt.Sprintf(
- "Line %d has insufficient items",
- lineNumber,
- ),
- )
- return false
+ return false, -1, wrapAny(errInsufficientFields, fmt.Sprintf(
+ "line %d has insufficient items",
+ lineNumber,
+ ))
}
if !checkCourseType(line[typeIndex]) {
- wstr(
- w,
- http.StatusBadRequest,
+ return false, -1, wrapAny(errInvalidCourseType,
fmt.Sprintf(
- "Line %d has invalid course type \"%s\"\nAllowed course types: %s",
+ "line %d has invalid course type \"%s\"\nallowed course types: %s",
lineNumber,
line[typeIndex],
strings.Join(getKeysOfMap(courseTypes), ", "),
),
)
- return false
}
if !checkCourseGroup(line[groupIndex]) {
- wstr(
- w,
- http.StatusBadRequest,
+ return false, -1, wrapAny(errInvalidCourseGroup,
fmt.Sprintf(
- "Line %d has invalid course group \"%s\"\nAllowed course groups: %s",
+ "line %d has invalid course group \"%s\"\nallowed course groups: %s",
lineNumber,
line[groupIndex],
strings.Join(getKeysOfMap(courseGroups), ", "),
),
)
- return false
}
_, err = tx.Exec(
ctx,
@@ -294,39 +200,26 @@ func handleNewCourses(w http.ResponseWriter, req *http.Request) {
line[courseIDIndex],
)
if err != nil {
- wstr(
- w,
- http.StatusInternalServerError,
- "Unexpected database error",
- )
- return false
+ return false, -1, wrapError(errUnexpectedDBError, err)
}
}
err = tx.Commit(ctx)
if err != nil {
- wstr(
- w,
- http.StatusInternalServerError,
- "Unexpected database error",
- )
- return false
+ return false, -1, wrapError(errUnexpectedDBError, err)
}
- return true
+ return true, -1, nil
}(req.Context())
if !ok {
- return
+ return "", statusCode, err
}
courses.Clear()
err = setupCourses(req.Context())
if err != nil {
- wstr(
- w,
- http.StatusInternalServerError,
- "Error setting up course table again, the data might be corrupted!",
- )
- return
+ return "", -1, wrapError(errWhileSetttingUpCourseTablesAgain, err)
}
http.Redirect(w, req, "/", http.StatusSeeOther)
+
+ return "", -1, nil
}