From fe54d76ab2e3b78c06ae64df1d15f7810fdc7477 Mon Sep 17 00:00:00 2001 From: Gonne Date: Fri, 4 Nov 2022 21:15:38 +0100 Subject: [PATCH] Verbessere Dokumentation --- config/config.go | 42 +++++++++++++++----------- controllers/addOfficeHourHandler.go | 9 ++++-- controllers/confirmRequestHandler.go | 11 +++++-- controllers/deleteOfficeHourHandler.go | 3 ++ models/course.go | 1 - models/date.go | 12 ++++++-- models/officeHour.go | 6 ++-- models/request.go | 4 +-- models/room.go | 1 - repositories/course.go | 4 ++- repositories/officeHour.go | 6 ++++ repositories/request.go | 5 +++ repositories/room.go | 1 - sqldb/sqldb.go | 4 ++- 14 files changed, 75 insertions(+), 34 deletions(-) diff --git a/config/config.go b/config/config.go index 1a98a5c..15bd87a 100644 --- a/config/config.go +++ b/config/config.go @@ -1,3 +1,7 @@ +// Package config implements the officeHours configuration +// +// It provides a struct type holding all necessary information for the programm. +// The configuration can be read from a JSON file. package config import ( @@ -8,32 +12,34 @@ import ( "log" ) +// A Config holds all the constants for the programm. +// It is passed to most repositories. type Config struct { Server struct { - ListenAddress string - ListenPort int - Protocol string - Domain string + ListenAddress string // Address on which the http server is supposed to listen + ListenPort int // Port on which the http server is supposed to listen + Protocol string // Protocol to access the server; either "http" or "https" + Domain string // A string indicating the tool's base domain and path, e.g. "example.com/subfolder" } Date struct { - MinuteGranularity int + MinuteGranularity int // Restricts the minutes on which office hours can start and end to multiples of it. } Request struct { - SecretLength int + SecretLength int // Length of the secret token for requests } Mailer struct { - Type string - FromAddress string - FromName template.HTML - SmtpHost string - SmtpPort int - SmtpUseAuth bool - SmtpIdentity string - SmtpPassword string + Type string // Send mail to "Stdout" or "Smtp" + FromAddress string // Send mail from this address + FromName template.HTML // Send mail from this name, e.g. "Office hours " + SmtpHost string // Host to use as smarthost + SmtpPort int // Port of the smarthost + SmtpUseAuth bool // Set whether to use authentication on the smarthosthost + SmtpIdentity string // Smarthost username + SmtpPassword string // Smarthost password } SQL struct { - Type string - SQLiteFile string + Type string // Can be "SQLite" or "Mysql" + SQLiteFile string // Path to SQLite file MysqlUser string MysqlPassword string MysqlHost string @@ -41,7 +47,8 @@ type Config struct { MysqlDatabase string } Tutor struct { - MailSuffix string + MailSuffix string // Restrict mailaddresses of tutors to suffixes. + // e.g. "example.com" allowes "foo@example.com", "foo@sub.example.com", but not "foo@another.org" or "foo@barexample.com". } } @@ -92,5 +99,4 @@ func validateConfig(conf *Config) error { log.Println(err.Error()) } return err - } diff --git a/controllers/addOfficeHourHandler.go b/controllers/addOfficeHourHandler.go index 00c79ca..e358cc8 100644 --- a/controllers/addOfficeHourHandler.go +++ b/controllers/addOfficeHourHandler.go @@ -28,6 +28,7 @@ type maskData struct { Config config.Config } +// Offer a form to add office hours and validate its input on receiving. func (b *BaseHandler) AddOfficeHourHandler(w http.ResponseWriter, req *http.Request) { var errors []string courses, err := b.courseRepo.GetAll() @@ -123,6 +124,9 @@ func (b *BaseHandler) AddOfficeHourHandler(w http.ResponseWriter, req *http.Requ } else if !allowed { errors = append(errors, "In dem Raum muss noch Platz für weitere Sprechstunden sein.") } + + // If there were errors in the data for the new office hour, + // answer with the form prefilled with the sent data. if len(errors) != 0 { var data maskData = maskData{ courses, @@ -141,6 +145,8 @@ func (b *BaseHandler) AddOfficeHourHandler(w http.ResponseWriter, req *http.Requ } b.writeAddOfficeHourMask(w, req, data) } else { + // if the data for a new office hour was sent correctly, save it. + officeHour := models.OfficeHour{Id: 0, Tutor: models.Tutor{Id: 0, Name: name, Email: email.Address}, Date: date, @@ -166,12 +172,11 @@ func (b *BaseHandler) AddOfficeHourHandler(w http.ResponseWriter, req *http.Requ log.Printf("Error adding request: %s", err.Error()) } templating.ServeTemplate(w, "addSuccess", nil) - } } func (b *BaseHandler) writeAddOfficeHourMask(w http.ResponseWriter, req *http.Request, data maskData) { - if req.Method == http.MethodGet { + if req.Method == http.MethodGet { // if the current request is GET, we assume no office hour addition was tried so far and reset the errors. data.Errors = []string{} } if len(data.Errors) != 0 { diff --git a/controllers/confirmRequestHandler.go b/controllers/confirmRequestHandler.go index fa53a4c..ba5904a 100644 --- a/controllers/confirmRequestHandler.go +++ b/controllers/confirmRequestHandler.go @@ -1,19 +1,27 @@ package controllers import ( + "database/sql" + "errors" "net/http" "officeHours/templating" ) +// Check the secret token for requests and execute the request for correct tokens func (b *BaseHandler) ConfirmRequestHandler(w http.ResponseWriter, req *http.Request) { secret := req.FormValue("code") request, err := b.requestRepo.FindBySecret(secret) - if err != nil { + if errors.Is(err, sql.ErrNoRows) { // There was no request with this secret w.WriteHeader(http.StatusNotFound) templating.ServeTemplate(w, "requestNotFound", nil) return } + if err != nil { // Some other error happened finding the request with this secret + w.WriteHeader(http.StatusInternalServerError) + templating.ServeTemplate(w, "executeFailure", err.Error()) + return + } err = b.requestRepo.Execute(request) if err != nil { @@ -22,5 +30,4 @@ func (b *BaseHandler) ConfirmRequestHandler(w http.ResponseWriter, req *http.Req return } templating.ServeTemplate(w, "executeSuccess", nil) - } diff --git a/controllers/deleteOfficeHourHandler.go b/controllers/deleteOfficeHourHandler.go index 4b609b7..036c36a 100644 --- a/controllers/deleteOfficeHourHandler.go +++ b/controllers/deleteOfficeHourHandler.go @@ -10,6 +10,9 @@ import ( "strconv" ) +// Offer a table of all office hours to delete, +// verify the corresponding mail address and +// then send a confirmation mail. func (b *BaseHandler) DeleteOfficeHourHandler(w http.ResponseWriter, req *http.Request) { if req.FormValue("id") != "" { id, err := strconv.Atoi(req.FormValue("id")) diff --git a/models/course.go b/models/course.go index c689842..d635b06 100644 --- a/models/course.go +++ b/models/course.go @@ -1,4 +1,3 @@ -// course package models type Course struct { diff --git a/models/date.go b/models/date.go index 65000f9..23462b0 100644 --- a/models/date.go +++ b/models/date.go @@ -1,13 +1,17 @@ -// date package models +import ( + "log" +) + type Date struct { - Week int + Week int // Set whether the date is all weeks (0), odd weeks (1) or even weeks (2). Day int Hour int Minute int } +// Return the name of the day for a given integer. func DayName(day int) string { switch day { case 0: @@ -21,10 +25,12 @@ func DayName(day int) string { case 4: return "Freitag" default: + log.Printf("No day name found for day %d", day) return "" } } +// Compare whether first date is strictly before second date. func DateLess(first Date, second Date) bool { if first.Day < second.Day { return true @@ -44,6 +50,7 @@ func DateLess(first Date, second Date) bool { return false } +// Get the end date for some duration. func GetEndDate(date Date, duration int, ignoreWeek bool) Date { var endDate Date if ignoreWeek { @@ -51,6 +58,7 @@ func GetEndDate(date Date, duration int, ignoreWeek bool) Date { } else { endDate = Date{date.Week, date.Day, date.Hour, date.Minute} } + endDate.Day = (endDate.Day + (endDate.Hour*60+endDate.Minute+duration)/(60*24)) % 7 endDate.Hour = endDate.Hour + (endDate.Minute+duration)/60 endDate.Minute = (endDate.Minute + duration) % 60 return endDate diff --git a/models/officeHour.go b/models/officeHour.go index 5be2be5..43d5d41 100644 --- a/models/officeHour.go +++ b/models/officeHour.go @@ -1,4 +1,4 @@ -// officeHour +// The package models defines the stucts for objects, their repositories and the signatures of their functions. package models type OfficeHour struct { @@ -7,10 +7,10 @@ type OfficeHour struct { Date Room Course - RoomName string + RoomName string // A description of the room for special rooms that are not preconfigured. Info string Active bool - Duration int + Duration int // Duration in minutes. Must be divisible by the config field "MinuteGranularity". } type OfficeHourRepository interface { diff --git a/models/request.go b/models/request.go index 640428a..f2c2063 100644 --- a/models/request.go +++ b/models/request.go @@ -8,8 +8,8 @@ type Request struct { Secret string } -const RequestActivate int = 1 -const RequestDelete int = 2 +const RequestActivate int = 1 // Fix integer to represent request for activation of an office hour. +const RequestDelete int = 2 // Fix integer to represent request for deletion of an office hour. type RequestRepository interface { Add(officeHour OfficeHour, action int) (int, error) diff --git a/models/room.go b/models/room.go index 5bc2b6c..dbafea0 100644 --- a/models/room.go +++ b/models/room.go @@ -1,4 +1,3 @@ -// raum package models type Room struct { diff --git a/repositories/course.go b/repositories/course.go index c53e25d..7c2587d 100644 --- a/repositories/course.go +++ b/repositories/course.go @@ -1,4 +1,3 @@ -// course package repositories import ( @@ -9,6 +8,7 @@ import ( "officeHours/models" ) +// A struct to hold the db connection type CourseRepo struct { db *sql.DB } @@ -38,6 +38,7 @@ func (r *CourseRepo) GetAll() ([]models.Course, error) { return r.getFromRows(rows) } +// Helper function to get a course from multiple SQL result rows func (r *CourseRepo) getFromRows(rows *sql.Rows) ([]models.Course, error) { var courses []models.Course for rows.Next() { @@ -51,6 +52,7 @@ func (r *CourseRepo) getFromRows(rows *sql.Rows) ([]models.Course, error) { return courses, nil } +// Helper function to get a course from an SQL result row func (r *CourseRepo) getFromRow(row *sql.Row) (models.Course, error) { var course models.Course if err := row.Scan(&course.Id, &course.Name); err != nil { diff --git a/repositories/officeHour.go b/repositories/officeHour.go index a5de5ad..7766896 100644 --- a/repositories/officeHour.go +++ b/repositories/officeHour.go @@ -89,6 +89,10 @@ func (r *OfficeHourRepo) FindById(id int) (models.OfficeHour, error) { return r.getFromRow(r.db.QueryRow("SELECT * FROM officeHour WHERE id=?", id)) } +// Add an office hour if it doesn't exist yet. +// Also add the incluyey tutor if it doesn't exist yet. +// +// Returns the id of the new office hour. func (r *OfficeHourRepo) Add(officeHour models.OfficeHour) (int, error) { // Find correct tutor or add if not existent _, err := r.tutorRepo.Add(officeHour.Tutor) @@ -215,6 +219,7 @@ func (r *OfficeHourRepo) getFromRows(rows *sql.Rows) ([]models.OfficeHour, error return officeHours, nil } +// Get the number of office hours that are maximally parallel during a time span. func (r *OfficeHourRepo) NumberByTimeSpanAndRoom(date models.Date, duration int, room models.Room, activeOnly bool) (int, error) { var rows *sql.Rows var err error @@ -252,6 +257,7 @@ func (r *OfficeHourRepo) NumberByTimeSpanAndRoom(date models.Date, duration int, return count, nil } +// Check whether the room capacity allows for another office hour during a time span. func (r *OfficeHourRepo) AllowedAt(date models.Date, duration int, room models.Room, activeOnly bool) (bool, error) { numberOfOfficeHours, err := r.NumberByTimeSpanAndRoom(date, duration, room, activeOnly) if err != nil { diff --git a/repositories/request.go b/repositories/request.go index 762bd7d..5125d11 100644 --- a/repositories/request.go +++ b/repositories/request.go @@ -68,6 +68,8 @@ func (r *RequestRepo) FindByOfficeHour(officeHour models.OfficeHour) ([]models.R return requests, nil } +// Add a request to the database if it doesnt already exist. +// Send a mail with the secret to the confirmation address in any case. func (r *RequestRepo) Add(officeHour models.OfficeHour, action int) (int, error) { existents, err := r.FindByOfficeHour(officeHour) if err != nil && !errors.Is(err, sql.ErrNoRows) { @@ -97,6 +99,7 @@ func (r *RequestRepo) Add(officeHour models.OfficeHour, action int) (int, error) return request.Id, r.sendConfirmationMail(request) } +// Execute a request and delete it. func (r *RequestRepo) Execute(request models.Request) error { var err error switch request.Action { @@ -107,11 +110,13 @@ func (r *RequestRepo) Execute(request models.Request) error { err = r.officeHourRepo.Delete(request.OfficeHour) r.db.Exec("DELETE FROM request WHERE officeHour=?", request.OfficeHour.Id) default: + log.Printf("Executing request: Action type %d unknown.", request.Action) _, err = r.db.Exec("DELETE FROM request WHERE id=?", request.Id) } return err } +// Find a new secret token with configured length that is currently unused. func (r *RequestRepo) newSecret() (string, error) { var err error var secret string diff --git a/repositories/room.go b/repositories/room.go index de91d18..f86aabb 100644 --- a/repositories/room.go +++ b/repositories/room.go @@ -1,4 +1,3 @@ -// raum package repositories import ( diff --git a/sqldb/sqldb.go b/sqldb/sqldb.go index f4c5e26..5460662 100644 --- a/sqldb/sqldb.go +++ b/sqldb/sqldb.go @@ -9,6 +9,7 @@ import ( _ "github.com/mattn/go-sqlite3" ) +// Connect to a database using or throw an error func Connect(config config.Config) (*sql.DB, error) { switch config.SQL.Type { case "SQLite": @@ -24,6 +25,7 @@ func Connect(config config.Config) (*sql.DB, error) { } } +// Connect to a SQLite database and check the connection. func connectSQLite(file string) (*sql.DB, error) { db, err := sql.Open("sqlite3", file) if err != nil { @@ -32,6 +34,7 @@ func connectSQLite(file string) (*sql.DB, error) { return db, nil } +// Connect to a Mysql database and check the connection. func connectMysql(user string, password string, address string, port int, database string) (*sql.DB, error) { db, err := sql.Open("mysql", fmt.Sprintf("%s:%s@tcp(%s:%d)/%s", user, password, address, port, database)) if err != nil { @@ -39,7 +42,6 @@ func connectMysql(user string, password string, address string, port int, databa } err = db.Ping() - // handle error if err != nil { return db, fmt.Errorf("Error pinging Mysql database: %w", err) }