From b679f8eb06e2c4c9fd165737c7c29f0b3a6083b8 Mon Sep 17 00:00:00 2001 From: yuetsh <517252939@qq.com> Date: Thu, 6 Aug 2026 03:51:54 -0600 Subject: [PATCH] =?UTF-8?q?refactor(reaction):=20=E4=B8=8B=E7=BA=BF=20comm?= =?UTF-8?q?ent=20app?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5 --- CLAUDE.md | 2 +- comment/__init__.py | 0 comment/migrations/0001_initial.py | 38 -------- .../0002_comment_comment_problem_time_idx.py | 21 ----- comment/migrations/__init__.py | 0 comment/models.py | 45 ---------- comment/serializers.py | 31 ------- comment/urls/__init__.py | 0 comment/urls/admin.py | 7 -- comment/urls/oj.py | 8 -- comment/views/__init__.py | 0 comment/views/admin.py | 27 ------ comment/views/oj.py | 90 ------------------- oj/settings.py | 1 - oj/urls.py | 2 - reaction/migrations/0002_delete_comment.py | 14 +++ utils/constants.py | 1 - 17 files changed, 15 insertions(+), 272 deletions(-) delete mode 100644 comment/__init__.py delete mode 100644 comment/migrations/0001_initial.py delete mode 100644 comment/migrations/0002_comment_comment_problem_time_idx.py delete mode 100644 comment/migrations/__init__.py delete mode 100644 comment/models.py delete mode 100644 comment/serializers.py delete mode 100644 comment/urls/__init__.py delete mode 100644 comment/urls/admin.py delete mode 100644 comment/urls/oj.py delete mode 100644 comment/views/__init__.py delete mode 100644 comment/views/admin.py delete mode 100644 comment/views/oj.py create mode 100644 reaction/migrations/0002_delete_comment.py diff --git a/CLAUDE.md b/CLAUDE.md index f6c2de1..bcab97f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -54,7 +54,7 @@ Each Django app follows the same structure: └── admin.py # Admin URL patterns ``` -Apps: `account`, `problem`, `submission`, `contest`, `ai`, `flowchart`, `problemset`, `class_pk`, `announcement`, `tutorial`, `message`, `comment`, `conf`, `options`, `judge` +Apps: `account`, `problem`, `submission`, `contest`, `ai`, `flowchart`, `problemset`, `class_pk`, `announcement`, `tutorial`, `message`, `reaction`, `conf`, `options`, `judge` `utils/` is itself a Django app (listed in `INSTALLED_APPS`) — not just a helpers package. It provides `RichTextField` (XSS-sanitized `TextField`), `APIError`, the base `APIView`, caching, WebSocket helpers, and the `inituser` management command. Import shared utilities from `utils.*`. diff --git a/comment/__init__.py b/comment/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/comment/migrations/0001_initial.py b/comment/migrations/0001_initial.py deleted file mode 100644 index 3bcf97d..0000000 --- a/comment/migrations/0001_initial.py +++ /dev/null @@ -1,38 +0,0 @@ -# Generated by Django 5.2.3 on 2025-06-14 08:51 - -import django.db.models.deletion -from django.conf import settings -from django.db import migrations, models - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - ('problem', '0001_initial'), - ('submission', '0001_initial'), - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ] - - operations = [ - migrations.CreateModel( - name='Comment', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('language', models.CharField(choices=[('Python', 'Python'), ('C', 'C'), ('C++', 'C++'), ('Java', 'Java')], default='Python', max_length=10, verbose_name='解决这道题使用的语言')), - ('description_rating', models.PositiveSmallIntegerField(default=5, verbose_name='题目描述的分数')), - ('difficulty_rating', models.PositiveSmallIntegerField(default=5, verbose_name='题目难度的分数')), - ('comprehensive_rating', models.PositiveSmallIntegerField(default=5, verbose_name='综合的分数')), - ('content', models.TextField(blank=True, null=True)), - ('create_time', models.DateTimeField(auto_now_add=True)), - ('problem', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='problem.problem')), - ('submission', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='submission.submission')), - ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), - ], - options={ - 'db_table': 'comment', - 'ordering': ('-create_time',), - }, - ), - ] diff --git a/comment/migrations/0002_comment_comment_problem_time_idx.py b/comment/migrations/0002_comment_comment_problem_time_idx.py deleted file mode 100644 index c2a4f7c..0000000 --- a/comment/migrations/0002_comment_comment_problem_time_idx.py +++ /dev/null @@ -1,21 +0,0 @@ -# Generated by Django 6.0 on 2026-04-23 20:07 - -from django.conf import settings -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('comment', '0001_initial'), - ('problem', '0007_problem_problem_visible_idx'), - ('submission', '0004_submission_problem_user_idx'), - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ] - - operations = [ - migrations.AddIndex( - model_name='comment', - index=models.Index(fields=['problem', 'create_time'], name='comment_problem_time_idx'), - ), - ] diff --git a/comment/migrations/__init__.py b/comment/migrations/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/comment/models.py b/comment/models.py deleted file mode 100644 index b1061ab..0000000 --- a/comment/models.py +++ /dev/null @@ -1,45 +0,0 @@ -from django.db import models - -from account.models import User -from problem.models import Problem -from submission.models import Submission - - -class Languages(models.TextChoices): - Python = "Python", "Python" - C = "C", "C" - Cpp = "C++", "C++" - Java = "Java", "Java" - - -class Comment(models.Model): - problem = models.ForeignKey(Problem, on_delete=models.CASCADE) - user = models.ForeignKey(User, on_delete=models.CASCADE) - submission = models.ForeignKey(Submission, on_delete=models.CASCADE) - language = models.CharField( - max_length=10, - default=Languages.Python, - choices=Languages.choices, - verbose_name="解决这道题使用的语言", - ) - description_rating = models.PositiveSmallIntegerField( - default=5, - verbose_name="题目描述的分数", - ) - difficulty_rating = models.PositiveSmallIntegerField( - default=5, - verbose_name="题目难度的分数", - ) - comprehensive_rating = models.PositiveSmallIntegerField( - default=5, - verbose_name="综合的分数", - ) - content = models.TextField(null=True, blank=True) - create_time = models.DateTimeField(auto_now_add=True) - - class Meta: - db_table = "comment" - ordering = ("-create_time",) - indexes = [ - models.Index(fields=["problem", "create_time"], name="comment_problem_time_idx"), - ] diff --git a/comment/serializers.py b/comment/serializers.py deleted file mode 100644 index 240f4f6..0000000 --- a/comment/serializers.py +++ /dev/null @@ -1,31 +0,0 @@ -from comment.models import Comment -from utils.api import UsernameSerializer, serializers - - -class CreateCommentSerializer(serializers.Serializer): - problem_id = serializers.IntegerField() - description_rating = serializers.IntegerField() - difficulty_rating = serializers.IntegerField() - comprehensive_rating = serializers.IntegerField() - content = serializers.CharField(required=False, allow_blank=True) - - -class CommentSerializer(serializers.ModelSerializer): - class Meta: - model = Comment - fields = [ - "comprehensive_rating", - "description_rating", - "difficulty_rating", - "content", - "create_time", - ] - - -class CommentListSerializer(serializers.ModelSerializer): - problem = serializers.SlugRelatedField(read_only=True, slug_field="_id") - user = UsernameSerializer() - - class Meta: - model = Comment - fields = "__all__" diff --git a/comment/urls/__init__.py b/comment/urls/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/comment/urls/admin.py b/comment/urls/admin.py deleted file mode 100644 index 34139ba..0000000 --- a/comment/urls/admin.py +++ /dev/null @@ -1,7 +0,0 @@ -from django.urls import path - -from ..views.admin import CommentAPI - -urlpatterns = [ - path("comment", CommentAPI.as_view()), -] diff --git a/comment/urls/oj.py b/comment/urls/oj.py deleted file mode 100644 index 0282308..0000000 --- a/comment/urls/oj.py +++ /dev/null @@ -1,8 +0,0 @@ -from django.urls import path - -from ..views.oj import CommentAPI, CommentStatisticsAPI - -urlpatterns = [ - path("comment", CommentAPI.as_view()), - path("comment/statistics", CommentStatisticsAPI.as_view()), -] diff --git a/comment/views/__init__.py b/comment/views/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/comment/views/admin.py b/comment/views/admin.py deleted file mode 100644 index d795d9a..0000000 --- a/comment/views/admin.py +++ /dev/null @@ -1,27 +0,0 @@ -from account.decorators import super_admin_required -from comment.models import Comment -from comment.serializers import CommentListSerializer -from problem.models import Problem -from utils.api import APIView - - -class CommentAPI(APIView): - @super_admin_required - def get(self, request): - comments = Comment.objects.select_related("problem").exclude(content="") - problem_id = request.GET.get("problem") - if problem_id: - try: - # 这里如果题目不可见,也需要显示该题目的评论 - problem = Problem.objects.get(_id__iexact=problem_id, contest_id__isnull=True) - except Problem.DoesNotExist: - return self.error("Problem doesn't exist") - comments = comments.filter(problem=problem) - return self.success(self.paginate_data(request, comments, CommentListSerializer)) - - @super_admin_required - def delete(self, request): - id = request.GET.get("id") - if id: - Comment.objects.filter(id=id).delete() - return self.success() diff --git a/comment/views/oj.py b/comment/views/oj.py deleted file mode 100644 index e24a06c..0000000 --- a/comment/views/oj.py +++ /dev/null @@ -1,90 +0,0 @@ -from django.db.models import Avg, Count -from django.db.models.functions import Round - -from account.decorators import login_required -from comment.models import Comment -from comment.serializers import CommentSerializer, CreateCommentSerializer -from problem.models import Problem -from submission.models import JudgeStatus, Submission -from utils.api import AsyncAPIView -from utils.api.api import validate_serializer -from utils.async_helpers import async_cache_delete, async_cache_get, async_cache_set -from utils.constants import CacheKey - - -class CommentAPI(AsyncAPIView): - @login_required - @validate_serializer(CreateCommentSerializer) - async def post(self, request): - data = request.data - try: - problem = await Problem.objects.aget(id=data["problem_id"], visible=True) - except Problem.DoesNotExist: - return self.error("problem is not exists") - - submission = await ( - Submission.objects.select_related("problem") - .filter( - user_id=request.user.id, - problem_id=data["problem_id"], - result__in=[JudgeStatus.ACCEPTED, JudgeStatus.AST_CHECK_FAILED], - ) - .afirst() - ) - if not submission: - return self.error("submission is not exists or not accepted") - - language = submission.language - if language == "Python3": - language = "Python" - - await Comment.objects.acreate( - user=request.user, - problem=problem, - submission=submission, - language=language, - description_rating=data["description_rating"], - difficulty_rating=data["difficulty_rating"], - comprehensive_rating=data["comprehensive_rating"], - content=data["content"], - ) - await async_cache_delete(f"{CacheKey.comment_stats}:{problem.id}") - return self.success() - - @login_required - async def get(self, request): - problem_id = request.GET.get("problem_id") - comment = await Comment.objects.select_related("problem").filter(user=request.user, problem_id=problem_id).afirst() - if comment: - return self.success(await self.async_serialize_data(CommentSerializer, comment)) - else: - return self.success() - - -class CommentStatisticsAPI(AsyncAPIView): - async def get(self, request): - problem_id = request.GET.get("problem_id") - cache_key = f"{CacheKey.comment_stats}:{problem_id}" - cached = await async_cache_get(cache_key) - if cached is not None: - return self.success(cached) - - agg = await Comment.objects.filter(problem_id=problem_id).aaggregate( - count=Count("id"), - description=Round(Avg("description_rating"), 2), - difficulty=Round(Avg("difficulty_rating"), 2), - comprehensive=Round(Avg("comprehensive_rating"), 2), - ) - if not agg["count"]: - return self.success() - - data = { - "count": agg["count"], - "rating": { - "description": agg["description"], - "difficulty": agg["difficulty"], - "comprehensive": agg["comprehensive"], - }, - } - await async_cache_set(cache_key, data, 3600) - return self.success(data) diff --git a/oj/settings.py b/oj/settings.py index cadaf85..a922520 100644 --- a/oj/settings.py +++ b/oj/settings.py @@ -54,7 +54,6 @@ LOCAL_APPS = [ "options", "judge", "message", - "comment", "reaction", "tutorial", "ai", diff --git a/oj/urls.py b/oj/urls.py index 91c7538..2a244a7 100644 --- a/oj/urls.py +++ b/oj/urls.py @@ -17,8 +17,6 @@ urlpatterns = [ path("api/", include("message.urls.oj")), path("api/", include("reaction.urls.oj")), path("api/admin/", include("reaction.urls.admin")), - path("api/", include("comment.urls.oj")), - path("api/admin/", include("comment.urls.admin")), path("api/", include("tutorial.urls.tutorial")), path("api/admin/", include("tutorial.urls.admin")), path("api/", include("ai.urls.oj")), diff --git a/reaction/migrations/0002_delete_comment.py b/reaction/migrations/0002_delete_comment.py new file mode 100644 index 0000000..0e8c4dd --- /dev/null +++ b/reaction/migrations/0002_delete_comment.py @@ -0,0 +1,14 @@ +from django.db import migrations + + +class Migration(migrations.Migration): + dependencies = [ + ("reaction", "0001_initial"), + ] + + operations = [ + migrations.RunSQL( + sql="DROP TABLE IF EXISTS comment CASCADE;", + reverse_sql=migrations.RunSQL.noop, + ), + ] diff --git a/utils/constants.py b/utils/constants.py index c5f8fe9..2af341f 100644 --- a/utils/constants.py +++ b/utils/constants.py @@ -17,7 +17,6 @@ class CacheKey: website_config = "website_config" problem_authors = "problem_authors" problem_tags = "problem_tags" - comment_stats = "comment_stats" reaction_stats = "reaction_stats" user_activity_rank = "user_activity_rank" problem_yearly_ac = "problem_yearly_ac"