فهرست منبع

Working a lot on vote module

JoostSijm 6 سال پیش
والد
کامیت
f573100886

+ 2 - 0
app/flaskr.py

@@ -9,6 +9,7 @@ from app import app
 from app.modules.static import Static
 from app.modules.backend import Backend
 from app.modules.auth import Auth
+from app.modules.vote import Vote
 from app.modules.backend.modules.page import Backend_Page
 from app.modules.backend.modules.file import Backend_File
 from app.modules.backend.modules.user import Backend_User
@@ -19,6 +20,7 @@ app.register_blueprint(Backend, url_prefix='/backend')
 app.register_blueprint(Backend_Page, url_prefix='/backend/page')
 app.register_blueprint(Backend_File, url_prefix='/backend/file')
 app.register_blueprint(Backend_User, url_prefix='/backend/user')
+app.register_blueprint(Vote, url_prefix='/vote')
 
 
 @app.errorhandler(404)

+ 4 - 2
app/models.py

@@ -180,6 +180,7 @@ class Question(db.Model):
     id = db.Column(db.Integer, primary_key=True)
     name = db.Column(db.String, nullable=False)
     description = db.Column(db.String)
+    combined_approval_voting = db.Column(db.Boolean, server_default='f', default=False)
 
     ballot_id = db.Column(
         db.Integer,
@@ -194,7 +195,8 @@ class Question(db.Model):
 class Option(db.Model):
     """Model for Option"""
     id = db.Column(db.Integer, primary_key=True)
-    motivation = db.Column(db.String)
+    name = db.Column(db.String)
+    description = db.Column(db.String)
 
     user_id = db.Column(
         db.Integer,
@@ -225,7 +227,7 @@ class Vote(db.Model):
         db.ForeignKey("option.id")
     )
     option = db.relationship(
-        "Motion",
+        "Option",
         backref=db.backref("votes", lazy="dynamic")
     )
 

+ 6 - 0
app/modules/vote/__init__.py

@@ -0,0 +1,6 @@
+
+"""
+Website login page
+"""
+
+from .app import BLUEPRINT as Vote

+ 124 - 0
app/modules/vote/app.py

@@ -0,0 +1,124 @@
+
+"""
+Authentication module
+"""
+
+import os
+
+from datetime import datetime
+from flask_login import login_required, current_user
+from flask_menu import Menu, register_menu
+from flask import render_template, request, flash, Blueprint, redirect, url_for
+from app.models import User, Page, Ballot, Priority, Question, Option
+from app import db
+
+
+BLUEPRINT = Blueprint(
+    'vote',
+    __name__,
+    template_folder='templates'
+)
+
+@register_menu(BLUEPRINT, 'vote', 'Vote')
+@login_required
+@BLUEPRINT.route("/")
+def main():
+    """Ballots overview"""
+    ballots = Ballot.query.all()
+
+    return render_template(
+        'main.j2',
+        ballots=ballots,
+    )
+
+
+@BLUEPRINT.route('/create', methods=["GET", "POST"])
+@login_required
+def create():
+    """Creating ballot"""
+    if request.method == 'POST':
+        ballot = Ballot()
+        print(request.form)
+        ballot.name = request.form['name']
+        ballot.description = request.form['description']
+        ballot.user_id = current_user.id
+
+        start_at = "%s %s" % (request.form['start_at_date'], request.form['start_at_time'])
+        ballot.start_at = datetime.strptime(start_at, "%Y-%m-%d %H:%M")
+        end_at = "%s %s" % (request.form['end_at_date'], request.form['end_at_time'])
+        ballot.end_at = datetime.strptime(end_at, "%Y-%m-%d %H:%M")
+
+        db.session.add(ballot)
+        db.session.commit()
+
+        flash('Page "%s" successfully created' % ballot.name, 'success')
+        return redirect(url_for('vote.view', ballot_id=ballot.id))
+
+    priorities = Priority.query.all()
+
+    return render_template(
+        'vote/create.j2',
+        priorities=priorities
+    )
+
+
+@BLUEPRINT.route('/<int:ballot_id>', methods=["GET", "POST"])
+@login_required
+def view(ballot_id):
+    """View ballot"""
+    ballot = Ballot.query.get(ballot_id)
+    if request.method == 'POST':
+        option = Option()
+        option.question_id = request.form['question_id']
+        option.name = request.form['name']
+
+        db.session.add(option)
+        db.session.commit()
+
+    return render_template(
+        'vote/view.j2',
+        ballot=ballot,
+    )
+
+
+@BLUEPRINT.route('/<int:ballot_id>/add_question', methods=["GET", "POST"])
+@login_required
+def add_question(ballot_id):
+    """Add question to ballot"""
+    ballot = Ballot.query.get(ballot_id)
+    if request.method == 'POST':
+        question = Question()
+        question.ballot_id = ballot.id
+        question.name = request.form['name']
+        question.description = request.form['description']
+        question.combined_approval_voting = 'combined_approval_voting' in request.form
+
+        db.session.add(question)
+        db.session.commit()
+
+        if question.combined_approval_voting:
+            options = ['Voor', 'Tegen', 'Onthouden']
+            for option_name in options:
+                option = Option()
+                option.question_id = question.id
+                option.name = option_name
+
+                db.session.add(option)
+
+            db.session.commit()
+        return redirect(url_for('vote.view', ballot_id=ballot.id))
+
+    return render_template(
+        'vote/add_question.j2',
+        ballot=ballot,
+    )
+
+
+@BLUEPRINT.route('/public/<int:ballot_id>', methods=["GET", "POST"])
+def public(ballot_id):
+    """Vote and view results of ballot"""
+    ballot = Ballot.query.get(ballot_id)
+    return render_template(
+        'vote/public.j2',
+        ballot=ballot,
+    )

+ 44 - 0
app/modules/vote/templates/main.j2

@@ -0,0 +1,44 @@
+{% extends "layout/backend.j2" %}
+{% block content %}
+<h1>Votes</h1>
+<div class="row">
+    <div class="col-sm">
+        <div class="card">
+            <div class="card-header">
+                Votes
+                <div class="float-right">
+                    <a href="{{ url_for('vote.create') }}" class="btn btn-secondary btn-sm">Create</a>
+                </div>
+            </div>
+            <div class="card-body">
+                <table class="table table-striped table-sm">
+                    <thead>
+                        <tr>
+                            <th></th>
+                            <th>Name</th>
+                            <th>Start</th>
+                            <th>End</th>
+                            <th>Priority</th>
+                            <th>Author</th>
+                        </tr>
+                    </thead>
+                    <tbody>
+                        {% for ballot in ballots %}
+                        <tr>
+                            <td>
+                                <a href="{{ url_for('vote.view', ballot_id=ballot.id) }}"><button class="btn btn-secondary btn-sm">View</button></a>
+                            </td>
+                            <td>{{ ballot.name }}</td>
+                            <td>{{ ballot.start_at }}</td>
+                            <td>{{ ballot.end_at }}</td>
+                            <td>{{ ballot.priority.name }}</td>
+                            <td>{{ ballot.user.name }}</td>
+                        </tr>
+                        {%- endfor -%}
+                    </tbody>
+                </table>
+            </div>
+        </div>
+    </div>
+</div>
+{% endblock %}

+ 45 - 0
app/modules/vote/templates/vote/add_question.j2

@@ -0,0 +1,45 @@
+{% extends "layout/backend.j2" %}
+{% block content %}
+<h1>Add question: {{ ballot.name }}</h1>
+<table class="table table-sm">
+	<tr>
+		<th scope="row">Description</th>
+		<td>{{ ballot.description }}</td>
+	</tr>
+	<tr>
+		<th scope="row">Start</th>
+		<td>{{ ballot.start_at }}</td>
+	</tr>
+	<tr>
+		<th scope="row">End</th>
+		<td>{{ ballot.end_at }}</td>
+	</tr>
+	<tr>
+		<th scope="row">User</th>
+		<td>{{ ballot.user.name }}</td>
+	</tr>
+	<tr>
+		<th scope="row">Priority</th>
+		<td>{{ ballot.priority.name }}</td>
+	</tr>
+</table>
+<form method="post">
+    <div class="form-group">
+        <label class="text-normal text-dark">Name</label>
+        <input type="text" class="form-control" name="name" placeholder="Name" required>
+    </div>
+    <div class="form-group">
+        <label class="text-normal text-dark">Description</label>
+        <textarea class="form-control" name="description" rows="10"></textarea>
+    </div>
+    <div class="form-group form-check">
+        <input class="form-check-input" type="checkbox" name="combined_approval_voting" value="1" id="combined_approval_voting_check">
+        <label class="form-check-label" for="combined_approval_voting_check">
+            Combined approval voting
+        </label>
+    </div>
+    <div class="form-group text-right">
+        <button class="btn btn-primary">Create</button>
+    </div>
+</form>
+{% endblock %}

+ 46 - 0
app/modules/vote/templates/vote/create.j2

@@ -0,0 +1,46 @@
+{% extends "layout/backend.j2" %}
+{% block content %}
+<h1>Create vote</h1>
+<form method="post">
+    <div class="form-group">
+        <label class="text-normal text-dark">Name</label>
+        <input type="text" class="form-control" name="name" placeholder="Name" required>
+    </div>
+    <div class="form-group">
+        <label class="text-normal text-dark">Description</label>
+        <textarea class="form-control" name="description" rows="10"></textarea>
+    </div>
+    <div class="row">
+        <div class="form-group col">
+            <label for="example-datetime-local-input" class="form-label">Start date</label>
+            <input class="form-control" type="date" name="start_at_date" required>
+        </div>
+        <div class="form-group col">
+            <label for="example-time-input" class="form-label">Start time</label>
+            <input class="form-control" type="time" name="start_at_time" required>
+        </div>
+    </div>
+    <div class="row">
+        <div class="form-group col">
+            <label for="example-datetime-local-input" class="form-label">End date</label>
+            <input class="form-control" type="date" name="end_at_date" required>
+        </div>
+        <div class="form-group col">
+            <label for="example-time-input" class="form-label">End time</label>
+            <input class="form-control" type="time" name="end_at_time" required>
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="text-normal text-dark">Priority</label>
+        <select class="form-control" name="priority_id">
+            <option></option>
+            {% for prioritiy in priorities %}
+            <option value="{{ page.id }}">{{ priority.name }}</option>
+            {% endfor %}
+        </select>
+    </div>
+    <div class="form-group text-right">
+        <button class="btn btn-primary">Create</button>
+    </div>
+</form>
+{% endblock %}

+ 63 - 0
app/modules/vote/templates/vote/public.j2

@@ -0,0 +1,63 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <title>{{ ballot.name }} - ssg</title>
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+    <script src="/static/js/main.js"></script>
+</head>
+<body>
+    <nav class="navbar navbar-expand-lg navbar-dark bg-dark">
+        <a class="navbar-brand" href="/"><img src="/static/uploads/logo.png" style="height: 27px"></a>
+        <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
+            <span class="navbar-toggler-icon"></span>
+        </button>
+        <div class="collapse navbar-collapse" id="navbarSupportedContent">
+            {% block nav %}{% endblock %}
+        </div>
+    </nav>
+    <div class="container mt-3">
+        <h1>{{ ballot.name }}</h1>
+        <table class="table table-sm">
+            <tr>
+                <th scope="row">Description</th>
+                <td>{{ ballot.description }}</td>
+            </tr>
+            <tr>
+                <th scope="row">Start</th>
+                <td>{{ ballot.start_at }}</td>
+            </tr>
+            <tr>
+                <th scope="row">End</th>
+                <td>{{ ballot.end_at }}</td>
+            </tr>
+            <tr>
+                <th scope="row">User</th>
+                <td>{{ ballot.user.name }}</td>
+            </tr>
+            <tr>
+                <th scope="row">Priority</th>
+                <td>{{ ballot.priority.name }}</td>
+            </tr>
+        </table>
+        {% for question in ballot.questions %}
+       	{% if question.options.all() | count %}
+        <div class="card mb-4">
+            <div class="card-body">
+                <h5 class="card-title">{{ question.name }}</h5>
+                {% if question.description %}
+                <p class="card-text">{{ question.description }}</p>
+                {% endif %}
+            </div>
+            <ul class="list-group list-group-flush">
+                {% for option in question.options %}
+                <li class="list-group-item d-flex justify-content-between align-items-center">
+                    {{ option.name }}
+                    <span class="badge badge-primary badge-pill">{{ option.votes.all() | count }}</span>
+                </li>
+                {% endfor %}
+            </ul>
+        </div>
+        {% endif %}
+        {% endfor %}
+    </div>
+</body>

+ 72 - 0
app/modules/vote/templates/vote/view.j2

@@ -0,0 +1,72 @@
+{% extends "layout/backend.j2" %}
+{% block content %}
+<div class="row">
+	<div class="col-10">
+		<h1>Vote: {{ ballot.name }}</h1>
+	</div>
+	<div class="col-2 text-right">
+		<a href="{{ url_for('vote.add_question', ballot_id=ballot.id) }}"><button class="btn btn-secondary btn-sm">Add question</button></a>
+	</div>
+</div>
+<table class="table table-sm">
+	<tr>
+		<th scope="row">Description</th>
+		<td>{{ ballot.description }}</td>
+	</tr>
+	<tr>
+		<th scope="row">Start</th>
+		<td>{{ ballot.start_at }}</td>
+	</tr>
+	<tr>
+		<th scope="row">End</th>
+		<td>{{ ballot.end_at }}</td>
+	</tr>
+	<tr>
+		<th scope="row">User</th>
+		<td>{{ ballot.user.name }}</td>
+	</tr>
+	<tr>
+		<th scope="row">Priority</th>
+		<td>{{ ballot.priority.name }}</td>
+	</tr>
+</table>
+<div class="row">
+	{% for question in ballot.questions %}
+	<div class="col-6 mb-4">
+		<div class="card">
+			<div class="card-body">
+			    <h5 class="card-title">{{ question.name }}</h5>
+				{% if question.description %}
+			    <p class="card-text">{{ question.description }}</p>
+			    {% endif %}
+			</div>
+			{% if question.options.all() | count %}
+			<ul class="list-group list-group-flush">
+				{% for option in question.options %}
+				<li class="list-group-item d-flex justify-content-between align-items-center">
+					{{ option.name }}
+					<span class="badge badge-primary badge-pill">{{ option.votes.all() | count }}</span>
+				</li>
+				{% endfor %}
+			</ul>
+			{% else %}
+			<hr>
+			{% endif %}
+			{% if not question.combined_approval_voting %}
+			<div class="card-body">
+				<form class="row" method="post">
+					<input type="hidden" name="question_id" value="{{ question.id }}">
+					<div class="col-10">
+				    	<input type="text" class="form-control" name="name" placeholder="Option" required>
+					</div>
+					<div class="col-2">
+					    <button type="submit" class="btn btn-primary">Add</button>
+					</div>
+				</form>
+			</div>
+			{% endif %}
+		</div>
+	</div>
+	{% endfor %}
+</div>
+{% endblock %}

+ 34 - 0
migrations/versions/3d9ba6086a23_improvin_voting_models.py

@@ -0,0 +1,34 @@
+"""improvin_voting_models
+
+Revision ID: 3d9ba6086a23
+Revises: c98e920c06ae
+Create Date: 2019-03-30 13:52:32.070847
+
+"""
+from alembic import op
+import sqlalchemy as sa
+
+
+# revision identifiers, used by Alembic.
+revision = '3d9ba6086a23'
+down_revision = 'c98e920c06ae'
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+    # ### commands auto generated by Alembic - please adjust! ###
+    op.add_column('option', sa.Column('description', sa.String(), nullable=True))
+    op.add_column('option', sa.Column('name', sa.String(), nullable=True))
+    op.drop_column('option', 'motivation')
+    op.add_column('question', sa.Column('combined_approval_voting', sa.Boolean(), server_default='f', nullable=True))
+    # ### end Alembic commands ###
+
+
+def downgrade():
+    # ### commands auto generated by Alembic - please adjust! ###
+    op.drop_column('question', 'combined_approval_voting')
+    op.add_column('option', sa.Column('motivation', sa.VARCHAR(), autoincrement=False, nullable=True))
+    op.drop_column('option', 'name')
+    op.drop_column('option', 'description')
+    # ### end Alembic commands ###