package main import ( "database/sql" "encoding/json" "fmt" "html/template" "log" "net/http" "strings" _ "github.com/mattn/go-sqlite3" ) type Question struct { ID int QuestionText string QuestionType string Options []string } type Match struct { Name string `json:"name"` Email string `json:"email"` Classes []string `json:"classes"` } var db *sql.DB func renderForm(w http.ResponseWriter, r *http.Request) { log.Println("[DEBUG] Entering renderForm handler") tmpl := template.Must(template.ParseFiles("index.html")) log.Println("[DEBUG] Rendering template with questions") err := tmpl.Execute(w, nil) if err != nil { log.Println("[ERROR] Error rendering template:", err) } } func handleFormSubmit(w http.ResponseWriter, r *http.Request) { log.Println("[DEBUG] Entering handleFormSubmit handler") // Parse the form data if err := r.ParseForm(); err != nil { http.Error(w, "Invalid form data", http.StatusBadRequest) return } // Log all form data log.Printf("[DEBUG] Form data: %v\n", r.Form) name := r.FormValue("name") email := r.FormValue("email") phone := r.FormValue("phone") dorm := r.FormValue("dorm") classes := r.FormValue("classes") // Insert the user data into the database tx, err := db.Begin() if err != nil { log.Fatalf("[ERROR] Failed to begin transaction: %v\n", err) return } result, err := tx.Exec(`INSERT INTO users (name, email, phone, dorm) VALUES (?, ?, ?, ?)`, name, email, phone, dorm) if err != nil { tx.Rollback() log.Fatalf("[ERROR] Failed to insert user: %v\n", err) return } userID, err := result.LastInsertId() if err != nil { tx.Rollback() log.Fatalf("[ERROR] Failed to get last insert ID: %v\n", err) return } // Insert the classes associated with this user classList := strings.Split(classes, ",") for _, class := range classList { log.Printf("[DEBUG] Proccessing class: %s\n", class) _, err := tx.Exec(`INSERT INTO classes (user_id, class_name) VALUES (?, ?)`, userID, strings.TrimSpace(class)) if err != nil { tx.Rollback() log.Fatalf("[ERROR] Failed to insert class: %v\n", err) return } } err = tx.Commit() if err != nil { log.Fatalf("[ERROR] Failed to commit transaction: %v\n", err) return } log.Println("[DEBUG] User and classes inserted successfully") fmt.Fprintf(w, "Thank you for your submission!") log.Println("[DEBUG] Exiting handleFormSubmit handler") } // func handleAdmin(w http.ResponseWriter, r *http.Request) { // log.Println("[DEBUG] Entering handleAdmin handler") // rows, err := db.Query(` // SELECT q.question_text, a.answer, GROUP_CONCAT(t.tag, ', ') as tags // FROM answers a // JOIN questions q ON a.question_id = q.id // LEFT JOIN tags t ON a.id = t.answer_id // GROUP BY a.id // ORDER BY q.question_order // `) // if err != nil { // log.Printf("[ERROR] 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 // Tags string // } // log.Println("[DEBUG] Fetching submissions") // // for rows.Next() { // var submission struct { // QuestionText string // Answer string // Tags string // } // err := rows.Scan(&submission.QuestionText, &submission.Answer, &submission.Tags) // if err != nil { // log.Printf("[ERROR] Error scanning submission: %v\n", err) // continue // } // submissions = append(submissions, submission) // log.Printf("[DEBUG] Fetched submission: %v\n", submission) // } // // tmpl := template.Must(template.ParseFiles("admin.html")) // log.Println("[DEBUG] Rendering admin template with submissions") // tmpl.Execute(w, submissions) // } // // func handleAddQuestion(w http.ResponseWriter, r *http.Request) { // log.Println("[DEBUG] Entering handleAddQuestion handler") // if r.Method != http.MethodPost { // log.Printf("[ERROR] Invalid request method: %v\n", r.Method) // http.Error(w, "Invalid request method", http.StatusMethodNotAllowed) // return // } // // r.ParseForm() // questionText := r.FormValue("question_text") // questionType := r.FormValue("question_type") // options := r.FormValue("options") // // log.Printf("[DEBUG] Adding new question: %s, Type: %s\n", questionText, questionType) // // insertQuery := `INSERT INTO questions (question_text, question_type, question_order) VALUES (?, ?, (SELECT IFNULL(MAX(question_order), 0) + 1 FROM questions));` // res, err := db.Exec(insertQuery, questionText, questionType) // if err != nil { // log.Printf("[ERROR] Error adding new question: %v\n", err) // http.Error(w, "Failed to add question", http.StatusInternalServerError) // return // } // // if questionType == "multiple_choice" || questionType == "single_choice" || questionType == "dropdown" || questionType == "tags" { // questionID, err := res.LastInsertId() // if err != nil { // log.Printf("[ERROR] Error getting question ID: %v\n", err) // http.Error(w, "Failed to add question", http.StatusInternalServerError) // return // } // log.Printf("[DEBUG] Inserted question ID: %d\n", questionID) // // optionsList := strings.Split(options, ",") // for _, option := range optionsList { // option = strings.TrimSpace(option) // log.Printf("[DEBUG] Adding option: %s to question ID: %d\n", option, questionID) // _, err = db.Exec("INSERT INTO question_options (question_id, option_text) VALUES (?, ?)", questionID, option) // if err != nil { // log.Printf("[ERROR] Error adding options: %v\n", err) // http.Error(w, "Failed to add options", http.StatusInternalServerError) // return // } // } // } // // log.Println("[DEBUG] Redirecting to manage page") // http.Redirect(w, r, "/manage", http.StatusSeeOther) // } // // func handleManage(w http.ResponseWriter, r *http.Request) { // log.Println("[DEBUG] Entering handleManage handler") // rows, err := db.Query("SELECT id, question_text, question_type FROM questions ORDER BY question_order") // if err != nil { // log.Printf("[ERROR] Error fetching questions: %v\n", err) // http.Error(w, "Failed to fetch questions", http.StatusInternalServerError) // return // } // defer rows.Close() // // var questions []Question // log.Println("[DEBUG] Fetching questions for management") // // for rows.Next() { // var question Question // err := rows.Scan(&question.ID, &question.QuestionText, &question.QuestionType) // if err != nil { // log.Printf("[ERROR] Error scanning question: %v\n", err) // continue // } // log.Printf("[DEBUG] Found question: %v\n", question) // // if question.QuestionType == "multiple_choice" || question.QuestionType == "single_choice" || question.QuestionType == "dropdown" { // optionRows, err := db.Query("SELECT option_text FROM question_options WHERE question_id = ?", question.ID) // if err != nil { // log.Printf("[ERROR] Error fetching options: %v\n", err) // continue // } // defer optionRows.Close() // // for optionRows.Next() { // var optionText string // err = optionRows.Scan(&optionText) // if err != nil { // log.Printf("[ERROR] Error scanning option: %v\n", err) // continue // } // question.Options = append(question.Options, optionText) // log.Printf("[DEBUG] Found option: %s\n", optionText) // } // } // // questions = append(questions, question) // } // // tmpl := template.Must(template.ParseFiles("manage.html")) // log.Println("[DEBUG] Rendering manage template with questions") // tmpl.Execute(w, questions) // } // // func handleRemoveQuestion(w http.ResponseWriter, r *http.Request) { // log.Println("[DEBUG] Entering handleRemoveQuestion handler") // questionID := r.URL.Query().Get("id") // log.Printf("[DEBUG] Removing question with ID: %s\n", questionID) // // deleteQuery := `DELETE FROM questions WHERE id = ?` // _, err := db.Exec(deleteQuery, questionID) // if err != nil { // log.Printf("[ERROR] Error removing question: %v\n", err) // http.Error(w, "Failed to remove question", http.StatusInternalServerError) // return // } // // log.Println("[DEBUG] Redirecting to manage page after deletion") // http.Redirect(w, r, "/manage", http.StatusSeeOther) // } func handleFetchMatches(w http.ResponseWriter, r *http.Request) { log.Println("[DEBUG] Entering handleFetchMatches handler") // Parse JSON request body var requestData struct { Classes []string `json:"classes"` } err := json.NewDecoder(r.Body).Decode(&requestData) if err != nil || len(requestData.Classes) == 0 { http.Error(w, "Invalid request data", http.StatusBadRequest) return } log.Printf("[DEBUG] Classes to match: %v\n", requestData.Classes) // Prepare query to fetch users who share any of the classes query := ` SELECT u.name, u.email, GROUP_CONCAT(c.class_name) as class_list FROM users u JOIN classes c ON u.id = c.user_id GROUP BY u.id HAVING COUNT(CASE WHEN c.class_name IN (` + strings.Trim(strings.Repeat("?,", len(requestData.Classes)), ",") + `) THEN 1 END) > 0; ` // Prepare the arguments for the query args := make([]interface{}, len(requestData.Classes)) for i, class := range requestData.Classes { args[i] = strings.TrimSpace(class) } // Execute the query rows, err := db.Query(query, args...) if err != nil { log.Printf("[ERROR] Failed to fetch matching users: %v\n", err) http.Error(w, "Failed to fetch matches", http.StatusInternalServerError) return } defer rows.Close() var matches []Match for rows.Next() { var match Match var classList string if err := rows.Scan(&match.Name, &match.Email, &classList); err != nil { log.Printf("[ERROR] Failed to scan result row: %v\n", err) continue } match.Classes = strings.Split(classList, ",") matches = append(matches, match) } // Return matches as JSON, always return an array, even if empty w.Header().Set("Content-Type", "application/json") if err := json.NewEncoder(w).Encode(matches); err != nil { log.Printf("[ERROR] Failed to encode matches: %v\n", err) http.Error(w, "Failed to return matches", http.StatusInternalServerError) } log.Println("[DEBUG] Exiting handleFetchMatches handler") } func main() { var err error log.Println("[DEBUG] Opening database connection") db, err = sql.Open("sqlite3", "./formdata.db") // db, err = sql.Open("sqlite3", ":memory:") if err != nil { log.Fatal(err) } createTableQueries := ` CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, email TEXT, phone TEXT, dorm TEXT ); CREATE TABLE IF NOT EXISTS classes ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER, class_name TEXT NOT NULL, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE ); ` log.Println("[DEBUG] Creating database tables") _, err = db.Exec(createTableQueries) if err != nil { log.Fatalf("[ERROR] Error creating tables: %v\n", err) return } log.Println("[DEBUG] Database tables created successfully") 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) http.HandleFunc("/fetch-matches", handleFetchMatches) log.Println("[DEBUG] Server starting at :8080") fmt.Println("Server started at :8080") log.Fatal(http.ListenAndServe(":8080", nil)) }