github.com/Azareal/Gosora@v0.0.0-20210729070923-553e66b59003/common/poll.go (about)

     1  package common
     2  
     3  import (
     4  	"database/sql"
     5  
     6  	qgen "github.com/Azareal/Gosora/query_gen"
     7  )
     8  
     9  var pollStmts PollStmts
    10  
    11  type Poll struct {
    12  	ID          int
    13  	ParentID    int
    14  	ParentTable string
    15  	Type        int  // 0: Single choice, 1: Multiple choice, 2: Multiple choice w/ points
    16  	AntiCheat   bool // Apply various mitigations for cheating
    17  	// GroupPower map[gid]points // The number of points a group can spend in this poll, defaults to 1
    18  
    19  	Options      map[int]string
    20  	Results      map[int]int  // map[optionIndex]points
    21  	QuickOptions []PollOption // TODO: Fix up the template transpiler so we don't need to use this hack anymore
    22  	VoteCount    int
    23  }
    24  
    25  // TODO: Use a transaction for this?
    26  // TODO: Add a voters table with castAt / IP data and only populate it when poll anti-cheat is on
    27  func (p *Poll) CastVote(optionIndex, uid int, ip string) error {
    28  	if Config.DisablePollIP || !p.AntiCheat {
    29  		ip = ""
    30  	}
    31  	_, e := pollStmts.addVote.Exec(p.ID, uid, optionIndex, ip)
    32  	if e != nil {
    33  		return e
    34  	}
    35  	_, e = pollStmts.incVoteCount.Exec(p.ID)
    36  	if e != nil {
    37  		return e
    38  	}
    39  	_, e = pollStmts.incVoteCountForOption.Exec(optionIndex, p.ID)
    40  	return e
    41  }
    42  
    43  func (p *Poll) Delete() error {
    44  	_, e := pollStmts.deletePollVotes.Exec(p.ID)
    45  	if e != nil {
    46  		return e
    47  	}
    48  	_, e = pollStmts.deletePollOptions.Exec(p.ID)
    49  	if e != nil {
    50  		return e
    51  	}
    52  	_, e = pollStmts.deletePoll.Exec(p.ID)
    53  	_ = Polls.GetCache().Remove(p.ID)
    54  	return e
    55  }
    56  
    57  func (p *Poll) Resultsf(f func(votes int) error) error {
    58  	rows, e := pollStmts.getResults.Query(p.ID)
    59  	if e != nil {
    60  		return e
    61  	}
    62  	defer rows.Close()
    63  
    64  	var votes int
    65  	for rows.Next() {
    66  		if e := rows.Scan(&votes); e != nil {
    67  			return e
    68  		}
    69  		if e := f(votes); e != nil {
    70  			return e
    71  		}
    72  	}
    73  	return rows.Err()
    74  }
    75  
    76  func (p *Poll) Copy() Poll {
    77  	return *p
    78  }
    79  
    80  type PollStmts struct {
    81  	getResults *sql.Stmt
    82  
    83  	addVote               *sql.Stmt
    84  	incVoteCount          *sql.Stmt
    85  	incVoteCountForOption *sql.Stmt
    86  
    87  	deletePoll        *sql.Stmt
    88  	deletePollOptions *sql.Stmt
    89  	deletePollVotes   *sql.Stmt
    90  }
    91  
    92  func init() {
    93  	DbInits.Add(func(acc *qgen.Accumulator) error {
    94  		p := "polls"
    95  		wh := "pollID=?"
    96  		pollStmts = PollStmts{
    97  			getResults: acc.Select("polls_options").Columns("votes").Where("pollID=?").Orderby("option ASC").Prepare(),
    98  
    99  			addVote:               acc.Insert("polls_votes").Columns("pollID,uid,option,castAt,ip").Fields("?,?,?,UTC_TIMESTAMP(),?").Prepare(),
   100  			incVoteCount:          acc.Update(p).Set("votes=votes+1").Where(wh).Prepare(),
   101  			incVoteCountForOption: acc.Update("polls_options").Set("votes=votes+1").Where("option=? AND pollID=?").Prepare(),
   102  
   103  			deletePoll:        acc.Delete(p).Where(wh).Prepare(),
   104  			deletePollOptions: acc.Delete("polls_options").Where(wh).Prepare(),
   105  			deletePollVotes:   acc.Delete("polls_votes").Where(wh).Prepare(),
   106  		}
   107  		return acc.FirstError()
   108  	})
   109  }