Files
sprintpadawan/lib/story.go
T
2026-04-24 00:00:19 -06:00

171 lines
3.6 KiB
Go

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)
}
}