diff --git a/resources/consts.py b/backend/consts.py similarity index 65% rename from resources/consts.py rename to backend/consts.py index 2114c00a..bc3855e3 100644 --- a/resources/consts.py +++ b/backend/consts.py @@ -1,7 +1,8 @@ """ # Resources / Consts -Constants used within the server, used to avoid buggy code +Constants used within the server, used to avoid buggy code when we refactor +things. """ MAIN_QUEUE = "Main" diff --git a/backend/models/post.py b/backend/models/post.py index 1e0e89c7..abf72242 100644 --- a/backend/models/post.py +++ b/backend/models/post.py @@ -1,7 +1,7 @@ """ # Backend / Models / Post """ -from .tables import TComment, TPost, TPostReacts, TPostTags +from .tables import TComment, TPost, TPostReacts, TPostTags, TReport from .user import User from .tag import Tag from .comment import Comment @@ -178,13 +178,6 @@ def deleted(self) -> bool: """ return self.queue == Queue.get_deleted_queue() - @property - def reported(self) -> bool: - """ - Whether this post is reported or not - """ - return self.queue == Queue.get_reported_queue() - def _get(self) -> TPost: """ Return a reference to the underlying database row @@ -425,13 +418,44 @@ def can_view_op(self, user: User) -> bool: else: return True - def reported_perspective(self, user: User) -> bool: + def report(self, user: User): + """ + Toggle whether the given user has reported the post + """ + if self.user_reported(user): + TReport.delete().where( + TReport.user == user.id + & TReport.post == self.id + ).run_sync() + else: + TReport(user=user.id, post=self.id).save().run_sync() + + def unreport(self): """ - Returns whether the post should view as reported from the given user's - perspective. Only mods and admins should be able to view reported posts - by default. + Remove all reports of this post """ - return self.reported and user.permissions.can(Permission.ViewReports) + TReport.delete().where(TReport.post == self.id).run_sync() + + def user_reported(self, user: User) -> bool: + """ + Returns whether this user has reported the post + """ + return TReport.exists().where( + TReport.user == user.id + & TReport.post == self.id + ).run_sync() + + def report_count(self, user: User) -> int: + """ + Returns the report count, or zero if the given user doesn't have + permission to view reports + """ + if not user.permissions.can(Permission.ViewReports): + return 0 + else: + return TReport.count().where( + TReport.post.id == self.id + ).run_sync() def basic_info(self, user: User) -> IPostBasicInfo: """ @@ -449,7 +473,7 @@ def basic_info(self, user: User) -> IPostBasicInfo: "private": self.private, "closed": self.closed, "deleted": self.deleted, - "reported": self.reported_perspective(user), + "reported": self.report_count(user) > 0, "anonymous": self.anonymous, "answered": self.answered is not None, } @@ -474,7 +498,8 @@ def full_info(self, user: User) -> IPostFullInfo: "anonymous": self.anonymous, "closed": self.closed, "deleted": self.deleted, - "reported": self.reported_perspective(user), + "report_count": self.report_count(user), + "user_reported": self.user_reported(user), "user_reacted": self.has_reacted(user), "answered": self.answered.id if self.answered else None, "queue": self.queue.name diff --git a/backend/models/post_queue.py b/backend/models/post_queue.py index c7fe62b2..5fd7e1f2 100644 --- a/backend/models/post_queue.py +++ b/backend/models/post_queue.py @@ -7,7 +7,7 @@ from backend.util.validators import assert_valid_str_field from backend.util import http_errors from backend.types.queue import IQueueFullInfo, IQueueBasicInfo -from resources import consts +from backend import consts from typing import cast, TYPE_CHECKING from .user import User if TYPE_CHECKING: diff --git a/backend/models/tables.py b/backend/models/tables.py index 3bf1a69a..d2a4d84c 100644 --- a/backend/models/tables.py +++ b/backend/models/tables.py @@ -203,6 +203,18 @@ class TNotification(_BaseTable): """Queue the notification is related to""" +class TReport(_BaseTable): + """ + Table mapping users to post reports + """ + + user = ForeignKey(TUser) + """User who made the report""" + + post = ForeignKey(TPost) + """Post that was reported""" + + class TExamMode(_BaseTable): """ Table storing whether exam mode is on or off diff --git a/backend/routes/browse/post.py b/backend/routes/browse/post.py index a8b449fc..efb16717 100644 --- a/backend/routes/browse/post.py +++ b/backend/routes/browse/post.py @@ -162,7 +162,7 @@ def report_post(user: User, *_): user.permissions.assert_can(Permission.ReportPosts) data = json.loads(request.data) post = Post(data["post_id"]) - post.queue = Queue.get_reported_queue() + post.report(user) for u in User.all_where( lambda u: u.permissions.can(Permission.ViewReports) @@ -179,9 +179,6 @@ def unreport_post(user: User, *_): user.permissions.assert_can(Permission.ViewReports) data = json.loads(request.data) post = Post(data["post_id"]) - if post.answered is not None: - post.queue = Queue.get_answered_queue() - else: - post.queue = Queue.get_main_queue() + post.unreport() return {} diff --git a/backend/types/post.py b/backend/types/post.py index 7bd2df1c..525f4a74 100644 --- a/backend/types/post.py +++ b/backend/types/post.py @@ -73,7 +73,10 @@ class IPostFullInfo(TypedDict): * `anonymous` (`bool`): * `closed` (`bool`): * `deleted` (`bool`): - * `reported` (`bool`): + * `report_count` (`int`): the number of people who have reported the post, + or zero if the viewer doesn't have permission to view reports + * `user_reported` (`bool`): whether the user viewing the post has clicked + report * `user_reacted` (`bool`): * `answered` (`Optional[CommentId]`): ID of chosen answer if answered else none @@ -92,7 +95,8 @@ class IPostFullInfo(TypedDict): anonymous: bool closed: bool deleted: bool - reported: bool + report_count: int + user_reported: bool answered: Optional[CommentId] queue: str diff --git a/backend/util/setup.py b/backend/util/setup.py index 5a691f5b..9a036c9d 100644 --- a/backend/util/setup.py +++ b/backend/util/setup.py @@ -3,7 +3,7 @@ Code used to initialise the server """ -from resources import consts +from backend import consts from backend.types.auth import IAuthInfo from backend.models.auth_config import AuthConfig from backend.models.permissions import PermissionGroup, Permission diff --git a/ensemble_request/browse/post.py b/ensemble_request/browse/post.py index 7d9ed861..3ae0c699 100644 --- a/ensemble_request/browse/post.py +++ b/ensemble_request/browse/post.py @@ -336,7 +336,7 @@ def report( """ # PUT `/browse/post/report` - Report a post + Toggle whether a post has been reported ## Header * `Authorization` (`str`): JWT of the user @@ -368,8 +368,8 @@ def unreport( """ # PUT `/browse/post/unreport` - Un-report a post. This action should be taken by moderators if they find - that a report is invalid. + Remove all reports on a post. This action should be taken by moderators if + they find that a report is invalid. ## Header * `Authorization` (`str`): JWT of the user diff --git a/resources/__init__.py b/resources/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/tests/integration/browse/comment/accept_test.py b/tests/integration/browse/comment/accept_test.py index 2f9bea69..8221a3f8 100644 --- a/tests/integration/browse/comment/accept_test.py +++ b/tests/integration/browse/comment/accept_test.py @@ -13,7 +13,7 @@ import pytest from backend.util import http_errors from ensemble_request import browse -from resources import consts +from backend import consts from ensemble_request.taskboard import queue_post_list, queue_list from tests.integration.helpers import get_queue from tests.integration.conftest import IAllUsers, ISimpleUsers, IMakePosts diff --git a/tests/integration/browse/comment/delete_test.py b/tests/integration/browse/comment/delete_test.py index 7bff82dd..d4989e12 100644 --- a/tests/integration/browse/comment/delete_test.py +++ b/tests/integration/browse/comment/delete_test.py @@ -15,7 +15,7 @@ post, comment, ) -from resources import consts +from backend import consts from ensemble_request.taskboard import queue_post_list, queue_list from tests.integration.helpers import get_queue from tests.integration.conftest import ISimpleUsers, IMakePosts diff --git a/tests/integration/browse/post/close_test.py b/tests/integration/browse/post/close_test.py index e3b3155c..6153fa88 100644 --- a/tests/integration/browse/post/close_test.py +++ b/tests/integration/browse/post/close_test.py @@ -13,7 +13,7 @@ un-closing sends it back to main queue """ import pytest -from resources import consts +from backend import consts from backend.util import http_errors from tests.integration.conftest import ( ISimpleUsers, diff --git a/tests/integration/browse/post/report_test.py b/tests/integration/browse/post/report_test.py index 5fdcb328..28da8900 100644 --- a/tests/integration/browse/post/report_test.py +++ b/tests/integration/browse/post/report_test.py @@ -14,7 +14,7 @@ """ import pytest from backend.util import http_errors -from resources import consts +from backend import consts from tests.integration.conftest import ( ISimpleUsers, IBasicServerSetup, diff --git a/tests/integration/conftest.py b/tests/integration/conftest.py index 0448aa2c..b1d6ef4e 100644 --- a/tests/integration/conftest.py +++ b/tests/integration/conftest.py @@ -5,7 +5,7 @@ """ import pytest from typing import TypedDict -from resources import consts +from backend import consts from backend.types.identifiers import PostId, QueueId from backend.types.permissions import IPermissionGroup from backend.types.auth import IAuthInfo diff --git a/tests/integration/taskboard/default_queue_test.py b/tests/integration/taskboard/default_queue_test.py index e4b03b32..57dab220 100644 --- a/tests/integration/taskboard/default_queue_test.py +++ b/tests/integration/taskboard/default_queue_test.py @@ -8,7 +8,7 @@ * All new posts are added to the default queue """ import pytest -from resources import consts +from backend import consts from backend.util.http_errors import BadRequest from tests.integration.helpers import get_queue from ensemble_request.taskboard import ( diff --git a/tests/integration/taskboard/queue_post_add_test.py b/tests/integration/taskboard/queue_post_add_test.py index 7e66ad70..3b0c4b2b 100644 --- a/tests/integration/taskboard/queue_post_add_test.py +++ b/tests/integration/taskboard/queue_post_add_test.py @@ -21,7 +21,7 @@ IMakeQueues, IMakePosts ) -from resources import consts +from backend import consts def test_no_permission(