package lib import ( "database/sql" "fmt" ) type Story struct { ID int RoomID int Title string Points *int Voted bool } type Vote struct { ID int StoryID int UserID int Value string } // create story func CreateStory(roomID int, title string) (*Story, error) { _, err := DB.Exec("INSERT INTO stories (room_id, title, voted) VALUES (?, ?, 0)", roomID, title) if err != nil { return nil, err } s := &Story{RoomID: roomID, Title: title} row := DB.QueryRow("SELECT id, room_id, title, points, voted FROM stories WHERE room_id = ? ORDER BY id DESC LIMIT 1", roomID) err = row.Scan(&s.ID, &s.RoomID, &s.Title, &s.Points, &s.Voted) return s, err } // get stories for room func GetStoriesForRoom(roomID int) ([]Story, error) { rows, err := DB.Query("SELECT id, room_id, title, points, voted FROM stories WHERE room_id = ? ORDER BY id", roomID) if err != nil { return nil, err } defer rows.Close() var stories []Story for rows.Next() { var s Story var points sql.NullInt64 var voted int err := rows.Scan(&s.ID, &s.RoomID, &s.Title, &points, &voted) if err != nil { return nil, err } if points.Valid { p := int(points.Int64) s.Points = &p } s.Voted = voted == 1 stories = append(stories, s) } return stories, nil } // vote on story func VoteOnStory(storyID, userID int, value string) error { _, err := DB.Exec("INSERT OR REPLACE INTO votes (story_id, user_id, value) VALUES (?, ?, ?)", storyID, userID, value) return err } // get votes for story func GetVotesForStory(storyID int) ([]Vote, error) { rows, err := DB.Query("SELECT story_id, user_id, value FROM votes WHERE story_id = ?", storyID) if err != nil { return nil, err } defer rows.Close() var votes []Vote for rows.Next() { var v Vote err := rows.Scan(&v.StoryID, &v.UserID, &v.Value) if err != nil { return nil, err } votes = append(votes, v) } return votes, nil } // reveal votes — just marks the story as revealed func RevealVotes(storyID int) error { _, err := DB.Exec("UPDATE stories SET voted = 1 WHERE id = ?", storyID) return err } type VoteView struct { Username string Value string } // get votes with usernames for display func GetVotesWithUsernames(storyID int) ([]VoteView, error) { rows, err := DB.Query(` SELECT u.username, v.value FROM votes v JOIN users u ON v.user_id = u.id WHERE v.story_id = ? `, storyID) if err != nil { return nil, err } defer rows.Close() var views []VoteView for rows.Next() { var vv VoteView err := rows.Scan(&vv.Username, &vv.Value) if err != nil { return nil, err } views = append(views, vv) } return views, nil } // unreveal story — set voted back to 0 and clear points func UnrevealStory(storyID int) error { _, err := DB.Exec("UPDATE stories SET voted = 0, points = NULL WHERE id = ?", storyID) return err } // clear all votes for a story func ClearVotesForStory(storyID int) error { _, err := DB.Exec("DELETE FROM votes WHERE story_id = ?", storyID) return err } // rename story func RenameStory(storyID int, title string) error { _, err := DB.Exec("UPDATE stories SET title = ? WHERE id = ?", title, storyID) return err } // delete story and its votes func DeleteStory(storyID int) error { _, err := DB.Exec("DELETE FROM votes WHERE story_id = ?", storyID) if err != nil { return err } _, err = DB.Exec("DELETE FROM stories WHERE id = ?", storyID) return err } func tshirtToPoints(s string) float64 { switch s { case "XS": return 1 case "S": return 3 case "M": return 5 case "L": return 8 case "XL": return 13 case "XXL": return 21 case "?": return 0 default: var n int fmt.Sscanf(s, "%d", &n) return float64(n) } } func GetStoryByID(id int) (*Story, error) { row := DB.QueryRow("SELECT id, room_id, title, points, voted FROM stories WHERE id = ?", id) var s Story var points sql.NullInt64 var voted int err := row.Scan(&s.ID, &s.RoomID, &s.Title, &points, &voted) if err != nil { return nil, err } if points.Valid { p := int(points.Int64) s.Points = &p } s.Voted = voted == 1 return &s, nil }