mirror of
https://github.com/logos-co/open-law.git
synced 2025-01-13 16:24:53 +00:00
interpretation voting
This commit is contained in:
parent
706c9463be
commit
cdbd530f38
19
.vscode/settings.json
vendored
19
.vscode/settings.json
vendored
@ -2,7 +2,7 @@
|
|||||||
"python.linting.pylintEnabled": false,
|
"python.linting.pylintEnabled": false,
|
||||||
"python.linting.flake8Enabled": true,
|
"python.linting.flake8Enabled": true,
|
||||||
"python.linting.enabled": true,
|
"python.linting.enabled": true,
|
||||||
"python.formatting.provider": "black",
|
"python.formatting.provider": "none",
|
||||||
"python.terminal.activateEnvironment": true,
|
"python.terminal.activateEnvironment": true,
|
||||||
"files.watcherExclude": {
|
"files.watcherExclude": {
|
||||||
"**/.git/objects/**": true,
|
"**/.git/objects/**": true,
|
||||||
@ -25,8 +25,19 @@
|
|||||||
"wsgi",
|
"wsgi",
|
||||||
"wtforms"
|
"wtforms"
|
||||||
],
|
],
|
||||||
"python.testing.unittestArgs": ["-v", "-s", "./tests", "-p", "*test.py"],
|
"python.testing.unittestArgs": [
|
||||||
|
"-v",
|
||||||
|
"-s",
|
||||||
|
"./tests",
|
||||||
|
"-p",
|
||||||
|
"*test.py"
|
||||||
|
],
|
||||||
"python.testing.pytestEnabled": true,
|
"python.testing.pytestEnabled": true,
|
||||||
"python.testing.unittestEnabled": false,
|
"python.testing.unittestEnabled": false,
|
||||||
"python.testing.pytestArgs": ["tests"]
|
"python.testing.pytestArgs": [
|
||||||
}
|
"tests"
|
||||||
|
],
|
||||||
|
"[python]": {
|
||||||
|
"editor.defaultFormatter": "ms-python.black-formatter"
|
||||||
|
}
|
||||||
|
}
|
@ -15,7 +15,6 @@ migration = Migrate()
|
|||||||
|
|
||||||
|
|
||||||
def create_app(environment="development"):
|
def create_app(environment="development"):
|
||||||
|
|
||||||
from config import config
|
from config import config
|
||||||
from app.views import (
|
from app.views import (
|
||||||
main_blueprint,
|
main_blueprint,
|
||||||
@ -24,6 +23,7 @@ def create_app(environment="development"):
|
|||||||
book_blueprint,
|
book_blueprint,
|
||||||
home_blueprint,
|
home_blueprint,
|
||||||
section_blueprint,
|
section_blueprint,
|
||||||
|
vote_blueprint,
|
||||||
)
|
)
|
||||||
from app.models import (
|
from app.models import (
|
||||||
User,
|
User,
|
||||||
@ -52,6 +52,7 @@ def create_app(environment="development"):
|
|||||||
app.register_blueprint(book_blueprint)
|
app.register_blueprint(book_blueprint)
|
||||||
app.register_blueprint(home_blueprint)
|
app.register_blueprint(home_blueprint)
|
||||||
app.register_blueprint(section_blueprint)
|
app.register_blueprint(section_blueprint)
|
||||||
|
app.register_blueprint(vote_blueprint)
|
||||||
|
|
||||||
# Set up flask login.
|
# Set up flask login.
|
||||||
@login_manager.user_loader
|
@login_manager.user_loader
|
||||||
|
@ -11,3 +11,4 @@ from .collection import CreateCollectionForm, EditCollectionForm
|
|||||||
from .section import CreateSectionForm, EditSectionForm
|
from .section import CreateSectionForm, EditSectionForm
|
||||||
from .interpretation import CreateInterpretationForm, EditInterpretationForm
|
from .interpretation import CreateInterpretationForm, EditInterpretationForm
|
||||||
from .comment import CreateCommentForm
|
from .comment import CreateCommentForm
|
||||||
|
from .vote import VoteForm
|
||||||
|
6
app/forms/vote.py
Normal file
6
app/forms/vote.py
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
from flask_wtf import FlaskForm
|
||||||
|
from wtforms import BooleanField
|
||||||
|
|
||||||
|
|
||||||
|
class VoteForm(FlaskForm):
|
||||||
|
positive = BooleanField("Positive")
|
@ -27,5 +27,17 @@ class Interpretation(BaseModel):
|
|||||||
back_populates="interpretations",
|
back_populates="interpretations",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def vote_count(self):
|
||||||
|
count = 0
|
||||||
|
|
||||||
|
for vote in self.votes:
|
||||||
|
if vote.positive:
|
||||||
|
count += 1
|
||||||
|
else:
|
||||||
|
count -= 1
|
||||||
|
|
||||||
|
return count
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f"<{self.id}: {self.label}>"
|
return f"<{self.id}: {self.label}>"
|
||||||
|
@ -5,3 +5,4 @@ from .user import bp as user_blueprint
|
|||||||
from .book import bp as book_blueprint
|
from .book import bp as book_blueprint
|
||||||
from .home import bp as home_blueprint
|
from .home import bp as home_blueprint
|
||||||
from .section import bp as section_blueprint
|
from .section import bp as section_blueprint
|
||||||
|
from .vote import bp as vote_blueprint
|
||||||
|
67
app/views/vote.py
Normal file
67
app/views/vote.py
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
from flask import (
|
||||||
|
Blueprint,
|
||||||
|
jsonify,
|
||||||
|
)
|
||||||
|
from flask_login import login_required, current_user
|
||||||
|
|
||||||
|
from app import models as m, db, forms as f
|
||||||
|
from app.logger import log
|
||||||
|
|
||||||
|
bp = Blueprint("vote", __name__, url_prefix="/vote")
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route(
|
||||||
|
"/interpretation/<int:interpretation_id>",
|
||||||
|
methods=["POST"],
|
||||||
|
)
|
||||||
|
@login_required
|
||||||
|
def vote_interpretation(interpretation_id: int):
|
||||||
|
interpretation: m.Interpretation = db.session.get(
|
||||||
|
m.Interpretation, interpretation_id
|
||||||
|
)
|
||||||
|
if not interpretation:
|
||||||
|
log(log.WARNING, "Interpretation with id [%s] not found", interpretation_id)
|
||||||
|
return jsonify({"message": "Interpretation not found"}), 404
|
||||||
|
|
||||||
|
form = f.VoteForm()
|
||||||
|
if form.validate_on_submit():
|
||||||
|
vote: m.InterpretationVote = m.InterpretationVote.query.filter_by(
|
||||||
|
user_id=current_user.id, interpretation_id=interpretation_id
|
||||||
|
).first()
|
||||||
|
if vote:
|
||||||
|
db.session.delete(vote)
|
||||||
|
|
||||||
|
positive = form.positive.data and "True" in form.positive.raw_data
|
||||||
|
if not vote or vote.positive != positive:
|
||||||
|
vote: m.InterpretationVote = m.InterpretationVote(
|
||||||
|
user_id=current_user.id,
|
||||||
|
interpretation_id=interpretation_id,
|
||||||
|
positive=positive,
|
||||||
|
)
|
||||||
|
log(
|
||||||
|
log.INFO,
|
||||||
|
"User [%s]. [%s] vote interpretation: [%s]",
|
||||||
|
current_user,
|
||||||
|
"Positive" if positive else "Negative",
|
||||||
|
interpretation,
|
||||||
|
)
|
||||||
|
vote.save(False)
|
||||||
|
else:
|
||||||
|
log(
|
||||||
|
log.INFO,
|
||||||
|
"User [%s]. Remove [%s] vote for interpretation: [%s]",
|
||||||
|
current_user,
|
||||||
|
"positive" if positive else "negative",
|
||||||
|
interpretation,
|
||||||
|
)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
return jsonify({"vote_count": interpretation.vote_count})
|
||||||
|
|
||||||
|
log(
|
||||||
|
log.CRITICAL,
|
||||||
|
"Unexpected error: User [%s]. Vote for interpretation: [%s]",
|
||||||
|
current_user,
|
||||||
|
interpretation,
|
||||||
|
)
|
||||||
|
return jsonify({"message": "Unexpected error"}), 400
|
93
tests/test_upvote.py
Normal file
93
tests/test_upvote.py
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
from flask import current_app as Response
|
||||||
|
from flask.testing import FlaskClient
|
||||||
|
|
||||||
|
from app import models as m
|
||||||
|
from tests.utils import login
|
||||||
|
|
||||||
|
|
||||||
|
def test_upvote(client: FlaskClient):
|
||||||
|
_, user = login(client)
|
||||||
|
|
||||||
|
response: Response = client.post(
|
||||||
|
"/vote/interpretation/999",
|
||||||
|
data=dict(
|
||||||
|
positive=True,
|
||||||
|
),
|
||||||
|
follow_redirects=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response
|
||||||
|
assert response.status_code == 404
|
||||||
|
assert response.json["message"] == "Interpretation not found"
|
||||||
|
|
||||||
|
interpretation = m.Interpretation(
|
||||||
|
label="Test Interpretation 1 Label",
|
||||||
|
text="Test Interpretation 1 Text",
|
||||||
|
user_id=user.id,
|
||||||
|
).save()
|
||||||
|
|
||||||
|
assert interpretation.vote_count == 0
|
||||||
|
|
||||||
|
response: Response = client.post(
|
||||||
|
f"/vote/interpretation/{interpretation.id}",
|
||||||
|
data=dict(
|
||||||
|
positive=True,
|
||||||
|
),
|
||||||
|
follow_redirects=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response
|
||||||
|
assert response.status_code == 200
|
||||||
|
json = response.json
|
||||||
|
assert json
|
||||||
|
assert "vote_count" in json
|
||||||
|
assert json["vote_count"] == 1
|
||||||
|
assert interpretation.vote_count == 1
|
||||||
|
|
||||||
|
response: Response = client.post(
|
||||||
|
f"/vote/interpretation/{interpretation.id}",
|
||||||
|
data=dict(
|
||||||
|
positive=True,
|
||||||
|
),
|
||||||
|
follow_redirects=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response
|
||||||
|
assert response.status_code == 200
|
||||||
|
json = response.json
|
||||||
|
assert json
|
||||||
|
assert "vote_count" in json
|
||||||
|
assert json["vote_count"] == 0
|
||||||
|
assert interpretation.vote_count == 0
|
||||||
|
|
||||||
|
response: Response = client.post(
|
||||||
|
f"/vote/interpretation/{interpretation.id}",
|
||||||
|
data=dict(
|
||||||
|
positive=False,
|
||||||
|
),
|
||||||
|
follow_redirects=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response
|
||||||
|
assert response.status_code == 200
|
||||||
|
json = response.json
|
||||||
|
assert json
|
||||||
|
assert "vote_count" in json
|
||||||
|
assert json["vote_count"] == -1
|
||||||
|
assert interpretation.vote_count == -1
|
||||||
|
|
||||||
|
response: Response = client.post(
|
||||||
|
f"/vote/interpretation/{interpretation.id}",
|
||||||
|
data=dict(
|
||||||
|
# positive=False,
|
||||||
|
),
|
||||||
|
follow_redirects=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response
|
||||||
|
assert response.status_code == 200
|
||||||
|
json = response.json
|
||||||
|
assert json
|
||||||
|
assert "vote_count" in json
|
||||||
|
assert json["vote_count"] == 0
|
||||||
|
assert interpretation.vote_count == 0
|
Loading…
x
Reference in New Issue
Block a user