FormMaker/main.go

240 lines
6.0 KiB
Go

package main
import (
"database/sql"
"fmt"
"html/template"
"log"
"net/http"
_ "github.com/mattn/go-sqlite3"
)
type Question struct {
ID int
QuestionText string
QuestionType string
}
var db *sql.DB
func renderForm(w http.ResponseWriter, r *http.Request) {
rows, err := db.Query("SELECT id, question_text, question_type FROM questions ORDER BY question_order")
if err != nil {
log.Printf("Error fetching questions: %v\n", err)
http.Error(w, "Failed to fetch form questions", http.StatusInternalServerError)
return
}
defer rows.Close()
var questions []Question
for rows.Next() {
var question Question
err := rows.Scan(&question.ID, &question.QuestionText, &question.QuestionType)
if err != nil {
log.Printf("Error scanning question: %v\n", err)
continue
}
questions = append(questions, question)
}
tmpl := template.Must(template.ParseFiles("index.html"))
tmpl.Execute(w, questions)
}
func handleFormSubmit(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Invalid request method", http.StatusMethodNotAllowed)
return
}
err := r.ParseForm()
if err != nil {
log.Printf("Error parsing form: %v\n", err)
http.Error(w, "Failed to parse form", http.StatusInternalServerError)
return
}
insertAnswerQuery := `INSERT INTO answers (question_id, answer) VALUES (?, ?)`
tx, err := db.Begin()
if err != nil {
log.Printf("Error starting transaction: %v\n", err)
http.Error(w, "Failed to save answers", http.StatusInternalServerError)
return
}
defer func() {
if err != nil {
tx.Rollback()
} else {
tx.Commit()
}
}()
for key, values := range r.Form {
if len(key) > 7 && key[:7] == "custom_" {
var questionID int
_, err := fmt.Sscanf(key, "custom_%d", &questionID)
if err != nil {
log.Printf("Error parsing question ID from field name %s: %v\n", key, err)
continue
}
answer := values[0]
_, err = tx.Exec(insertAnswerQuery, questionID, answer)
if err != nil {
log.Printf("Error saving answer for question %d: %v\n", questionID, err)
http.Error(w, "Failed to save answers", http.StatusInternalServerError)
return
}
}
}
fmt.Fprintf(w, "Thank you for your submission!")
}
func handleAdmin(w http.ResponseWriter, r *http.Request) {
rows, err := db.Query(`
SELECT q.question_text, a.answer
FROM answers a
JOIN questions q ON a.question_id = q.id
ORDER BY q.question_order
`)
if err != nil {
log.Printf("Error fetching submissions: %v\n", err)
http.Error(w, "Failed to fetch submissions", http.StatusInternalServerError)
return
}
defer rows.Close()
var submissions []struct {
QuestionText string
Answer string
}
for rows.Next() {
var submission struct {
QuestionText string
Answer string
}
err := rows.Scan(&submission.QuestionText, &submission.Answer)
if err != nil {
log.Printf("Error scanning submission: %v\n", err)
continue
}
submissions = append(submissions, submission)
}
tmpl := template.Must(template.ParseFiles("admin.html"))
tmpl.Execute(w, submissions)
}
func handleAddQuestion(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Invalid request method", http.StatusMethodNotAllowed)
return
}
r.ParseForm()
questionText := r.FormValue("question_text")
insertQuery := `INSERT INTO questions (question_text, question_type, question_order) VALUES (?, 'text', (SELECT IFNULL(MAX(question_order), 0) + 1 FROM questions));`
_, err := db.Exec(insertQuery, questionText)
if err != nil {
log.Printf("Error adding new question: %v\n", err)
http.Error(w, "Failed to add question", http.StatusInternalServerError)
return
}
http.Redirect(w, r, "/manage", http.StatusSeeOther)
}
func handleManage(w http.ResponseWriter, r *http.Request) {
rows, err := db.Query("SELECT id, question_text, question_type FROM questions ORDER BY question_order")
if err != nil {
log.Printf("Error fetching questions: %v\n", err)
http.Error(w, "Failed to fetch questions", http.StatusInternalServerError)
return
}
defer rows.Close()
var questions []Question
for rows.Next() {
var question Question
err := rows.Scan(&question.ID, &question.QuestionText, &question.QuestionType)
if err != nil {
log.Printf("Error scanning question: %v\n", err)
continue
}
questions = append(questions, question)
}
if len(questions) == 0 {
log.Println("No questions found")
}
tmpl := template.Must(template.ParseFiles("manage.html"))
err = tmpl.Execute(w, questions)
if err != nil {
log.Printf("Error executing template: %v\n", err)
}
}
func handleRemoveQuestion(w http.ResponseWriter, r *http.Request) {
questionID := r.URL.Query().Get("id")
deleteQuery := `DELETE FROM questions WHERE id = ?`
_, err := db.Exec(deleteQuery, questionID)
if err != nil {
log.Printf("Error removing question: %v\n", err)
http.Error(w, "Failed to remove question", http.StatusInternalServerError)
return
}
http.Redirect(w, r, "/manage", http.StatusSeeOther)
}
func main() {
var err error
db, err = sql.Open("sqlite3", "./formdata.db")
if err != nil {
log.Fatal(err)
}
createTableQueries := `
CREATE TABLE IF NOT EXISTS questions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
question_text TEXT NOT NULL,
question_type TEXT NOT NULL DEFAULT 'text',
question_order INTEGER NOT NULL
);
CREATE TABLE IF NOT EXISTS answers (
id INTEGER PRIMARY KEY AUTOINCREMENT,
question_id INTEGER NOT NULL,
answer TEXT NOT NULL,
FOREIGN KEY (question_id) REFERENCES questions(id)
);
`
_, err = db.Exec(createTableQueries)
if err != nil {
log.Fatal(err)
return
}
http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))
http.HandleFunc("/", renderForm)
http.HandleFunc("/submit", handleFormSubmit)
http.HandleFunc("/admin", handleAdmin)
http.HandleFunc("/manage", handleManage)
http.HandleFunc("/manage/add", handleAddQuestion)
http.HandleFunc("/manage/remove", handleRemoveQuestion)
fmt.Println("Server started at :8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}