style: ruff format 全仓库
行宽 180 下把历史遗留的折行表达式合并,无语义改动。 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
"""
|
||||
WebSocket consumers for flowchart evaluation updates
|
||||
"""
|
||||
|
||||
import json
|
||||
import logging
|
||||
|
||||
@@ -18,31 +19,25 @@ class FlowchartConsumer(AsyncWebsocketConsumer):
|
||||
async def connect(self):
|
||||
"""处理 WebSocket 连接"""
|
||||
self.user = self.scope["user"]
|
||||
|
||||
|
||||
# 只允许认证用户连接
|
||||
if not self.user.is_authenticated:
|
||||
await self.close()
|
||||
return
|
||||
|
||||
|
||||
# 使用用户 ID 作为组名,这样可以向特定用户推送消息
|
||||
self.group_name = f"flowchart_user_{self.user.id}"
|
||||
|
||||
|
||||
# 加入用户专属的组
|
||||
await self.channel_layer.group_add(
|
||||
self.group_name,
|
||||
self.channel_name
|
||||
)
|
||||
|
||||
await self.channel_layer.group_add(self.group_name, self.channel_name)
|
||||
|
||||
await self.accept()
|
||||
logger.info(f"Flowchart WebSocket connected: user_id={self.user.id}, channel={self.channel_name}")
|
||||
|
||||
async def disconnect(self, close_code):
|
||||
"""处理 WebSocket 断开连接"""
|
||||
if hasattr(self, 'group_name'):
|
||||
await self.channel_layer.group_discard(
|
||||
self.group_name,
|
||||
self.channel_name
|
||||
)
|
||||
if hasattr(self, "group_name"):
|
||||
await self.channel_layer.group_discard(self.group_name, self.channel_name)
|
||||
logger.info(f"Flowchart WebSocket disconnected: user_id={self.user.id}, close_code={close_code}")
|
||||
|
||||
async def receive(self, text_data):
|
||||
@@ -53,13 +48,10 @@ class FlowchartConsumer(AsyncWebsocketConsumer):
|
||||
try:
|
||||
data = json.loads(text_data)
|
||||
message_type = data.get("type")
|
||||
|
||||
|
||||
if message_type == "ping":
|
||||
# 响应心跳包
|
||||
await self.send(text_data=json.dumps({
|
||||
"type": "pong",
|
||||
"timestamp": data.get("timestamp")
|
||||
}))
|
||||
await self.send(text_data=json.dumps({"type": "pong", "timestamp": data.get("timestamp")}))
|
||||
elif message_type == "subscribe":
|
||||
# 订阅特定流程图提交的更新
|
||||
submission_id = data.get("submission_id")
|
||||
|
||||
@@ -6,61 +6,59 @@ from utils.shortcuts import rand_str
|
||||
|
||||
User = get_user_model()
|
||||
|
||||
|
||||
class FlowchartSubmissionStatus:
|
||||
PENDING = 0 # 等待AI评分
|
||||
PROCESSING = 1 # AI评分中
|
||||
COMPLETED = 2 # 评分完成
|
||||
FAILED = 3 # 评分失败
|
||||
PENDING = 0 # 等待AI评分
|
||||
PROCESSING = 1 # AI评分中
|
||||
COMPLETED = 2 # 评分完成
|
||||
FAILED = 3 # 评分失败
|
||||
|
||||
|
||||
class FlowchartSubmission(models.Model):
|
||||
"""流程图提交模型"""
|
||||
|
||||
id = models.TextField(default=rand_str, primary_key=True, db_index=True)
|
||||
|
||||
|
||||
# 基础信息
|
||||
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='flowchart_submissions')
|
||||
problem = models.ForeignKey(Problem, on_delete=models.CASCADE, related_name='flowchart_submissions')
|
||||
|
||||
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="flowchart_submissions")
|
||||
problem = models.ForeignKey(Problem, on_delete=models.CASCADE, related_name="flowchart_submissions")
|
||||
|
||||
# 提交内容
|
||||
mermaid_code = models.TextField() # Mermaid代码
|
||||
flowchart_data = models.JSONField(default=dict) # 流程图元数据
|
||||
|
||||
|
||||
# 状态信息
|
||||
status = models.IntegerField(default=FlowchartSubmissionStatus.PENDING)
|
||||
create_time = models.DateTimeField(auto_now_add=True)
|
||||
|
||||
|
||||
# AI评分结果
|
||||
ai_score = models.FloatField(null=True, blank=True) # AI评分 (0-100)
|
||||
ai_grade = models.CharField(max_length=10, null=True, blank=True) # 等级 (S/A/B/C)
|
||||
ai_feedback = models.TextField(null=True, blank=True) # AI反馈
|
||||
ai_suggestions = models.TextField(null=True, blank=True) # AI建议
|
||||
ai_criteria_details = models.JSONField(default=dict) # 详细评分标准
|
||||
|
||||
|
||||
# 处理信息
|
||||
ai_provider = models.CharField(max_length=50, default='deepseek')
|
||||
ai_model = models.CharField(max_length=50, default='deepseek-v4-flash')
|
||||
ai_provider = models.CharField(max_length=50, default="deepseek")
|
||||
ai_model = models.CharField(max_length=50, default="deepseek-v4-flash")
|
||||
processing_time = models.FloatField(null=True, blank=True) # AI处理耗时(秒)
|
||||
evaluation_time = models.DateTimeField(null=True, blank=True) # 评分完成时间
|
||||
|
||||
|
||||
evaluation_time = models.DateTimeField(null=True, blank=True) # 评分完成时间
|
||||
|
||||
class Meta:
|
||||
db_table = 'flowchart_submission'
|
||||
ordering = ['-create_time']
|
||||
db_table = "flowchart_submission"
|
||||
ordering = ["-create_time"]
|
||||
indexes = [
|
||||
models.Index(fields=['user', 'create_time'], name='flowchart_user_time_idx'),
|
||||
models.Index(fields=['problem', 'create_time'], name='flowchart_problem_time_idx'),
|
||||
models.Index(fields=['status'], name='flowchart_status_idx'),
|
||||
models.Index(fields=["user", "create_time"], name="flowchart_user_time_idx"),
|
||||
models.Index(fields=["problem", "create_time"], name="flowchart_problem_time_idx"),
|
||||
models.Index(fields=["status"], name="flowchart_status_idx"),
|
||||
]
|
||||
|
||||
|
||||
def __str__(self):
|
||||
return f"FlowchartSubmission {self.id}"
|
||||
|
||||
|
||||
def check_user_permission(self, user, check_share=True):
|
||||
"""检查用户权限"""
|
||||
if (
|
||||
self.user_id == user.id
|
||||
or not user.is_regular_user()
|
||||
or self.problem.created_by_id == user.id
|
||||
):
|
||||
if self.user_id == user.id or not user.is_regular_user() or self.problem.created_by_id == user.id:
|
||||
return True
|
||||
|
||||
|
||||
return False
|
||||
|
||||
@@ -18,6 +18,7 @@ class CreateFlowchartSubmissionSerializer(serializers.Serializer):
|
||||
|
||||
def validate_flowchart_data(self, value):
|
||||
import json
|
||||
|
||||
if len(json.dumps(value)) > 500 * 1024:
|
||||
raise serializers.ValidationError("流程图数据过大")
|
||||
return value
|
||||
@@ -55,6 +56,7 @@ class FlowchartSubmissionListSerializer(serializers.ModelSerializer):
|
||||
username = serializers.CharField(source="user.username")
|
||||
problem = serializers.CharField(source="problem._id")
|
||||
problem_title = serializers.CharField(source="problem.title")
|
||||
|
||||
class Meta:
|
||||
model = FlowchartSubmission
|
||||
fields = [
|
||||
|
||||
@@ -20,16 +20,16 @@ def evaluate_flowchart_task(submission_id):
|
||||
submission = None
|
||||
try:
|
||||
submission = FlowchartSubmission.objects.get(id=submission_id)
|
||||
|
||||
|
||||
# 更新状态为处理中
|
||||
submission.status = FlowchartSubmissionStatus.PROCESSING
|
||||
submission.save()
|
||||
|
||||
|
||||
start_time = time.time()
|
||||
|
||||
|
||||
# 使用固定评分标准
|
||||
system_prompt = build_evaluation_prompt(submission.problem)
|
||||
|
||||
|
||||
# 构建用户提示词,包含标准答案对比
|
||||
user_prompt = f"""
|
||||
请对以下Mermaid流程图进行评分:
|
||||
@@ -53,16 +53,13 @@ def evaluate_flowchart_task(submission_id):
|
||||
user_prompt += f"\n设计提示:{submission.problem.flowchart_hint}\n"
|
||||
|
||||
user_prompt += "\n请按照评分标准进行详细评估,并给出0-100的分数。\n"
|
||||
|
||||
|
||||
# 调用AI进行评分
|
||||
client = get_ai_client()
|
||||
|
||||
|
||||
response = client.chat.completions.create(
|
||||
model="deepseek-v4-flash",
|
||||
messages=[
|
||||
{"role": "system", "content": system_prompt},
|
||||
{"role": "user", "content": user_prompt}
|
||||
],
|
||||
messages=[{"role": "system", "content": system_prompt}, {"role": "user", "content": user_prompt}],
|
||||
temperature=0,
|
||||
extra_body={"thinking": {"type": "disabled"}},
|
||||
)
|
||||
@@ -74,30 +71,31 @@ def evaluate_flowchart_task(submission_id):
|
||||
|
||||
# 保存评分结果
|
||||
with transaction.atomic():
|
||||
submission.ai_score = score_data['score']
|
||||
submission.ai_grade = score_data['grade']
|
||||
submission.ai_feedback = score_data['feedback']
|
||||
submission.ai_suggestions = score_data.get('suggestions', '')
|
||||
submission.ai_criteria_details = score_data.get('criteria_details', {})
|
||||
submission.ai_provider = 'deepseek'
|
||||
submission.ai_model = 'deepseek-v4-flash'
|
||||
submission.ai_score = score_data["score"]
|
||||
submission.ai_grade = score_data["grade"]
|
||||
submission.ai_feedback = score_data["feedback"]
|
||||
submission.ai_suggestions = score_data.get("suggestions", "")
|
||||
submission.ai_criteria_details = score_data.get("criteria_details", {})
|
||||
submission.ai_provider = "deepseek"
|
||||
submission.ai_model = "deepseek-v4-flash"
|
||||
submission.processing_time = processing_time
|
||||
submission.status = FlowchartSubmissionStatus.COMPLETED
|
||||
submission.evaluation_time = timezone.now()
|
||||
submission.save()
|
||||
|
||||
|
||||
# 推送评分完成通知
|
||||
from utils.websocket import push_flowchart_evaluation_update
|
||||
|
||||
push_flowchart_evaluation_update(
|
||||
submission_id=str(submission.id),
|
||||
user_id=submission.user_id,
|
||||
data={
|
||||
"type": "flowchart_evaluation_completed",
|
||||
"score": score_data['score'],
|
||||
"grade": score_data['grade'],
|
||||
}
|
||||
"score": score_data["score"],
|
||||
"grade": score_data["grade"],
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
except Exception as e:
|
||||
logger.exception("evaluate_flowchart_task failed for submission %s", submission_id)
|
||||
if submission is not None:
|
||||
@@ -105,6 +103,7 @@ def evaluate_flowchart_task(submission_id):
|
||||
submission.save()
|
||||
|
||||
from utils.websocket import push_flowchart_evaluation_update
|
||||
|
||||
push_flowchart_evaluation_update(
|
||||
submission_id=str(submission.id),
|
||||
user_id=submission.user_id,
|
||||
@@ -116,9 +115,10 @@ def evaluate_flowchart_task(submission_id):
|
||||
)
|
||||
raise e
|
||||
|
||||
|
||||
def build_evaluation_prompt(problem):
|
||||
"""构建AI评分提示词 - 使用固定标准"""
|
||||
|
||||
|
||||
# 使用固定的评分标准
|
||||
criteria_text = """
|
||||
- 逻辑正确性 (权重: 1.0, 最高分: 40): 检查流程图的逻辑是否正确,包括条件判断、循环结构等
|
||||
@@ -126,7 +126,7 @@ def build_evaluation_prompt(problem):
|
||||
- 规范性 (权重: 0.6, 最高分: 20): 检查流程图符号使用是否规范,是否符合标准;不要评价节点ID
|
||||
- 清晰度 (权重: 0.4, 最高分: 10): 评估流程图的整体布局和连线情况;不要因节点ID扣分
|
||||
"""
|
||||
|
||||
|
||||
return f"""
|
||||
你是一个专业的编程教学助手,负责评估学生提交的Mermaid流程图。
|
||||
|
||||
@@ -166,15 +166,17 @@ def build_evaluation_prompt(problem):
|
||||
}}
|
||||
"""
|
||||
|
||||
|
||||
def parse_ai_evaluation_response(ai_response):
|
||||
"""解析AI评分响应,解析失败时抛出异常由调用方处理"""
|
||||
import re
|
||||
|
||||
# 优先匹配代码块中的 JSON,避免贪婪匹配误抓 reasoning 段落
|
||||
code_block = re.search(r'```(?:json)?\s*(\{[\s\S]*?\})\s*```', ai_response, re.DOTALL)
|
||||
code_block = re.search(r"```(?:json)?\s*(\{[\s\S]*?\})\s*```", ai_response, re.DOTALL)
|
||||
if code_block:
|
||||
json_str = code_block.group(1)
|
||||
else:
|
||||
json_match = re.search(r'\{.*\}', ai_response, re.DOTALL)
|
||||
json_match = re.search(r"\{.*\}", ai_response, re.DOTALL)
|
||||
if not json_match:
|
||||
raise ValueError("AI响应中未找到JSON数据")
|
||||
json_str = json_match.group()
|
||||
|
||||
@@ -9,9 +9,9 @@ from ..views.oj import (
|
||||
)
|
||||
|
||||
urlpatterns = [
|
||||
path('flowchart/submission', FlowchartSubmissionAPI.as_view()),
|
||||
path('flowchart/submissions', FlowchartSubmissionListAPI.as_view()),
|
||||
path('flowchart/submission/retry', FlowchartSubmissionRetryAPI.as_view()),
|
||||
path('flowchart/submission/detail', FlowchartSubmissionDetailAPI.as_view()),
|
||||
path('flowchart/submission/current', FlowchartSubmissionCurrentAPI.as_view()),
|
||||
path("flowchart/submission", FlowchartSubmissionAPI.as_view()),
|
||||
path("flowchart/submissions", FlowchartSubmissionListAPI.as_view()),
|
||||
path("flowchart/submission/retry", FlowchartSubmissionRetryAPI.as_view()),
|
||||
path("flowchart/submission/detail", FlowchartSubmissionDetailAPI.as_view()),
|
||||
path("flowchart/submission/current", FlowchartSubmissionCurrentAPI.as_view()),
|
||||
]
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
|
||||
# Create your views here.
|
||||
|
||||
@@ -20,16 +20,44 @@ STOPWORDS = frozenset(
|
||||
)
|
||||
|
||||
CUSTOM_WORDS = [
|
||||
"循环结构", "条件判断", "判断条件", "结束条件", "循环条件",
|
||||
"异常处理", "边界条件", "输入输出", "输入验证",
|
||||
"开始结束", "结束节点", "开始节点", "判断节点",
|
||||
"流程走向", "逻辑错误", "逻辑缺陷", "逻辑不清",
|
||||
"缺少分支", "缺少步骤", "缺少判断", "缺少循环",
|
||||
"死循环", "无限循环", "循环出口", "循环体",
|
||||
"条件分支", "分支结构", "分支不全", "分支缺失",
|
||||
"符号使用", "符号不规范", "连线混乱",
|
||||
"变量初始化", "赋值操作", "累加操作",
|
||||
"终止条件", "退出条件", "返回值",
|
||||
"循环结构",
|
||||
"条件判断",
|
||||
"判断条件",
|
||||
"结束条件",
|
||||
"循环条件",
|
||||
"异常处理",
|
||||
"边界条件",
|
||||
"输入输出",
|
||||
"输入验证",
|
||||
"开始结束",
|
||||
"结束节点",
|
||||
"开始节点",
|
||||
"判断节点",
|
||||
"流程走向",
|
||||
"逻辑错误",
|
||||
"逻辑缺陷",
|
||||
"逻辑不清",
|
||||
"缺少分支",
|
||||
"缺少步骤",
|
||||
"缺少判断",
|
||||
"缺少循环",
|
||||
"死循环",
|
||||
"无限循环",
|
||||
"循环出口",
|
||||
"循环体",
|
||||
"条件分支",
|
||||
"分支结构",
|
||||
"分支不全",
|
||||
"分支缺失",
|
||||
"符号使用",
|
||||
"符号不规范",
|
||||
"连线混乱",
|
||||
"变量初始化",
|
||||
"赋值操作",
|
||||
"累加操作",
|
||||
"终止条件",
|
||||
"退出条件",
|
||||
"返回值",
|
||||
]
|
||||
|
||||
for _w in CUSTOM_WORDS:
|
||||
@@ -38,7 +66,7 @@ for _w in CUSTOM_WORDS:
|
||||
|
||||
def get_real_name(username, class_name):
|
||||
if class_name and username.startswith("ks"):
|
||||
return username[len(f"ks{class_name}"):]
|
||||
return username[len(f"ks{class_name}") :]
|
||||
return username
|
||||
|
||||
|
||||
@@ -63,9 +91,7 @@ class FlowchartStatisticsAPI(APIView):
|
||||
problem_id = request.GET.get("problem_id")
|
||||
if problem_id:
|
||||
try:
|
||||
problem = Problem.objects.get(
|
||||
_id__iexact=problem_id, contest_id__isnull=True, visible=True
|
||||
)
|
||||
problem = Problem.objects.get(_id__iexact=problem_id, contest_id__isnull=True, visible=True)
|
||||
except Problem.DoesNotExist:
|
||||
return self.error("Problem doesn't exist")
|
||||
submissions = submissions.filter(problem=problem)
|
||||
@@ -85,23 +111,21 @@ class FlowchartStatisticsAPI(APIView):
|
||||
|
||||
total_count = submissions.count()
|
||||
if total_count == 0:
|
||||
return self.success({
|
||||
"total_count": 0,
|
||||
"avg_score": 0,
|
||||
"grade_distribution": {},
|
||||
"criteria_averages": {},
|
||||
"person_count": len(all_users_dict),
|
||||
"completed_count": 0,
|
||||
"word_frequencies": [],
|
||||
"data_unaccepted": [],
|
||||
})
|
||||
return self.success(
|
||||
{
|
||||
"total_count": 0,
|
||||
"avg_score": 0,
|
||||
"grade_distribution": {},
|
||||
"criteria_averages": {},
|
||||
"person_count": len(all_users_dict),
|
||||
"completed_count": 0,
|
||||
"word_frequencies": [],
|
||||
"data_unaccepted": [],
|
||||
}
|
||||
)
|
||||
|
||||
# 1. Grade distribution
|
||||
grade_counts = dict(
|
||||
submissions.values_list("ai_grade")
|
||||
.annotate(count=Count("id"))
|
||||
.values_list("ai_grade", "count")
|
||||
)
|
||||
grade_counts = dict(submissions.values_list("ai_grade").annotate(count=Count("id")).values_list("ai_grade", "count"))
|
||||
|
||||
# 2. Average score
|
||||
avg_score = submissions.aggregate(avg=Avg("ai_score"))["avg"] or 0
|
||||
@@ -113,9 +137,7 @@ class FlowchartStatisticsAPI(APIView):
|
||||
|
||||
wordcloud_texts = []
|
||||
|
||||
for row in submissions.values_list(
|
||||
"ai_criteria_details", "ai_feedback", "ai_suggestions"
|
||||
).iterator():
|
||||
for row in submissions.values_list("ai_criteria_details", "ai_feedback", "ai_suggestions").iterator():
|
||||
details, feedback, suggestions = row
|
||||
if details and isinstance(details, dict):
|
||||
for key, val in details.items():
|
||||
@@ -139,9 +161,7 @@ class FlowchartStatisticsAPI(APIView):
|
||||
}
|
||||
|
||||
# 4. Completion stats
|
||||
submitted_users = set(
|
||||
submissions.values_list("user__username", flat=True).distinct()
|
||||
)
|
||||
submitted_users = set(submissions.values_list("user__username", flat=True).distinct())
|
||||
completed_count = len(submitted_users)
|
||||
|
||||
# Unaccepted users
|
||||
@@ -155,16 +175,18 @@ class FlowchartStatisticsAPI(APIView):
|
||||
# 5. Word cloud from feedback + suggestions + criteria comments
|
||||
word_freq = self._build_word_frequencies(wordcloud_texts)
|
||||
|
||||
return self.success({
|
||||
"total_count": total_count,
|
||||
"avg_score": round(avg_score, 1),
|
||||
"grade_distribution": grade_counts,
|
||||
"criteria_averages": criteria_averages,
|
||||
"person_count": len(all_users_dict),
|
||||
"completed_count": completed_count,
|
||||
"word_frequencies": word_freq,
|
||||
"data_unaccepted": unaccepted,
|
||||
})
|
||||
return self.success(
|
||||
{
|
||||
"total_count": total_count,
|
||||
"avg_score": round(avg_score, 1),
|
||||
"grade_distribution": grade_counts,
|
||||
"criteria_averages": criteria_averages,
|
||||
"person_count": len(all_users_dict),
|
||||
"completed_count": completed_count,
|
||||
"word_frequencies": word_freq,
|
||||
"data_unaccepted": unaccepted,
|
||||
}
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _build_word_frequencies(texts, top_n=80):
|
||||
|
||||
@@ -47,11 +47,7 @@ class FlowchartSubmissionAPI(AsyncAPIView):
|
||||
return self.error("submission_id is required")
|
||||
|
||||
try:
|
||||
submission = await (
|
||||
FlowchartSubmission.objects.select_related("user", "problem")
|
||||
.filter(id=submission_id)
|
||||
.afirst()
|
||||
)
|
||||
submission = await FlowchartSubmission.objects.select_related("user", "problem").filter(id=submission_id).afirst()
|
||||
if submission is None:
|
||||
raise FlowchartSubmission.DoesNotExist
|
||||
except FlowchartSubmission.DoesNotExist:
|
||||
@@ -74,9 +70,7 @@ class FlowchartSubmissionListAPI(AsyncAPIView):
|
||||
|
||||
if problem_id:
|
||||
try:
|
||||
problem = await Problem.objects.aget(
|
||||
_id__iexact=problem_id, contest_id__isnull=True, visible=True
|
||||
)
|
||||
problem = await Problem.objects.aget(_id__iexact=problem_id, contest_id__isnull=True, visible=True)
|
||||
except Problem.DoesNotExist:
|
||||
return self.error("Problem doesn't exist")
|
||||
queryset = queryset.filter(problem=problem)
|
||||
@@ -90,9 +84,7 @@ class FlowchartSubmissionListAPI(AsyncAPIView):
|
||||
|
||||
if request.GET.get("today") == "1":
|
||||
now = timezone.now()
|
||||
queryset = queryset.filter(
|
||||
create_time__gte=now.replace(hour=0, minute=0, second=0, microsecond=0)
|
||||
)
|
||||
queryset = queryset.filter(create_time__gte=now.replace(hour=0, minute=0, second=0, microsecond=0))
|
||||
|
||||
grade = request.GET.get("grade")
|
||||
if grade in ("S", "A", "B", "C"):
|
||||
@@ -115,11 +107,7 @@ class FlowchartSubmissionRetryAPI(AsyncAPIView):
|
||||
return self.error("submission_id is required")
|
||||
|
||||
try:
|
||||
submission = await (
|
||||
FlowchartSubmission.objects.select_related("problem")
|
||||
.filter(id=submission_id)
|
||||
.afirst()
|
||||
)
|
||||
submission = await FlowchartSubmission.objects.select_related("problem").filter(id=submission_id).afirst()
|
||||
if submission is None:
|
||||
raise FlowchartSubmission.DoesNotExist
|
||||
except FlowchartSubmission.DoesNotExist:
|
||||
@@ -187,7 +175,7 @@ class FlowchartSubmissionDetailAPI(AsyncAPIView):
|
||||
else:
|
||||
if page < 0 or page > count:
|
||||
return self.error("Page out of range")
|
||||
result = [s async for s in submissions[page - 1:page]]
|
||||
result = [s async for s in submissions[page - 1 : page]]
|
||||
submission = result[0]
|
||||
data = await self.async_serialize_data(FlowchartSubmissionSerializer, submission)
|
||||
return self.success({"submission": data, "count": count})
|
||||
|
||||
Reference in New Issue
Block a user