Skip to content

Commit

Permalink
Merge pull request #1317 from Amsterdam-Music-Lab/feature/rhythm-play…
Browse files Browse the repository at this point in the history
…list-validation

Add `validate_playlist` method for rhythm experiments
  • Loading branch information
BeritJanssen authored Oct 22, 2024
2 parents 639f5af + e87bd3a commit bca864a
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 2 deletions.
3 changes: 2 additions & 1 deletion backend/experiment/rules/anisochrony.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class Anisochrony(DurationDiscrimination):
practice_diff = 270000
max_turnpoints = 8
catch_condition = 'REGULAR'
section_count = 249

def get_response_explainer(self, correct, correct_response, button_label=_('Next fragment')):
correct_response = _('REGULAR') if correct_response=='REGULAR' else _('IRREGULAR')
Expand Down Expand Up @@ -64,7 +65,7 @@ def next_trial_action(self, session, trial_condition, difficulty):
result_id=prepare_result(key, session, section=section, expected_response=expected_response),
submits=True
)

playback = Autoplay([section])
form = Form([question])
config = {
Expand Down
32 changes: 32 additions & 0 deletions backend/experiment/rules/beat_alignment.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from experiment.actions.playback import Autoplay
from experiment.actions.utils import final_action_with_optional_button, render_feedback_trivia
from result.utils import prepare_result
from section.models import Playlist

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -139,3 +140,34 @@ def next_trial_action(self, session):
}
)
return view

def validate_playlist(self, playlist: Playlist):
errors = []
errors += super().validate_playlist(playlist)
sections = playlist.section_set.all()
n_examples = sections.filter(song__name__startswith="ex").count()
if n_examples != 3:
errors.append(
"There should be three example files, with associated song objects whose names start with `ex`"
)
trial_stimuli = sections.exclude(song__name__startswith="ex")
if trial_stimuli.count() != 17:
errors.append("There should be 17 files to be played during the experiment")
song_names = trial_stimuli.values_list("song__name", flat=True)
try:
groups, tags = zip(*[s.split("_") for s in song_names])
try:
[int(g) for g in groups]
except:
errors.append("The first part of the song name should be an integer")
if len(list(set(groups))) != 9:
errors.append("There should be 9 different audio files")
if sorted(list(set(tags))) != ["on", "phase", "tempo"]:
errors.append(
"The sections should have song names which contain condition on, phase or tempo"
)
except:
errors.append(
"The sections should have song names with an integer, followed by a condition"
)
return errors
21 changes: 21 additions & 0 deletions backend/experiment/rules/duration_discrimination.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from experiment.rules.util.practice import get_trial_condition_block, get_practice_views
from experiment.rules.util.staircasing import register_turnpoint
from result.utils import prepare_result
from section.models import Playlist
from session.models import Session

logger = logging.getLogger(__name__)
Expand All @@ -28,6 +29,7 @@ class DurationDiscrimination(Base):
max_turnpoints = 8
catch_condition = 'EQUAL'
block_size = 5
section_count = 247
increase_difficulty_multiplier = .5
decrease_difficulty_multiplier = 1.5

Expand Down Expand Up @@ -306,3 +308,22 @@ def last_non_catch_correct(self, previous_results):
else:
break
return answer

def validate_playlist(self, playlist: Playlist):
errors = []
errors += super().validate_playlist(playlist)
sections = playlist.section_set.all()
if sections.count() is not self.section_count:
errors.append("The playlist should contain 247 sections")
try:
numerical_song_names = [int(section.song_name()) for section in sections]
if self.start_diff not in numerical_song_names:
errors.append(
f"The file for the starting difference of {self.start_diff} is missing"
)
except:
errors.append(
"The sections should have an associated song with an integer name"
)

return errors
24 changes: 23 additions & 1 deletion backend/experiment/rules/h_bat.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from experiment.actions.utils import get_average_difference_level_based
from experiment.rules.util.staircasing import register_turnpoint
from result.utils import prepare_result
from section.models import Section
from section.models import Playlist, Section
from session.models import Session

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -164,6 +164,28 @@ def get_trivia(self):
return _("When people listen to music, they often perceive an underlying regular pulse, like the woodblock \
in this task. This allows us to clap along with the music at a concert and dance together in synchrony.")

def validate_playlist(self, playlist: Playlist):
errors = []
errors += super().validate_playlist(playlist)
sections = playlist.section_set.all()
if sections.count() != 32:
errors.append("This block should have a playlist with 32 sections")
groups, tags = zip(*[(s.group, s.tag) for s in sections])
try:
group_numbers = sorted(list(set([int(g) for g in groups])))
if group_numbers != [*range(1, 17)]:
errors.append("Groups should be ascending integers from 1 to 16")
except:
errors.append("The groups should be integers")
try:
tag_numbers = sorted(list(set([int(t) for t in tags])))
if tag_numbers != [0, 1]:
errors.append("Tags should be 0 and 1")
except:
errors.append("Tags should be integers")

return errors


def get_previous_condition(previous_result):
""" check if previous section was slower / in 2 (1) or faster / in 3 (0) """
Expand Down
45 changes: 45 additions & 0 deletions backend/experiment/rules/rhythm_discrimination.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from experiment.actions.form import ChoiceQuestion, Form

from result.utils import prepare_result
from section.models import Playlist

from .base import Base

Expand Down Expand Up @@ -90,6 +91,50 @@ def next_round(self, session):
return next_trial_actions(
session, next_round_number)

def validate_playlist(self, playlist: Playlist):
errors = []
errors += super().validate_playlist(playlist)
sections = playlist.section_set.all()
if not sections.count():
return errors
if sections.count() != 720:
errors.append("The block needs a playlist with 720 sections")
tags, groups = zip(*[(s.tag, s.group) for s in sections])
try:
tag_numbers = sorted(list(set([int(t) for t in tags])))
if tag_numbers != [150, 160, 170, 180, 190, 200]:
errors.append("Tags should have values 150, 160, 170, 180, 190, 200")
except:
errors.append("The sections should have integer tags")
try:
group_numbers = sorted(list(set([int(g) for g in groups])))
if group_numbers != [0, 1]:
errors.append("Groups should have values 0, 1")
except:
errors.append("The sections should have integer groups")

def pattern_error(pattern: str) -> str:
return f"There should be 12 sections with pattern {pattern}"

metric_standard = STIMULI["metric"]["standard"]
for m in metric_standard:
if sections.filter(song__name__startswith=m).count() != 12:
errors.append(pattern_error(m))
metric_deviant = STIMULI["metric"]["deviant"]
for m in metric_deviant:
if sections.filter(song__name__startswith=m).count() != 12:
errors.append(pattern_error(m))
nonmetric_standard = STIMULI["nonmetric"]["standard"]
for n in nonmetric_standard:
if sections.filter(song__name__startswith=n).count() != 12:
errors.append(pattern_error(n))
nonmetric_deviant = STIMULI["nonmetric"]["deviant"]
for n in nonmetric_deviant:
if sections.filter(song__name__startswith=n).count() != 12:
errors.append(pattern_error(n))

return errors


def next_trial_actions(session, round_number):
"""
Expand Down

0 comments on commit bca864a

Please sign in to comment.