Skip to content

Commit

Permalink
API - init report creation
Browse files Browse the repository at this point in the history
  • Loading branch information
SamR1 committed Aug 2, 2023
1 parent 281cf8e commit 7946c05
Show file tree
Hide file tree
Showing 11 changed files with 620 additions and 40 deletions.
2 changes: 2 additions & 0 deletions fittrackee/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ def create_app(init_email: bool = True) -> Flask:
from .application.app_config import config_blueprint # noqa
from .comments.comments import comments_blueprint # noqa
from .oauth2.routes import oauth2_blueprint # noqa
from .reports.reports import reports_blueprint # noqa
from .users.auth import auth_blueprint # noqa
from .users.follow_requests import follow_requests_blueprint # noqa
from .users.notifications import notifications_blueprint # noqa
Expand All @@ -152,6 +153,7 @@ def create_app(init_email: bool = True) -> Flask:
app.register_blueprint(follow_requests_blueprint, url_prefix='/api')
app.register_blueprint(timeline_blueprint, url_prefix='/api')
app.register_blueprint(notifications_blueprint, url_prefix='/api')
app.register_blueprint(reports_blueprint, url_prefix='/api')

if app.debug:
logging.getLogger('sqlalchemy').setLevel(logging.WARNING)
Expand Down
26 changes: 7 additions & 19 deletions fittrackee/comments/decorators.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
from functools import wraps
from typing import TYPE_CHECKING, Any, Callable, Optional

from fittrackee.privacy_levels import can_view
from fittrackee.responses import ForbiddenErrorResponse, NotFoundErrorResponse
from fittrackee.utils import decode_short_id

from .models import Comment
from .exceptions import CommentForbiddenException
from .utils import get_comment

if TYPE_CHECKING:
from fittrackee.users.models import User
Expand All @@ -18,25 +17,14 @@ def wrapper_check_workout_comment(
*args: Any, **kwargs: Any
) -> Callable:
auth_user: Optional['User'] = args[0]
comment_short_id = kwargs["comment_short_id"]

workout_comment_uuid = decode_short_id(comment_short_id)
comment = Comment.query.filter(
Comment.uuid == workout_comment_uuid,
Comment.user_id.not_in(
auth_user.get_blocked_user_ids()
+ auth_user.get_blocked_by_user_ids()
)
if auth_user
else True,
).first()
if not comment or not can_view(
comment, "text_visibility", auth_user
):
comment_short_id: str = kwargs["comment_short_id"]

try:
comment = get_comment(comment_short_id, auth_user)
except CommentForbiddenException:
return NotFoundErrorResponse(
f"workout comment not found (id: {comment_short_id})"
)

if check_owner and (
not auth_user or auth_user.id != comment.user.id
):
Expand Down
5 changes: 0 additions & 5 deletions fittrackee/comments/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,3 @@
class CommentForbiddenException(GenericException):
def __init__(self) -> None:
super().__init__('error', 'you do not have permissions')


class WorkoutForbiddenException(GenericException):
def __init__(self) -> None:
super().__init__('error', 'you do not have permissions')
24 changes: 23 additions & 1 deletion fittrackee/comments/utils.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import re
from typing import TYPE_CHECKING, Set, Tuple
from typing import TYPE_CHECKING, Optional, Set, Tuple

from sqlalchemy import func

from fittrackee.comments.exceptions import CommentForbiddenException
from fittrackee.privacy_levels import can_view
from fittrackee.utils import decode_short_id

from .models import Comment

if TYPE_CHECKING:
from fittrackee.users.models import User

Expand Down Expand Up @@ -30,3 +36,19 @@ def handle_mentions(text: str) -> Tuple[str, Set['User']]:
),
)
return text, mentioned_users


def get_comment(comment_short_id: str, auth_user: Optional['User']) -> Comment:
workout_comment_uuid = decode_short_id(comment_short_id)
comment = Comment.query.filter(
Comment.uuid == workout_comment_uuid,
Comment.user_id.not_in(
auth_user.get_blocked_user_ids()
+ auth_user.get_blocked_by_user_ids()
)
if auth_user
else True,
).first()
if not comment or not can_view(comment, "text_visibility", auth_user):
raise CommentForbiddenException()
return comment
Original file line number Diff line number Diff line change
Expand Up @@ -236,8 +236,38 @@ def upgrade():
batch_op.create_index(batch_op.f('ix_blocked_users_by_user_id'), ['by_user_id'], unique=False)
batch_op.create_index(batch_op.f('ix_blocked_users_user_id'), ['user_id'], unique=False)

op.create_table('reports',
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.Column('reported_by', sa.Integer(), nullable=True),
sa.Column('reported_comment_id', sa.Integer(), nullable=True),
sa.Column('reported_user_id', sa.Integer(), nullable=True),
sa.Column('reported_workout_id', sa.Integer(), nullable=True),
sa.Column('resolved', sa.Boolean(), nullable=False),
sa.Column('note', sa.String(), nullable=False),
sa.ForeignKeyConstraint(['reported_by'], ['users.id'], ondelete='CASCADE'),
sa.ForeignKeyConstraint(['reported_comment_id'], ['comments.id'], ondelete='CASCADE'),
sa.ForeignKeyConstraint(['reported_user_id'], ['users.id'], ondelete='CASCADE'),
sa.ForeignKeyConstraint(['reported_workout_id'], ['workouts.id'], ondelete='CASCADE'),
sa.PrimaryKeyConstraint('id')
)
with op.batch_alter_table('reports', schema=None) as batch_op:
batch_op.create_index(batch_op.f('ix_reports_reported_by'), ['reported_by'], unique=False)
batch_op.create_index(batch_op.f('ix_reports_reported_comment_id'), ['reported_comment_id'], unique=False)
batch_op.create_index(batch_op.f('ix_reports_reported_user_id'), ['reported_user_id'], unique=False)
batch_op.create_index(batch_op.f('ix_reports_reported_workout_id'), ['reported_workout_id'], unique=False)


def downgrade():
with op.batch_alter_table('reports', schema=None) as batch_op:
batch_op.drop_index(batch_op.f('ix_reports_reported_workout_id'))
batch_op.drop_index(batch_op.f('ix_reports_reported_user_id'))
batch_op.drop_index(batch_op.f('ix_reports_reported_comment_id'))
batch_op.drop_index(batch_op.f('ix_reports_reported_by'))

op.drop_table('reports')

with op.batch_alter_table('blocked_users', schema=None) as batch_op:
batch_op.drop_index(batch_op.f('ix_blocked_users_user_id'))
batch_op.drop_index(batch_op.f('ix_blocked_users_by_user_id'))
Expand Down
1 change: 1 addition & 0 deletions fittrackee/oauth2/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
'notifications:write',
'profile:read',
'profile:write',
'reports:write',
'users:read',
'users:write',
'workouts:read',
Expand Down
88 changes: 88 additions & 0 deletions fittrackee/reports/reports.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
from typing import Dict, Tuple, Union

from flask import Blueprint, request
from sqlalchemy import func

from fittrackee import db
from fittrackee.comments.exceptions import CommentForbiddenException
from fittrackee.comments.utils import get_comment
from fittrackee.oauth2.server import require_auth
from fittrackee.reports.models import REPORT_OBJECT_TYPES
from fittrackee.responses import (
HttpResponse,
InvalidPayloadErrorResponse,
NotFoundErrorResponse,
handle_error_and_return_response,
)
from fittrackee.users.models import User
from fittrackee.workouts.exceptions import WorkoutForbiddenException
from fittrackee.workouts.utils.workouts import get_workout

from .models import Report

reports_blueprint = Blueprint('reports', __name__)


@reports_blueprint.route("/reports", methods=["POST"])
@require_auth(scopes=["reports:write"])
def create_report(auth_user: User) -> Union[Tuple[Dict, int], HttpResponse]:
data = request.get_json()
object_id = data.get("object_id")
object_type = data.get("object_type")
note = data.get("note")
if (
not data
or not note
or not object_id
or object_type not in REPORT_OBJECT_TYPES
):
return InvalidPayloadErrorResponse()

if object_type == "comment":
try:
target_object = get_comment(object_id, auth_user)
except CommentForbiddenException:
return NotFoundErrorResponse(
f"comment not found (id: {object_id})"
)

elif object_type == "workout":
try:
target_object = get_workout(object_id, auth_user)
except WorkoutForbiddenException:
return NotFoundErrorResponse(
f"workout not found (id: {object_id})"
)

else: # object_type == "user"
target_object = User.query.filter(
func.lower(User.username) == func.lower(object_id),
).first()
if not target_object or not target_object.is_active:
return NotFoundErrorResponse(
f"user not found (username: {object_id})"
)
try:
new_report = Report(
reported_by=auth_user.id,
note=note,
object_id=target_object.id,
object_type=object_type,
)
db.session.add(new_report)
db.session.commit()

return (
{
"status": "created",
"report": new_report.serialize(auth_user),
},
201,
)
except Exception as e:
return handle_error_and_return_response(
error=e,
message="Error during report save.",
status="fail",
db=db,
)
Loading

0 comments on commit 7946c05

Please sign in to comment.