Commit | Line | Data |
---|---|---|
e68ddb80 AC |
1 | structure Poll :> POLL = |
2 | struct | |
3 | ||
4 | open Util Sql Init | |
5 | ||
6 | type poll = {id : int, usr : int, title : string, descr : string, starts : string, ends : string, votes : int} | |
7 | ||
8 | fun 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 | ||
14 | fun 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 | ||
21 | fun 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 | ||
26 | fun 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 | ||
32 | fun 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 | ||
43 | fun 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 | ||
55 | fun deletePoll id = | |
56 | ignore (C.dml (getDb ()) ($`DELETE FROM Poll WHERE id = ^(C.intToSql id)`)) | |
57 | ||
58 | ||
59 | (* Poll choices *) | |
60 | ||
61 | type choice = {id : int, pol : int, seq : real, descr : string} | |
62 | ||
63 | fun 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 | ||
68 | fun 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 | ||
75 | fun 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 | ||
81 | fun 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 | ||
91 | fun 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 | ||
101 | fun deleteChoice id = | |
102 | ignore (C.dml (getDb ()) ($`DELETE FROM PollChoice WHERE id = ^(C.intToSql id)`)) | |
103 | ||
104 | ||
105 | (* Member voting *) | |
106 | ||
107 | fun 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 | ||
121 | fun 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 | ||
126 | fun 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 | ||
131 | fun 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 | ||
136 | fun canModify (poll : poll) = | |
137 | Group.inGroupName "poll" | |
138 | orelse (#usr poll = Init.getUserId() | |
139 | andalso dateLtNow (#starts poll)) | |
140 | ||
141 | fun requireCanModify poll = | |
142 | if canModify poll then | |
143 | () | |
144 | else | |
145 | raise Init.Access "Not authorized to edit that poll" | |
146 | ||
147 | fun 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 | ||
154 | end |