summaryrefslogtreecommitdiff
path: root/blavote.go
blob: 06897b4d86af5accf920695a485db660e3976680 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
package main

// import "blavote"
import "fmt"
import "github.com/mxk/go-sqlite/sqlite3"
import "github.com/jessevdk/go-flags"
import "os"
import "io/ioutil"
import "strings"
import "strconv"
import "errors"

var version string

func main() {

    version = "0.7.3"

    //Command line arguments
    var opts struct {
        Version bool `short:"v" long:"version" description:"Show program version"`
        Add string `short:"a" long:"add" description:"Title for a new poll"`
        New string `short:"n" long:"new" description:"Title for a new poll"`
        Username string `short:"u" long:"username" description:"Username of user adding poll"`
        Remove int `short:"r" long:"remove" description:"ID of a poll to delete"`
        Info int `short:"i" long:"info" description:"Get info and vote stats for a given poll ID"`
        List bool `short:"l" long:"list" description:"List recent polls"`
        Args struct {
            
            Rest []string

        }   `positional-args:"yes"`
    }

    var args []string

    //If there are no command line arguments, read them from stdin
    if (len(os.Args) > 1) {
       flags.Parse(&opts)
    } else {
        bytes, err := ioutil.ReadAll(os.Stdin)
        if (err == nil) {
            args = strings.Split(strings.Split(string(bytes),"\n")[0], " ")
            flags.ParseArgs(&opts, args)
        } else {
            fmt.Println(err)
            //No args given, deal with it
        }

    }

    db, err := connectDb("blavote.db")

    if (err != nil) {
        fmt.Print("Could not connect to vote database: ")
        fmt.Println(err)
        return
    }

    if (opts.Version) {
        fmt.Println("v" + version)
    } else if (opts.Add != "") {

        pollId, err := addPoll(db, opts.Add, opts.Args.Rest, opts.Username)
        if (err == nil) {
            fmt.Print("Poll added with ID ")
            fmt.Println(pollId)
        } else {
            fmt.Println(err)
        }
    } else if (opts.New != "") {
        pollId, err := addPoll(db, opts.New, opts.Args.Rest, opts.Username)
        if (err == nil) {
            fmt.Print("Poll added with ID ")
            fmt.Println(pollId)
        } else {
            fmt.Println(err)
        }
    } else if (opts.Remove > 0) {
        err = deletePoll(db, opts.Remove, opts.Username)

        if (err == nil) {
            fmt.Print("Poll removed with ID ")
            fmt.Println(opts.Remove)
        } else {
            fmt.Println(err)
        }
    } else if (opts.Info > 0) {
        err = pollInfo(db, opts.Info)
    } else if (opts.List) {
        err = listPolls(db)
        if (err != nil) {
            fmt.Println(err)
        }
    } else {
        if (len(opts.Args.Rest) > 1) {
            err = vote(db, opts.Username, opts.Args.Rest)

            if (err != nil) {
                fmt.Println(err)
            }
        } else {

        }
    }


}

func connectDb(name string) (*sqlite3.Conn, error) {
    db, err := sqlite3.Open(name)

    if (err != nil) {
        return nil, err
    }

    //Check the version in the DB, if it doesn't exist we'll create it
    //Can use this later to update the DB if needed
    sql := "select * from info where key = 'version'"
    _, err = db.Query(sql)

    if (err != nil) {
        fmt.Println("info table does not exist, creating database")
        initTables(db)
        return nil, err
    }

    return db, err
}

func initTables(db *sqlite3.Conn) {

   db.Exec("create table info(id int, key text, value text)")
   db.Exec("insert into info (key, value) values('version', '$a')", version)

   db.Exec("create table users(id integer primary key autoincrement, name text, admin boolean)")
   db.Exec("create table polls(id integer primary key autoincrement, title text, user_id int)")
   db.Exec("create table options(id integer primary key autoincrement, text text, poll_id int)")
   db.Exec("create table votes(id integer primary key autoincrement, user_id int, poll_id int, option_id int)")

}

func vote(db *sqlite3.Conn, nick string, options []string) error {

    pollId, err := strconv.Atoi(options[0])

    if (err != nil) {
        return errors.New("Poll ID must be a number")
    }

    poll, err := getPollFromId(db, pollId)

    if (err != nil) {
        return err
    }

    option, err := getOptionFromText(db, options[1], pollId)

    if (err != nil) {
        return err
    }

    user := getUserForName(db, nick)

    //Record the user in the DB if they aren't already
    if (user.id == 0) {
        user, err = createUser(db, nick, false)

        if (err != nil) {
            return err
        }
    }

    if (!hasUserVotedInPoll(db, poll, user)) {

        args := sqlite3.NamedArgs{"$a": user.id, "$b": poll.id, "$c": option.id}
        sql := "INSERT INTO votes (user_id, poll_id, option_id) VALUES ($a, $b, $c)"

        err = db.Exec(sql, args)

        if (err != nil ) {
            return err
        } else {
            fmt.Println("Vote added")
            return nil
        }

    } else {
        return errors.New("You have already voted in this poll")
    }
}

func pollInfo(db *sqlite3.Conn, id int) error {

    poll , err := getPollFromId(db, id)
    fmt.Print(poll.title + " - ")
    options, err := getOptionsForPoll(db, poll)

    for _, option := range options {
        option, err = getVotesForOption(db, option)
        fmt.Print(option.text + " (")
        fmt.Print(option.numVotes)
        fmt.Print(") ")
    }
    fmt.Println()

    return err
}

func listPolls(db *sqlite3.Conn) error {

    polls, err := getRecentPolls(db)

    for _, poll := range polls {
        if (poll.id != 0) {
            fmt.Print(poll.id)
            fmt.Print(": " + poll.title + " ")
        } else {
            fmt.Println()
            return nil
        }
    }


    return err
}