Poll administration
[hcoop/portal.git] / poll.sml
CommitLineData
e68ddb80
AC
1structure Poll :> POLL =
2struct
3
4open Util Sql Init
5
6type poll = {id : int, usr : int, title : string, descr : string, starts : string, ends : string, votes : int}
7
8fun mkPollRow [id, usr, title, descr, starts, ends, votes] =
9 {id = C.intFromSql id, usr = C.intFromSql usr, title = C.stringFromSql title,
10 descr = C.stringFromSql descr, starts = C.stringFromSql starts,
11 ends = C.stringFromSql ends, votes = C.intFromSql votes}
12 | mkPollRow row = raise Fail ("Bad poll row : " ^ makeSet id row)
13
14fun lookupPoll id =
15 case C.oneOrNoRows (getDb ()) ($`SELECT id, usr, title, descr, starts, ends, votes
16 FROM Poll
17 WHERE id = ^(C.intToSql id)`) of
18 NONE => raise Fail "Poll not found"
19 | SOME row => mkPollRow row
20
21fun listPolls () =
22 C.map (getDb ()) mkPollRow ($`SELECT id, usr, title, descr, starts, ends, votes
23 FROM Poll
24 ORDER BY starts DESC, ends, title`)
25
26fun listPollsLimit lim =
27 C.map (getDb ()) mkPollRow ($`SELECT id, usr, title, descr, starts, ends, votes
28 FROM Poll
29 ORDER BY starts DESC, ends, title
30 LIMIT ^(C.intToSql lim)`)
31
32fun addPoll (usr, title, descr, starts, ends, votes) =
33 let
34 val db = getDb ()
35 val id = nextSeq (db, "PollSeq")
36 in
37 C.dml db ($`INSERT INTO Poll (id, usr, title, descr, starts, ends, votes)
38 VALUES (^id, ^(C.intToSql usr), ^(C.stringToSql title), ^(C.stringToSql descr),
39 ^(C.stringToSql starts), ^(C.stringToSql ends), ^(C.intToSql votes))`);
40 C.intFromSql id
41 end
42
43fun modPoll (poll : poll) =
44 let
45 val db = getDb ()
46 in
47 ignore (C.dml db ($`UPDATE Poll SET
48 usr = ^(C.intToSql (#usr poll)), title = ^(C.stringToSql (#title poll)),
49 descr = ^(C.stringToSql (#descr poll)),
50 starts = ^(C.stringToSql (#starts poll)), ends = ^(C.stringToSql (#ends poll)),
51 votes = ^(C.intToSql (#votes poll))
52 WHERE id = ^(C.intToSql (#id poll))`))
53 end
54
55fun deletePoll id =
56 ignore (C.dml (getDb ()) ($`DELETE FROM Poll WHERE id = ^(C.intToSql id)`))
57
58
59(* Poll choices *)
60
61type choice = {id : int, pol : int, seq : real, descr : string}
62
63fun mkChoiceRow [id, pol, seq, descr] =
64 {id = C.intFromSql id, pol = C.intFromSql pol,
65 seq = C.realFromSql seq, descr = C.stringFromSql descr}
66 | mkChoiceRow row = raise Fail ("Bad choice row : " ^ makeSet id row)
67
68fun lookupChoice id =
69 case C.oneOrNoRows (getDb ()) ($`SELECT id, pol, seq, descr
70 FROM PollChoice
71 WHERE id = ^(C.intToSql id)`) of
72 NONE => raise Fail "Poll choice not found"
73 | SOME row => mkChoiceRow row
74
75fun listChoices pol =
76 C.map (getDb ()) mkChoiceRow ($`SELECT id, pol, seq, descr
77 FROM PollChoice
78 WHERE pol = ^(C.intToSql pol)
79 ORDER BY seq`)
80
81fun addChoice (pol, seq, descr) =
82 let
83 val db = getDb ()
84 val id = nextSeq (db, "PollChoiceSeq")
85 in
86 C.dml db ($`INSERT INTO PollChoice (id, pol, seq, descr)
87 VALUES (^id, ^(C.intToSql pol), ^(C.realToSql seq), ^(C.stringToSql descr))`);
88 C.intFromSql id
89 end
90
91fun modChoice (choice : choice) =
92 let
93 val db = getDb ()
94 in
95 ignore (C.dml db ($`UPDATE PollChoice SET
96 pol = ^(C.intToSql (#pol choice)), seq = ^(C.realToSql (#seq choice)),
97 descr = ^(C.stringToSql (#descr choice))
98 WHERE id = ^(C.intToSql (#id choice))`))
99 end
100
101fun deleteChoice id =
102 ignore (C.dml (getDb ()) ($`DELETE FROM PollChoice WHERE id = ^(C.intToSql id)`))
103
104
105(* Member voting *)
106
107fun vote (usr, pol, chos) =
108 let
109 val db = getDb ()
110
111 fun voteOnce cho =
112 ignore (C.dml db ($`INSERT INTO Vote (usr, cho) VALUES (^(C.intToSql usr), ^(C.intToSql cho))`))
113 in
114 ignore (C.dml db ($`DELETE FROM Vote WHERE cho IN (SELECT id FROM PollChoice WHERE pol = ^(C.intToSql pol)) AND usr = ^(C.intToSql usr)`));
115 app voteOnce chos
116 end
117
118
119(* Date comparison *)
120
121fun dateLe (d1, d2) =
122 case C.oneRow (getDb ()) ($`SELECT (EXTRACT(EPOCH FROM DATE ^(C.stringToSql d1)) <= EXTRACT(EPOCH FROM DATE ^(C.stringToSql d2)))`) of
123 [res] => C.boolFromSql res
124 | row => raise Fail ("Bad dateLe row: " ^ makeSet id row)
125
126fun dateGeNow d =
127 case C.oneRow (getDb ()) ($`SELECT (EXTRACT(EPOCH FROM CURRENT_DATE) <= EXTRACT(EPOCH FROM DATE ^(C.stringToSql d)))`) of
128 [res] => C.boolFromSql res
129 | row => raise Fail ("Bad dateGeNow row: " ^ makeSet id row)
130
131fun dateLtNow d =
132 case C.oneRow (getDb ()) ($`SELECT (EXTRACT(EPOCH FROM CURRENT_DATE) > EXTRACT(EPOCH FROM DATE ^(C.stringToSql d)))`) of
133 [res] => C.boolFromSql res
134 | row => raise Fail ("Bad dateLtNow row: " ^ makeSet id row)
135
136fun canModify (poll : poll) =
137 Group.inGroupName "poll"
138 orelse (#usr poll = Init.getUserId()
139 andalso dateLtNow (#starts poll))
140
141fun requireCanModify poll =
142 if canModify poll then
143 ()
144 else
145 raise Init.Access "Not authorized to edit that poll"
146
147fun nextSeq pol =
148 case C.oneRow (getDb ()) ($`SELECT MAX(seq)+1
149 FROM PollChoice
150 WHERE pol = ^(C.intToSql pol)`) of
151 [max] => if C.isNull max then 1.0 else C.realFromSql max
152 | row => raise Fail ("Bad nextSeq row: " ^ makeSet id row)
153
154end