Erstelle Maske, um Sprechstunden hinzuzufügen

This commit is contained in:
Gonne 2022-08-31 22:49:14 +02:00
parent c7a9522c2c
commit 369f4ebcec
15 changed files with 357 additions and 50 deletions

View file

@ -0,0 +1,174 @@
package controllers
import (
"fmt"
"html/template"
"net/http"
"net/mail"
"sprechstundentool/models"
"strconv"
"strings"
)
type maskData struct {
Courses []models.Course
Rooms []models.Room
MinuteGranularity int
SelectedCourse int
SelectedRoom int
Week int
Day int
Hour int
Minute int
Duration int
Roomname string
Name string
Email string
Info string
Errors []string
}
func (b *BaseHandler) AddOfficeHourHandler(w http.ResponseWriter, req *http.Request) {
var errors []string
courses, err := b.courseRepo.GetAll()
if err != nil {
errors = append(errors, err.Error())
}
rooms, err := b.roomRepo.GetAll()
if err != nil {
errors = append(errors, err.Error())
}
//Parse course
courseid, err := strconv.Atoi(req.FormValue("veranstaltung"))
if err != nil {
errors = append(errors, "Die Veranstaltung muss eine ganze Zahl sein.")
}
course, err := b.courseRepo.FindById(courseid)
if err != nil {
errors = append(errors, "Die Veranstaltung muss existieren.")
}
//Parse room
roomid, err := strconv.Atoi(req.FormValue("raum"))
if err != nil {
errors = append(errors, "Der Raum muss eine ganze Zahl sein.")
}
room, err := b.roomRepo.FindById(roomid)
if err != nil {
errors = append(errors, "Der Raum muss existieren.")
}
//Parse date
week, err := strconv.Atoi(req.FormValue("woche"))
if err != nil {
errors = append(errors, "Die Woche muss eine ganze Zahl sein.")
}
if !(week >= 0 && week <= 2) {
errors = append(errors, "Sprechstunden müssen jede, jede gerade oder jede ungerade Woche stattfinden.")
}
day, err := strconv.Atoi(req.FormValue("tag"))
if err != nil {
errors = append(errors, "Der Tag muss eine ganze Zahl sein.")
}
if !(day >= 0 && day <= 4) {
errors = append(errors, "Sprechstunden müssen von Montag bis Freitag stattfinden.")
}
time := strings.SplitN(req.FormValue("startzeit"), ":", 2)
var hour, minute int
if len(time) != 2 {
errors = append(errors, "Die Zeit muss im Format HH:MM sein.")
} else {
hour, err = strconv.Atoi(time[0])
if err != nil {
errors = append(errors, "Die Stunde muss eine ganze Zahl sein.")
}
if !(hour >= 8 && hour <= 17) {
errors = append(errors, fmt.Sprintf("Sprechstunden müssen zwischen 08:00 Uhr und 17:%d starten.", 60-models.MinuteGranularity))
}
minute, err = strconv.Atoi(time[1])
if err != nil {
errors = append(errors, "Die Minute muss eine ganze Zahl sein.")
}
if !(minute >= 0 && minute <= 60-models.MinuteGranularity && minute%models.MinuteGranularity == 0) {
errors = append(errors, fmt.Sprintf("Sprechstunden dürfen nur alle %d Minuten starten.", models.MinuteGranularity))
}
}
duration, err := strconv.Atoi(req.FormValue("dauer"))
if err != nil {
errors = append(errors, "Die Dauer muss eine ganze Zahl sein.")
}
if !(duration >= models.MinuteGranularity && duration <= 120 && duration%models.MinuteGranularity == 0) {
errors = append(errors, fmt.Sprintf("Sprechstunden müssen zwischen %d und 120 Minuten lang sein.", models.MinuteGranularity))
}
roomname := req.FormValue("raumname")
name := req.FormValue("name")
if name == "" {
errors = append(errors, "Der Name darf nicht leer sein.")
}
email, err := mail.ParseAddress(req.FormValue("email"))
if err != nil {
email = &mail.Address{"", req.FormValue("email")}
errors = append(errors, "Mailaddresse konnte nicht geparst werden.")
} else if !strings.HasSuffix(email.Address, "tu-darmstadt.de") {
errors = append(errors, "Mailaddresse muss auf „tu-darmstadt.de“ enden.")
}
info := req.FormValue("info")
if len(errors) != 0 {
var data maskData = maskData{
courses,
rooms,
models.MinuteGranularity,
courseid,
roomid,
week,
day,
hour,
minute,
duration,
roomname,
name,
email.Address,
info,
errors,
}
b.writeAddOfficeHourMask(w, req, data)
} else {
officeHour := models.OfficeHour{0,
models.Tutor{0, name, email.Address},
models.Date{week, day, hour, minute},
room,
course,
info,
false,
duration,
}
err := b.officeHourRepo.Add(officeHour)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
failureTemplate, parseErr := template.ParseFiles("templates/addFailure.html")
if parseErr != nil {
w.Write([]byte(fmt.Sprintf("Template konnte nicht geparst werden : %s", string(err.Error()))))
return
}
failureTemplate.Execute(w, err)
}
http.Redirect(w, req, "/", http.StatusTemporaryRedirect)
}
}
func (b *BaseHandler) writeAddOfficeHourMask(w http.ResponseWriter, req *http.Request, data maskData) {
tmpl, err := template.ParseFiles("templates/addMask.html")
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(fmt.Sprintf("Template konnte nicht geparst werden : %s", string(err.Error()))))
return
}
if len(data.Errors) != 0 {
w.WriteHeader(http.StatusBadRequest)
}
err = tmpl.Execute(w, data)
if err != nil {
w.Write([]byte(fmt.Sprintf("Template konnte nicht geparst werden : %s", string(err.Error()))))
return
}
}

View file

@ -0,0 +1,16 @@
package controllers
import "sprechstundentool/models"
// BaseHandler will hold everything that controller needs
type BaseHandler struct {
roomRepo models.RoomRepository
officeHourRepo models.OfficeHourRepository
courseRepo models.CourseRepository
tutorRepo models.TutorRepository
}
// NewBaseHandler returns a new BaseHandler
func NewBaseHandler(roomRepo models.RoomRepository, officeHourRepo models.OfficeHourRepository, courseRepo models.CourseRepository, tutorRepo models.TutorRepository) *BaseHandler {
return &BaseHandler{roomRepo, officeHourRepo, courseRepo, tutorRepo}
}

View file

@ -8,19 +8,6 @@ import (
"strconv" "strconv"
) )
// BaseHandler will hold everything that controller needs
type BaseHandler struct {
roomRepo models.RoomRepository
officeHourRepo models.OfficeHourRepository
courseRepo models.CourseRepository
tutorRepo models.TutorRepository
}
// NewBaseHandler returns a new BaseHandler
func NewBaseHandler(roomRepo models.RoomRepository, officeHourRepo models.OfficeHourRepository, courseRepo models.CourseRepository, tutorRepo models.TutorRepository) *BaseHandler {
return &BaseHandler{roomRepo, officeHourRepo, courseRepo, tutorRepo}
}
func (b *BaseHandler) RootHandler(w http.ResponseWriter, req *http.Request) { func (b *BaseHandler) RootHandler(w http.ResponseWriter, req *http.Request) {
b.writeTimetablePage(w, req, template.HTML("")) b.writeTimetablePage(w, req, template.HTML(""))
} }
@ -33,7 +20,7 @@ func (b *BaseHandler) GetByRoomHandler(w http.ResponseWriter, req *http.Request)
b.RootHandler(w, req) b.RootHandler(w, req)
return return
} }
officeHours, _ := b.officeHourRepo.FindByRoom(room) officeHours, _ := b.officeHourRepo.FindByRoom(room, true)
b.writeTimetablePage(w, req, printTimetable(GetTimetable(officeHours))) b.writeTimetablePage(w, req, printTimetable(GetTimetable(officeHours)))
} }
@ -47,7 +34,7 @@ func (b *BaseHandler) GetByCourseHandler(w http.ResponseWriter, req *http.Reques
b.RootHandler(w, req) b.RootHandler(w, req)
return return
} }
officeHours, _ := b.officeHourRepo.FindByCourse(course) officeHours, _ := b.officeHourRepo.FindByCourse(course, true)
b.writeTimetablePage(w, req, printTimetable(GetTimetable(officeHours))) b.writeTimetablePage(w, req, printTimetable(GetTimetable(officeHours)))
} }

View file

@ -13,20 +13,20 @@ func GetTimetable(officeHours []models.OfficeHour) (timetable map[models.Date]ma
for _, officeHour := range officeHours { for _, officeHour := range officeHours {
var slot int = 0 var slot int = 0
for minute := 0; minute < officeHour.Duration; minute += models.MinuteGranularity { // find slot id for minute := 0; minute < officeHour.Duration; minute += models.MinuteGranularity { // find slot id
_, exists := fullTimetable[models.Date{officeHour.Day, officeHour.Hour + (officeHour.Minute+minute)/60, (officeHour.Minute + minute) % 60}] _, exists := fullTimetable[models.Date{0, officeHour.Day, officeHour.Hour + (officeHour.Minute+minute)/60, (officeHour.Minute + minute) % 60}]
if exists { if exists {
_, exists := fullTimetable[models.Date{officeHour.Day, officeHour.Hour + (officeHour.Minute+minute)/60, (officeHour.Minute + minute) % 60}][slot] _, exists := fullTimetable[models.Date{0, officeHour.Day, officeHour.Hour + (officeHour.Minute+minute)/60, (officeHour.Minute + minute) % 60}][slot]
if exists { if exists {
slot += 1 slot += 1
minute = 0 minute = 0
continue continue
} }
} else { } else {
fullTimetable[models.Date{officeHour.Day, officeHour.Hour + (officeHour.Minute+minute)/60, (officeHour.Minute + minute) % 60}] = make(map[int]models.OfficeHour) fullTimetable[models.Date{0, officeHour.Day, officeHour.Hour + (officeHour.Minute+minute)/60, (officeHour.Minute + minute) % 60}] = make(map[int]models.OfficeHour)
} }
} }
for minute := 0; minute < officeHour.Duration; minute += models.MinuteGranularity { // write officeHour id to timetable for minute := 0; minute < officeHour.Duration; minute += models.MinuteGranularity { // write officeHour id to timetable
fullTimetable[models.Date{officeHour.Day, officeHour.Hour + (officeHour.Minute+minute)/60, (officeHour.Minute + minute) % 60}][slot] = officeHour fullTimetable[models.Date{0, officeHour.Day, officeHour.Hour + (officeHour.Minute+minute)/60, (officeHour.Minute + minute) % 60}][slot] = officeHour
} }
} }
slots = []int{1, 1, 1, 1, 1} slots = []int{1, 1, 1, 1, 1}
@ -61,16 +61,16 @@ func printTimetable(timetable map[models.Date]map[int]models.OfficeHour, slots [
} }
for day := 0; day < 5; day += 1 { for day := 0; day < 5; day += 1 {
for slot := 0; slot < slots[day]; slot += 1 { for slot := 0; slot < slots[day]; slot += 1 {
current, currentExists := timetable[models.Date{day, hour, minute}][slot] current, currentExists := timetable[models.Date{0, day, hour, minute}][slot]
if currentExists { // This slot is taken by some office hour if currentExists { // This slot is taken by some office hour
var continued bool = false // is this slot occupied by the same office hour the previous minute? var continued bool = false // is this slot occupied by the same office hour the previous minute?
var predecessorExists bool var predecessorExists bool
var predecessor models.OfficeHour var predecessor models.OfficeHour
if hour > 0 && minute < models.MinuteGranularity { if hour > 0 && minute < models.MinuteGranularity {
predecessor, predecessorExists = timetable[models.Date{day, hour - 1, 60 - models.MinuteGranularity}][slot] predecessor, predecessorExists = timetable[models.Date{0, day, hour - 1, 60 - models.MinuteGranularity}][slot]
} else { } else {
predecessor, predecessorExists = timetable[models.Date{day, hour, minute - models.MinuteGranularity}][slot] predecessor, predecessorExists = timetable[models.Date{0, day, hour, minute - models.MinuteGranularity}][slot]
} }
if predecessorExists { if predecessorExists {
continued = (predecessor == current) continued = (predecessor == current)

View file

@ -1,4 +1,4 @@
INSERT INTO `room` (name, max_occupy) VALUES ('S2 15 345',2),('S2 15 415',1),('S2 15 336',2),('S2 15 444',1),('S2 15 333',1),('S2 14 420',1),('Sonstige',1024); INSERT INTO `room` (name, max_occupy) VALUES ('S2 15 345',2),('S2 15 415',1),('S2 15 336',2),('S2 15 444',1),('S2 15 333',1),('S2 14 420',1),('Sonstige',1024);
INSERT INTO `officeHour` VALUES (0,1,1,12,0,1,1,0,'',true,60),(1,1,1,12,15,2,1,0,'',true,60),(2,1,1,12,30,1,2,0,'',true,60); INSERT INTO `officeHour` (tutor, day, hour, minute, room, course, week, info, active, duration) VALUES (1,1,12,0,1,1,0,'',true,60),(1,1,12,15,2,1,0,'',true,60),(1,1,12,30,1,2,0,'',true,60);
INSERT INTO `tutor` (name, email) VALUES ('Dummyname','dummy@example.com'); INSERT INTO `tutor` (name, email) VALUES ('Dummyname','dummy@example.com');
INSERT INTO `course` (name) VALUES ('Integrationstheorie'),('Analysis II (engl.)'),('Analysis II'),('Mathe II für Informatik'),('Mathe II für Bauwesen'),('Diskrete Optimierung'),('Lineare Algebra II'),('Einführung in die Algebra'),('FGdI I&II'),('Algorithmic Discrete Mathematics'),('Einführung in die Stochastik'),('Mathe II für ET'),('Mathe II für Maschinenbau'),('Mathe IV für Maschinenbau'),('Num Mathe für MB (IV)'),('Höhere Mathematik II'),('Elementare PDE'),('LA für Physik und Lehramt'),('Analysis I'),('Mathe III ET'),('Mathe I für Informatik'),('Complex Analysis'),('Mathe & Statistik für Biologen'),('Mathe I für Maschinenbau'),('Mathe I für Bau'),('Nichtlineare Optimierung'),('Einführung in die Numerische Mathematik'),('Differentialgeometrie'),('Mathe I für ET'),('Mathe III (Bau)'),('W. Theorie'),('Gewöhnliche Differentialgleichungen'),('Geometrie für Lehramt'),('Mathe III MB'),('Statistik I für Humanwissenschaftler'),('Einführung in die Optimierung'),('Algebra'),('Lineare Algebra I'),('Höhere Mathematik 1'),('Darstellende Geometrie'),('Statistik 1 für WI'),('Diskrete Mathematik'),('Linear Algebra I (engl.)'),('Introduction to mathematical logic'),('Statistik I für WI'),('Weihnachtsknobelstraße'),('Numerische Lineare Algebra'),('Mathe IV (ET) / Mathe III (Inf) / Praktische Mathe (MEd)'),('Topologie'),('Linear Algebra II (engl.)'),('Einführung in die Finanzmathematik'),('Einführung in die mathematische Software'),('Mathematische Statistik'),('Algebraische Kurven'),('Analysis I (engl.)'),('Funktionalanalysis'),('Lebensversicherungsmathematik'),('Automaten, Formale Sprachen und Entscheidbarkeit (FGdI)'),('Mathematik für Chemiker'),('Numerik gewöhnlicher Differentialgleichungen'),('Darstellungstheorie'),('Aussagen- und Prädikatenlogik'),('Riemannsche Flächen'),('Mathematische Grundlagen der Quantenmechanik'),('Elementare Zahlentheorie'),('Einführung in die mathematische Modellierung'),('Stochastische Prozesse I'),('Mathematik im Kontext'),('Funktionalanalysis II'),('Mathematik IV (für ET) /Mathematik III (für Inf) /PraktischeMathematik (für M.Ed.Math)'),('Differentialgeometrie fuer VI'),('Probability Theory'),('Mathe für MINT'),('Mathematik als gemeinsame Sprache der Naturwissenschaften'),('Lineare Algebra I für Physiker'),('Manifolds'),('Kurvenschätzung'),('Spieltheorie'),('Einführung in die Programmierung'),('Algebraische Topologie'),('Schadenversicherungsmathematik'),('Partial Differential Equations I'); INSERT INTO `course` (name) VALUES ('Integrationstheorie'),('Analysis II (engl.)'),('Analysis II'),('Mathe II für Informatik'),('Mathe II für Bauwesen'),('Diskrete Optimierung'),('Lineare Algebra II'),('Einführung in die Algebra'),('FGdI I&II'),('Algorithmic Discrete Mathematics'),('Einführung in die Stochastik'),('Mathe II für ET'),('Mathe II für Maschinenbau'),('Mathe IV für Maschinenbau'),('Num Mathe für MB (IV)'),('Höhere Mathematik II'),('Elementare PDE'),('LA für Physik und Lehramt'),('Analysis I'),('Mathe III ET'),('Mathe I für Informatik'),('Complex Analysis'),('Mathe & Statistik für Biologen'),('Mathe I für Maschinenbau'),('Mathe I für Bau'),('Nichtlineare Optimierung'),('Einführung in die Numerische Mathematik'),('Differentialgeometrie'),('Mathe I für ET'),('Mathe III (Bau)'),('W. Theorie'),('Gewöhnliche Differentialgleichungen'),('Geometrie für Lehramt'),('Mathe III MB'),('Statistik I für Humanwissenschaftler'),('Einführung in die Optimierung'),('Algebra'),('Lineare Algebra I'),('Höhere Mathematik 1'),('Darstellende Geometrie'),('Statistik 1 für WI'),('Diskrete Mathematik'),('Linear Algebra I (engl.)'),('Introduction to mathematical logic'),('Statistik I für WI'),('Weihnachtsknobelstraße'),('Numerische Lineare Algebra'),('Mathe IV (ET) / Mathe III (Inf) / Praktische Mathe (MEd)'),('Topologie'),('Linear Algebra II (engl.)'),('Einführung in die Finanzmathematik'),('Einführung in die mathematische Software'),('Mathematische Statistik'),('Algebraische Kurven'),('Analysis I (engl.)'),('Funktionalanalysis'),('Lebensversicherungsmathematik'),('Automaten, Formale Sprachen und Entscheidbarkeit (FGdI)'),('Mathematik für Chemiker'),('Numerik gewöhnlicher Differentialgleichungen'),('Darstellungstheorie'),('Aussagen- und Prädikatenlogik'),('Riemannsche Flächen'),('Mathematische Grundlagen der Quantenmechanik'),('Elementare Zahlentheorie'),('Einführung in die mathematische Modellierung'),('Stochastische Prozesse I'),('Mathematik im Kontext'),('Funktionalanalysis II'),('Mathematik IV (für ET) /Mathematik III (für Inf) /PraktischeMathematik (für M.Ed.Math)'),('Differentialgeometrie fuer VI'),('Probability Theory'),('Mathe für MINT'),('Mathematik als gemeinsame Sprache der Naturwissenschaften'),('Lineare Algebra I für Physiker'),('Manifolds'),('Kurvenschätzung'),('Spieltheorie'),('Einführung in die Programmierung'),('Algebraische Topologie'),('Schadenversicherungsmathematik'),('Partial Differential Equations I');

View file

@ -20,7 +20,7 @@ func main() {
http.HandleFunc("/getByRoom", h.GetByRoomHandler) http.HandleFunc("/getByRoom", h.GetByRoomHandler)
http.HandleFunc("/getByCourse", h.GetByCourseHandler) http.HandleFunc("/getByCourse", h.GetByCourseHandler)
http.HandleFunc("/addOfficeHour", h.AddOfficeHourHandler)
http.HandleFunc("/", h.RootHandler) http.HandleFunc("/", h.RootHandler)
http.ListenAndServe(":8080", nil) http.ListenAndServe(":8080", nil)

View file

@ -2,6 +2,7 @@
package models package models
type Date struct { type Date struct {
Week int
Day int Day int
Hour int Hour int
Minute int Minute int

View file

@ -4,7 +4,6 @@ package models
type OfficeHour struct { type OfficeHour struct {
Id int Id int
Tutor Tutor Tutor Tutor
Week int
Date Date
Room Room Room Room
Course Course Course Course
@ -15,9 +14,9 @@ type OfficeHour struct {
type OfficeHourRepository interface { type OfficeHourRepository interface {
FindById(id int) (OfficeHour, error) FindById(id int) (OfficeHour, error)
FindByCourse(course Course) ([]OfficeHour, error) FindByCourse(course Course, activeOnly bool) ([]OfficeHour, error)
FindByRoom(room Room) ([]OfficeHour, error) FindByRoom(room Room, activatedOnly bool) ([]OfficeHour, error)
GetAll() ([]OfficeHour, error) GetAll(activatedOnly bool) ([]OfficeHour, error)
Delete(officeHour OfficeHour) error Delete(officeHour OfficeHour) error
Add(officeHour OfficeHour) error Add(officeHour OfficeHour) error
} }

View file

@ -9,6 +9,7 @@ type Tutor struct {
type TutorRepository interface { type TutorRepository interface {
FindByEmail(Email string) ([]Tutor, error) FindByEmail(Email string) ([]Tutor, error)
FindByNameAndEmail(name string, email string) (Tutor, error)
FindById(Id int) (Tutor, error) FindById(Id int) (Tutor, error)
GetAll() ([]Tutor, error) GetAll() ([]Tutor, error)
Add(tutor Tutor) error Add(tutor Tutor) error

View file

@ -22,8 +22,14 @@ func NewOfficeHourRepo(db *sql.DB, roomRepo *RoomRepo, tutorRepo *TutorRepo, cou
} }
} }
func (r *OfficeHourRepo) GetAll() ([]models.OfficeHour, error) { func (r *OfficeHourRepo) GetAll(activeOnly bool) ([]models.OfficeHour, error) {
rows, err := r.db.Query("SELECT * FROM officeHour") var rows *sql.Rows
var err error
if activeOnly {
rows, err = r.db.Query("SELECT * FROM officeHour WHERE active")
} else {
rows, err = r.db.Query("SELECT * FROM officeHour")
}
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -31,8 +37,14 @@ func (r *OfficeHourRepo) GetAll() ([]models.OfficeHour, error) {
return r.getFromRows(rows) return r.getFromRows(rows)
} }
func (r *OfficeHourRepo) FindByCourse(course models.Course) ([]models.OfficeHour, error) { func (r *OfficeHourRepo) FindByCourse(course models.Course, activeOnly bool) ([]models.OfficeHour, error) {
rows, err := r.db.Query("SELECT * FROM officeHour WHERE course=?", course.Id) var rows *sql.Rows
var err error
if activeOnly {
rows, err = r.db.Query("SELECT * FROM officeHour WHERE course=? and active", course.Id)
} else {
rows, err = r.db.Query("SELECT * FROM officeHour WHERE course=?", course.Id)
}
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -40,8 +52,14 @@ func (r *OfficeHourRepo) FindByCourse(course models.Course) ([]models.OfficeHour
return r.getFromRows(rows) return r.getFromRows(rows)
} }
func (r *OfficeHourRepo) FindByRoom(room models.Room) ([]models.OfficeHour, error) { func (r *OfficeHourRepo) FindByRoom(room models.Room, activeOnly bool) ([]models.OfficeHour, error) {
rows, err := r.db.Query("SELECT * FROM officeHour WHERE room=?", room.Id) var rows *sql.Rows
var err error
if activeOnly {
rows, err = r.db.Query("SELECT * FROM officeHour WHERE room=? AND active", room.Id)
} else {
rows, err = r.db.Query("SELECT * FROM officeHour WHERE room=?", room.Id)
}
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -55,8 +73,44 @@ func (r *OfficeHourRepo) FindById(id int) (models.OfficeHour, error) {
} }
func (r *OfficeHourRepo) Add(officeHour models.OfficeHour) error { func (r *OfficeHourRepo) Add(officeHour models.OfficeHour) error {
// Find correct tutor or add if not existent
r.tutorRepo.Add(officeHour.Tutor)
var err error
officeHour.Tutor, err = r.tutorRepo.FindByNameAndEmail(officeHour.Tutor.Name, officeHour.Tutor.Email)
if err != nil {
return err
}
//Don't add identical officeHours
officeHours, err := r.FindByCourse(officeHour.Course, false)
if err != nil {
return err
}
for _, oldOfficeHour := range officeHours {
if officeHour.Tutor == oldOfficeHour.Tutor &&
officeHour.Date == oldOfficeHour.Date &&
officeHour.Room == oldOfficeHour.Room &&
// officeHour.Course == oldOfficeHour.Course && already filtered above
officeHour.Info == oldOfficeHour.Info &&
officeHour.Active == oldOfficeHour.Active &&
officeHour.Duration == oldOfficeHour.Duration {
return nil return nil
} }
}
_, err = r.db.Exec("INSERT INTO `officeHour` (tutor, day, hour, minute, room, course, week, info, active, duration) VALUES (?,?,?,?,?,?,?,?,?,?)",
officeHour.Tutor.Id,
officeHour.Date.Day,
officeHour.Date.Hour,
officeHour.Date.Minute,
officeHour.Room.Id,
officeHour.Course.Id,
officeHour.Date.Week,
officeHour.Info,
officeHour.Active,
officeHour.Duration)
return err
}
func (r *OfficeHourRepo) Delete(officeHour models.OfficeHour) error { func (r *OfficeHourRepo) Delete(officeHour models.OfficeHour) error {
return nil return nil
@ -64,13 +118,13 @@ func (r *OfficeHourRepo) Delete(officeHour models.OfficeHour) error {
func (r *OfficeHourRepo) getFromRow(row *sql.Row) (models.OfficeHour, error) { func (r *OfficeHourRepo) getFromRow(row *sql.Row) (models.OfficeHour, error) {
var officeHour models.OfficeHour var officeHour models.OfficeHour
var day, hour, minute, tutorid int var week, day, hour, minute, tutorid int
var roomName, courseName string var roomName, courseName string
err := row.Scan(&officeHour.Id, &tutorid, &day, &hour, &minute, &roomName, &courseName, &officeHour.Week, &officeHour.Info, &officeHour.Active, &officeHour.Duration) err := row.Scan(&officeHour.Id, &tutorid, &day, &hour, &minute, &roomName, &courseName, &week, &officeHour.Info, &officeHour.Active, &officeHour.Duration)
if err != nil { if err != nil {
return models.OfficeHour{}, err return models.OfficeHour{}, err
} }
officeHour.Date = models.Date{day, hour, minute} officeHour.Date = models.Date{week, day, hour, minute}
officeHour.Room, _ = r.roomRepo.FindByName(roomName) officeHour.Room, _ = r.roomRepo.FindByName(roomName)
officeHour.Tutor, _ = r.tutorRepo.FindById(tutorid) officeHour.Tutor, _ = r.tutorRepo.FindById(tutorid)
officeHour.Course, _ = r.courseRepo.FindByName(courseName) officeHour.Course, _ = r.courseRepo.FindByName(courseName)
@ -81,11 +135,11 @@ func (r *OfficeHourRepo) getFromRows(rows *sql.Rows) ([]models.OfficeHour, error
var officeHours []models.OfficeHour var officeHours []models.OfficeHour
for rows.Next() { for rows.Next() {
var officeHour models.OfficeHour var officeHour models.OfficeHour
var day, hour, minute, tutorId, roomId, courseId int var week, day, hour, minute, tutorId, roomId, courseId int
if err := rows.Scan(&officeHour.Id, &tutorId, &day, &hour, &minute, &roomId, &courseId, &officeHour.Week, &officeHour.Info, &officeHour.Active, &officeHour.Duration); err != nil { if err := rows.Scan(&officeHour.Id, &tutorId, &day, &hour, &minute, &roomId, &courseId, &week, &officeHour.Info, &officeHour.Active, &officeHour.Duration); err != nil {
return officeHours, err return officeHours, err
} }
officeHour.Date = models.Date{day, hour, minute} officeHour.Date = models.Date{week, day, hour, minute}
officeHour.Room, _ = r.roomRepo.FindById(roomId) officeHour.Room, _ = r.roomRepo.FindById(roomId)
officeHour.Tutor, _ = r.tutorRepo.FindById(tutorId) officeHour.Tutor, _ = r.tutorRepo.FindById(tutorId)
officeHour.Course, _ = r.courseRepo.FindById(courseId) officeHour.Course, _ = r.courseRepo.FindById(courseId)

View file

@ -43,6 +43,15 @@ func (r *TutorRepo) FindById(id int) (models.Tutor, error) {
return tutor, nil return tutor, nil
} }
func (r *TutorRepo) FindByNameAndEmail(name string, email string) (models.Tutor, error) {
row := r.db.QueryRow("SELECT * FROM tutor WHERE email=? AND name=?", email, name)
var tutor models.Tutor
if err := row.Scan(&tutor.Id, &tutor.Name, &tutor.Email); err != nil {
return models.Tutor{}, err
}
return tutor, nil
}
func (r *TutorRepo) GetAll() ([]models.Tutor, error) { func (r *TutorRepo) GetAll() ([]models.Tutor, error) {
rows, err := r.db.Query("SELECT * FROM tutor") rows, err := r.db.Query("SELECT * FROM tutor")
if err != nil { if err != nil {
@ -64,5 +73,12 @@ func (r *TutorRepo) Save(tutor models.Tutor) error {
return nil return nil
} }
func (r *TutorRepo) Add(tutor models.Tutor) error { func (r *TutorRepo) Add(tutor models.Tutor) error {
return nil //Don't add identical tutors
_, err := r.FindByNameAndEmail(tutor.Name, tutor.Email)
if err == sql.ErrNoRows {
_, err = r.db.Exec("INSERT INTO `tutor` (name, email) VALUES (?,?);", tutor.Name, tutor.Email)
return err
}
return err
} }

10
templates/addFailure.html Normal file
View file

@ -0,0 +1,10 @@
<html>
<head>
<title>Sprechstunde anlegen</title>
</head>
<body>
Irgendetwas ist schief gegangen. Bitte sende folgende Daten an <a href="mailto:sprechstundentool@mathebau.de">sprechstundentool@mathebau.de</a> mit einer Beschreibung, was du tun wolltest.
<br />
{{.}}
</body>
</html>

49
templates/addMask.html Normal file
View file

@ -0,0 +1,49 @@
<html>
<head>
<title>Sprechstunde anlegen</title>
</head>
<body>
<p>
{{range .Errors}}{{.}}<br />{{end}}
</p>
<p>
<form method="POST" action="addOfficeHour">
<label for="veranstaltung">Veranstaltung</label>:
<select name="veranstaltung">{{range $course := .Courses}}
<option value="{{$course.Id}}"{{if eq $course.Id $.SelectedCourse}} selected{{end}}>{{$course.Name}}</option>{{end}}
</select><br />
<label for="woche">Woche</label>:
<select name="woche">
<option value="0"{{if eq 0 $.Week}} selected{{end}}>Jede</option>
<option value="1"{{if eq 1 $.Week}} selected{{end}}>Gerade</option>
<option value="2"{{if eq 2 $.Week}} selected{{end}}>Ungerade</option>
</select><br />
<label for="tag">Tag</label>: <select name="tag">
<option value="0"{{if eq 0 $.Day}} selected{{end}}>Montag</option>
<option value="1"{{if eq 1 $.Day}} selected{{end}}>Dienstag</option>
<option value="2"{{if eq 2 $.Day}} selected{{end}}>Mittwoch</option>
<option value="3"{{if eq 3 $.Day}} selected{{end}}>Donnerstag</option>
<option value="4"{{if eq 4 $.Day}} selected{{end}}>Freitag</option>
</select><br />
<label for="startzeit">Startzeit</label>: <input type="time" name="startzeit" min="08:00" max="17:30" /><br />
<label for="dauer">Dauer in Minuten</label>: <input name="dauer" type="number" min="{{.MinuteGranularity}}" max="120" step="{{.MinuteGranularity}}" value="{{.Duration}}"/><br />
<label for="raum">Raum</label>:
<select name="raum">{{range $room := .Rooms}}
<option value="{{$room.Id}}"{{if eq $room.Id $.SelectedRoom}} selected{{end}}>{{$room.Name}}</option>{{end}}
</select><br />
<label for="raumname">Raumname (für Sonderräume)</label>: <input type="text" name="raumname" value="{{.Roomname}}"/><br />
<label for="name">Name</label>: <input name="name" type="text" size="50" value="{{.Name}}"/><br />
<label for="email">Email-Adresse</label>:
<input name="email" type="email" size="50" value="{{.Email}}"/><br />
<label for="info">Info</label>: <input name="info" type="text" size="50" value="{{.Info}}"/><br />
<input type="submit">
</form>
</p>
Du musst hier eine Email-Adresse angeben, die auf „tu-darmstadt.de“ endet.<br />
Außerdem dürfen in Räumen nur begrenzt viele Sprechstunden gleichzeitig stattfinden, nämlich
<dl>
{{range $room := .Rooms}}
<dt>{{$room.Name}}</dt><dl>{{$room.MaxOccupy}} Sprechstunde{{if gt $room.MaxOccupy 1}}n{{end}}</dl><br />{{end}}
</dl>
</body>
</html>