Compare commits
	
		
			19 Commits
		
	
	
		
			online_que
			...
			sessions
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| ba53bba0f6 | |||
| 33cbf4b215 | |||
| 34ef95ad76 | |||
| e3fb756bb1 | |||
| 387f662355 | |||
| 3496fe5f86 | |||
| 10247722c8 | |||
| c7bb630043 | |||
| 5049db064c | |||
| aded71394d | |||
| d054b3644b | |||
| b78e30d109 | |||
| 52cd5756d8 | |||
| 1e6d83de1d | |||
| 4de7d36385 | |||
| 35d565ec7d | |||
| 6359caf3e9 | |||
| 8622f81f89 | |||
| 4e0c8ec1ac | 
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -0,0 +1,2 @@
 | 
			
		||||
tmp
 | 
			
		||||
test.sql
 | 
			
		||||
 
 | 
			
		||||
@@ -1,21 +1,23 @@
 | 
			
		||||
USE sicherheitsunterweisung;
 | 
			
		||||
 | 
			
		||||
SET FOREIGN_KEY_CHECKS = 0;
 | 
			
		||||
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;
 | 
			
		||||
SET FOREIGN_KEY_CHECKS = 1;
 | 
			
		||||
 | 
			
		||||
CREATE TABLE instructors (
 | 
			
		||||
  id           INT         NOT NULL AUTO_INCREMENT,
 | 
			
		||||
  id         INT         NOT NULL,
 | 
			
		||||
  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,
 | 
			
		||||
  id            INT         AUTO_INCREMENT,
 | 
			
		||||
  date          DATE        NOT NULL,
 | 
			
		||||
  time          TIME        NOT NULL,
 | 
			
		||||
  location      VARCHAR(32) NOT NULL,
 | 
			
		||||
@@ -28,7 +30,7 @@ CREATE TABLE briefings (
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
CREATE TABLE participants (
 | 
			
		||||
  id         INT         NOT NULL AUTO_INCREMENT,
 | 
			
		||||
  id         INT         AUTO_INCREMENT,
 | 
			
		||||
  first_name VARCHAR(32) NOT NULL,
 | 
			
		||||
  last_name  VARCHAR(32) NOT NULL,
 | 
			
		||||
  company    VARCHAR(32) NOT NULL,
 | 
			
		||||
@@ -37,7 +39,7 @@ CREATE TABLE participants (
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
CREATE TABLE questions (
 | 
			
		||||
  id             INT          NOT NULL AUTO_INCREMENT,
 | 
			
		||||
  id             INT          AUTO_INCREMENT,
 | 
			
		||||
  question       VARCHAR(256) NOT NULL,
 | 
			
		||||
  answer_1       VARCHAR(64)  NOT NULL,
 | 
			
		||||
  answer_2       VARCHAR(64)  NOT NULL,
 | 
			
		||||
@@ -61,17 +63,17 @@ CREATE TABLE given_answers (
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
INSERT INTO instructors
 | 
			
		||||
  (first_name, last_name, personnel_id)
 | 
			
		||||
  (id, first_name, last_name)
 | 
			
		||||
VALUES
 | 
			
		||||
  ( 'Jason', 'Streifling',     '123456' ),
 | 
			
		||||
  ( 'Tim',   'Taler',          '123457' ),
 | 
			
		||||
  ( 'Georg', 'aus dem Jungel', '123458' );
 | 
			
		||||
  ( '123456', 'Jason', 'Streifling'     ),
 | 
			
		||||
  ( '123457', 'Tim',   'Taler'          ),
 | 
			
		||||
  ( '123458', 'Georg', 'aus dem Jungel' );
 | 
			
		||||
 | 
			
		||||
INSERT INTO briefings (
 | 
			
		||||
  date, time, location, as_of, instructor_id
 | 
			
		||||
  date, time, location, document_name, 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' );
 | 
			
		||||
  ( '2023-10-16', '17:00:00', 'Werk Langenhagen', 'ICS-2021-LGH', '2021-02-01', '123456' ),
 | 
			
		||||
  ( '2023-10-16', '17:05:00', 'Werk Langenhagen', 'ICS-2021-LGH', '2021-02-01', '123457' );
 | 
			
		||||
 | 
			
		||||
INSERT INTO participants (
 | 
			
		||||
  first_name, last_name, company
 | 
			
		||||
@@ -84,7 +86,8 @@ INSERT INTO questions (
 | 
			
		||||
) 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' );
 | 
			
		||||
  ( 'Was ist 2+2?', '1', '2', '3', '4', '4' ),
 | 
			
		||||
  ( 'Was ist 0+1?', '1', '2', '3', '4', '1' );
 | 
			
		||||
 | 
			
		||||
INSERT INTO given_answers (
 | 
			
		||||
  briefing_id, participant_id, question_id, given_answer
 | 
			
		||||
@@ -92,6 +95,8 @@ INSERT INTO given_answers (
 | 
			
		||||
  ( '1', '1', '1', '2' ),
 | 
			
		||||
  ( '1', '1', '2', '3' ),
 | 
			
		||||
  ( '1', '1', '3', '3' ),
 | 
			
		||||
  ( '1', '1', '4', '1' ),
 | 
			
		||||
  ( '2', '2', '1', '2' ),
 | 
			
		||||
  ( '2', '2', '2', '3' ),
 | 
			
		||||
  ( '2', '2', '3', '4' );
 | 
			
		||||
  ( '2', '2', '3', '4' ),
 | 
			
		||||
  ( '2', '2', '4', '1' );
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								go.mod
									
									
									
									
									
								
							@@ -4,6 +4,7 @@ go 1.21.1
 | 
			
		||||
 | 
			
		||||
require (
 | 
			
		||||
	github.com/go-sql-driver/mysql v1.7.1
 | 
			
		||||
	github.com/google/uuid v1.3.1
 | 
			
		||||
	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/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/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek=
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										19
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										19
									
								
								main.go
									
									
									
									
									
								
							@@ -6,28 +6,27 @@ import (
 | 
			
		||||
	"net/http"
 | 
			
		||||
 | 
			
		||||
	"streifling.com/jason/sicherheitsunterweisung/packages/data"
 | 
			
		||||
	"streifling.com/jason/sicherheitsunterweisung/packages/server"
 | 
			
		||||
	"streifling.com/jason/sicherheitsunterweisung/packages/session"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	logins := make([]string, 0)
 | 
			
		||||
 | 
			
		||||
	db, err := data.OpenDB("sicherheitsunterweisung")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatalln(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	mux := http.NewServeMux()
 | 
			
		||||
	mux := session.NewMux()
 | 
			
		||||
	sessions := make([]*session.Session, 0)
 | 
			
		||||
	sessionChan := make(chan *session.Session)
 | 
			
		||||
 | 
			
		||||
	mux.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static/"))))
 | 
			
		||||
	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		template.Must(template.ParseFiles("templates/index.html", "templates/login.html")).Execute(w, nil)
 | 
			
		||||
	})
 | 
			
		||||
	mux.HandleFunc("/search/", server.DisplaySearchResults(db))
 | 
			
		||||
	mux.HandleFunc("/new-briefing/", server.DisplayInstructorForm())
 | 
			
		||||
	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))
 | 
			
		||||
	mux.HandleFunc("/internal-login/", session.HandleInternalLogin(db, &sessions, sessionChan))
 | 
			
		||||
	mux.HandleFunc("/external-login/", session.HandleExternalLogin(&sessions))
 | 
			
		||||
 | 
			
		||||
	go mux.HandleSessions(db, sessionChan, &sessions)
 | 
			
		||||
 | 
			
		||||
	log.Fatalln(http.ListenAndServe(":8080", mux))
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,10 @@
 | 
			
		||||
package types
 | 
			
		||||
package data
 | 
			
		||||
 | 
			
		||||
import "database/sql"
 | 
			
		||||
 | 
			
		||||
type DB struct {
 | 
			
		||||
	*sql.DB
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Person struct {
 | 
			
		||||
	ID        int64
 | 
			
		||||
@@ -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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										326
									
								
								packages/data/dbFuncs.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										326
									
								
								packages/data/dbFuncs.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,326 @@
 | 
			
		||||
package data
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"database/sql"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/go-sql-driver/mysql"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
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("error: OpenDB: getCredentials(): %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	db.DB, err = sql.Open("mysql", cfg.FormatDSN())
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("error: OpenDB: sql.Open(\"mysql\", cfg.FormatDSN()): %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	if err := db.Ping(); err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("error: OpenDB: db.Ping(): %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return db, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (db *DB) WriteBriefing(b *Briefing) error {
 | 
			
		||||
	query := `
 | 
			
		||||
  INSERT INTO briefings
 | 
			
		||||
  	(date, time, location, document_name, as_of, instructor_id)
 | 
			
		||||
  VALUES
 | 
			
		||||
  	(?, ?, ?, ?, ?, ?)
 | 
			
		||||
  `
 | 
			
		||||
 | 
			
		||||
	result, err := db.Exec(query, b.Date, b.Time, b.Location, b.DocumentName, b.AsOf, b.InstructorID)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("error: *DB.writeBriefing: db.Exec(): %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	b.ID, err = result.LastInsertId()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("error: *DB.writeBriefing: result.LastInsertId(): %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (db *DB) WriteParticipant(p *Participant) error {
 | 
			
		||||
	query := `
 | 
			
		||||
  INSERT INTO participants
 | 
			
		||||
  	(first_name, last_name, company)
 | 
			
		||||
  VALUES
 | 
			
		||||
  	(?, ?, ?)
 | 
			
		||||
  `
 | 
			
		||||
 | 
			
		||||
	result, err := db.Exec(query, p.FirstName, p.LastName, p.Company)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("error: *DB.writeParticipants: db.Exec(): %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	p.ID, err = result.LastInsertId()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("error: *DB.writeParticipants: result.LastInsertId(): %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (db *DB) WriteGivenAnswers(b Briefing, p Participant, sq []Question, givenAnswers []int) error {
 | 
			
		||||
	query := `
 | 
			
		||||
    INSERT INTO given_answers
 | 
			
		||||
    	(briefing_id, participant_id, question_id, given_answer)
 | 
			
		||||
    VALUES
 | 
			
		||||
    	(?, ?, ?, ?)
 | 
			
		||||
    `
 | 
			
		||||
 | 
			
		||||
	for i, q := range sq {
 | 
			
		||||
		_, err := db.Exec(query, b.ID, p.ID, q.ID, givenAnswers[i])
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return fmt.Errorf("error: *DB.WriteGivenAnswers: db.Exec(): %v", err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (db *DB) GetAllOverviewTableData() ([]OverviewTableData, error) {
 | 
			
		||||
	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 questions AS q
 | 
			
		||||
      ON q.id = g.question_id
 | 
			
		||||
    INNER JOIN instructors AS i
 | 
			
		||||
      ON i.id = b.instructor_id
 | 
			
		||||
  WHERE
 | 
			
		||||
    q.id = 1
 | 
			
		||||
  ORDER BY
 | 
			
		||||
    b.id DESC,
 | 
			
		||||
    p.id
 | 
			
		||||
  `
 | 
			
		||||
 | 
			
		||||
	rows, err := db.Query(query)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("error: *DB.ReadAllBriefings: db.Query(): %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	defer rows.Close()
 | 
			
		||||
 | 
			
		||||
	data := make([]OverviewTableData, 0)
 | 
			
		||||
	for rows.Next() {
 | 
			
		||||
		otd := new(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("error: *DB.ReadAllBriefings: rows.Scan(): %v", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		data = append(data, *otd)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return data, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (db *DB) GetOverviewTableDataByName(n string) ([]OverviewTableData, error) {
 | 
			
		||||
	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
 | 
			
		||||
    q.id = 1 AND
 | 
			
		||||
    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
 | 
			
		||||
  `
 | 
			
		||||
 | 
			
		||||
	rows, err := db.Query(query, "%"+n+"%", "%"+n+"%", "%"+n+"%", "%"+n+"%")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("error: *DB.GetOverviewTableDataByName: db.Query(): %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	defer rows.Close()
 | 
			
		||||
 | 
			
		||||
	data := make([]OverviewTableData, 0)
 | 
			
		||||
	for rows.Next() {
 | 
			
		||||
		otd := new(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("error: *DB.ReadAllBriefings: rows.Scan(): %v", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		data = append(data, *otd)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return data, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (db *DB) GetLastID(table string) (int, error) {
 | 
			
		||||
	var id int
 | 
			
		||||
	query := `
 | 
			
		||||
  SELECT id
 | 
			
		||||
  FROM ?
 | 
			
		||||
  ORDER BY id DESC
 | 
			
		||||
    LIMIT 0, 1
 | 
			
		||||
  `
 | 
			
		||||
 | 
			
		||||
	row := db.QueryRow(query, table)
 | 
			
		||||
	if err := row.Scan(&id); err != nil {
 | 
			
		||||
		return -1, fmt.Errorf("error: *DB.GetLastID: row.Scan(): %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return id, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (db *DB) GetInstructors() ([]*Instructor, error) {
 | 
			
		||||
	query := `
 | 
			
		||||
  SELECT *
 | 
			
		||||
  FROM instructors
 | 
			
		||||
  ORDER BY
 | 
			
		||||
    last_name,
 | 
			
		||||
    first_name
 | 
			
		||||
  `
 | 
			
		||||
 | 
			
		||||
	rows, err := db.Query(query)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("error: *DB.GetInstructors: db.Query(): %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	defer rows.Close()
 | 
			
		||||
 | 
			
		||||
	instructors := make([]*Instructor, 0)
 | 
			
		||||
	for rows.Next() {
 | 
			
		||||
		instructor := new(Instructor)
 | 
			
		||||
		if err = rows.Scan(&instructor.ID, &instructor.FirstName, &instructor.LastName); err != nil {
 | 
			
		||||
			return nil, fmt.Errorf("error: *DB.GetInstructors: rows.Scan(): %v", err)
 | 
			
		||||
		}
 | 
			
		||||
		instructors = append(instructors, instructor)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return instructors, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (db *DB) GetQuestions(nums []string) ([]Question, error) {
 | 
			
		||||
	query := `
 | 
			
		||||
  SELECT *
 | 
			
		||||
  FROM questions
 | 
			
		||||
  WHERE id IN (` + strings.Join(nums, ", ") + `)
 | 
			
		||||
  `
 | 
			
		||||
 | 
			
		||||
	rows, err := db.Query(query)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("error: *DB.GetQuestions: db.Query(): %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	defer rows.Close()
 | 
			
		||||
 | 
			
		||||
	// TODO: not scalable
 | 
			
		||||
	questions := make([]Question, 0)
 | 
			
		||||
	for rows.Next() {
 | 
			
		||||
		q := new(Question)
 | 
			
		||||
		a1 := new(Answer)
 | 
			
		||||
		a2 := new(Answer)
 | 
			
		||||
		a3 := new(Answer)
 | 
			
		||||
		a4 := new(Answer)
 | 
			
		||||
 | 
			
		||||
		a1.ID = 1
 | 
			
		||||
		a2.ID = 2
 | 
			
		||||
		a3.ID = 3
 | 
			
		||||
		a4.ID = 4
 | 
			
		||||
 | 
			
		||||
		if err := rows.Scan(&q.ID, &q.Text, &a1.Text, &a2.Text, &a3.Text, &a4.Text, &q.Correct); err != nil {
 | 
			
		||||
			return nil, fmt.Errorf("error: *DB.GetQuestions: rows.Scan(): %v", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		q.Answers = append(q.Answers, *a1)
 | 
			
		||||
		q.Answers = append(q.Answers, *a2)
 | 
			
		||||
		q.Answers = append(q.Answers, *a3)
 | 
			
		||||
		q.Answers = append(q.Answers, *a4)
 | 
			
		||||
 | 
			
		||||
		questions = append(questions, *q)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return questions, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (db *DB) GetGivenAnswers(bid, pid int64, sq []Question) ([]int, error) {
 | 
			
		||||
	answers := make([]int, 0)
 | 
			
		||||
	query := `
 | 
			
		||||
  SELECT given_answer
 | 
			
		||||
  FROM given_answers
 | 
			
		||||
  WHERE
 | 
			
		||||
    briefing_id = ? AND
 | 
			
		||||
    participant_id = ? AND
 | 
			
		||||
    question_id = ?
 | 
			
		||||
  `
 | 
			
		||||
 | 
			
		||||
	for _, q := range sq {
 | 
			
		||||
		var answer int
 | 
			
		||||
 | 
			
		||||
		row := db.QueryRow(query, bid, pid, q.ID)
 | 
			
		||||
		if err := row.Scan(&answer); err != nil {
 | 
			
		||||
			return nil, fmt.Errorf("error: *DB.GetGivenAnswers: row.Scan(): %v", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		answers = append(answers, answer)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return answers, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										52
									
								
								packages/data/helperFuncs.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								packages/data/helperFuncs.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,52 @@
 | 
			
		||||
package data
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bufio"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"os"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"syscall"
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/term"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
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("error: getUsername: bufio.NewReader().ReadString(): %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("error: getPassword: term.ReadPassword(): %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("error: getCredentials: getUsername(): %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pass, err := getPassword()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", "", fmt.Errorf("error: getCredentials: getPassword(): %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return user, pass, 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
 | 
			
		||||
}
 | 
			
		||||
@@ -1,193 +0,0 @@
 | 
			
		||||
package server
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"crypto/rand"
 | 
			
		||||
	"encoding/hex"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"html/template"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"streifling.com/jason/sicherheitsunterweisung/packages/data"
 | 
			
		||||
	"streifling.com/jason/sicherheitsunterweisung/packages/types"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// type questionData struct {
 | 
			
		||||
// 	ID int64
 | 
			
		||||
// 	Q  types.Question
 | 
			
		||||
// 	I  int
 | 
			
		||||
// 	J  int
 | 
			
		||||
// }
 | 
			
		||||
 | 
			
		||||
func DisplayTable(db *data.DB) http.HandlerFunc {
 | 
			
		||||
	return func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		bs, err := db.GetAllOverviewTableData()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			http.Error(w, "DisplayTable: *DB.GetAllOverviewTableData(): "+fmt.Sprint(err), http.StatusInternalServerError)
 | 
			
		||||
		}
 | 
			
		||||
		template.Must(template.ParseFiles("templates/table.html")).ExecuteTemplate(w, "content", bs)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func DisplaySearchResults(db *data.DB) http.HandlerFunc {
 | 
			
		||||
	return func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		bs, err := db.GetOverviewTableDataByName(r.PostFormValue("search"))
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			http.Error(w, "DisplayResults: db.ReadByName(r.PostFormValue()): "+fmt.Sprint(err), http.StatusInternalServerError)
 | 
			
		||||
		}
 | 
			
		||||
		template.Must(template.ParseFiles("templates/table.html")).ExecuteTemplate(w, "rows", bs)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func DisplayInstructorForm() http.HandlerFunc {
 | 
			
		||||
	return func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		template.Must(template.ParseFiles("templates/briefing.html")).ExecuteTemplate(w, "content", nil)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func generateUUID() (string, error) {
 | 
			
		||||
	bs := make([]byte, 4)
 | 
			
		||||
 | 
			
		||||
	if _, err := rand.Read(bs); err != nil {
 | 
			
		||||
		return "", fmt.Errorf("GenerateUUID: rand.Read(bs): %v\n", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return hex.EncodeToString(bs), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func AddParticipant(sl *[]string) http.HandlerFunc {
 | 
			
		||||
	return func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		login, err := generateUUID()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			http.Error(w, "AddParticipant: generateUUID(): "+fmt.Sprint(err), http.StatusInternalServerError)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		(*sl) = append(*sl, login)
 | 
			
		||||
		template.Must(template.ParseFiles("templates/briefing.html")).ExecuteTemplate(w, "new", login)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TODO: Hier weiter machen, irgendwie die b.ID herausgeben,
 | 
			
		||||
// 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) {
 | 
			
		||||
		now := time.Now()
 | 
			
		||||
		briefing := new(types.Briefing)
 | 
			
		||||
 | 
			
		||||
		// TODO: Dropdownmenü
 | 
			
		||||
		// 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 {
 | 
			
		||||
		return nil, fmt.Errorf("newParticipant: strconv.Atoi(idString): %v\n", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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)
 | 
			
		||||
// 	}
 | 
			
		||||
// }
 | 
			
		||||
							
								
								
									
										254
									
								
								packages/session/handlerFuncs.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										254
									
								
								packages/session/handlerFuncs.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,254 @@
 | 
			
		||||
package session
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"html/template"
 | 
			
		||||
	"log"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/google/uuid"
 | 
			
		||||
	"streifling.com/jason/sicherheitsunterweisung/packages/data"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func HandleInternalLogin(db *data.DB, ss *[]*Session, cs chan<- *Session) http.HandlerFunc {
 | 
			
		||||
	return func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		instructors, err := db.GetInstructors()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			http.Error(w, err.Error(), http.StatusInternalServerError)
 | 
			
		||||
			log.Panicln(err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, i := range instructors {
 | 
			
		||||
			if r.PostFormValue("login") == fmt.Sprint(i.ID) {
 | 
			
		||||
				session := new(Session)
 | 
			
		||||
				session.ID = uuid.New()
 | 
			
		||||
				session.Briefing = new(data.Briefing)
 | 
			
		||||
				session.InstructorID = i.ID
 | 
			
		||||
				(*ss) = append((*ss), session)
 | 
			
		||||
				cs <- session
 | 
			
		||||
 | 
			
		||||
				data := new(tableHTMLData)
 | 
			
		||||
				data.SessionID = session.ID
 | 
			
		||||
				data.OTD, err = db.GetAllOverviewTableData()
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					http.Error(w, err.Error(), http.StatusInternalServerError)
 | 
			
		||||
					log.Panicln(err)
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				template.Must(template.ParseFiles("templates/table.html")).ExecuteTemplate(w, "content", data)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		template.Must(template.ParseFiles("templates/login.html")).ExecuteTemplate(w, "content", nil)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *Session) HandleSearch(db *data.DB) http.HandlerFunc {
 | 
			
		||||
	return func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		log.Println("hier")
 | 
			
		||||
		var err error
 | 
			
		||||
		data := tableHTMLData{}
 | 
			
		||||
 | 
			
		||||
		data.SessionID = s.ID
 | 
			
		||||
		data.OTD, err = db.GetOverviewTableDataByName(r.PostFormValue("search"))
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			http.Error(w, err.Error(), http.StatusInternalServerError)
 | 
			
		||||
			log.Panicln(err)
 | 
			
		||||
		}
 | 
			
		||||
		template.Must(template.ParseFiles("templates/table.html")).ExecuteTemplate(w, "rows", data)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *Session) HandleNewBriefing() http.HandlerFunc {
 | 
			
		||||
	return func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		data := new(participantHTMLData)
 | 
			
		||||
		data.SessionID = s.ID
 | 
			
		||||
		template.Must(template.ParseFiles("templates/briefing.html")).ExecuteTemplate(w, "content", data)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *Session) HandleNewParticipant(cp chan<- *BriefingParticipant) http.HandlerFunc {
 | 
			
		||||
	return func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		var err error
 | 
			
		||||
		p := new(BriefingParticipant)
 | 
			
		||||
		p.Participant = new(data.Participant)
 | 
			
		||||
		p.NoIncorrect = -1
 | 
			
		||||
		p.AllowRetry = false
 | 
			
		||||
 | 
			
		||||
		p.Login, err = generateLogin()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			http.Error(w, err.Error(), http.StatusInternalServerError)
 | 
			
		||||
			log.Panicln(err)
 | 
			
		||||
		}
 | 
			
		||||
		s.Participants = append(s.Participants, p)
 | 
			
		||||
		cp <- p
 | 
			
		||||
 | 
			
		||||
		data := new(participantHTMLData)
 | 
			
		||||
		data.SessionID = s.ID
 | 
			
		||||
		data.Login = p.Login
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			http.Error(w, err.Error(), http.StatusInternalServerError)
 | 
			
		||||
			log.Panicln(err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		template.Must(template.ParseFiles("templates/briefing.html")).ExecuteTemplate(w, "new", data)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *Session) HandleBriefingForm(db *data.DB) http.HandlerFunc {
 | 
			
		||||
	return func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		now := time.Now()
 | 
			
		||||
 | 
			
		||||
		s.Date = now.Format("2006-01-02")
 | 
			
		||||
		s.Time = now.Format("15:04:05")
 | 
			
		||||
		s.Location = r.PostFormValue("location")
 | 
			
		||||
		s.DocumentName = r.PostFormValue("document")
 | 
			
		||||
		s.AsOf = r.PostFormValue("as-of")
 | 
			
		||||
 | 
			
		||||
		err := db.WriteBriefing(s.Briefing)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			http.Error(w, err.Error(), http.StatusInternalServerError)
 | 
			
		||||
			log.Panicln(err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		data := new(summaryHTMLData)
 | 
			
		||||
		data.SessionID = s.ID
 | 
			
		||||
		data.ParticipantsData = make([]participantHTMLData, len(s.Participants))
 | 
			
		||||
		for i, p := range s.Participants {
 | 
			
		||||
			data.ParticipantsData[i].SessionID = s.ID
 | 
			
		||||
			data.ParticipantsData[i].BriefingParticipant = *p
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		template.Must(template.ParseFiles("templates/summary.html")).ExecuteTemplate(w, "content", data)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func HandleExternalLogin(ss *[]*Session) http.HandlerFunc {
 | 
			
		||||
	return func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		session, participant, loginCorrect := findCorrectLogin(r.PostFormValue("login"), ss)
 | 
			
		||||
		if loginCorrect {
 | 
			
		||||
			data := new(participantHTMLData)
 | 
			
		||||
			data.SessionID = session.ID
 | 
			
		||||
			data.Login = participant.Login
 | 
			
		||||
 | 
			
		||||
			template.Must(template.ParseFiles("templates/participant.html")).ExecuteTemplate(w, "content", data)
 | 
			
		||||
		} else {
 | 
			
		||||
			template.Must(template.ParseFiles("templates/login.html")).ExecuteTemplate(w, "content", nil)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *Session) HandleParticipant(db *data.DB, p *BriefingParticipant) http.HandlerFunc {
 | 
			
		||||
	return func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		p.FirstName = r.PostFormValue("first-" + fmt.Sprint(p.Login))
 | 
			
		||||
		p.LastName = r.PostFormValue("last-" + fmt.Sprint(p.Login))
 | 
			
		||||
		p.Company = r.PostFormValue("company-" + fmt.Sprint(p.Login))
 | 
			
		||||
 | 
			
		||||
		err := db.WriteParticipant(p.Participant)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			http.Error(w, err.Error(), http.StatusInternalServerError)
 | 
			
		||||
			log.Panicln(err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		data := new(questionHTMLData)
 | 
			
		||||
		data.SessionID = s.ID
 | 
			
		||||
		data.Login = p.Login
 | 
			
		||||
		data.Question = s.Questions[0]
 | 
			
		||||
		data.QuestionID = 1
 | 
			
		||||
 | 
			
		||||
		template.Must(template.ParseFiles("templates/question.html")).ExecuteTemplate(w, "content", data)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *Session) HandleAnswer(db *data.DB, p *BriefingParticipant, i int64) http.HandlerFunc {
 | 
			
		||||
	return func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		if i < int64(len(s.Questions)) {
 | 
			
		||||
			if err := handleGivenAnswer(p, i-1, r); err != nil {
 | 
			
		||||
				http.Error(w, err.Error(), http.StatusInternalServerError)
 | 
			
		||||
				log.Panicln(err)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			data := new(questionHTMLData)
 | 
			
		||||
			data.SessionID = s.ID
 | 
			
		||||
			data.Login = p.Login
 | 
			
		||||
			data.Question = s.Questions[i]
 | 
			
		||||
			data.QuestionID = i + 1
 | 
			
		||||
 | 
			
		||||
			template.Must(template.ParseFiles("templates/question.html")).ExecuteTemplate(w, "content", data)
 | 
			
		||||
		} else {
 | 
			
		||||
			if err := handleGivenAnswer(p, i-1, r); err != nil {
 | 
			
		||||
				http.Error(w, err.Error(), http.StatusInternalServerError)
 | 
			
		||||
				log.Panicln(err)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			p.NoIncorrect = 0
 | 
			
		||||
			for i, q := range s.Questions {
 | 
			
		||||
				if p.GivenAnswers[i] != q.Correct {
 | 
			
		||||
					p.NoIncorrect++
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			data := new(resultHTMLData)
 | 
			
		||||
			data.SessionID = s.ID
 | 
			
		||||
			data.BriefingParticipant = *p
 | 
			
		||||
			data.Questions = makeHTMLQuestions(s.Questions, p.GivenAnswers)
 | 
			
		||||
 | 
			
		||||
			if data.NoIncorrect == 0 {
 | 
			
		||||
				if err := db.WriteGivenAnswers(*s.Briefing, *p.Participant, s.Questions, p.GivenAnswers); err != nil {
 | 
			
		||||
					http.Error(w, err.Error(), http.StatusInternalServerError)
 | 
			
		||||
					log.Panicln(err)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			template.Must(template.ParseFiles("templates/result.html")).ExecuteTemplate(w, "content", data)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *Session) HandleAllowRetry(p *BriefingParticipant) http.HandlerFunc {
 | 
			
		||||
	return func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		p.NoIncorrect = -1
 | 
			
		||||
		p.AllowRetry = true
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *Session) HandleRetry(p *BriefingParticipant, i *int) http.HandlerFunc {
 | 
			
		||||
	return func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		if p.AllowRetry {
 | 
			
		||||
			p.AllowRetry = false
 | 
			
		||||
			(*i) = 0
 | 
			
		||||
 | 
			
		||||
			data := new(questionHTMLData)
 | 
			
		||||
			data.SessionID = s.ID
 | 
			
		||||
			data.Login = p.Login
 | 
			
		||||
			data.Question = s.Questions[*i]
 | 
			
		||||
			data.QuestionID = int64(*i + 1)
 | 
			
		||||
 | 
			
		||||
			template.Must(template.ParseFiles("templates/question.html")).ExecuteTemplate(w, "content", data)
 | 
			
		||||
		} else {
 | 
			
		||||
			data := new(resultHTMLData)
 | 
			
		||||
			data.SessionID = s.ID
 | 
			
		||||
			data.BriefingParticipant = *p
 | 
			
		||||
			data.Questions = makeHTMLQuestions(s.Questions, p.GivenAnswers)
 | 
			
		||||
 | 
			
		||||
			template.Must(template.ParseFiles("templates/result.html")).ExecuteTemplate(w, "content", data)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *Session) HandleRefresh(p *BriefingParticipant) http.HandlerFunc {
 | 
			
		||||
	return func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		data := new(participantHTMLData)
 | 
			
		||||
		data.SessionID = s.ID
 | 
			
		||||
		data.BriefingParticipant = *p
 | 
			
		||||
 | 
			
		||||
		template.Must(template.ParseFiles("templates/summary.html")).ExecuteTemplate(w, "participant", data)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *Session) HandleBriefingDone() http.HandlerFunc {
 | 
			
		||||
	return func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		template.Must(template.ParseFiles("templates/login.html")).ExecuteTemplate(w, "content", nil)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										84
									
								
								packages/session/helperFuncs.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								packages/session/helperFuncs.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,84 @@
 | 
			
		||||
package session
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"crypto/rand"
 | 
			
		||||
	"encoding/hex"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"strconv"
 | 
			
		||||
 | 
			
		||||
	"streifling.com/jason/sicherheitsunterweisung/packages/data"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func generateLogin() (string, error) {
 | 
			
		||||
	bs := make([]byte, 4)
 | 
			
		||||
 | 
			
		||||
	if _, err := rand.Read(bs); err != nil {
 | 
			
		||||
		return "", fmt.Errorf("error: generateLogin: rand.Read(bs): %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return hex.EncodeToString(bs), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func findCorrectLogin(l string, ss *[]*Session) (*Session, *BriefingParticipant, bool) {
 | 
			
		||||
	for _, session := range *ss {
 | 
			
		||||
		for _, p := range session.Participants {
 | 
			
		||||
			if l == p.Login {
 | 
			
		||||
				return session, p, true
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil, nil, false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newParticipant(l string) (*data.Participant, error) {
 | 
			
		||||
	var err error
 | 
			
		||||
	p := new(data.Participant)
 | 
			
		||||
 | 
			
		||||
	p.ID, err = strconv.ParseInt(l, 10, 64)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("error: newParticipant: strconv.Atoi(): %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return p, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func handleGivenAnswer(p *BriefingParticipant, i int64, r *http.Request) error {
 | 
			
		||||
	answer, err := strconv.Atoi(r.PostFormValue("answer"))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("error: handleGivenAnswer: strconv.Atoi(): %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	p.GivenAnswers[i] = answer
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func makeHTMLQuestions(sq []data.Question, givenAnswers []int) []resultQuestion {
 | 
			
		||||
	questions := make([]resultQuestion, 0)
 | 
			
		||||
	for i, q := range sq {
 | 
			
		||||
		question := new(resultQuestion)
 | 
			
		||||
		question.Text = q.Text
 | 
			
		||||
 | 
			
		||||
		question.Answers = make([]resultAnswer, 0)
 | 
			
		||||
		for j, a := range q.Answers {
 | 
			
		||||
			answer := new(resultAnswer)
 | 
			
		||||
			answer.Text = a.Text
 | 
			
		||||
 | 
			
		||||
			if j+1 == q.Correct {
 | 
			
		||||
				answer.Correct = true
 | 
			
		||||
			} else {
 | 
			
		||||
				answer.Correct = false
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if j+1 == givenAnswers[i] {
 | 
			
		||||
				answer.Chosen = true
 | 
			
		||||
			} else {
 | 
			
		||||
				answer.Chosen = false
 | 
			
		||||
			}
 | 
			
		||||
			question.Answers = append(question.Answers, *answer)
 | 
			
		||||
		}
 | 
			
		||||
		questions = append(questions, *question)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return questions
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										43
									
								
								packages/session/htmlStructs.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								packages/session/htmlStructs.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,43 @@
 | 
			
		||||
package session
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/google/uuid"
 | 
			
		||||
	"streifling.com/jason/sicherheitsunterweisung/packages/data"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type tableHTMLData struct {
 | 
			
		||||
	SessionID uuid.UUID
 | 
			
		||||
	OTD       []data.OverviewTableData
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type participantHTMLData struct {
 | 
			
		||||
	SessionID uuid.UUID
 | 
			
		||||
	BriefingParticipant
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type questionHTMLData struct {
 | 
			
		||||
	participantHTMLData
 | 
			
		||||
	Question   data.Question
 | 
			
		||||
	QuestionID int64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type resultAnswer struct {
 | 
			
		||||
	Text    string
 | 
			
		||||
	Correct bool
 | 
			
		||||
	Chosen  bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type resultQuestion struct {
 | 
			
		||||
	Text    string
 | 
			
		||||
	Answers []resultAnswer
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type resultHTMLData struct {
 | 
			
		||||
	participantHTMLData
 | 
			
		||||
	Questions []resultQuestion
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type summaryHTMLData struct {
 | 
			
		||||
	SessionID        uuid.UUID
 | 
			
		||||
	ParticipantsData []participantHTMLData
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										56
									
								
								packages/session/muxFuncs.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								packages/session/muxFuncs.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,56 @@
 | 
			
		||||
package session
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"log"
 | 
			
		||||
	"net/http"
 | 
			
		||||
 | 
			
		||||
	"streifling.com/jason/sicherheitsunterweisung/packages/data"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (mux *Mux) handleParticipants(db *data.DB, cp <-chan *BriefingParticipant, s *Session) {
 | 
			
		||||
	for p := range cp {
 | 
			
		||||
		p.GivenAnswers = make([]int, len(s.Questions))
 | 
			
		||||
 | 
			
		||||
		mux.HandleFunc("/submit-participant/"+fmt.Sprint(s.ID)+"/"+fmt.Sprint(p.Login)+"/", s.HandleParticipant(db, p))
 | 
			
		||||
		var i int
 | 
			
		||||
		for i = range s.Questions {
 | 
			
		||||
			mux.HandleFunc("/submit-answer/"+fmt.Sprint(s.ID)+"/"+fmt.Sprint(p.Login)+"/"+fmt.Sprint(i+1)+"/", s.HandleAnswer(db, p, int64(i+1)))
 | 
			
		||||
		}
 | 
			
		||||
		mux.HandleFunc("/allow-retry/"+fmt.Sprint(s.ID)+"/"+fmt.Sprint(p.Login)+"/", s.HandleAllowRetry(p))
 | 
			
		||||
		mux.HandleFunc("/retry/"+fmt.Sprint(s.ID)+"/"+fmt.Sprint(p.Login)+"/", s.HandleRetry(p, &i))
 | 
			
		||||
		mux.HandleFunc("/refresh-summary/"+fmt.Sprint(s.ID)+"/"+fmt.Sprint(p.Login)+"/", s.HandleRefresh(p))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewMux() *Mux {
 | 
			
		||||
	mux := new(Mux)
 | 
			
		||||
	mux.ServeMux = http.NewServeMux()
 | 
			
		||||
	return mux
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (mux *Mux) HandleSessions(db *data.DB, cs <-chan *Session, ss *[]*Session) {
 | 
			
		||||
	for s := range cs {
 | 
			
		||||
		(*ss) = append(*ss, s)
 | 
			
		||||
		participantChan := make(chan *BriefingParticipant)
 | 
			
		||||
		questionIDs := make([]string, 4)
 | 
			
		||||
 | 
			
		||||
		for i := 0; i < len(questionIDs); i++ {
 | 
			
		||||
			questionIDs[i] = fmt.Sprint(i + 1)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		var err error
 | 
			
		||||
		s.Questions, err = db.GetQuestions(questionIDs)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			log.Fatalln(err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		mux.HandleFunc("/search/"+fmt.Sprint(s.ID)+"/", s.HandleSearch(db))
 | 
			
		||||
		mux.HandleFunc("/new-briefing/"+fmt.Sprint(s.ID)+"/", s.HandleNewBriefing())
 | 
			
		||||
		mux.HandleFunc("/new-participant/"+fmt.Sprint(s.ID)+"/", s.HandleNewParticipant(participantChan))
 | 
			
		||||
		mux.HandleFunc("/submit-form/"+fmt.Sprint(s.ID)+"/", s.HandleBriefingForm(db))
 | 
			
		||||
		mux.HandleFunc("/briefing-done/"+fmt.Sprint(s.ID)+"/", s.HandleBriefingDone())
 | 
			
		||||
 | 
			
		||||
		go mux.handleParticipants(db, participantChan, s)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										27
									
								
								packages/session/sessionStructs.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								packages/session/sessionStructs.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
			
		||||
package session
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"net/http"
 | 
			
		||||
 | 
			
		||||
	"github.com/google/uuid"
 | 
			
		||||
	"streifling.com/jason/sicherheitsunterweisung/packages/data"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Mux struct {
 | 
			
		||||
	*http.ServeMux
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type BriefingParticipant struct {
 | 
			
		||||
	*data.Participant
 | 
			
		||||
	Login        string
 | 
			
		||||
	GivenAnswers []int
 | 
			
		||||
	NoIncorrect  int
 | 
			
		||||
	AllowRetry   bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Session struct {
 | 
			
		||||
	ID uuid.UUID
 | 
			
		||||
	*data.Briefing
 | 
			
		||||
	Participants []*BriefingParticipant
 | 
			
		||||
	Questions    []data.Question
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										7
									
								
								static/css/style.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								static/css/style.css
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
			
		||||
.correct {
 | 
			
		||||
  color: #00ff00;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.incorrect {
 | 
			
		||||
  color: #ff0000;
 | 
			
		||||
}
 | 
			
		||||
@@ -1,38 +1,37 @@
 | 
			
		||||
{{ define "add-button" }}
 | 
			
		||||
<button type="button" hx-post="/add-participant/" hx-target="this" hx-swap="outerHTML">
 | 
			
		||||
{{define "add-buttons"}}
 | 
			
		||||
<div id="briefing-buttons">
 | 
			
		||||
  <button type="button" hx-post="/new-participant/{{.SessionID}}/" hx-target="#briefing-buttons" 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>
 | 
			
		||||
 | 
			
		||||
  <button type="submit" hx-post="/submit-form/{{.SessionID}}/" hx-target="#content">
 | 
			
		||||
    Weiter
 | 
			
		||||
  </button>
 | 
			
		||||
</div>
 | 
			
		||||
{{end}}
 | 
			
		||||
 | 
			
		||||
{{define "new"}}
 | 
			
		||||
{{template "add-buttons" .}}
 | 
			
		||||
<p>{{.Login}}</p>
 | 
			
		||||
{{end}}
 | 
			
		||||
 | 
			
		||||
{{define "content"}}
 | 
			
		||||
<form>
 | 
			
		||||
  <div>
 | 
			
		||||
    <label for="location">Ort</label>
 | 
			
		||||
    <input id="location" name="location" required type="text" value="Werk Langenhagen" />
 | 
			
		||||
  </div>
 | 
			
		||||
 | 
			
		||||
  <div>
 | 
			
		||||
    <label for="document">Dokument</label>
 | 
			
		||||
    <input id="document" name="document" required type="text" value="ICL-1901-LGH" />
 | 
			
		||||
  </div>
 | 
			
		||||
 | 
			
		||||
  <div>
 | 
			
		||||
    <label for="as-of">Stand vom</label>
 | 
			
		||||
    <input id="as-of" name="as-of" required type="date" value="2021-02-01" />
 | 
			
		||||
  </div>
 | 
			
		||||
 | 
			
		||||
  {{template "add-buttons" .}}
 | 
			
		||||
</form>
 | 
			
		||||
{{ end }}
 | 
			
		||||
{{end}}
 | 
			
		||||
 
 | 
			
		||||
@@ -12,7 +12,7 @@
 | 
			
		||||
  <h1>Sicherheitsunterweisung</h1>
 | 
			
		||||
 | 
			
		||||
  <div id="content">
 | 
			
		||||
    {{ template "content" . }}
 | 
			
		||||
    {{template "content" .}}
 | 
			
		||||
  </div>
 | 
			
		||||
 | 
			
		||||
  <script src="/static/js/htmx.min.js" type="text/javascript"></script>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,16 +1,17 @@
 | 
			
		||||
{{ define "content" }}
 | 
			
		||||
<h2>Login</h2>
 | 
			
		||||
{{define "content"}}
 | 
			
		||||
<h2>Anmeldung</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>
 | 
			
		||||
  <input autocomplete="off" id="login-input" name="login" placeholder="Code" required type="text" />
 | 
			
		||||
 | 
			
		||||
  <div>
 | 
			
		||||
    <button type="submit" hx-post="/internal-login/" hx-target="#content">
 | 
			
		||||
      Intern
 | 
			
		||||
    </button>
 | 
			
		||||
 | 
			
		||||
    <button type="submit" hx-post="/external-login/" hx-target="#content">
 | 
			
		||||
      Gast
 | 
			
		||||
    </button>
 | 
			
		||||
  </div>
 | 
			
		||||
</form>
 | 
			
		||||
{{ end }}
 | 
			
		||||
{{end}}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,16 +1,16 @@
 | 
			
		||||
{{ define "content" }}
 | 
			
		||||
<form id="participant-{{ . }}">
 | 
			
		||||
  <label for="participant-first-input-{{ . }}">Vorname</label>
 | 
			
		||||
  <input type="text" name="participant-first-{{ . }}" id="participant-first-input-{{ . }}" />
 | 
			
		||||
{{define "content"}}
 | 
			
		||||
<form>
 | 
			
		||||
  <label for="first-{{.Login}}">Vorname</label>
 | 
			
		||||
  <input type="text" name="first-{{.Login}}" id="first-{{.Login}}" />
 | 
			
		||||
 | 
			
		||||
  <label for="participant-last-input-{{ . }}">Nachname</label>
 | 
			
		||||
  <input type="text" name="participant-last-{{ . }}" id="participant-last-input-{{ . }}" />
 | 
			
		||||
  <label for="last-{{.Login}}">Nachname</label>
 | 
			
		||||
  <input type="text" name="last-{{.Login}}" id="last-{{.Login}}" />
 | 
			
		||||
 | 
			
		||||
  <label for="participant-company-input-{{ . }}">Firma</label>
 | 
			
		||||
  <input type="text" name="participant-company-{{ . }}" id="participant-company-input-{{ . }}" />
 | 
			
		||||
  <label for="company-{{.Login}}">Firma</label>
 | 
			
		||||
  <input type="text" name="company-{{.Login}}" id="company-{{.Login}}" />
 | 
			
		||||
 | 
			
		||||
  <button type="button" hx-post="/display-question-{{ . }}-0/" hx-target="#content">
 | 
			
		||||
  <button type="button" hx-post="/submit-participant/{{.SessionID}}/{{.Login}}/" hx-target="#content">
 | 
			
		||||
    Fertig
 | 
			
		||||
  </button>
 | 
			
		||||
</form>
 | 
			
		||||
{{ end }}
 | 
			
		||||
{{end}}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,21 +1,21 @@
 | 
			
		||||
{{ define "answers" }}
 | 
			
		||||
{{ range .Q.Answers }}
 | 
			
		||||
{{define "answers"}}
 | 
			
		||||
{{range .Question.Answers}}
 | 
			
		||||
<div>
 | 
			
		||||
  <input type="radio" name="answer" id="answer-{{ .ID }}" value="{{ .ID }}" />
 | 
			
		||||
  <label for="answer-{{ .ID }}">{{ .Text }}</label>
 | 
			
		||||
  <input type="radio" name="answer" id="answer-{{.ID}}" value="{{.ID}}" />
 | 
			
		||||
  <label for="answer-{{.ID}}">{{.Text}}</label>
 | 
			
		||||
</div>
 | 
			
		||||
{{ end }}
 | 
			
		||||
{{ end }}
 | 
			
		||||
{{end}}
 | 
			
		||||
{{end}}
 | 
			
		||||
 | 
			
		||||
{{ define "content" }}
 | 
			
		||||
<h2>Frage {{ .I }}</h2>
 | 
			
		||||
<p>{{ .Q.Text }}</p>
 | 
			
		||||
{{define "content"}}
 | 
			
		||||
<h2>Frage {{.QuestionID}}</h2>
 | 
			
		||||
<p>{{.Question.Text}}</p>
 | 
			
		||||
 | 
			
		||||
<form>
 | 
			
		||||
  {{ template "answers" . }}
 | 
			
		||||
  {{template "answers" .}}
 | 
			
		||||
 | 
			
		||||
  <button type="submit" hx-post="/display-question-{{ .ID }}-{{ .J }}/" hx-target="#content" hx-swap="innerHTML">
 | 
			
		||||
  <button hx-post="/submit-answer/{{.SessionID}}/{{.Login}}/{{.QuestionID}}/" hx-target="#content" type="submit">
 | 
			
		||||
    Weiter
 | 
			
		||||
  </button>
 | 
			
		||||
</form>
 | 
			
		||||
{{ end }}
 | 
			
		||||
{{end}}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										19
									
								
								templates/result.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								templates/result.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
			
		||||
{{define "answers"}}
 | 
			
		||||
{{range .Answers}}
 | 
			
		||||
<p class="{{if and .Chosen .Correct}} correct {{else if and .Chosen (not .Correct)}} incorrect {{end}}">
 | 
			
		||||
  {{.Text}}
 | 
			
		||||
</p>
 | 
			
		||||
{{end}}
 | 
			
		||||
{{end}}
 | 
			
		||||
 | 
			
		||||
{{define "content"}}
 | 
			
		||||
<p>{{.BriefingParticipant.NoIncorrect}} Fehler</p>
 | 
			
		||||
{{if gt .BriefingParticipant.NoIncorrect 0}}
 | 
			
		||||
<p>Bitte nachschulen lassen und anschließend wiederholen.</p>
 | 
			
		||||
<button hx-post="/retry/{{.SessionID}}/{{.Login}}/" hx-target="#content" type="submit">Wiederholen</button>
 | 
			
		||||
{{end}}
 | 
			
		||||
{{range .Questions}}
 | 
			
		||||
<p>{{.Text}}</p>
 | 
			
		||||
{{template "answers" .}}
 | 
			
		||||
{{end}}
 | 
			
		||||
{{end}}
 | 
			
		||||
@@ -1,11 +0,0 @@
 | 
			
		||||
{{ define "passed" }}
 | 
			
		||||
{{ end }}
 | 
			
		||||
 | 
			
		||||
{{ define "failed" }}
 | 
			
		||||
{{ end }}
 | 
			
		||||
 | 
			
		||||
{{ define "content" }}
 | 
			
		||||
{{ q := range .Participant.Questions }}
 | 
			
		||||
<p>{{ . }}{{ q.Text }}</p>
 | 
			
		||||
{{ end }}
 | 
			
		||||
{{ end }}
 | 
			
		||||
							
								
								
									
										35
									
								
								templates/summary.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								templates/summary.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,35 @@
 | 
			
		||||
{{define "refresh"}}
 | 
			
		||||
<button hx-post="/refresh-summary/{{.SessionID}}/{{.Login}}/" hx-target="#id-{{.Login}}"
 | 
			
		||||
  type="button">Aktualisieren</button>
 | 
			
		||||
{{end}}
 | 
			
		||||
 | 
			
		||||
{{define "retry"}}
 | 
			
		||||
<button hx-post="/allow-retry/{{.SessionID}}/{{.Login}}/" type="button">Wiederholen erlauben</button>
 | 
			
		||||
{{end}}
 | 
			
		||||
 | 
			
		||||
{{define "participant"}}
 | 
			
		||||
<div id="id-{{.Login}}">
 | 
			
		||||
  <h2>{{.Login}}</h2>
 | 
			
		||||
  {{if not .LastName}}
 | 
			
		||||
  {{template "refresh" .}}
 | 
			
		||||
  {{else}}
 | 
			
		||||
  <p>{{.FirstName}} {{.LastName}}</p>
 | 
			
		||||
  <p>{{.Company}}</p>
 | 
			
		||||
  {{if lt .NoIncorrect 0}}
 | 
			
		||||
  {{template "refresh" .}}
 | 
			
		||||
  {{else if gt .NoIncorrect 0}}
 | 
			
		||||
  {{template "retry" .}}
 | 
			
		||||
  {{template "refresh" .}}
 | 
			
		||||
  {{else}}
 | 
			
		||||
  <p>{{.NoIncorrect}} Fehler</p>
 | 
			
		||||
  {{end}}
 | 
			
		||||
  {{end}}
 | 
			
		||||
</div>
 | 
			
		||||
{{end}}
 | 
			
		||||
 | 
			
		||||
{{define "content"}}
 | 
			
		||||
<button hx-post="/briefing-done/{{.SessionID}}/" hx-target="#content" type="button">Beenden</button>
 | 
			
		||||
{{range .ParticipantsData}}
 | 
			
		||||
{{template "participant" .}}
 | 
			
		||||
{{end}}
 | 
			
		||||
{{end}}
 | 
			
		||||
@@ -1,30 +1,28 @@
 | 
			
		||||
{{ define "rows" }}
 | 
			
		||||
{{ range . }}
 | 
			
		||||
{{define "rows"}}
 | 
			
		||||
{{range .OTD}}
 | 
			
		||||
<tr>
 | 
			
		||||
  <td>{{ .FirstName }}</td>
 | 
			
		||||
  <td>{{ .LastName }}</td>
 | 
			
		||||
  <td>{{ .Date }}</td>
 | 
			
		||||
  <td>{{ .Time }}</td>
 | 
			
		||||
  <td>{{ .State }}</td>
 | 
			
		||||
  <td>{{ .Location }}</td>
 | 
			
		||||
  {{ range .Participants }}
 | 
			
		||||
  <td>{{ .FirstName }}</td>
 | 
			
		||||
  <td>{{ .LastName }}</td>
 | 
			
		||||
  <td>{{ .Company }}</td>
 | 
			
		||||
  {{ end }}
 | 
			
		||||
  <td>{{.InstructorFirstName}}</td>
 | 
			
		||||
  <td>{{.InstructorLastName}}</td>
 | 
			
		||||
  <td>{{.BriefingDate}}</td>
 | 
			
		||||
  <td>{{.BriefingTime}}</td>
 | 
			
		||||
  <td>{{.BriefingLocation}}</td>
 | 
			
		||||
  <td>{{.BriefingDocumentName}}</td>
 | 
			
		||||
  <td>{{.BriefingAsOf}}</td>
 | 
			
		||||
  <td>{{.ParticipantFirstName}}</td>
 | 
			
		||||
  <td>{{.ParticipantLastName}}</td>
 | 
			
		||||
  <td>{{.ParticipantCompany}}</td>
 | 
			
		||||
</tr>
 | 
			
		||||
{{ end }}
 | 
			
		||||
{{ end }}
 | 
			
		||||
{{end}}
 | 
			
		||||
{{end}}
 | 
			
		||||
 | 
			
		||||
{{ define "content" }}
 | 
			
		||||
{{define "content"}}
 | 
			
		||||
<form>
 | 
			
		||||
  <label for="search-input">Suche</label>
 | 
			
		||||
  <input type="text" name="search" id="search-input" hx-post="/search/" hx-target="#results" hx-swap="innerHTML"
 | 
			
		||||
    hx-trigger="keyup changed delay:200ms" />
 | 
			
		||||
  <input type="text" name="search" id="search-input" hx-post="/search/{{.SessionID}}/" hx-target="#results">
 | 
			
		||||
</form>
 | 
			
		||||
 | 
			
		||||
<form>
 | 
			
		||||
  <button type="submit" hx-post="/new-briefing/" hx-target="#content" hx-swap="innerHTML">
 | 
			
		||||
  <button type="submit" hx-post="/new-briefing/{{.SessionID}}/" hx-target="#content">
 | 
			
		||||
    Neue Unterweisung
 | 
			
		||||
  </button>
 | 
			
		||||
</form>
 | 
			
		||||
@@ -35,15 +33,16 @@
 | 
			
		||||
      <th colspan="2">Unterweiser</th>
 | 
			
		||||
      <th>Datum</th>
 | 
			
		||||
      <th>Uhrzeit</th>
 | 
			
		||||
      <th>Stand</th>
 | 
			
		||||
      <th>Ort</th>
 | 
			
		||||
      <th>Dokument</th>
 | 
			
		||||
      <th>Stand</th>
 | 
			
		||||
      <th colspan="2">Teilnehmer</th>
 | 
			
		||||
      <th>Firma</th>
 | 
			
		||||
    </tr>
 | 
			
		||||
  </thead>
 | 
			
		||||
 | 
			
		||||
  <tbody id="results">
 | 
			
		||||
    {{ template "rows" . }}
 | 
			
		||||
    {{template "rows" .}}
 | 
			
		||||
  </tbody>
 | 
			
		||||
</table>
 | 
			
		||||
{{ 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