diff options
Diffstat (limited to 'models.go')
-rw-r--r-- | models.go | 97 |
1 files changed, 75 insertions, 22 deletions
@@ -2,6 +2,7 @@ package main import ( "database/sql" + "fmt" "github.com/jmoiron/sqlx" "time" ) @@ -22,6 +23,7 @@ const ( sqlUpdateDecision sqlUpdateDecisionStatus sqlSelectClosableDecisions + sqlGetNextPendingDecisionDue ) var sqlStatements = map[sqlKey]string{ @@ -105,11 +107,13 @@ UPDATE decisions SET status=:status, modified=:modified WHERE id=:id `, sqlSelectClosableDecisions: ` -SELECT decisions.id, decisions.tag, decisions.proponent, voters.name AS proposer, decisions.proposed, decisions.title, - decisions.content, decisions.votetype, decisions.status, decisions.due, decisions.modified +SELECT decisions.id, decisions.tag, decisions.proponent, decisions.proposed, + decisions.title, decisions.content, decisions.votetype, decisions.status, + decisions.due, decisions.modified FROM decisions -JOIN voters ON decisions.proponent=voters.id WHERE decisions.status=0 AND :now > due`, + sqlGetNextPendingDecisionDue: ` +SELECT due FROM decisions WHERE status=0 ORDER BY due LIMIT 1`, } var db *sqlx.DB @@ -241,6 +245,18 @@ func (v *VoteSums) VoteCount() int { return v.Ayes + v.Nayes + v.Abstains } +func (v *VoteSums) TotalVotes() int { + return v.Ayes + v.Nayes +} + +func (v *VoteSums) Percent() int { + totalVotes := v.TotalVotes() + if totalVotes == 0 { + return 0 + } + return v.Ayes * 100 / totalVotes +} + type VoteForDisplay struct { Vote Name string @@ -413,6 +429,7 @@ func (d *Decision) Create() (err error) { logger.Println("Error getting id of inserted motion:", err) return } + rescheduleChannel <- JobIdCloseDecisions getDecisionStmt, err := db.Preparex(sqlStatements[sqlLoadDecisionById]) if err != nil { @@ -465,6 +482,7 @@ func (d *Decision) Update() (err error) { } else if affectedRows != 1 { logger.Printf("WARNING wrong number of affected rows: %d (1 expected)\n", affectedRows) } + rescheduleChannel <- JobIdCloseDecisions err = d.LoadWithId() return @@ -486,14 +504,20 @@ func (d *Decision) UpdateStatus() (err error) { affectedRows, err := result.RowsAffected() if err != nil { logger.Print("Problem determining the affected rows") + return } else if affectedRows != 1 { logger.Printf("WARNING wrong number of affected rows: %d (1 expected)\n", affectedRows) } + rescheduleChannel <- JobIdCloseDecisions err = d.LoadWithId() return } +func (d *Decision) String() string { + return fmt.Sprintf("%s %s (Id %d)", d.Tag, d.Title, d.Id) +} + func FindVoterByAddress(emailAddress string) (voter *Voter, err error) { findVoterStmt, err := db.Preparex(sqlStatements[sqlLoadEnabledVoterByEmail]) if err != nil { @@ -515,13 +539,6 @@ func FindVoterByAddress(emailAddress string) (voter *Voter, err error) { } func (d *Decision) Close() (err error) { - closeDecisionStmt, err := db.PrepareNamed(sqlStatements[sqlUpdateDecisionStatus]) - if err != nil { - logger.Println("Error preparing statement:", err) - return - } - defer closeDecisionStmt.Close() - quorum, majority := d.VoteType.QuorumAndMajority() voteSums, err := d.VoteSums() @@ -535,7 +552,7 @@ func (d *Decision) Close() (err error) { if votes < quorum { d.Status = voteStatusDeclined } else { - votes = voteSums.Ayes + voteSums.Nayes + votes = voteSums.TotalVotes() if (voteSums.Ayes / votes) > (majority / 100) { d.Status = voteStatusApproved } else { @@ -543,6 +560,13 @@ func (d *Decision) Close() (err error) { } } + closeDecisionStmt, err := db.PrepareNamed(sqlStatements[sqlUpdateDecisionStatus]) + if err != nil { + logger.Println("Error preparing statement:", err) + return + } + defer closeDecisionStmt.Close() + result, err := closeDecisionStmt.Exec(d) if err != nil { logger.Println("Error closing vote:", err) @@ -562,32 +586,61 @@ func (d *Decision) Close() (err error) { } func CloseDecisions() (err error) { - getClosedDecisionsStmt, err := db.PrepareNamed(sqlStatements[sqlSelectClosableDecisions]) + getClosableDecisionsStmt, err := db.PrepareNamed(sqlStatements[sqlSelectClosableDecisions]) if err != nil { logger.Println("Error preparing statement:", err) return } - defer getClosedDecisionsStmt.Close() + defer getClosableDecisionsStmt.Close() - params := make(map[string]interface{}, 1) - params["now"] = time.Now().UTC() - rows, err := getClosedDecisionsStmt.Queryx(params) + decisions := make([]*Decision, 0) + rows, err := getClosableDecisionsStmt.Queryx(struct{ Now time.Time }{time.Now().UTC()}) if err != nil { - logger.Println("Error fetching closed decisions", err) + logger.Println("Error fetching closable decisions", err) return } defer rows.Close() for rows.Next() { - d := &Decision{} - if err = rows.StructScan(d); err != nil { - logger.Println("Error filling decision from database row", err) + decision := &Decision{} + if err = rows.StructScan(decision); err != nil { + logger.Println("Error scanning row", err) return } - if err = d.Close(); err != nil { - logger.Printf("Error closing decision %s: %s\n", d.Tag, err) + decisions = append(decisions, decision) + } + rows.Close() + + for _, decision := range decisions { + logger.Println("DEBUG found closable decision", decision) + if err = decision.Close(); err != nil { + logger.Printf("Error closing decision %s: %s\n", decision, err) return } } return } + +func GetNextPendingDecisionDue() (due *time.Time, err error) { + getNextPendingDecisionDueStmt, err := db.Preparex(sqlStatements[sqlGetNextPendingDecisionDue]) + if err != nil { + logger.Println("Error preparing statement:", err) + return + } + defer getNextPendingDecisionDueStmt.Close() + + row := getNextPendingDecisionDueStmt.QueryRow() + + var dueTimestamp time.Time + if err = row.Scan(&dueTimestamp); err != nil { + if err == sql.ErrNoRows { + logger.Println("DEBUG No pending decisions") + return nil, nil + } + logger.Println("Error parsing result", err) + return + } + due = &dueTimestamp + + return +} |