This commit is contained in:
@@ -177,13 +177,14 @@ class Project(models.Model):
|
||||
def calculate_score(self):
|
||||
"""
|
||||
计算项目得分
|
||||
支持两种模式:
|
||||
支持三种模式:
|
||||
1. 默认加权平均:每个评委的得分 = sum(维度分数 × 维度权重),然后所有评委取平均
|
||||
2. 自定义算式:使用比赛级别的 custom_score_formula 计算最终得分
|
||||
2. 自定义算式(比赛级别):使用比赛级别的 custom_score_formula 计算最终得分
|
||||
3. 公式配置(公式级别):使用 ScoreFormula 模型中的公式配置
|
||||
|
||||
自定义算式变量格式:
|
||||
- dimension_X: 第X个维度的平均分(所有评委对该维度的平均分)
|
||||
- 也可以在算式中直接使用维度ID
|
||||
- 也可以使用维度名称作为变量
|
||||
"""
|
||||
scores = self.scores.all()
|
||||
if not scores.exists():
|
||||
@@ -193,6 +194,15 @@ class Project(models.Model):
|
||||
|
||||
competition = self.competition
|
||||
|
||||
active_formula = ScoreFormula.objects.filter(
|
||||
competition=competition,
|
||||
is_active=True,
|
||||
is_default=True
|
||||
).first()
|
||||
|
||||
if active_formula:
|
||||
return self._calculate_formula_score(scores, active_formula)
|
||||
|
||||
if competition.score_calculation_type == 'custom' and competition.custom_score_formula:
|
||||
return self._calculate_custom_score(scores, competition.custom_score_formula)
|
||||
|
||||
@@ -227,7 +237,7 @@ class Project(models.Model):
|
||||
|
||||
def _calculate_custom_score(self, scores, formula):
|
||||
"""
|
||||
自定义算式模式
|
||||
自定义算式模式(比赛级别)
|
||||
使用比赛配置的自定义算式计算得分
|
||||
"""
|
||||
dimension_scores = {}
|
||||
@@ -238,6 +248,7 @@ class Project(models.Model):
|
||||
if dim_scores.exists():
|
||||
avg = sum(float(s.score) for s in dim_scores) / dim_scores.count()
|
||||
dimension_scores[f'dimension_{dimension.id}'] = avg
|
||||
dimension_scores[dimension.name] = avg
|
||||
|
||||
if not dimension_scores:
|
||||
self.final_score = 0
|
||||
@@ -254,6 +265,37 @@ class Project(models.Model):
|
||||
print(f"算式计算错误: {e}, formula: {formula}, values: {dimension_scores}")
|
||||
return self._calculate_default_score(scores)
|
||||
|
||||
def _calculate_formula_score(self, scores, formula_obj):
|
||||
"""
|
||||
公式配置模式(使用 ScoreFormula 模型)
|
||||
使用公式配置中的公式计算得分
|
||||
"""
|
||||
dimension_scores = {}
|
||||
|
||||
dimensions = self.competition.score_dimensions.all()
|
||||
for dimension in dimensions:
|
||||
dim_scores = scores.filter(dimension=dimension)
|
||||
if dim_scores.exists():
|
||||
avg = sum(float(s.score) for s in dim_scores) / dim_scores.count()
|
||||
dimension_scores[dimension.name] = avg
|
||||
|
||||
if not dimension_scores:
|
||||
self.final_score = 0
|
||||
self.save()
|
||||
return 0
|
||||
|
||||
formula = formula_obj.formula
|
||||
|
||||
try:
|
||||
result = eval(formula, {"__builtins__": {}}, dimension_scores)
|
||||
final_score = float(result)
|
||||
self.final_score = round(final_score, 2)
|
||||
self.save()
|
||||
return self.final_score
|
||||
except Exception as e:
|
||||
print(f"公式计算错误: {e}, formula: {formula}, values: {dimension_scores}")
|
||||
return self._calculate_default_score(scores)
|
||||
|
||||
def calculate_judge_score(self, judge):
|
||||
"""
|
||||
计算指定评委对该项目的得分
|
||||
@@ -346,3 +388,70 @@ class Comment(models.Model):
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.judge.user.nickname} -> {self.project.title}"
|
||||
|
||||
|
||||
class ScoreFormula(models.Model):
|
||||
"""
|
||||
评分公式配置
|
||||
用于可视化配置得分计算公式
|
||||
"""
|
||||
competition = models.ForeignKey(Competition, on_delete=models.CASCADE, related_name='score_formulas', verbose_name="所属比赛")
|
||||
name = models.CharField(max_length=100, verbose_name="公式名称", help_text="用于标识这个公式,方便管理")
|
||||
description = models.TextField(verbose_name="公式说明", blank=True)
|
||||
|
||||
formula = models.TextField(verbose_name="计算公式", help_text="使用维度名称作为变量,支持四则运算和函数")
|
||||
|
||||
is_active = models.BooleanField(default=True, verbose_name="是否启用")
|
||||
is_default = models.BooleanField(default=False, verbose_name="是否设为默认公式")
|
||||
|
||||
created_at = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
|
||||
updated_at = models.DateTimeField(auto_now=True, verbose_name="更新时间")
|
||||
|
||||
class Meta:
|
||||
verbose_name = "评分公式"
|
||||
verbose_name_plural = "评分公式配置"
|
||||
ordering = ['-is_default', '-created_at']
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.competition.title} - {self.name}"
|
||||
|
||||
def get_formula_preview(self):
|
||||
"""
|
||||
获取公式预览,将维度变量替换为维度名称
|
||||
"""
|
||||
if not self.formula:
|
||||
return ""
|
||||
|
||||
dimension_map = {f'd["{d.name}"]': f'[{d.name}]' for d in self.competition.score_dimensions.all()}
|
||||
dimension_map.update({f"d['{d.name}']": f'[{d.name}]' for d in self.competition.score_dimensions.all()})
|
||||
|
||||
result = self.formula
|
||||
for old, new in dimension_map.items():
|
||||
result = result.replace(old, new)
|
||||
|
||||
return result
|
||||
|
||||
def generate_python_code(self):
|
||||
"""
|
||||
生成可执行的 Python 代码
|
||||
"""
|
||||
if not self.formula:
|
||||
return ""
|
||||
|
||||
dimension_names = [d.name for d in self.competition.score_dimensions.all()]
|
||||
|
||||
code_lines = [
|
||||
"def calculate_score(d):",
|
||||
" '''",
|
||||
f" 计算公式: {self.name}",
|
||||
" 参数 d: 字典,键为维度名称,值为该维度的平均分",
|
||||
" '''",
|
||||
]
|
||||
|
||||
for name in dimension_names:
|
||||
code_lines.append(f" {name} = d.get('{name}', 0)")
|
||||
|
||||
code_lines.append("")
|
||||
code_lines.append(f" return {self.formula}")
|
||||
|
||||
return "\n".join(code_lines)
|
||||
|
||||
Reference in New Issue
Block a user