Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Docs: Enhance type annotations and documentation for Score action #1439

Merged
merged 1 commit into from
Dec 23, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 55 additions & 45 deletions backend/experiment/actions/score.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import random
from typing import Optional, TypedDict

from django.utils.translation import gettext as _

Expand All @@ -7,86 +8,92 @@
from .base_action import BaseAction


class ScoreConfig(TypedDict):
"""
Configuration for the Score action

Args:
show_section: whether metadata of the previous section should be shown, often the title of the last played song
show_total_score: whether the total score should be shown in the view
"""

show_section: bool
show_total_score: bool


class Score(BaseAction):
"""
Provide data for a score view, presenting feedback to a participant after a Trial
Relates to client component: Score.ts
Provide data for a score view, presenting feedback to a participant after a Trial.

Args:
session: a Session object
title: the title of the score page
result: the result for which section and/or score should be reported
score: the score to report, will override result.score
session (Session): a Session object
title (str): the title of the score page
result (Optional[Result]): the result for which section and/or score should be reported
score (Optional[float]): the score to report, will override result.score
score_message: a function which constructs feedback text based on the score
config: a dict with the following settings:
- show_section: whether metadata of the previous section should be shown
- show_total_score: whether the total score should be shown
icon: the name of a themify-icon shown with the view or None
timer: int or None. If int, wait for as many seconds until showing the next view
feedback: An additional feedback text
config (Optional[ScoreConfig]): a dict with the following settings:
icon (Optional[str]): the name of a themify-icon shown with the view or None
timer (Optional[int]): int or None. If int, wait for as many seconds until showing the next view
feedback (Optional[str]): An additional feedback text

Note:
Relates to the Score.tsx component in the frontend
"""

ID = 'SCORE'
ID = "SCORE"

def __init__(
self,
session: Session,
title: str = '',
result: Result = None,
score: float = None,
score_message: str = '',
config: dict = {},
icon: str = None,
timer: int = None,
feedback: str = None,
title: str = "",
result: Optional[Result] = None,
score: Optional[float] = None,
score_message: str = "",
config: Optional[ScoreConfig] = None,
icon: Optional[str] = None,
timer: Optional[int] = None,
feedback: Optional[str] = None,
):
self.title = title or _('Round {get_rounds_passed} / {total_rounds}').format(
self.title = title or _("Round {get_rounds_passed} / {total_rounds}").format(
get_rounds_passed=session.get_rounds_passed(),
total_rounds=session.block.rounds,
)
self.session = session
self.score = self.get_score(score, result)
self.score_message = score_message or self.default_score_message(self.score)
self.feedback = feedback
self.config = {
'show_section': False,
'show_total_score': False
}
self.config = {"show_section": False, "show_total_score": False}
if config:
self.config.update(config)
self.icon = icon
self.texts = {
'score': _('Total Score'),
'next': _('Next'),
'listen_explainer': _('You listened to:')
}
self.texts = {"score": _("Total Score"), "next": _("Next"), "listen_explainer": _("You listened to:")}
self.last_song = result.section.song_label() if result else session.last_song()
self.timer = timer

def action(self) -> dict:
"""Serialize score data

Returns:
dictionary with the relevant data for the Score.ts view
dictionary with the relevant data for the Score.tsx view
"""
# Create action
action = {
'view': self.ID,
'title': self.title,
'score': self.score,
'score_message': self.score_message,
'texts': self.texts,
'feedback': self.feedback,
'icon': self.icon,
'timer': self.timer,
"view": self.ID,
"title": self.title,
"score": self.score,
"score_message": self.score_message,
"texts": self.texts,
"feedback": self.feedback,
"icon": self.icon,
"timer": self.timer,
}
if self.config['show_section']:
action['last_song'] = self.last_song
if self.config['show_total_score']:
action['total_score'] = self.session.total_score()
if self.config["show_section"]:
action["last_song"] = self.last_song
if self.config["show_total_score"]:
action["total_score"] = self.session.total_score()
return action

def get_score(self, score: float = None, result: Result = None) -> float:
def get_score(self, score: Optional[float] = None, result: Optional[Result] = None) -> float:
"""Retrieve the last relevant score, fall back to session.last_score() if neither score nor result are defined

Args:
Expand All @@ -105,13 +112,16 @@ def default_score_message(self, score):
# None
if score is None:
score = 0

# Zero
if score == 0:
# "Too bad!", "Come on!", "Try another!", "Try again!"
return random.choice([_("No points")])

# Negative
if score < 0:
return random.choice([_("Incorrect")]) # "Too bad!", "Fail!", "Nooo!"

# Positive
# "Well done!", "Nice job!", "Great one!", "Score!", "You're good!", "Awesome!", "Nice one!"
return random.choice([_("Correct")])
Loading