interpretation voting

This commit is contained in:
SvyatoslavArtymovych 2023-05-09 17:56:29 +03:00
parent 706c9463be
commit cdbd530f38
8 changed files with 197 additions and 5 deletions

19
.vscode/settings.json vendored
View File

@ -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"
}
}

View File

@ -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

View File

@ -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
View File

@ -0,0 +1,6 @@
from flask_wtf import FlaskForm
from wtforms import BooleanField
class VoteForm(FlaskForm):
positive = BooleanField("Positive")

View File

@ -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}>"

View File

@ -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
View 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
View 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