@app.route("/")
def main():
start=int(request.args.get("start", "-1"));
- q = "SELECT *, motion.deadline > CURRENT_TIMESTAMP AS running FROM motion LEFT JOIN (SELECT motion_id, voter_id, "\
+ q = "SELECT motion.*, votes.*, poser.email AS poser, canceler.email AS canceler, (motion.deadline > CURRENT_TIMESTAMP AND canceled is NULL) AS running FROM motion LEFT JOIN (SELECT motion_id, voter_id, "\
+ "COUNT(CASE WHEN result='yes' THEN 'yes' ELSE NULL END) as yes, "\
+ "COUNT(CASE WHEN result='no' THEN 'no' ELSE NULL END) as no, "\
+ "COUNT(CASE WHEN result='abstain' THEN 'abstain' ELSE NULL END) as abstain "\
- + "FROM vote GROUP BY motion_id, voter_id) as votes ON votes.motion_id=motion.id "
+ + "FROM vote GROUP BY motion_id, voter_id) as votes ON votes.motion_id=motion.id "\
+ + "LEFT JOIN voter poser ON poser.id = motion.posed_by "\
+ + "LEFT JOIN voter canceler ON canceler.id = motion.canceled_by "
prev=None
if start == -1:
- p = get_db().prepare(q + "ORDER BY id DESC LIMIT 11")
+ p = get_db().prepare(q + "ORDER BY motion.id DESC LIMIT 11")
rv = p()
else:
- p = get_db().prepare(q + "WHERE id <= $1 ORDER BY id DESC LIMIT 11")
+ p = get_db().prepare(q + "WHERE motion.id <= $1 ORDER BY motion.id DESC LIMIT 11")
rv = p(start)
- rs = get_db().prepare("SELECT id FROM motion WHERE id > $1 ORDER BY id ASC LIMIT 10")(start)
+ rs = get_db().prepare("SELECT id FROM motion WHERE motion.id > $1 ORDER BY id ASC LIMIT 10")(start)
if len(rs) == 10:
prev = rs[9][0]
else:
time = int(request.form.get("days", "3"));
if time not in times:
return "Error, invalid length"
- p = get_db().prepare("INSERT INTO motion(\"name\", \"content\", \"deadline\") VALUES($1, $2, CURRENT_TIMESTAMP + $3 * interval '1 days')")
- p(request.form.get("title", ""), request.form.get("content",""), time)
+ p = get_db().prepare("INSERT INTO motion(\"name\", \"content\", \"deadline\", \"posed_by\") VALUES($1, $2, CURRENT_TIMESTAMP + $3 * interval '1 days', $4)")
+ p(request.form.get("title", ""), request.form.get("content",""), time, voter)
return redirect("/")
voter=1
-@app.route("/motion/<int:id>")
-def show_motion(id):
- p = get_db().prepare("SELECT motion.*, motion.deadline > CURRENT_TIMESTAMP AS running, vote.result FROM motion LEFT JOIN vote on vote.motion_id=motion.id AND vote.voter_id=$2 WHERE id=$1")
- rv = p(id,voter)
+def motion_edited(motion):
+ return redirect("/?start=" + str(motion) + "#motion-" + str(motion))
+
+@app.route("/motion/<int:id>/cancel", methods=['POST'])
+def cancel_motion(id):
+ if request.form.get("reason", "none") == "none":
+ return "Error, form requires reason"
+ rv = get_db().prepare("UPDATE motion SET canceled=CURRENT_TIMESTAMP, cancelation_reason=$1, canceled_by=$2 WHERE id=$3 AND canceled is NULL")(request.form.get("reason", ""), voter, id)
+ print(rv)
+ return motion_edited(id)
+
+@app.route("/motion/<int:motion>")
+def show_motion(motion):
+ p = get_db().prepare("SELECT motion.*, poser.email AS poser, canceler.email AS canceler, (motion.deadline > CURRENT_TIMESTAMP AND canceled is NULL) AS running, vote.result FROM motion "\
+ + "LEFT JOIN vote on vote.motion_id=motion.id AND vote.voter_id=$2 "\
+ + "LEFT JOIN voter poser ON poser.id = motion.posed_by "\
+ + "LEFT JOIN voter canceler ON canceler.id = motion.canceled_by "
+ + "WHERE motion.id=$1")
+ rv = p(motion,voter)
+ if len(rv) == 0:
+ return "Error, motion not found" # TODO 404
return render_template('single_motion.html', motion=rv[0])
@app.route("/motion/<int:motion>/vote", methods=['POST'])
p = db.prepare("SELECT * FROM vote WHERE motion_id = $1 AND voter_id = $2")
rv = p(motion, voter)
if len(rv) == 0:
- db.prepare("INSERT INTO vote (motion_id, voter_id, result) VALUES($1,$2,$3)")(motion,voter,v)
+ db.prepare("INSERT INTO vote(motion_id, voter_id, result) VALUES($1,$2,$3)")(motion,voter,v)
else:
db.prepare("UPDATE vote SET result=$3, entered=CURRENT_TIMESTAMP WHERE motion_id=$1 AND voter_id = $2")(motion,voter,v)
- return redirect("/?start=" + str(motion) + "#motion-" + str(motion))
+ return motion_edited(motion)
-# TODO cancel running motion (with comment)
# TODO authentication/user management
+# TODO load config with flask mechanism
DROP TABLE IF EXISTS voter;
-CREATE TABLE voter (id serial NOT NULL, name VARCHAR(10) NOT NULL, PRIMARY KEY(id));
+CREATE TABLE voter (id serial NOT NULL, email VARCHAR(255) NOT NULL, PRIMARY KEY(id));
DROP TABLE IF EXISTS motion;
name VARCHAR(250) NOT NULL,
content text NOT NULL,
posed timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ posed_by int NOT NULL,
deadline timestamp NOT NULL DEFAULT (CURRENT_TIMESTAMP + interval '3 days'),
+ canceled timestamp NULL DEFAULT NULL,
+ cancelation_reason text NULL DEFAULT NULL,
+ canceled_by int NULL DEFAULT NULL,
PRIMARY KEY(id));
+
DROP TABLE IF EXISTS vote;
DROP TYPE IF EXISTS "vote_type";
CREATE TYPE "vote_type" AS ENUM ('yes', 'no', 'abstain');
<div class="motion panel panel-default" id="motion-{{motion.id}}">
<div class="motion-title panel-heading">
<a href="/motion/{{motion.id}}" class="anchor">#</a>
- <span class="title-text">{{motion.name}}</span> ({{ 'Running' if motion.running else 'Finished' }})
+ <span class="title-text">{{motion.name}}</span> ({{ 'Running' if motion.running else ('Canceled' if motion.canceled != None else 'Finished') }})
<div class="date">
- <div>Posed: {{motion.posed|timestamp}}</div>
+ <div>Posed: {{motion.posed|timestamp}} by {{motion.poser}}</div>
+{%- if motion.canceled != None %}
+ <div>Canceled: {{motion.canceled|timestamp}} by {{motion.canceler}}</div></div>
+{%- else %}
<div>Votes until: {{motion.deadline|timestamp}}</div></div>
+{%- endif %}
</div>
<div class="panel-body">
<p>{{motion.content}}</p>
{{vote|capitalize}} <span class="badge">{{motion[vote]}}</span><br>
{%- endfor %}
</p>
+{%- endif %}
+{%- if motion.canceled != None %}
+ <p>Cancelation reason: {{motion.cancelation_reason}}</p>
{%- endif %}
</div>
{%- block content %}{% endblock %}