Skip to content

Commit e107fd8

Browse files
authored
Merge pull request #66 from profcomff/Comment-anon-data
2 parents 2209bbd + 7dc047e commit e107fd8

4 files changed

Lines changed: 85 additions & 22 deletions

File tree

rating_api/exceptions.py

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import datetime
21
from typing import Type
32

43

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

3029

3130
class TooManyCommentRequests(RatingAPIError):
32-
delay_time: datetime.timedelta
31+
frequency: int
32+
limit: int
3333

34-
def __init__(self, dtime: datetime.timedelta):
35-
self.delay_time = dtime
34+
def __init__(self, frequency: int, limit: int):
35+
self.frequency = frequency
36+
self.limit = limit
3637
super().__init__(
37-
f'Too many comment requests. Delay: {dtime}',
38-
f'Слишком много попыток оставить комментарий. Задержка: {dtime}',
38+
f'Too many comment requests. Allowed: {limit} comments per {frequency} months.',
39+
f'Слишком много попыток оставить комментарий. Разрешено: {limit} комментариев за {frequency} месяцев.',
40+
)
41+
42+
43+
class TooManyCommentsToLecturer(RatingAPIError):
44+
frequency: int
45+
limit: int
46+
47+
def __init__(self, frequency: int, limit: int):
48+
self.frequency = frequency
49+
self.limit = limit
50+
super().__init__(
51+
f"Too many comments to lecturer. Allowed: {limit} comments per {frequency} months.",
52+
f"Превышен лимит комментариев лектору. Разрешено: {limit} комментариев за {frequency} месяцев.",
3953
)
4054

4155

rating_api/routes/comment.py

Lines changed: 46 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from fastapi import APIRouter, Depends, Query
77
from fastapi_sqlalchemy import db
88

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

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

3739
if not has_create_scope:
38-
user_comments: list[LecturerUserComment] = (
39-
LecturerUserComment.query(session=db.session).filter(LecturerUserComment.user_id == user.get("id")).all()
40+
# Определяем дату, до которой учитываем комментарии для проверки общего лимита.
41+
date_count = datetime.datetime(
42+
now.year + (now.month - settings.COMMENT_FREQUENCY_IN_MONTH) // 12,
43+
(now.month - settings.COMMENT_FREQUENCY_IN_MONTH) % 12,
44+
1,
45+
)
46+
user_comments_count = (
47+
LecturerUserComment.query(session=db.session)
48+
.filter(
49+
LecturerUserComment.user_id == user.get("id"),
50+
LecturerUserComment.update_ts >= date_count,
51+
)
52+
.count()
53+
)
54+
if user_comments_count >= settings.COMMENT_LIMIT:
55+
raise TooManyCommentRequests(settings.COMMENT_FREQUENCY_IN_MONTH, settings.COMMENT_LIMIT)
56+
57+
# Дата, до которой учитываем комментарии для проверки лимита на комментарии конкретному лектору.
58+
cutoff_date_lecturer = datetime.datetime(
59+
now.year + (now.month - settings.COMMENT_LECTURER_FREQUENCE_IN_MONTH) // 12,
60+
(now.month - settings.COMMENT_LECTURER_FREQUENCE_IN_MONTH) % 12,
61+
1,
62+
)
63+
lecturer_comments_count = (
64+
LecturerUserComment.query(session=db.session)
65+
.filter(
66+
LecturerUserComment.user_id == user.get("id"),
67+
LecturerUserComment.lecturer_id == lecturer_id,
68+
LecturerUserComment.update_ts >= cutoff_date_lecturer,
69+
)
70+
.count()
4071
)
41-
for user_comment in user_comments:
42-
if datetime.datetime.utcnow() - user_comment.update_ts < datetime.timedelta(
43-
minutes=settings.COMMENT_CREATE_FREQUENCY_IN_MINUTES
44-
):
45-
raise TooManyCommentRequests(
46-
dtime=user_comment.update_ts
47-
+ datetime.timedelta(minutes=settings.COMMENT_CREATE_FREQUENCY_IN_MINUTES)
48-
- datetime.datetime.utcnow()
49-
)
72+
if lecturer_comments_count >= settings.COMMENT_TO_LECTURER_LIMIT:
73+
raise TooManyCommentsToLecturer(
74+
settings.COMMENT_LECTURER_FREQUENCE_IN_MONTH, settings.COMMENT_TO_LECTURER_LIMIT
75+
)
5076

5177
# Сначала добавляем с user_id, который мы получили при авторизации,
5278
# в LecturerUserComment, чтобы нельзя было слишком быстро добавлять комментарии
53-
LecturerUserComment.create(session=db.session, lecturer_id=lecturer_id, user_id=user.get('id'))
54-
79+
create_ts = datetime.datetime(now.year, now.month, 1)
80+
LecturerUserComment.create(
81+
session=db.session,
82+
lecturer_id=lecturer_id,
83+
user_id=user.get('id'),
84+
create_ts=create_ts,
85+
update_ts=create_ts,
86+
)
5587
# Обрабатываем анонимность комментария, и удаляем этот флаг чтобы добавить запись в БД
5688
user_id = None if comment_info.is_anonymous else user.get('id')
5789

rating_api/routes/exc_handlers.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
11
import starlette.requests
22
from starlette.responses import JSONResponse
33

4-
from rating_api.exceptions import AlreadyExists, ForbiddenAction, ObjectNotFound, TooManyCommentRequests, WrongMark
4+
from rating_api.exceptions import (
5+
AlreadyExists,
6+
ForbiddenAction,
7+
ObjectNotFound,
8+
TooManyCommentRequests,
9+
TooManyCommentsToLecturer,
10+
WrongMark,
11+
)
512
from rating_api.schemas.base import StatusResponseModel
613

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

3037

38+
@app.exception_handler(TooManyCommentsToLecturer)
39+
async def too_many_comment_handler(req: starlette.requests.Request, exc: AlreadyExists):
40+
return JSONResponse(
41+
content=StatusResponseModel(status="Error", message=exc.eng, ru=exc.ru).model_dump(), status_code=429
42+
)
43+
44+
3145
@app.exception_handler(ForbiddenAction)
3246
async def forbidden_action_handler(req: starlette.requests.Request, exc: AlreadyExists):
3347
return JSONResponse(

rating_api/settings.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,10 @@ class Settings(BaseSettings):
1010

1111
DB_DSN: PostgresDsn = 'postgresql://postgres@localhost:5432/postgres'
1212
ROOT_PATH: str = '/' + os.getenv("APP_NAME", "")
13-
COMMENT_CREATE_FREQUENCY_IN_MINUTES: int = 1
13+
COMMENT_FREQUENCY_IN_MONTH: int = 10
14+
COMMENT_LECTURER_FREQUENCE_IN_MONTH: int = 6
15+
COMMENT_LIMIT: int = 20
16+
COMMENT_TO_LECTURER_LIMIT: int = 5
1417
CORS_ALLOW_ORIGINS: list[str] = ['*']
1518
CORS_ALLOW_CREDENTIALS: bool = True
1619
CORS_ALLOW_METHODS: list[str] = ['*']

0 commit comments

Comments
 (0)