Skip to content

Commit

Permalink
Merge branch 'develop' into fix/session-creation
Browse files Browse the repository at this point in the history
  • Loading branch information
BeritJanssen committed Feb 14, 2024
2 parents b91158e + 6b0e7b5 commit 7797c30
Show file tree
Hide file tree
Showing 34 changed files with 550 additions and 317 deletions.
3 changes: 1 addition & 2 deletions backend/experiment/management/commands/compileplaylist.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,13 +74,12 @@ def handle(self, *args, **options):
start_position = 0.0
with audioread.audio_open(audio_file) as f:
duration = f.duration
restrict_to_nl = 0
group_tag_option = options.get('tag_group')
if group_tag_option:
group, tag = calculate_group_tag(
filename, group_tag_option, i)
row = [artist_name, song_name,
start_position, duration, filename, restrict_to_nl,
start_position, duration, filename,
tag, group]
csv_writer.writerow(row)

Expand Down
87 changes: 60 additions & 27 deletions backend/experiment/management/commands/templates/experiment.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,46 +2,47 @@
from django.utils.translation import gettext_lazy as _
from django.template.loader import render_to_string

from .experiment.rules import Base
from .experiment.actions import Consent, Explainer, Final, Playlist, Step
from .section.models import Playlist
from .experiment.questions.demographics import EXTRA_DEMOGRAPHICS
from .experiment.questions.utils import question_by_key
from experiment.actions import Consent, BooleanQuestion, Explainer, Final, Form, Playlist, Step, Trial
from experiment.actions.playback import Autoplay
from experiment.questions.demographics import EXTRA_DEMOGRAPHICS
from experiment.questions.utils import question_by_key
from experiment.rules.base import Base
from result.utils import prepare_result


class NewExperiment(Base):
''' An experiment type that could be used to test musical preferences '''
ID = 'NEW_EXPERIMENT'
contact_email = 'info@example.com'

def __init__(self):
super().__init__(self.ID)


# Add your questions here
self.questions = [
question_by_key('dgf_gender_identity'),
question_by_key('dgf_generation'),
question_by_key('dgf_musical_experience', EXTRA_DEMOGRAPHICS),
question_by_key('dgf_country_of_origin'),
question_by_key('dgf_education', drop_choices=['isced-2', 'isced-5'])
question_by_key('dgf_education', drop_choices=[
'isced-2', 'isced-5'])
]

def first_round(self, experiment):

# 1. Informed consent
rendered = render_to_string('consent/consent_new_experiment.html')
# 1. Informed consent (optional)
rendered = render_to_string('consent/consent.html')
consent = Consent(rendered, title=_(
'Informed consent'), confirm=_('I agree'), deny=_('Stop'))

# 2. Choose playlist.
# 2. Choose playlist (only relevant if there are multiple playlists the participant can choose from)
playlist = Playlist(experiment.playlists.all())

# 3. Explainer
# 3. Explainer (optional)
explainer = Explainer(
instruction='',
instruction='Welcome to this new experiment',
steps=[
Step(description=_('New Experiment is ...')),
Step(description=_('Next page of explanation')),
Step(description=_('Another page of explanation')),
Step(description=_('Please read the instructions carefully')),
Step(description=_('Next step of explanation')),
Step(description=_('Another step of explanation')),
],
step_numbers=True
)
Expand All @@ -53,17 +54,49 @@ def first_round(self, experiment):
]

def next_round(self, session):
# ask any questions defined in the admin interface
actions = self.get_questionnaire(session)

if actions:
return actions

return [Final()]

def feedback_info(self):
info = super().feedback_info()
info['header'] = _("Any remarks or questions (optional):")
info['thank_you'] = _("Thank you for your feedback!")

return info

elif session.rounds_complete():
# we have as many results as rounds in this experiment
# finish session and show Final view
session.finish()
session.save()
return [
Final(
session,
final_text=_('Thank you for participating!'),
feedback_info=self.feedback_info() # show feedback bar, this line can be removed
)
]
else:
return self.get_trial(session)

def get_trial(self, session):
# define a key, by which responses to this trial can be found in the database
key = 'test_trial'
# get a random section
section = session.section_from_any_song()
question = BooleanQuestion(
question=_(
"Do you like this song?"),
key=key,
result_id=prepare_result(key, session, section=section),
submits=True
)
form = Form([question])
playback = Autoplay([section])
view = Trial(
playback=playback,
feedback_form=form,
title=_('Test experiment'),
config={
'response_time': section.duration,
# listen_first: whether response buttons will be greyed out during `response_time`
'listen_first': True
}
)
return view

3 changes: 1 addition & 2 deletions backend/experiment/management/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,13 @@ def test_output_csv(self):
filename = join(settings.MEDIA_ROOT,'tests','compileplaylist','audiofiles.csv')
try:
with open(filename) as csv_file:
rows = csv.DictReader(csv_file, fieldnames = ('artist','name','start_time','duration','filename','restrict_to_nl','tag','group'))
rows = csv.DictReader(csv_file, fieldnames = ('artist','name','start_time','duration','filename','tag','group'))
for row in rows:
if row['filename'] == 'tests/compileplaylist/silence_20sec.wav':
self.assertEqual(row['artist'], 'default')
self.assertEqual(row['name'], 'silence_20sec')
self.assertEqual(row['start_time'], '0.0')
self.assertEqual(row['duration'], '20.025850340136053')
self.assertEqual(row['restrict_to_nl'], '0')
self.assertEqual(row['tag'], '0')
self.assertEqual(row['group'], '0')
finally:
Expand Down
1 change: 1 addition & 0 deletions backend/experiment/rules/matching_pairs.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ def get_matching_pairs_trial(self, session):
degradations = session.playlist.section_set.filter(group__in=selected_pairs, tag=degradation_type)
player_sections = list(originals) + list(degradations)
random.shuffle(player_sections)

playback = MatchingPairs(
sections=player_sections,
stop_audio_after=5,
Expand Down
30 changes: 15 additions & 15 deletions backend/experiment/rules/tests/test_matching_pairs.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,21 @@ class MatchingPairsTest(TestCase):
@classmethod
def setUpTestData(cls):
section_csv = (
"default,Crown_1_E1,0.0,10.0,MatchingPairs/Original/Crown_1_E1.mp3,0,Original,6\n"
"default,Crown_1_E1,0.0,10.0,MatchingPairs/1stDegradation/Crown_1_E1.mp3,0,1stDegradation,6\n"
"default,Crown_1_E1,0.0,10.0,MatchingPairs/2ndDegradation/Crown_1_E1.mp3,0,2ndDegradation,6\n"
"default,ER_2_E1,0.0,10.0,MatchingPairs/Original/ER_2_E1.mp3,0,Original,21\n"
"default,ER_2_E1,0.0,10.0,MatchingPairs/1stDegradation/ER_2_E1.mp3,0,1stDegradation,21\n"
"default,ER_2_E1,0.0,10.0,MatchingPairs/2ndDegradation/ER_2_E1.mp3,0,2ndDegradation,21\n"
"default,GameOfThrones_1_E1,0.0,10.0,MatchingPairs/Original/GameOfThrones_1_E1.mp3,0,Original,26\n"
"default,GameOfThrones_1_E1,0.0,10.0,MatchingPairs/1stDegradation/GameOfThrones_1_E1.mp3,0,1stDegradation,26\n"
"default,GameOfThrones_1_E1,0.0,10.0,MatchingPairs/2ndDegradation/GameOfThrones_1_E1.mp3,0,2ndDegradation,26\n"
"default,RickandMorty_12_E1,0.0,10.0,MatchingPairs/Original/RickandMorty_12_E1.mp3,0,Original,44\n"
"default,RickandMorty_12_E1,0.0,10.0,MatchingPairs/1stDegradation/RickandMorty_12_E1.mp3,0,1stDegradation,44\n"
"default,RickandMorty_12_E1,0.0,10.0,MatchingPairs/2ndDegradation/RickandMorty_12_E1.mp3,0,2ndDegradation,44\n"
"default,TwinPeaks_0_E1,0.0,10.0,MatchingPairs/1stDegradation/TwinPeaks_0_E1.mp3,0,1stDegradation,86\n"
"default,TwinPeaks_0_E1,0.0,10.0,MatchingPairs/2ndDegradation/TwinPeaks_0_E1.mp3,0,2ndDegradation,86\n"
"default,TwinPeaks_1_E1,0.0,10.0,MatchingPairs/Original/TwinPeaks_1_E1.mp3,0,Original,86\n"
"default,Crown_1_E1,0.0,10.0,MatchingPairs/Original/Crown_1_E1.mp3,Original,6\n"
"default,Crown_1_E1,0.0,10.0,MatchingPairs/1stDegradation/Crown_1_E1.mp3,1stDegradation,6\n"
"default,Crown_1_E1,0.0,10.0,MatchingPairs/2ndDegradation/Crown_1_E1.mp3,2ndDegradation,6\n"
"default,ER_2_E1,0.0,10.0,MatchingPairs/Original/ER_2_E1.mp3,Original,21\n"
"default,ER_2_E1,0.0,10.0,MatchingPairs/1stDegradation/ER_2_E1.mp3,1stDegradation,21\n"
"default,ER_2_E1,0.0,10.0,MatchingPairs/2ndDegradation/ER_2_E1.mp3,2ndDegradation,21\n"
"default,GameOfThrones_1_E1,0.0,10.0,MatchingPairs/Original/GameOfThrones_1_E1.mp3,Original,26\n"
"default,GameOfThrones_1_E1,0.0,10.0,MatchingPairs/1stDegradation/GameOfThrones_1_E1.mp3,1stDegradation,26\n"
"default,GameOfThrones_1_E1,0.0,10.0,MatchingPairs/2ndDegradation/GameOfThrones_1_E1.mp3,2ndDegradation,26\n"
"default,RickandMorty_12_E1,0.0,10.0,MatchingPairs/Original/RickandMorty_12_E1.mp3,Original,44\n"
"default,RickandMorty_12_E1,0.0,10.0,MatchingPairs/1stDegradation/RickandMorty_12_E1.mp3,1stDegradation,44\n"
"default,RickandMorty_12_E1,0.0,10.0,MatchingPairs/2ndDegradation/RickandMorty_12_E1.mp3,2ndDegradation,44\n"
"default,TwinPeaks_0_E1,0.0,10.0,MatchingPairs/1stDegradation/TwinPeaks_0_E1.mp3,1stDegradation,86\n"
"default,TwinPeaks_0_E1,0.0,10.0,MatchingPairs/2ndDegradation/TwinPeaks_0_E1.mp3,2ndDegradation,86\n"
"default,TwinPeaks_1_E1,0.0,10.0,MatchingPairs/Original/TwinPeaks_1_E1.mp3,Original,86\n"
)
cls.playlist = Playlist.objects.create(name='TestMatchingPairs')
cls.playlist.csv = section_csv
Expand Down
16 changes: 8 additions & 8 deletions backend/experiment/rules/tests/test_visual_matching_pairs.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@ class VisualMatchingPairsTest(TestCase):
@classmethod
def setUpTestData(self):
section_csv = (
"owner,George,0.0,1.0,https://m.media-amazon.com/images/M/MV5BMTUyNjE0NzAzMl5BMl5BanBnXkFtZTYwMjQzMzU3._V1_FMjpg_UX1000_.jpg,0, vmp, 3\n"
"owner,George,0.0,1.0,https://m.media-amazon.com/images/M/MV5BMTUyNjE0NzAzMl5BMl5BanBnXkFtZTYwMjQzMzU3._V1_FMjpg_UX1000_.jpg,0, vmp, 3\n"
"owner,John,0.0,1.0,https://m.media-amazon.com/images/M/MV5BMTYwMDE4MzgzMF5BMl5BanBnXkFtZTYwMDQzMzU3._V1_FMjpg_UX1000_.jpg,0, vmp, 1\n"
"owner,John,0.0,1.0,https://m.media-amazon.com/images/M/MV5BMTYwMDE4MzgzMF5BMl5BanBnXkFtZTYwMDQzMzU3._V1_FMjpg_UX1000_.jpg,0, vmp, 1\n"
"owner,Paul,0.0,1.0,https://m.media-amazon.com/images/M/MV5BMTkyNTY0MzUxOV5BMl5BanBnXkFtZTYwNTQzMzU3._V1_.jpg,0, vmp, 2\n"
"owner,Paul,0.0,1.0,https://m.media-amazon.com/images/M/MV5BMTkyNTY0MzUxOV5BMl5BanBnXkFtZTYwNTQzMzU3._V1_.jpg,0, vmp, 2\n"
"owner,Ringo,0.0,1.0,https://m.media-amazon.com/images/M/MV5BMTU1NjY5NTY4N15BMl5BanBnXkFtZTYwNzQzMzU3._V1_.jpg,0, vmp, 4\n"
"owner,Ringo,0.0,1.0,https://m.media-amazon.com/images/M/MV5BMTU1NjY5NTY4N15BMl5BanBnXkFtZTYwNzQzMzU3._V1_.jpg,0, vmp, 4\n"
"owner,George,0.0,1.0,https://m.media-amazon.com/images/M/MV5BMTUyNjE0NzAzMl5BMl5BanBnXkFtZTYwMjQzMzU3._V1_FMjpg_UX1000_.jpg, vmp, 3\n"
"owner,George,0.0,1.0,https://m.media-amazon.com/images/M/MV5BMTUyNjE0NzAzMl5BMl5BanBnXkFtZTYwMjQzMzU3._V1_FMjpg_UX1000_.jpg, vmp, 3\n"
"owner,John,0.0,1.0,https://m.media-amazon.com/images/M/MV5BMTYwMDE4MzgzMF5BMl5BanBnXkFtZTYwMDQzMzU3._V1_FMjpg_UX1000_.jpg, vmp, 1\n"
"owner,John,0.0,1.0,https://m.media-amazon.com/images/M/MV5BMTYwMDE4MzgzMF5BMl5BanBnXkFtZTYwMDQzMzU3._V1_FMjpg_UX1000_.jpg, vmp, 1\n"
"owner,Paul,0.0,1.0,https://m.media-amazon.com/images/M/MV5BMTkyNTY0MzUxOV5BMl5BanBnXkFtZTYwNTQzMzU3._V1_.jpg, vmp, 2\n"
"owner,Paul,0.0,1.0,https://m.media-amazon.com/images/M/MV5BMTkyNTY0MzUxOV5BMl5BanBnXkFtZTYwNTQzMzU3._V1_.jpg, vmp, 2\n"
"owner,Ringo,0.0,1.0,https://m.media-amazon.com/images/M/MV5BMTU1NjY5NTY4N15BMl5BanBnXkFtZTYwNzQzMzU3._V1_.jpg, vmp, 4\n"
"owner,Ringo,0.0,1.0,https://m.media-amazon.com/images/M/MV5BMTU1NjY5NTY4N15BMl5BanBnXkFtZTYwNzQzMzU3._V1_.jpg, vmp, 4\n"
)
self.playlist = Playlist.objects.create(name='TestVisualMatchingPairs')
self.playlist.csv = section_csv
Expand Down
7 changes: 0 additions & 7 deletions backend/experiment/templates/edit-sections.html
Original file line number Diff line number Diff line change
Expand Up @@ -79,13 +79,6 @@ <h1>Edit sections for playlist: {{playlist.name}}</h1>
<input type="text" name="{{section.id}}_tag" maxlength="128" id="{{section.id}}_tag"
value="{{ section.tag}}">
</td>

<td>
{% if section.song %}
<input type="checkbox" name="{{section.id}}_restricted" id="{{section.id}}_restricted"
{% if section.song.restricted %} checked {% endif %}>
{% endif %}
</td>
</tr>
{% endfor %}
<tr class="song-padding">
Expand Down
2 changes: 1 addition & 1 deletion backend/requirements/dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ coverage==7.3.2
# via -r requirements.in/base.txt
defusedxml==0.7.1
# via genbadge
django==3.2.23
django==3.2.24
# via
# -r requirements.in/base.txt
# django-cors-headers
Expand Down
2 changes: 1 addition & 1 deletion backend/requirements/prod.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ coverage==7.3.2
# via -r requirements.in/base.txt
defusedxml==0.7.1
# via genbadge
django==3.2.23
django==3.2.24
# via
# -r requirements.in/base.txt
# django-cors-headers
Expand Down
4 changes: 0 additions & 4 deletions backend/section/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,10 +138,6 @@ def edit_sections(self, request, obj, parent_obj=None):
section.song = this_song
else:
section.song = this_song
if request.POST.get(pre_fix + '_restricted'):
section.song.restricted = [{"restricted": "nl"}]
else:
section.song.restricted = []
section.song.save()
section.start_time = request.POST.get(pre_fix + '_start_time')
section.duration = request.POST.get(pre_fix + '_duration')
Expand Down
34 changes: 17 additions & 17 deletions backend/section/fixtures/playlist.json

Large diffs are not rendered by default.

22 changes: 22 additions & 0 deletions backend/section/migrations/0006_auto_20240126_1606.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Generated by Django 3.2.23 on 2024-01-26 15:06

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('section', '0005_section_add_validator'),
]

operations = [
migrations.RemoveField(
model_name='song',
name='restricted',
),
migrations.AlterField(
model_name='playlist',
name='csv',
field=models.TextField(blank=True, help_text='CSV Format: artist_name [string], song_name [string], start_position [float], duration [float], "path/filename.mp3" [string], tag [string], group [string]'),
),
]
21 changes: 3 additions & 18 deletions backend/section/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class Playlist(models.Model):

default_csv_row = 'CSV Format: artist_name [string],\
song_name [string], start_position [float], duration [float],\
"path/filename.mp3" [string], restricted_to_nl [int 0=False 1=True], tag [string], group [string]'
"path/filename.mp3" [string], tag [string], group [string]'
csv = models.TextField(blank=True, help_text=default_csv_row)

def save(self, *args, **kwargs):
Expand Down Expand Up @@ -68,7 +68,7 @@ def update_sections(self):
# Add new sections from csv
try:
reader = csv.DictReader(self.csv.splitlines(), fieldnames=(
'artist', 'name', 'start_time', 'duration', 'filename', 'restrict_to_nl', 'tag', 'group'))
'artist', 'name', 'start_time', 'duration', 'filename', 'tag', 'group'))
except csv.Error:
return {
'status': self.CSV_ERROR,
Expand Down Expand Up @@ -97,8 +97,7 @@ def is_number(string):

# check for valid numbers
if not (is_number(row['start_time'])
and is_number(row['duration'])
and is_number(row['restrict_to_nl'])):
and is_number(row['duration'])):
return {
'status': self.CSV_ERROR,
'message': "Error: Expected number fields on line: " + str(lines)
Expand All @@ -109,12 +108,6 @@ def is_number(string):
if row['artist'] and row['name']:
song, created = Song.objects.get_or_create(artist=row['artist'], name=row['name'])

if int(row['restrict_to_nl']) == 1:
song.restricted = [{"restricted": "nl"}]
song.save()
elif int(row['restrict_to_nl']) == 0:
song.restricted = []
song.save()
section = Section(playlist=self,
start_time=float(row['start_time']),
duration=float(row['duration']),
Expand All @@ -132,9 +125,6 @@ def is_number(string):
if not ex_section.song:
ex_section.song = song
ex_section.save()
elif ex_section.song.restricted != song.restricted:
ex_section.song.restricted = song.restricted
ex_section.song.save()
ex_section.start_time = section.start_time
ex_section.duration = section.duration
ex_section.tag = section.tag
Expand Down Expand Up @@ -208,17 +198,14 @@ def update_admin_csv(self):
if section.song:
this_artist = section.song.artist
this_name = section.song.name
this_restricted = '1' if section.song.restricted else '0'
else:
this_artist = ''
this_name = ''
this_restricted = ''
writer.writerow([this_artist,
this_name,
section.start_time,
section.duration,
section.filename,
this_restricted,
section.tag,
section.group])
csv_string = csvfile.csv_string
Expand All @@ -229,7 +216,6 @@ class Song(models.Model):
""" A Song object with an artist and name (unique together)"""
artist = models.CharField(db_index=True, blank=True, default='', max_length=128)
name = models.CharField(db_index=True, blank=True, default='' ,max_length=128)
restricted = models.JSONField(default=list, blank=True)

class Meta:
unique_together = ("artist", "name")
Expand Down Expand Up @@ -323,7 +309,6 @@ def export_admin_csv(self):
self.start_time,
self.duration,
self.filename,
1 if self.song.restricted else 0,
self.tag,
self.group,
]
Expand Down
5 changes: 3 additions & 2 deletions backend/section/templates/change_form.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,14 @@
margin: 1rem 0;
gap: 1rem;
}

</style>
{% endblock %}


{% block after_field_sets %}
{% if original.pk %}
<div class="custom-actions">
<a href="{% url 'admin:section_playlist_export_csv' original.pk %}" class="button">Export to CSV</a>
</div>
{% endblock %}
{% endif %}
{% endblock %}
Loading

0 comments on commit 7797c30

Please sign in to comment.