add heatmap

This commit is contained in:
2025-09-25 15:25:39 +08:00
parent 6bb25ad807
commit 8436a4602f
5 changed files with 86 additions and 6 deletions

View File

@@ -4,10 +4,12 @@ from ..views.oj import (
AIAnalysisAPI, AIAnalysisAPI,
AIDetailDataAPI, AIDetailDataAPI,
AIWeeklyDataAPI, AIWeeklyDataAPI,
AIHeatmapDataAPI,
) )
urlpatterns = [ urlpatterns = [
path("ai/detail", AIDetailDataAPI.as_view()), path("ai/detail", AIDetailDataAPI.as_view()),
path("ai/weekly", AIWeeklyDataAPI.as_view()), path("ai/weekly", AIWeeklyDataAPI.as_view()),
path("ai/analysis", AIAnalysisAPI.as_view()), path("ai/analysis", AIAnalysisAPI.as_view()),
path("ai/heatmap", AIHeatmapDataAPI.as_view()),
] ]

View File

@@ -5,7 +5,8 @@ import json
from dateutil.relativedelta import relativedelta from dateutil.relativedelta import relativedelta
from django.core.cache import cache from django.core.cache import cache
from django.db.models import Min from django.db.models import Min, Count
from django.db.models.functions import TruncDate
from django.http import StreamingHttpResponse from django.http import StreamingHttpResponse
from django.utils import timezone from django.utils import timezone
from openai import OpenAI from openai import OpenAI
@@ -412,3 +413,48 @@ class AIAnalysisAPI(APIView):
) )
response["Cache-Control"] = "no-cache" response["Cache-Control"] = "no-cache"
return response return response
class AIHeatmapDataAPI(APIView):
@login_required
def get(self, request):
user = request.user
cache_key = get_cache_key("ai_heatmap", user.id, user.class_name or "")
cached_result = cache.get(cache_key)
if cached_result:
return self.success(cached_result)
end = datetime.now()
start = end - timedelta(days=365)
# 使用单次查询获取所有数据,按日期分组统计
submission_counts = (
Submission.objects.filter(
user_id=user.id, create_time__gte=start, create_time__lte=end
)
.annotate(date=TruncDate("create_time"))
.values("date")
.annotate(count=Count("id"))
.order_by("date")
)
# 将查询结果转换为字典,便于快速查找
submission_dict = {item["date"]: item["count"] for item in submission_counts}
# 生成365天的热力图数据
heatmap_data = []
current_date = start.date()
for i in range(365):
day_date = current_date + timedelta(days=i)
submission_count = submission_dict.get(day_date, 0)
heatmap_data.append(
{
"timestamp": int(datetime.combine(
day_date, datetime.min.time()
).timestamp() * 1000),
"value": submission_count,
}
)
cache.set(cache_key, heatmap_data, CACHE_TIMEOUT)
return self.success(heatmap_data)

View File

@@ -46,10 +46,13 @@ class Contest(models.Model):
# 是否有权查看problem 的一些统计信息 诸如submission_number, accepted_number 等 # 是否有权查看problem 的一些统计信息 诸如submission_number, accepted_number 等
def problem_details_permission(self, user): def problem_details_permission(self, user):
return self.rule_type == ContestRuleType.ACM or \ return (
self.status == ContestStatus.CONTEST_ENDED or \ self.rule_type == ContestRuleType.ACM
user.is_authenticated and user.is_contest_admin(self) or \ or self.status == ContestStatus.CONTEST_ENDED
self.real_time_rank or user.is_authenticated
and user.is_contest_admin(self)
or self.real_time_rank
)
class Meta: class Meta:
db_table = "contest" db_table = "contest"

View File

@@ -0,0 +1,19 @@
# Generated by Django 5.2.3 on 2025-09-25 07:03
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('contest', '0001_initial'),
('problem', '0001_initial'),
('submission', '0001_initial'),
]
operations = [
migrations.AddIndex(
model_name='submission',
index=models.Index(fields=['user_id', 'create_time'], name='user_create_time_idx'),
),
]

View File

@@ -41,7 +41,12 @@ class Submission(models.Model):
ip = models.TextField(null=True) ip = models.TextField(null=True)
def check_user_permission(self, user, check_share=True): def check_user_permission(self, user, check_share=True):
if self.user_id == user.id or user.is_super_admin() or user.can_mgmt_all_problem() or self.problem.created_by_id == user.id: if (
self.user_id == user.id
or user.is_super_admin()
or user.can_mgmt_all_problem()
or self.problem.created_by_id == user.id
):
return True return True
if check_share: if check_share:
@@ -54,6 +59,11 @@ class Submission(models.Model):
class Meta: class Meta:
db_table = "submission" db_table = "submission"
ordering = ("-create_time",) ordering = ("-create_time",)
indexes = [
models.Index(
fields=["user_id", "create_time"], name="user_create_time_idx"
),
]
def __str__(self): def __str__(self):
return self.id return self.id