Skip to content

Commit

Permalink
[back][front] feat: support patching score and score max of criteria (#…
Browse files Browse the repository at this point in the history
…1947)


Co-authored-by: Adrien Matissart <amatissart@users.noreply.github.com>
  • Loading branch information
GresilleSiffle and amatissart authored May 2, 2024
1 parent 9ad2f33 commit f92145b
Show file tree
Hide file tree
Showing 24 changed files with 643 additions and 112 deletions.
6 changes: 5 additions & 1 deletion backend/ml/inputs.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ def get_comparisons(self, criteria=None, user_id=None) -> pd.DataFrame:

values = scores_queryset.values(
"score",
"score_max",
"criteria",
"weight",
entity_a=F("comparison__entity_1_id"),
Expand All @@ -78,7 +79,9 @@ def get_comparisons(self, criteria=None, user_id=None) -> pd.DataFrame:
)
if len(values) > 0:
dtf = pd.DataFrame(values)
return dtf[["user_id", "entity_a", "entity_b", "criteria", "score", "weight"]]
return dtf[
["user_id", "entity_a", "entity_b", "criteria", "score", "score_max", "weight"]
]

return pd.DataFrame(
columns=[
Expand All @@ -87,6 +90,7 @@ def get_comparisons(self, criteria=None, user_id=None) -> pd.DataFrame:
"entity_b",
"criteria",
"score",
"score_max",
"weight",
]
)
Expand Down
3 changes: 1 addition & 2 deletions backend/ml/mehestan/parameters.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
from solidago.pipeline.legacy2023.parameters import PipelineParameters

from tournesol.utils.constants import COMPARISON_MAX, MEHESTAN_MAX_SCALED_SCORE
from tournesol.utils.constants import MEHESTAN_MAX_SCALED_SCORE


class MehestanParameters(PipelineParameters):
r_max = COMPARISON_MAX
max_squashed_score = MEHESTAN_MAX_SCALED_SCORE
12 changes: 11 additions & 1 deletion backend/tournesol/lib/public_dataset.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ def get_comparisons_data(poll_name: str, until_: datetime) -> QuerySet:
comparisoncriteriascore.criteria,
comparisoncriteriascore.weight,
comparisoncriteriascore.score,
comparisoncriteriascore.score_max,
DATE(DATE_TRUNC('week', datetime_add)) AS week_date
FROM tournesol_comparison
Expand Down Expand Up @@ -283,7 +284,15 @@ def write_comparisons_file(

# If we want this function to be generic, the specific video_a and video_b
# columns should be renamed entity_a and entity_b.
fieldnames = ["public_username", "video_a", "video_b", "criteria", "score", "week_date"]
fieldnames = [
"public_username",
"video_a",
"video_b",
"criteria",
"score",
"score_max",
"week_date"
]
writer = csv.DictWriter(write_target, fieldnames=fieldnames)
writer.writeheader()
writer.writerows(
Expand All @@ -293,6 +302,7 @@ def write_comparisons_file(
"video_b": comparison.uid_b.split(UID_DELIMITER)[1],
"criteria": comparison.criteria,
"score": int(round(comparison.score)),
"score_max": comparison.score_max,
"week_date": comparison.week_date,
}
for comparison in get_comparisons_data(poll_name, until_).iterator()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ def handle(self, *args, **options):
comparison=comparison,
criteria=values["criteria"],
score=values["score"],
score_max=values["score_max"],
)
nb_comparisons += 1
print(f"Created {nb_comparisons} comparisons")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Generated by Django 4.2.11 on 2024-05-02 09:05

import django.core.validators
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("tournesol", "0060_remove_entity_tournesol_score"),
]

operations = [
migrations.AddField(
model_name="comparisoncriteriascore",
name="score_max",
field=models.IntegerField(
default=10,
help_text="The absolute value of the maximum score.",
validators=[django.core.validators.MinValueValidator(1)],
),
preserve_default=False,
),
migrations.AlterField(
model_name="comparisoncriteriascore",
name="score",
field=models.FloatField(help_text="Score for the given comparison"),
),
]
22 changes: 19 additions & 3 deletions backend/tournesol/models/comparisons.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,11 @@
import uuid

import computed_property
from django.core.validators import MaxValueValidator, MinValueValidator
from django.core.validators import MinValueValidator
from django.db import models
from django.db.models import F, ObjectDoesNotExist, Q

from core.models import User
from tournesol.utils.constants import COMPARISON_MAX

from .poll import Poll

Expand Down Expand Up @@ -129,7 +128,10 @@ class ComparisonCriteriaScore(models.Model):
)
score = models.FloatField(
help_text="Score for the given comparison",
validators=[MinValueValidator(-COMPARISON_MAX), MaxValueValidator(COMPARISON_MAX)],
)
score_max = models.IntegerField(
help_text="The absolute value of the maximum score.",
validators=[MinValueValidator(1)],
)
# TODO: ask Lê if weights should be in a certain range (maybe always > 0)
# and add validation if required
Expand All @@ -144,3 +146,17 @@ class Meta:

def __str__(self):
return f"{self.comparison}/{self.criteria}/{self.score}"

def save(self, *args, **kwargs):
if self.score_max is None:
raise TypeError("The value of score_max cannot be None.")

if self.score_max <= 0:
raise ValueError("The value of score_max must be greater than or equal to 1.")

if abs(self.score) > self.score_max:
raise ValueError(
f"The absolute value of the score {self.score} given to the criterion "
f"{self.criteria} can't be greater than the value of score_max {self.score_max}."
)
return super().save(*args, **kwargs)
13 changes: 9 additions & 4 deletions backend/tournesol/resources/export_readme.txt
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,15 @@ List of columns:

- score:

The score is an integer between -10 and +10. Negative values indicate that
the user considers the video_a better, and positive values indicate that
they prefer the video_b. A score of 0 or close to 0 means that they find the
two videos similar.
The score is an integer in the range [-score_max, +score_max]. Negative
values indicate that the user considers the video_a better, and positive
values indicate that they prefer the video_b. A score of 0 or close to 0
means that they find the two videos similar.

- score_max:

The absolute value of the minimum and maximum score. 10 means the user could
have given a score between -10 and +10 for the criterion.

- week_date:

Expand Down
32 changes: 23 additions & 9 deletions backend/tournesol/serializers/comparison.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
class ComparisonCriteriaScoreSerializer(ModelSerializer):
class Meta:
model = ComparisonCriteriaScore
fields = ["criteria", "score", "weight"]
fields = ["criteria", "score", "score_max", "weight"]

def validate_criteria(self, value):
current_poll = self.context["poll"]
Expand All @@ -37,11 +37,15 @@ def reverse_criteria_scores(self, criteria_scores):

def validate_criteria_scores(self, value):
current_poll = self.context["poll"]
missing_criterias = set(current_poll.required_criterias_list) - set(
score["criteria"] for score in value
)
if missing_criterias:
raise ValidationError(f"Missing required criteria: {','.join(missing_criterias)}")
partial_update = self.context.get("partial_update")

if not partial_update:
missing_criterias = set(current_poll.required_criterias_list) - set(
score["criteria"] for score in value
)
if missing_criterias:
raise ValidationError(f"Missing required criteria: {','.join(missing_criterias)}")

return value


Expand Down Expand Up @@ -193,9 +197,19 @@ def update(self, instance, validated_data):
instance.duration_ms = validated_data.get("duration_ms")

instance.save()
instance.criteria_scores.all().delete()

for criteria_score in validated_data.pop("criteria_scores"):
instance.criteria_scores.create(**criteria_score)
partial_update = self.context.get("partial_update")

if partial_update:
for criteria_score in validated_data.pop("criteria_scores"):
instance.criteria_scores.update_or_create(
criteria=criteria_score["criteria"],
defaults={**criteria_score}
)
else:
instance.criteria_scores.all().delete()

for criteria_score in validated_data.pop("criteria_scores"):
instance.criteria_scores.create(**criteria_score)

return instance
1 change: 1 addition & 0 deletions backend/tournesol/serializers/inconsistencies.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ class ScoreInconsistencySerializer(Serializer):
entity_1_rating = serializers.FloatField()
entity_2_rating = serializers.FloatField()
comparison_score = serializers.FloatField()
comparison_score_max = serializers.IntegerField()
expected_comparison_score = serializers.FloatField()


Expand Down
1 change: 1 addition & 0 deletions backend/tournesol/tests/factories/comparison.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,4 @@ class Meta:
comparison = factory.SubFactory(ComparisonFactory)
criteria = "better_habits"
score = fuzzy.FuzzyDecimal(-10, 10)
score_max = 10
Loading

0 comments on commit f92145b

Please sign in to comment.