4 Commits

19 changed files with 300 additions and 797 deletions

View File

@ -1,44 +0,0 @@
root = "."
testdata_dir = "testdata"
tmp_dir = "tmp"
[build]
args_bin = []
bin = "./tmp/main"
cmd = "go build -o ./tmp/main ."
delay = 0
exclude_dir = ["assets", "tmp", "vendor", "testdata"]
exclude_file = []
exclude_regex = ["_test.go"]
exclude_unchanged = false
follow_symlink = false
full_bin = ""
include_dir = []
include_ext = ["go", "tpl", "tmpl", "html"]
include_file = []
kill_delay = "0s"
log = "build-errors.log"
poll = false
poll_interval = 0
rerun = false
rerun_delay = 500
send_interrupt = false
stop_on_error = false
[color]
app = ""
build = "yellow"
main = "magenta"
runner = "green"
watcher = "cyan"
[log]
main_only = false
time = false
[misc]
clean_on_exit = false
[screen]
clear_on_rebuild = false
keep_scroll = true

1
.gitignore vendored
View File

@ -0,0 +1 @@
create_tables.sql

View File

@ -1,97 +0,0 @@
USE sicherheitsunterweisung;
DROP TABLE IF EXISTS instructors;
DROP TABLE IF EXISTS briefings;
DROP TABLE IF EXISTS participants;
DROP TABLE IF EXISTS questions;
DROP TABLE IF EXISTS given_answers;
CREATE TABLE instructors (
id INT NOT NULL AUTO_INCREMENT,
first_name VARCHAR(32) NOT NULL,
last_name VARCHAR(32) NOT NULL,
personnel_id INT NOT NULL,
PRIMARY KEY(id)
);
CREATE TABLE briefings (
id INT NOT NULL AUTO_INCREMENT,
date DATE NOT NULL,
time TIME NOT NULL,
location VARCHAR(32) NOT NULL,
document_name VARCHAR(16) NOT NULL,
as_of DATE NOT NULL,
instructor_id INT NOT NULL,
PRIMARY KEY(id),
FOREIGN KEY(instructor_id) REFERENCES instructors(id)
);
CREATE TABLE participants (
id INT NOT NULL AUTO_INCREMENT,
first_name VARCHAR(32) NOT NULL,
last_name VARCHAR(32) NOT NULL,
company VARCHAR(32) NOT NULL,
PRIMARY KEY(id)
);
CREATE TABLE questions (
id INT NOT NULL AUTO_INCREMENT,
question VARCHAR(256) NOT NULL,
answer_1 VARCHAR(64) NOT NULL,
answer_2 VARCHAR(64) NOT NULL,
answer_3 VARCHAR(64) NOT NULL,
answer_4 VARCHAR(64) NOT NULL,
correct_answer INT NOT NULL,
PRIMARY KEY(id)
);
CREATE TABLE given_answers (
briefing_id INT NOT NULL,
participant_id INT NOT NULL,
question_id INT NOT NULL,
given_answer INT NOT NULL,
PRIMARY KEY(briefing_id, participant_id, question_id),
FOREIGN KEY(briefing_id) REFERENCES briefings(id),
FOREIGN KEY(participant_id) REFERENCES participants(id),
FOREIGN KEY(question_id) REFERENCES questions(id)
);
INSERT INTO instructors
(first_name, last_name, personnel_id)
VALUES
( 'Jason', 'Streifling', '123456' ),
( 'Tim', 'Taler', '123457' ),
( 'Georg', 'aus dem Jungel', '123458' );
INSERT INTO briefings (
date, time, location, as_of, instructor_id
) VALUES
( '2023-10-16', '17:00:00', 'Werk Langenhagen', 'ICS-2021-LGH', '2021-02-01', '1' ),
( '2023-10-16', '17:05:00', 'Werk Langenhagen', 'ICS-2021-LGH', '2021-02-01', '2' );
INSERT INTO participants (
first_name, last_name, company
) VALUES
( 'Peter', 'Enis', 'Körber' ),
( 'Dürüm', 'Döner', 'MP Technic' );
INSERT INTO questions (
question, answer_1, answer_2, answer_3, answer_4, correct_answer
) VALUES
( 'Was ist 1+1?', '1', '2', '3', '4', '2' ),
( 'Was ist 1+2?', '1', '2', '3', '4', '3' ),
( 'Was ist 2+2?', '1', '2', '3', '4', '4' );
INSERT INTO given_answers (
briefing_id, participant_id, question_id, given_answer
) VALUES
( '1', '1', '1', '2' ),
( '1', '1', '2', '3' ),
( '1', '1', '3', '3' ),
( '2', '2', '1', '2' ),
( '2', '2', '2', '3' ),
( '2', '2', '3', '4' );

1
go.mod
View File

@ -4,6 +4,7 @@ go 1.21.1
require ( require (
github.com/go-sql-driver/mysql v1.7.1 github.com/go-sql-driver/mysql v1.7.1
github.com/google/uuid v1.3.1
golang.org/x/term v0.13.0 golang.org/x/term v0.13.0
) )

2
go.sum
View File

@ -1,5 +1,7 @@
github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek=

24
main.go
View File

@ -1,33 +1,29 @@
package main package main
import ( import (
"html/template"
"log" "log"
"net/http" "net/http"
"streifling.com/jason/sicherheitsunterweisung/packages/data" "streifling.com/jason/sicherheitsunterweisung/packages/db"
"streifling.com/jason/sicherheitsunterweisung/packages/server" "streifling.com/jason/sicherheitsunterweisung/packages/server"
) )
func main() { func main() {
logins := make([]string, 0) var i, j int64
i, j = 1, 1
mux := http.NewServeMux()
db, err := data.OpenDB("sicherheitsunterweisung") db, err := db.Open("sicherheitsunterweisung")
if err != nil { if err != nil {
log.Fatalln(err) log.Fatalln(err)
} }
mux := http.NewServeMux()
mux.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static/")))) mux.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static/"))))
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { mux.HandleFunc("/", server.DisplayTable(db))
template.Must(template.ParseFiles("templates/index.html", "templates/login.html")).Execute(w, nil) mux.HandleFunc("/search/", server.DisplayResults(db))
}) mux.HandleFunc("/new-briefing/", server.DisplayForm(&i))
mux.HandleFunc("/search/", server.DisplaySearchResults(db)) mux.HandleFunc("/add-participant/", server.AddParticipant(&i))
mux.HandleFunc("/new-briefing/", server.DisplayInstructorForm()) mux.HandleFunc("/submit/", server.SubmitForm(db, &i, &j))
mux.HandleFunc("/add-participant/", server.AddParticipant(&logins))
mux.HandleFunc("/submit-form/", server.SubmitBriefingForm(db, &logins))
mux.HandleFunc("/internal-login/", server.DisplayTable(db))
mux.HandleFunc("/external-login/", server.DisplayParticipantForm(&logins))
log.Fatalln(http.ListenAndServe(":8080", mux)) log.Fatalln(http.ListenAndServe(":8080", mux))
} }

View File

@ -1,309 +0,0 @@
package data
import (
"bufio"
"database/sql"
"fmt"
"os"
"strings"
"syscall"
"streifling.com/jason/sicherheitsunterweisung/packages/types"
"github.com/go-sql-driver/mysql"
"golang.org/x/term"
)
type DB struct {
*sql.DB
Name string
}
func getUsername() (string, error) {
user := os.Getenv("DB_USER")
if user == "" {
var err error
fmt.Printf("DB Benutzer: ")
user, err = bufio.NewReader(os.Stdin).ReadString('\n')
if err != nil {
return "", fmt.Errorf("getUsername: bufio.NewReader(os.Stdin).ReadString('\n'): %v", err)
}
}
return strings.TrimSpace(user), nil
}
func getPassword() (string, error) {
pass := os.Getenv("DB_PASS")
if pass == "" {
fmt.Printf("DB Passwort: ")
bytePass, err := term.ReadPassword(int(syscall.Stdin))
if err != nil {
return "", fmt.Errorf("getCredentials: term.ReadPassword(int(syscall.Stdin)): %v", err)
}
fmt.Println()
pass = strings.TrimSpace(string(bytePass))
}
return pass, nil
}
func getCredentials() (string, string, error) {
user, err := getUsername()
if err != nil {
return "", "", fmt.Errorf("getCredentials: getUsername(): %v", err)
}
pass, err := getPassword()
if err != nil {
return "", "", fmt.Errorf("getCredentials: getPassword(): %v", err)
}
return user, pass, nil
}
func OpenDB(dbName string) (*DB, error) {
var err error
db := new(DB)
cfg := mysql.NewConfig()
cfg.DBName = dbName
cfg.User, cfg.Passwd, err = getCredentials()
if err != nil {
return nil, fmt.Errorf("Open: getCredentials(): %v\n", err)
}
db.Name = dbName
db.DB, err = sql.Open("mysql", cfg.FormatDSN())
if err != nil {
return nil, fmt.Errorf("Open: sql.Open(\"mysql\", cfg.FormatDSN()): %v\n", err)
}
if err := db.Ping(); err != nil {
return nil, fmt.Errorf("Open: db.Ping(): %v\n", err)
}
return db, nil
}
func (db *DB) WriteBriefing(b *types.Briefing) error {
result, err := db.Exec(`
INSERT INTO briefings
(date, time, location, document_name, as_of, instructor_id)
VALUES
(?, ?, ?, ?, ?, ?)
`, b.Date, b.Time, b.Location, b.DocumentName, b.AsOf, b.InstructorID)
if err != nil {
return fmt.Errorf("*DB.writeBriefing: db.Exec(): %v\n", err)
}
b.ID, err = result.LastInsertId()
if err != nil {
return fmt.Errorf("*DB.writeBriefing: result.LastInsertId(): %v\n", err)
}
return nil
}
func (db *DB) WriteParticipant(p *types.Participant) error {
result, err := db.Exec(`
INSERT INTO participants
(first_name, last_name, company)
VALUES
(?, ?, ?)
`, p.FirstName, p.LastName, p.Company)
if err != nil {
return fmt.Errorf("*DB.writeParticipants: db.Exec(): %v\n", err)
}
p.ID, err = result.LastInsertId()
if err != nil {
return fmt.Errorf("*DB.writeParticipants: result.LastInsertId(): %v\n", err)
}
return nil
}
func (db *DB) WriteGivenAnswer(b *types.Briefing, p *types.Participant, q *types.Question, g *types.GivenAnswer) error {
_, err := db.Exec(`
INSERT INTO given_answers
(briefing_id, participant_id, question_id, given_answer)
VALUES
(?, ?, ?, ?)
`, b.ID, p.ID, q.ID, g)
if err != nil {
return fmt.Errorf("*DB.writeGivenAnswers: db.Exec(): %v\n", err)
}
return nil
}
func (db *DB) WriteAllDataOfBriefing(b *types.Briefing, sp *[]*types.Participant, sq *[]*types.Question, sg *[]*types.GivenAnswer) error {
if err := db.WriteBriefing(b); err != nil {
return fmt.Errorf("*DB.WriteAllDataOfBriefing: db.writeBriefing(): %v\n", err)
}
for _, p := range *sp {
if err := db.WriteParticipant(p); err != nil {
return fmt.Errorf("*DB.WriteAllDataOfBriefing: db.writeParticipants(): %v\n", err)
}
}
for _, p := range *sp {
for i, q := range *sq {
db.WriteGivenAnswer(b, p, q, (*sg)[i])
}
}
return nil
}
func (db *DB) GetAllOverviewTableData() (*[]*types.OverviewTableData, error) {
rows, err := db.Query(`
SELECT
i.first_name,
i.last_name,
b.date,
b.time,
b.location,
b.document_name,
b.as_of,
p.first_name,
p.last_name,
p.company
FROM given_answers AS g
INNER JOIN briefings AS b
ON b.id = g.briefing_id
INNER JOIN participants AS p
ON p.id = g.participant_id
INNER JOIN instructors AS i
ON i.id = b.instructor_id
ORDER BY
b.id DESC,
p.id
`)
if err != nil {
return nil, fmt.Errorf("*DB.ReadAllBriefings: db.Query(): %v\n", err)
}
defer rows.Close()
data := make([]*types.OverviewTableData, 0)
for rows.Next() {
otd := new(types.OverviewTableData)
err := rows.Scan(
&otd.InstructorFirstName,
&otd.InstructorLastName,
&otd.BriefingDate,
&otd.BriefingTime,
&otd.BriefingLocation,
&otd.BriefingDocumentName,
&otd.BriefingAsOf,
&otd.ParticipantFirstName,
&otd.ParticipantLastName,
&otd.ParticipantCompany,
)
if err != nil {
return nil, fmt.Errorf("*DB.ReadAllBriefings: rows.Scan(): %v\n", err)
}
data = append(data, otd)
}
return &data, nil
}
func (db *DB) GetOverviewTableDataByName(n string) (*[]*types.OverviewTableData, error) {
rows, err := db.Query(`
SELECT
i.first_name,
i.last_name,
b.date,
b.time,
b.location,
b.document_name,
b.as_of,
p.first_name,
p.last_name,
p.company
FROM given_answers AS g
INNER JOIN briefings AS b
ON b.id = g.briefing_id
INNER JOIN participants AS p
ON p.id = g.participant_id
INNER JOIN instructors AS i
ON i.id = b.instructor_id
WHERE
i.first_name LIKE ? OR
i.last_name LIKE ? OR
p.first_name LIKE ? OR
p.last_name LIKE ?
ORDER BY
b.id DESC,
p.id
`, "%"+n+"%", "%"+n+"%", "%"+n+"%", "%"+n+"%")
if err != nil {
return nil, fmt.Errorf("*DB.GetOverviewTableDataByName: db.Query(): %v\n", err)
}
defer rows.Close()
data := make([]*types.OverviewTableData, 0)
for rows.Next() {
otd := new(types.OverviewTableData)
err := rows.Scan(
&otd.InstructorFirstName,
&otd.InstructorLastName,
&otd.BriefingDate,
&otd.BriefingTime,
&otd.BriefingLocation,
&otd.BriefingDocumentName,
&otd.BriefingAsOf,
&otd.ParticipantFirstName,
&otd.ParticipantLastName,
&otd.ParticipantCompany,
)
if err != nil {
return nil, fmt.Errorf("*DB.ReadAllBriefings: rows.Scan(): %v\n", err)
}
data = append(data, otd)
}
return &data, nil
}
func (db *DB) GetLastID(table string) (int, error) {
var id int
row := db.QueryRow(`
SELECT id
FROM ?
ORDER BY id DESC
LIMIT 0, 1
`, table)
if err := row.Scan(&id); err != nil {
return -1, fmt.Errorf("*DB.GetLastID: row.Scan(): %v\n", err)
}
return id, nil
}
func (db *DB) GetInstructors() (*[]*types.Instructor, error) {
rows, err := db.Query(`
SELECT *
FROM instructors
`)
if err != nil {
return nil, fmt.Errorf("*DB.GetInstructors: db.Query(): %v\n", err)
}
instructors := make([]*types.Instructor, 0)
for rows.Next() {
instructor := new(types.Instructor)
if err = rows.Scan(instructor); err != nil {
return nil, fmt.Errorf("*DB.GetInstructors: rows.Scan(): %v\n", err)
}
instructors = append(instructors, instructor)
}
return &instructors, nil
}

View File

@ -1,42 +0,0 @@
package data
import "streifling.com/jason/sicherheitsunterweisung/packages/types"
func InitQuestions() []types.Question {
Q := make([]types.Question, 0)
Q = append(Q, types.Question{
Text: "Wie viel ist 1+1?",
Answers: []types.Answer{
{ID: 0, Text: "1"},
{ID: 1, Text: "2"},
{ID: 2, Text: "3"},
{ID: 3, Text: "4"},
},
Correct: 1,
})
Q = append(Q, types.Question{
Text: "Wie viel ist 2+2?",
Answers: []types.Answer{
{ID: 0, Text: "1"},
{ID: 1, Text: "2"},
{ID: 2, Text: "3"},
{ID: 3, Text: "4"},
},
Correct: 3,
})
Q = append(Q, types.Question{
Text: "Wie viel ist 1+2?",
Answers: []types.Answer{
{ID: 0, Text: "1"},
{ID: 1, Text: "2"},
{ID: 2, Text: "3"},
{ID: 3, Text: "4"},
},
Correct: 2,
})
return Q
}

157
packages/db/db.go Normal file
View File

@ -0,0 +1,157 @@
package db
import (
"bufio"
"database/sql"
"fmt"
"os"
"strings"
"syscall"
"streifling.com/jason/sicherheitsunterweisung/packages/types"
"github.com/go-sql-driver/mysql"
"golang.org/x/term"
)
type DB struct {
*sql.DB
Name string
}
func getCredentials() (string, string, error) {
fmt.Printf("DB Benutzer: ")
user, err := bufio.NewReader(os.Stdin).ReadString('\n')
if err != nil {
return "", "", fmt.Errorf("getCredentials: bufio.NewReader(os.Stdin).ReadString('\n'): %v", err)
}
fmt.Printf("DB Passwort: ")
bytePass, err := term.ReadPassword(int(syscall.Stdin))
if err != nil {
return "", "", fmt.Errorf("getCredentials: term.ReadPassword(int(syscall.Stdin)): %v", err)
}
fmt.Println()
pass := string(bytePass)
return strings.TrimSpace(user), strings.TrimSpace(pass), nil
}
func reverseOrder(bs *[]types.Briefing) {
for i, j := 0, len(*bs)-1; i < j; i, j = i+1, j-1 {
(*bs)[i], (*bs)[j] = (*bs)[j], (*bs)[i]
}
}
func Open(dbName string) (*DB, error) {
var err error
db := new(DB)
cfg := mysql.NewConfig()
cfg.DBName = dbName
cfg.User, cfg.Passwd, err = getCredentials()
if err != nil {
return nil, fmt.Errorf("Open: getCredentials(): %v\n", err)
}
db.Name = dbName
db.DB, err = sql.Open("mysql", cfg.FormatDSN())
if err != nil {
return nil, fmt.Errorf("Open: sql.Open(\"mysql\", cfg.FormatDSN()): %v\n", err)
}
if err := db.Ping(); err != nil {
return nil, fmt.Errorf("Open: db.Ping(): %v\n", err)
}
return db, nil
}
func (db *DB) WriteBriefing(b *types.Briefing) error {
for i := 0; i < len(b.Participants); i++ {
result, err := db.Exec("INSERT INTO "+db.Name+" (instructor_first,"+
" instructor_last, date, time, state, location, participant_first,"+
" participant_last, company) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
b.FirstName, b.LastName, b.Date, b.Time, b.State, b.Location,
b.Participants[i].FirstName, b.Participants[i].LastName,
b.Participants[i].Company)
if err != nil {
return fmt.Errorf("*DB.WriteBriefing: db.Exec(\"INSERT INTO"+
" \"+db.Name+\" (instructor_first, instructor_last, date, time, state,"+
" location, participant_first, participant_last, company) VALUES (?, ?,"+
" ?, ?, ?, ?, ?, ?, ?)\", b.FirstName, b.LastName, b.Date, b.Time,"+
" b.State, b.Location, b.Participants[i].FirstName,"+
" b.Participants[i].LastName, b.Participants[i].Company): %v\n", err)
}
_, err = result.LastInsertId()
if err != nil {
return fmt.Errorf("*DB.WriteBriefing: result.LastInsertId(): %v\n", err)
}
}
return nil
}
func (db *DB) ReadAll() (*[]types.Briefing, error) {
bs := make([]types.Briefing, 0)
rows, err := db.Query("SELECT *" + " FROM " + db.Name)
if err != nil {
return nil, fmt.Errorf("*DB.ReadAll: db.Query(\"SELECT * FROM \"+db.Name): %v\n", err)
}
defer rows.Close()
for rows.Next() {
b := new(types.Briefing)
p := new(types.Participant)
if err := rows.Scan(&p.ID, &b.FirstName, &b.LastName, &b.Date, &b.Time,
&b.State, &b.Location, &p.FirstName, &p.LastName, &p.Company); err != nil {
return nil, fmt.Errorf("*DB.ReadAll: db.Query(): %v\n", err)
}
b.Participants = append(b.Participants, *p)
bs = append(bs, *b)
}
reverseOrder(&bs)
return &bs, nil
}
func (db *DB) ReadByName(name string) (*[]types.Briefing, error) {
bs := make([]types.Briefing, 0)
rows, err := db.Query("SELECT *"+
" FROM "+db.Name+
" WHERE instructor_first LIKE ?"+
" OR instructor_last LIKE ?"+
" OR participant_first LIKE ?"+
" OR participant_last LIKE ?",
"%"+name+"%", "%"+name+"%", "%"+name+"%", "%"+name+"%")
if err != nil {
return nil, fmt.Errorf("*DB.ReadByName: db.Query(\"SELECT *"+
" FROM \"+db.Name+"+
" WHERE instructor_first LIKE ?"+
" OR instructor_last LIKE ?"+
" OR participant_first LIKE ?"+
" OR participant_last LIKE ?\"): %v\n", err)
}
defer rows.Close()
for rows.Next() {
b := new(types.Briefing)
p := new(types.Participant)
if err := rows.Scan(&p.ID, &b.FirstName, &b.LastName, &b.Date, &b.Time, &b.State,
&b.Location, &p.FirstName, &p.LastName, &p.Company); err != nil {
return nil, fmt.Errorf("*DB.ReadByName: rows.Scan(&p.ID, &b.FirstName,"+
" &b.LastName, &b.Date, &b.Time, &b.State, &b.Location, &p.FirstName,"+
" &p.LastName, &p.Company): %v\n", err)
}
b.Participants = append(b.Participants, *p)
bs = append(bs, *b)
}
reverseOrder(&bs)
return &bs, nil
}

View File

@ -1,193 +1,106 @@
package server package server
import ( import (
"crypto/rand"
"encoding/hex"
"fmt" "fmt"
"github.com/google/uuid"
"html/template" "html/template"
"io"
"log"
"net/http" "net/http"
"strconv" "os"
"time" "streifling.com/jason/sicherheitsunterweisung/packages/db"
"streifling.com/jason/sicherheitsunterweisung/packages/data"
"streifling.com/jason/sicherheitsunterweisung/packages/types" "streifling.com/jason/sicherheitsunterweisung/packages/types"
) )
// type questionData struct { func DisplayTable(db *db.DB) http.HandlerFunc {
// ID int64
// Q types.Question
// I int
// J int
// }
func DisplayTable(db *data.DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
bs, err := db.GetAllOverviewTableData() bs, err := db.ReadAll()
if err != nil { if err != nil {
http.Error(w, "DisplayTable: *DB.GetAllOverviewTableData(): "+fmt.Sprint(err), http.StatusInternalServerError) _ = fmt.Errorf("DisplayTable: %v\n", err)
return
} }
template.Must(template.ParseFiles("templates/table.html")).ExecuteTemplate(w, "content", bs) template.Must(template.ParseFiles("templates/index.html", "templates/table.html")).Execute(w, bs)
} }
} }
func DisplaySearchResults(db *data.DB) http.HandlerFunc { func DisplayResults(db *db.DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
bs, err := db.GetOverviewTableDataByName(r.PostFormValue("search")) bs, err := db.ReadByName(r.PostFormValue("search"))
if err != nil { if err != nil {
http.Error(w, "DisplayResults: db.ReadByName(r.PostFormValue()): "+fmt.Sprint(err), http.StatusInternalServerError) _ = fmt.Errorf("DisplayResults: db.ReadByName(r.PostFormValue()): %v\n", err)
return
} }
template.Must(template.ParseFiles("templates/table.html")).ExecuteTemplate(w, "rows", bs) template.Must(template.ParseFiles("templates/table.html")).ExecuteTemplate(w, "rows", bs)
} }
} }
func DisplayInstructorForm() http.HandlerFunc { func DisplayForm(i *int64) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
template.Must(template.ParseFiles("templates/briefing.html")).ExecuteTemplate(w, "content", nil) template.Must(template.ParseFiles("templates/form.html")).ExecuteTemplate(w, "content", i)
} }
} }
func generateUUID() (string, error) { func AddParticipant(i *int64) http.HandlerFunc {
bs := make([]byte, 4) return func(w http.ResponseWriter, r *http.Request) {
*i++
if _, err := rand.Read(bs); err != nil { template.Must(template.ParseFiles("templates/form.html")).ExecuteTemplate(w, "participant", i)
return "", fmt.Errorf("GenerateUUID: rand.Read(bs): %v\n", err)
} }
return hex.EncodeToString(bs), nil
} }
func AddParticipant(sl *[]string) http.HandlerFunc { func SubmitForm(db *db.DB, i, j *int64) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
login, err := generateUUID() b := new(types.Briefing)
b.FirstName = r.PostFormValue("instructor-first")
b.LastName = r.PostFormValue("instructor-last")
b.Date = r.PostFormValue("date")
b.Time = r.PostFormValue("time")
b.State = r.PostFormValue("state")
b.Location = r.PostFormValue("location")
for ; *j <= *i; *j++ {
b.Participants = append(b.Participants, types.Participant{
ID: *j,
Person: types.Person{
FirstName: r.PostFormValue("participant-first-" + fmt.Sprint(*j)),
LastName: r.PostFormValue(("participant-last-" + fmt.Sprint(*j))),
},
Company: r.PostFormValue(("participant-company-" + fmt.Sprint(*j))),
})
}
log.Println(b)
db.WriteBriefing(b)
bs, err := db.ReadAll()
if err != nil { if err != nil {
http.Error(w, "AddParticipant: generateUUID(): "+fmt.Sprint(err), http.StatusInternalServerError) _ = fmt.Errorf("SubmitForm: db.ReadAll(): %v\n", err)
return
} }
template.Must(template.ParseFiles("templates/index.html", "templates/table.html")).Execute(w, bs)
(*sl) = append(*sl, login)
template.Must(template.ParseFiles("templates/briefing.html")).ExecuteTemplate(w, "new", login)
} }
} }
// TODO: Hier weiter machen, irgendwie die b.ID herausgeben, func Upload(fn, ln, d, t string) http.HandlerFunc {
// am besten hier auch die p.IDs rausgeben, damit diese später verknüpft werden können
func SubmitBriefingForm(db *data.DB, sl *[]string) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
now := time.Now() r.ParseMultipartForm(10 << 20) // 10 MiB
briefing := new(types.Briefing)
// TODO: Dropdownmenü tmpFile, _, err := r.FormFile("questionaire")
// instructorFirstName := r.PostFormValue("instructor-first")
// instructorLastName := r.PostFormValue("instructor-last")
briefing.Date = now.Format("2006-01-02")
briefing.Time = now.Format("15:04:05")
briefing.Location = r.PostFormValue("location")
briefing.DocumentName = r.PostFormValue("document") // TODO: in HTML einfügen
briefing.AsOf = r.PostFormValue("state") // TODO: Umbenennen
// briefing.InstructorID = r.PostFormValue("instructor-id") // TODO: aus Dropdown holen
db.WriteBriefing(briefing)
}
}
// TODO: Make it only serve one purpose
func loginIsCorrect(l string, logins *[]string) bool {
for i, v := range *logins {
if l == v {
(*logins) = append((*logins)[:i], (*logins)[i+1:]...)
return true
}
}
return false
}
func newParticipant(l string) (*types.Participant, error) {
var err error
p := new(types.Participant)
p.ID, err = strconv.ParseInt(l, 10, 64)
if err != nil { if err != nil {
return nil, fmt.Errorf("newParticipant: strconv.Atoi(idString): %v\n", err) http.Error(w, err.Error(), http.StatusInternalServerError)
return
} }
defer tmpFile.Close()
return p, nil file, err := os.Create(ln + fn + d + t + uuid.New().String() + ".png")
}
func DisplayParticipantForm(sl *[]string) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if loginIsCorrect(r.PostFormValue("login"), sl) {
uuid, err := generateUUID()
if err != nil { if err != nil {
http.Error(w, "DisplayParticipantForm: generateUUID(): "+fmt.Sprint(err), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
return
} }
defer file.Close()
template.Must(template.ParseFiles("templates/participant.html")).ExecuteTemplate(w, "content", uuid) if _, err := io.Copy(file, tmpFile); err != nil {
} else { http.Error(w, err.Error(), http.StatusInternalServerError)
template.Must(template.ParseFiles("templates/login.html")).ExecuteTemplate(w, "content", nil) return
} }
} }
} }
// func readAnswer(r *http.Request, p *types.Participant, i int) error {
// v, err := strconv.Atoi(r.PostFormValue("answer"))
// if err != nil {
// return fmt.Errorf("readAnswer: strconv.Atoi(): %v\n", err)
// }
//
// p.Questions[i].Chosen = v
//
// return nil
// }
//
// func DisplayQuestion(i int, p *types.Participant) http.HandlerFunc {
// return func(w http.ResponseWriter, r *http.Request) {
// if i == 0 {
// p.FirstName = r.PostFormValue("participant-first-" + fmt.Sprintf("%d", p.ID))
// p.LastName = r.PostFormValue("participant-last-" + fmt.Sprintf("%d", p.ID))
// p.Company = r.PostFormValue("participant-company-" + fmt.Sprintf("%d", p.ID))
// } else {
// if err := readAnswer(r, p, i-1); err != nil {
// http.Error(w, "DisplayQuestion: readAnswer(r, p, i): "+fmt.Sprint(err), http.StatusInternalServerError)
// }
// }
//
// data := new(questionData)
// data.ID = p.ID
// data.Q = p.Questions[i]
// data.I = i
// data.J = i + 1
//
// template.Must(template.ParseFiles("templates/question.html")).ExecuteTemplate(w, "content", data)
// }
// }
//
// func DisplayTestResults(b *types.Briefing, p *types.Participant) http.HandlerFunc {
// return func(w http.ResponseWriter, r *http.Request) {
// numQuestions := len(p.Questions)
// wrongAnswers := make([]int, 0)
// fmt.Println(wrongAnswers)
//
// if err := readAnswer(r, p, numQuestions-1); err != nil {
// http.Error(w, "DisplayTestResults: readAnswer(r, p, i): "+fmt.Sprint(err), http.StatusInternalServerError)
// }
//
// for i, q := range p.Questions {
// if q.Chosen != q.Correct {
// wrongAnswers = append(wrongAnswers, i)
// }
// }
//
// if wrongAnswers == nil {
// b.Participants = append(b.Participants, p)
// } else {
// data := new(questionData)
// data.ID = p.ID
// data.Q = p.Questions[0]
// data.I = 0
// data.J = data.I + 1
// template.Must(template.ParseFiles("templates/question.html")).ExecuteTemplate(w, "content", data)
// }
//
// template.Must(template.ParseFiles("templates/results.html")).ExecuteTemplate(w, "content", nil)
// }
// }

View File

@ -1,7 +1,8 @@
package types package types
import "os"
type Person struct { type Person struct {
ID int64
FirstName string FirstName string
LastName string LastName string
} }
@ -9,48 +10,17 @@ type Person struct {
type Instructor Person type Instructor Person
type Participant struct { type Participant struct {
ID int64
Person Person
Company string Company string
Questionaire string
} }
type Briefing struct { type Briefing struct {
ID int64 Instructor
Date string Date string
Time string Time string
State string
Location string Location string
DocumentName string Participants []Participant
AsOf string
InstructorID int64
}
type Answer struct {
ID int64
Text string
}
type Question struct {
ID int64
Text string
Answers []Answer
Correct int
}
type GivenAnswer struct {
BriefingID int64
ParticipantID int64
QuestionID int64
GivenAnswer int
}
type OverviewTableData struct {
InstructorFirstName string
InstructorLastName string
BriefingDate string
BriefingTime string
BriefingLocation string
BriefingDocumentName string
BriefingAsOf string
ParticipantFirstName string
ParticipantLastName string
ParticipantCompany string
} }

View File

@ -1,38 +0,0 @@
{{ define "add-button" }}
<button type="button" hx-post="/add-participant/" hx-target="this" hx-swap="outerHTML">
Neuer Teilnehmer
</button>
{{ end }}
{{ define "new" }}
<span>{{ . }}</span>
{{ template "add-button" . }}
{{ end }}
{{ define "content" }}
<form>
<div id="instructor">
<label for="instructor-first-input">Unterweiser Vorname</label>
<input type="text" name="instructor-first" id="instructor-first-input" />
<label for="instructor-last-input">Unterweiser Nachname</label>
<input type="text" name="instructor-last" id="instructor-last-input" />
</div>
<div id="location">
<label for="location-input">Ort</label>
<input type="text" name="location" id="location-input" />
</div>
<div id="state">
<label for="state-input">Stand vom</label>
<input type="date" name="state" id="state-input" />
</div>
{{ template "add-button" . }}
<button type="submit" hx-post="/submit-form/" hx-target="#content" hx-swap="innerHTML">
OK
</button>
</form>
{{ end }}

58
templates/form.html Normal file
View File

@ -0,0 +1,58 @@
{{ define "participant" }}
<div id="participant-{{ . }}">
<label for="participant-first-input-{{ . }}">Vorname</label>
<input type="text" name="participant-first-{{ . }}" id="participant-first-input-{{ . }}" />
<label for="participant-last-input-{{ . }}">Nachname</label>
<input type="text" name="participant-last-{{ . }}" id="participant-last-input-{{ . }}" />
<label for="participant-company-input-{{ . }}">Firma</label>
<input type="text" name="participant-company-{{ . }}" id="participant-company-input-{{ . }}" />
<button type="button" hx-post="/upload/">Fragebogen hochladen</button>
</div>
{{ end }}
{{ define "content" }}
<form>
<div id="instructor">
<label for="instructor-first-input">Unterweiser Vorname</label>
<input type="text" name="instructor-first" id="instructor-first-input" />
<label for="instructor-last-input">Unterweiser Nachname</label>
<input type="text" name="instructor-last" id="instructor-last-input" />
</div>
<div id="date">
<label for="date-input">Datum</label>
<input type="date" name="date" id="date-input" />
</div>
<div id="time">
<label for="time-input">Uhrzeit</label>
<input type="time" name="time" id="time-input" />
</div>
<div id="state">
<label for="state-input">Stand vom</label>
<input type="date" name="state" id="state-input" />
</div>
<div id="location">
<label for="location-input">Ort</label>
<input type="text" name="location" id="location-input" />
</div>
<div id="participants">
<button type="button" hx-post="/add-participant/" hx-target="#participants" hx-swap="beforeend" hx-trigger="click">
+
</button>
{{ template "participant" . }}
</div>
<button type="submit" hx-post="/submit/" hx-target="#content" hx-swap="innerHTML">
Senden
</button>
</form>
{{ end }}

View File

@ -1,16 +0,0 @@
{{ define "content" }}
<h2>Login</h2>
<form>
<label for="login-input">Code</label>
<input type="text" name="login" id="login-input" />
<button type="submit" hx-post="/external-login/" hx-target="#content">
Anmelden
</button>
<button type="submit" hx-post="/internal-login/" hx-target="#content">
Intern
</button>
</form>
{{ end }}

View File

@ -1,16 +0,0 @@
{{ define "content" }}
<form id="participant-{{ . }}">
<label for="participant-first-input-{{ . }}">Vorname</label>
<input type="text" name="participant-first-{{ . }}" id="participant-first-input-{{ . }}" />
<label for="participant-last-input-{{ . }}">Nachname</label>
<input type="text" name="participant-last-{{ . }}" id="participant-last-input-{{ . }}" />
<label for="participant-company-input-{{ . }}">Firma</label>
<input type="text" name="participant-company-{{ . }}" id="participant-company-input-{{ . }}" />
<button type="button" hx-post="/display-question-{{ . }}-0/" hx-target="#content">
Fertig
</button>
</form>
{{ end }}

View File

@ -1,21 +0,0 @@
{{ define "answers" }}
{{ range .Q.Answers }}
<div>
<input type="radio" name="answer" id="answer-{{ .ID }}" value="{{ .ID }}" />
<label for="answer-{{ .ID }}">{{ .Text }}</label>
</div>
{{ end }}
{{ end }}
{{ define "content" }}
<h2>Frage {{ .I }}</h2>
<p>{{ .Q.Text }}</p>
<form>
{{ template "answers" . }}
<button type="submit" hx-post="/display-question-{{ .ID }}-{{ .J }}/" hx-target="#content" hx-swap="innerHTML">
Weiter
</button>
</form>
{{ end }}

View File

@ -1,11 +0,0 @@
{{ define "passed" }}
{{ end }}
{{ define "failed" }}
{{ end }}
{{ define "content" }}
{{ q := range .Participant.Questions }}
<p>{{ . }}{{ q.Text }}</p>
{{ end }}
{{ end }}

View File

@ -1 +0,0 @@
exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1

BIN
tmp/main

Binary file not shown.