Skip to content

Commit

Permalink
Merge pull request #66 from profcomff/Comment-anon-data
Browse files Browse the repository at this point in the history
  • Loading branch information
Temmmmmo authored Dec 16, 2024
2 parents 2209bbd + 7dc047e commit e107fd8
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 22 deletions.
26 changes: 20 additions & 6 deletions rating_api/exceptions.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import datetime
from typing import Type


Expand Down Expand Up @@ -29,13 +28,28 @@ def __init__(self, obj: type, obj_id_or_name: int | str):


class TooManyCommentRequests(RatingAPIError):
delay_time: datetime.timedelta
frequency: int
limit: int

def __init__(self, dtime: datetime.timedelta):
self.delay_time = dtime
def __init__(self, frequency: int, limit: int):
self.frequency = frequency
self.limit = limit
super().__init__(
f'Too many comment requests. Delay: {dtime}',
f'Слишком много попыток оставить комментарий. Задержка: {dtime}',
f'Too many comment requests. Allowed: {limit} comments per {frequency} months.',
f'Слишком много попыток оставить комментарий. Разрешено: {limit} комментариев за {frequency} месяцев.',
)


class TooManyCommentsToLecturer(RatingAPIError):
frequency: int
limit: int

def __init__(self, frequency: int, limit: int):
self.frequency = frequency
self.limit = limit
super().__init__(
f"Too many comments to lecturer. Allowed: {limit} comments per {frequency} months.",
f"Превышен лимит комментариев лектору. Разрешено: {limit} комментариев за {frequency} месяцев.",
)


Expand Down
60 changes: 46 additions & 14 deletions rating_api/routes/comment.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from fastapi import APIRouter, Depends, Query
from fastapi_sqlalchemy import db

from rating_api.exceptions import ForbiddenAction, ObjectNotFound, TooManyCommentRequests
from rating_api.exceptions import ForbiddenAction, ObjectNotFound, TooManyCommentRequests, TooManyCommentsToLecturer
from rating_api.models import Comment, Lecturer, LecturerUserComment, ReviewStatus
from rating_api.schemas.base import StatusResponseModel
from rating_api.schemas.models import CommentGet, CommentGetAll, CommentImportAll, CommentPost
Expand All @@ -27,6 +27,8 @@ async def create_comment(lecturer_id: int, comment_info: CommentPost, user=Depen
Для возможности создания комментария с указанием времени создания и изменения необходим скоуп ["rating.comment.import"]
"""
lecturer = Lecturer.get(session=db.session, id=lecturer_id)
now = datetime.datetime.now(tz=datetime.timezone.utc)

if not lecturer:
raise ObjectNotFound(Lecturer, lecturer_id)

Expand All @@ -35,23 +37,53 @@ async def create_comment(lecturer_id: int, comment_info: CommentPost, user=Depen
raise ForbiddenAction(Comment)

if not has_create_scope:
user_comments: list[LecturerUserComment] = (
LecturerUserComment.query(session=db.session).filter(LecturerUserComment.user_id == user.get("id")).all()
# Определяем дату, до которой учитываем комментарии для проверки общего лимита.
date_count = datetime.datetime(
now.year + (now.month - settings.COMMENT_FREQUENCY_IN_MONTH) // 12,
(now.month - settings.COMMENT_FREQUENCY_IN_MONTH) % 12,
1,
)
user_comments_count = (
LecturerUserComment.query(session=db.session)
.filter(
LecturerUserComment.user_id == user.get("id"),
LecturerUserComment.update_ts >= date_count,
)
.count()
)
if user_comments_count >= settings.COMMENT_LIMIT:
raise TooManyCommentRequests(settings.COMMENT_FREQUENCY_IN_MONTH, settings.COMMENT_LIMIT)

# Дата, до которой учитываем комментарии для проверки лимита на комментарии конкретному лектору.
cutoff_date_lecturer = datetime.datetime(
now.year + (now.month - settings.COMMENT_LECTURER_FREQUENCE_IN_MONTH) // 12,
(now.month - settings.COMMENT_LECTURER_FREQUENCE_IN_MONTH) % 12,
1,
)
lecturer_comments_count = (
LecturerUserComment.query(session=db.session)
.filter(
LecturerUserComment.user_id == user.get("id"),
LecturerUserComment.lecturer_id == lecturer_id,
LecturerUserComment.update_ts >= cutoff_date_lecturer,
)
.count()
)
for user_comment in user_comments:
if datetime.datetime.utcnow() - user_comment.update_ts < datetime.timedelta(
minutes=settings.COMMENT_CREATE_FREQUENCY_IN_MINUTES
):
raise TooManyCommentRequests(
dtime=user_comment.update_ts
+ datetime.timedelta(minutes=settings.COMMENT_CREATE_FREQUENCY_IN_MINUTES)
- datetime.datetime.utcnow()
)
if lecturer_comments_count >= settings.COMMENT_TO_LECTURER_LIMIT:
raise TooManyCommentsToLecturer(
settings.COMMENT_LECTURER_FREQUENCE_IN_MONTH, settings.COMMENT_TO_LECTURER_LIMIT
)

# Сначала добавляем с user_id, который мы получили при авторизации,
# в LecturerUserComment, чтобы нельзя было слишком быстро добавлять комментарии
LecturerUserComment.create(session=db.session, lecturer_id=lecturer_id, user_id=user.get('id'))

create_ts = datetime.datetime(now.year, now.month, 1)
LecturerUserComment.create(
session=db.session,
lecturer_id=lecturer_id,
user_id=user.get('id'),
create_ts=create_ts,
update_ts=create_ts,
)
# Обрабатываем анонимность комментария, и удаляем этот флаг чтобы добавить запись в БД
user_id = None if comment_info.is_anonymous else user.get('id')

Expand Down
16 changes: 15 additions & 1 deletion rating_api/routes/exc_handlers.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
import starlette.requests
from starlette.responses import JSONResponse

from rating_api.exceptions import AlreadyExists, ForbiddenAction, ObjectNotFound, TooManyCommentRequests, WrongMark
from rating_api.exceptions import (
AlreadyExists,
ForbiddenAction,
ObjectNotFound,
TooManyCommentRequests,
TooManyCommentsToLecturer,
WrongMark,
)
from rating_api.schemas.base import StatusResponseModel

from .base import app
Expand All @@ -28,6 +35,13 @@ async def too_many_comment_handler(req: starlette.requests.Request, exc: Already
)


@app.exception_handler(TooManyCommentsToLecturer)
async def too_many_comment_handler(req: starlette.requests.Request, exc: AlreadyExists):
return JSONResponse(
content=StatusResponseModel(status="Error", message=exc.eng, ru=exc.ru).model_dump(), status_code=429
)


@app.exception_handler(ForbiddenAction)
async def forbidden_action_handler(req: starlette.requests.Request, exc: AlreadyExists):
return JSONResponse(
Expand Down
5 changes: 4 additions & 1 deletion rating_api/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ class Settings(BaseSettings):

DB_DSN: PostgresDsn = 'postgresql://postgres@localhost:5432/postgres'
ROOT_PATH: str = '/' + os.getenv("APP_NAME", "")
COMMENT_CREATE_FREQUENCY_IN_MINUTES: int = 1
COMMENT_FREQUENCY_IN_MONTH: int = 10
COMMENT_LECTURER_FREQUENCE_IN_MONTH: int = 6
COMMENT_LIMIT: int = 20
COMMENT_TO_LECTURER_LIMIT: int = 5
CORS_ALLOW_ORIGINS: list[str] = ['*']
CORS_ALLOW_CREDENTIALS: bool = True
CORS_ALLOW_METHODS: list[str] = ['*']
Expand Down

0 comments on commit e107fd8

Please sign in to comment.