Compare commits
4 Commits
online_que
...
dateien_ho
Author | SHA1 | Date | |
---|---|---|---|
ea8bf5e38e | |||
08fb23d0f5 | |||
6972948ee3 | |||
fc7aae83d2 |
44
.air.toml
44
.air.toml
@ -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
1
.gitignore
vendored
@ -0,0 +1 @@
|
|||||||
|
create_tables.sql
|
||||||
|
@ -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
1
go.mod
@ -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
2
go.sum
@ -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
24
main.go
@ -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))
|
||||||
}
|
}
|
||||||
|
@ -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
|
|
||||||
}
|
|
@ -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
157
packages/db/db.go
Normal 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
|
||||||
|
}
|
@ -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")
|
if err != nil {
|
||||||
// instructorLastName := r.PostFormValue("instructor-last")
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
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
|
|
||||||
}
|
}
|
||||||
}
|
defer tmpFile.Close()
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func newParticipant(l string) (*types.Participant, error) {
|
file, err := os.Create(ln + fn + d + t + uuid.New().String() + ".png")
|
||||||
var err error
|
if err != nil {
|
||||||
p := new(types.Participant)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
p.ID, err = strconv.ParseInt(l, 10, 64)
|
if _, err := io.Copy(file, tmpFile); err != nil {
|
||||||
if err != nil {
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
return nil, fmt.Errorf("newParticipant: strconv.Atoi(idString): %v\n", err)
|
return
|
||||||
}
|
|
||||||
|
|
||||||
return p, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
|
||||||
http.Error(w, "DisplayParticipantForm: generateUUID(): "+fmt.Sprint(err), http.StatusInternalServerError)
|
|
||||||
}
|
|
||||||
|
|
||||||
template.Must(template.ParseFiles("templates/participant.html")).ExecuteTemplate(w, "content", uuid)
|
|
||||||
} else {
|
|
||||||
template.Must(template.ParseFiles("templates/login.html")).ExecuteTemplate(w, "content", nil)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
@ -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
|
|
||||||
}
|
}
|
||||||
|
@ -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
58
templates/form.html
Normal 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 }}
|
@ -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 }}
|
|
@ -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 }}
|
|
@ -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 }}
|
|
@ -1,11 +0,0 @@
|
|||||||
{{ define "passed" }}
|
|
||||||
{{ end }}
|
|
||||||
|
|
||||||
{{ define "failed" }}
|
|
||||||
{{ end }}
|
|
||||||
|
|
||||||
{{ define "content" }}
|
|
||||||
{{ q := range .Participant.Questions }}
|
|
||||||
<p>{{ . }}{{ q.Text }}</p>
|
|
||||||
{{ end }}
|
|
||||||
{{ end }}
|
|
@ -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
|
|
Reference in New Issue
Block a user