Mercurial > hg > webaudioevaluationtool
comparison scripts/generate_report.py @ 1075:056a8454500e
Bug fixes in scripts: display plays in last timeline segment of each fragment (post last move); call sub-processes of generate_report.py in same folder (pass on argument); indentation in LaTeX file.
author | Brecht De Man <BrechtDeMan@users.noreply.github.com> |
---|---|
date | Wed, 16 Sep 2015 13:31:40 +0100 |
parents | 522a2ed8afa2 |
children | 9ef4bdff3ad4 |
comparison
equal
deleted
inserted
replaced
1074:2b8c36924bfd | 1075:056a8454500e |
---|---|
90 at \texttt{code.soundsoftware.ac.uk/projects/webaudioevaluationtool}. | 90 at \texttt{code.soundsoftware.ac.uk/projects/webaudioevaluationtool}. |
91 \tableofcontents | 91 \tableofcontents |
92 | 92 |
93 ''' | 93 ''' |
94 | 94 |
95 footer = r'''\begin{thebibliography}{9} | 95 footer = '\n\t\t'+r'''\begin{thebibliography}{9} |
96 \bibitem{WAET} % reference to accompanying publication | 96 \bibitem{WAET} % reference to accompanying publication |
97 Nicholas Jillings, Brecht De Man, David Moffat and Joshua D. Reiss, | 97 Nicholas Jillings, Brecht De Man, David Moffat and Joshua D. Reiss, |
98 ``Web Audio Evaluation Tool: A browser-based listening test environment,'' | 98 ``Web Audio Evaluation Tool: A browser-based listening test environment,'' |
99 presented at the 12th Sound and Music Computing Conference, July 2015. | 99 presented at the 12th Sound and Music Computing Conference, July 2015. |
100 \end{thebibliography} | 100 \end{thebibliography} |
102 | 102 |
103 body = '' | 103 body = '' |
104 | 104 |
105 # generate images for later use | 105 # generate images for later use |
106 if render_figures: | 106 if render_figures: |
107 subprocess.call("python timeline_view_movement.py", shell=True) | 107 subprocess.call("python timeline_view_movement.py "+folder_name, shell=True) |
108 subprocess.call("python score_parser.py", shell=True) | 108 subprocess.call("python score_parser.py "+folder_name, shell=True) |
109 subprocess.call("python score_plot.py", shell=True) | 109 subprocess.call("python score_plot.py "+folder_name, shell=True) |
110 | 110 |
111 # get every XML file in folder | 111 # get every XML file in folder |
112 files_list = os.listdir(folder_name) | 112 files_list = os.listdir(folder_name) |
113 for file in files_list: # iterate over all files in files_list | 113 for file in files_list: # iterate over all files in files_list |
114 if file.endswith(".xml"): # check if XML file | 114 if file.endswith(".xml"): # check if XML file |
115 number_of_XML_files += 1 | 115 number_of_XML_files += 1 |
116 tree = ET.parse(folder_name + '/' + file) | 116 tree = ET.parse(folder_name + '/' + file) |
117 root = tree.getroot() | 117 root = tree.getroot() |
118 | 118 |
119 # PRINT name as section | 119 # PRINT name as section |
120 body+= '\section{'+file[:-4].capitalize()+'}\n' # make section header from name without extension | 120 body+= '\n\section{'+file[:-4].capitalize()+'}\n' # make section header from name without extension |
121 | 121 |
122 # reset for new subject | 122 # reset for new subject |
123 total_duration = 0 | 123 total_duration = 0 |
124 page_number = 0 | 124 page_number = 0 |
125 | 125 |
126 individual_table = '' # table with stats for this individual test file | 126 individual_table = '\n' # table with stats for this individual test file |
127 timeline_plots = '' # plots of timeline (movements and plays) | 127 timeline_plots = '' # plots of timeline (movements and plays) |
128 | 128 |
129 # DEMO survey stats | 129 # DEMO survey stats |
130 # get gender | 130 # get gender |
131 this_subjects_gender = root.find("./posttest/radio/[@id='gender']") | 131 this_subjects_gender = root.find("./posttest/radio/[@id='gender']") |
135 gender.append('UNAVAILABLE') | 135 gender.append('UNAVAILABLE') |
136 # get age | 136 # get age |
137 this_subjects_age = root.find("./posttest/number/[@id='age']") | 137 this_subjects_age = root.find("./posttest/number/[@id='age']") |
138 if this_subjects_age is not None: | 138 if this_subjects_age is not None: |
139 age.append(this_subjects_age.text) | 139 age.append(this_subjects_age.text) |
140 | 140 #TODO add plot of age |
141 | |
141 # get list of all page names | 142 # get list of all page names |
142 for audioholder in root.findall("./audioholder"): # iterate over pages | 143 for audioholder in root.findall("./audioholder"): # iterate over pages |
143 page_name = audioholder.get('id') # get page name | 144 page_name = audioholder.get('id') # get page name |
144 | 145 |
145 if page_name is None: # ignore 'empty' audio_holders | 146 if page_name is None: # ignore 'empty' audio_holders |
181 total_not_moved += len(not_moved) | 182 total_not_moved += len(not_moved) |
182 | 183 |
183 # PRINT alerts when elements not played or markers not moved | 184 # PRINT alerts when elements not played or markers not moved |
184 # number of audio elements not played | 185 # number of audio elements not played |
185 if len(not_played) > 1: | 186 if len(not_played) > 1: |
186 body += '\\emph{\\textbf{ATTENTION: '+str(len(not_played))+\ | 187 body += '\t\t\\emph{\\textbf{ATTENTION: '+str(len(not_played))+\ |
187 ' fragments were not listened to in '+page_name+'! }}'+\ | 188 ' fragments were not listened to in '+page_name+'! }}'+\ |
188 ', '.join(not_played)+'\\\\ \n' | 189 ', '.join(not_played)+'\\\\ \n' |
189 if len(not_played) == 1: | 190 if len(not_played) == 1: |
190 body += '\\emph{\\textbf{ATTENTION: one fragment was not listened to in '+page_name+'! }}'+\ | 191 body += '\t\t\\emph{\\textbf{ATTENTION: one fragment was not listened to in '+page_name+'! }}'+\ |
191 not_played[0]+'\\\\ \n' | 192 not_played[0]+'\\\\ \n' |
192 | 193 |
193 # number of audio element markers not moved | 194 # number of audio element markers not moved |
194 if len(not_moved) > 1: | 195 if len(not_moved) > 1: |
195 body += '\\emph{\\textbf{ATTENTION: '+str(len(not_moved))+\ | 196 body += '\t\t\\emph{\\textbf{ATTENTION: '+str(len(not_moved))+\ |
196 ' markers were not moved in '+page_name+'! }}'+\ | 197 ' markers were not moved in '+page_name+'! }}'+\ |
197 ', '.join(not_moved)+'\\\\ \n' | 198 ', '.join(not_moved)+'\\\\ \n' |
198 if len(not_moved) == 1: | 199 if len(not_moved) == 1: |
199 body += '\\emph{\\textbf{ATTENTION: one marker was not moved in '+page_name+'! }}'+\ | 200 body += '\t\t\\emph{\\textbf{ATTENTION: one marker was not moved in '+page_name+'! }}'+\ |
200 not_moved[0]+'\\\\ \n' | 201 not_moved[0]+'\\\\ \n' |
201 | 202 |
202 # PRINT song-specific statistic | 203 # PRINT song-specific statistic |
203 individual_table += page_name+'&'+\ | 204 individual_table += '\t\t'+page_name+'&'+\ |
204 str(number_of_comments) + '/' +\ | 205 str(number_of_comments) + '/' +\ |
205 str(number_of_comments+number_of_missing_comments)+'&'+\ | 206 str(number_of_comments+number_of_missing_comments)+'&'+\ |
206 seconds2timestr(duration)+'\\\\' | 207 seconds2timestr(duration)+'\\\\\n' |
207 | 208 |
208 # get timeline for this audioholder | 209 # get timeline for this audioholder |
209 img_path = 'timelines_movement/'+file[:-4]+'-'+page_name+'.pdf' | 210 img_path = 'timelines_movement/'+file[:-4]+'-'+page_name+'.pdf' |
210 | 211 |
211 # check if available | 212 # check if available |
212 if os.path.isfile(folder_name+'/'+img_path): | 213 if os.path.isfile(folder_name+'/'+img_path): |
213 # SHOW timeline image | 214 # SHOW timeline image |
214 timeline_plots += '\\includegraphics[width=\\textwidth]{'+\ | 215 timeline_plots += '\\includegraphics[width=\\textwidth]{'+\ |
215 folder_name+'/'+img_path+'}\n\n' | 216 folder_name+'/'+img_path+'}\n\t\t' |
216 | 217 |
217 # keep track of duration in function of page index | 218 # keep track of duration in function of page index |
218 if len(duration_order)>page_number: | 219 if len(duration_order)>page_number: |
219 duration_order[page_number].append(duration) | 220 duration_order[page_number].append(duration) |
220 else: | 221 else: |
259 page_number += 1 # increase page count for this specific test | 260 page_number += 1 # increase page count for this specific test |
260 number_of_pages += 1 # increase total number of pages | 261 number_of_pages += 1 # increase total number of pages |
261 time_per_page_accum += duration # total duration (for average time spent per page) | 262 time_per_page_accum += duration # total duration (for average time spent per page) |
262 | 263 |
263 # PRINT table with statistics about this test | 264 # PRINT table with statistics about this test |
264 body += r'''\begin{tabular}{|p{3.5cm}|c|p{2.5cm}|} | 265 body += '\t\t'+r'''\begin{tabular}{|p{3.5cm}|c|p{2.5cm}|} |
265 \hline | 266 \hline |
266 \textbf{Song name} & \textbf{Comments} & \textbf{Duration} \\ \hline '''+\ | 267 \textbf{Song name} & \textbf{Comments} & \textbf{Duration} \\ \hline '''+\ |
267 individual_table+\ | 268 individual_table+'\t\t'+\ |
268 r'''\hline | 269 r'''\hline |
269 \textbf{TOTAL} & & \textbf{'''+\ | 270 \textbf{TOTAL} & & \textbf{'''+\ |
270 seconds2timestr(total_duration)+\ | 271 seconds2timestr(total_duration)+\ |
271 r'''}\\ | 272 r'''}\\ |
272 \hline | 273 \hline |
282 # empty body again | 283 # empty body again |
283 body = '' | 284 body = '' |
284 | 285 |
285 # PRINT summary of everything (at start) | 286 # PRINT summary of everything (at start) |
286 # unnumbered so that number of sections equals number of files | 287 # unnumbered so that number of sections equals number of files |
287 body += '\section*{Summary}\n\\addcontentsline{toc}{section}{Summary}' | 288 body += '\section*{Summary}\n\t\t\\addcontentsline{toc}{section}{Summary}\n' |
288 | 289 |
289 # PRINT table with statistics | 290 # PRINT table with statistics |
290 body += '\\begin{tabular}{ll}' | 291 body += '\t\t\\begin{tabular}{ll}\n\t\t\t' |
291 body += r'Number of XML files: &' + str(number_of_XML_files) + r'\\' | 292 body += r'Number of XML files: &' + str(number_of_XML_files) + r'\\'+'\n\t\t\t' |
292 body += r'Number of pages: &' + str(number_of_pages) + r'\\' | 293 body += r'Number of pages: &' + str(number_of_pages) + r'\\'+'\n\t\t\t' |
293 body += r'Number of fragments: &' + str(number_of_fragments) + r'\\' | 294 body += r'Number of fragments: &' + str(number_of_fragments) + r'\\'+'\n\t\t\t' |
294 body += r'Number of empty comments: &' + str(total_empty_comments) +\ | 295 body += r'Number of empty comments: &' + str(total_empty_comments) +\ |
295 " (" + str(round(100.0*total_empty_comments/number_of_fragments,2)) + r"\%)\\" | 296 " (" + str(round(100.0*total_empty_comments/number_of_fragments,2)) + r"\%)\\"+'\n\t\t\t' |
296 body += r'Number of unplayed fragments: &' + str(total_not_played) +\ | 297 body += r'Number of unplayed fragments: &' + str(total_not_played) +\ |
297 " (" + str(round(100.0*total_not_played/number_of_fragments,2)) + r"\%)\\" | 298 " (" + str(round(100.0*total_not_played/number_of_fragments,2)) + r"\%)\\"+'\n\t\t\t' |
298 body += r'Number of unmoved markers: &' + str(total_not_moved) +\ | 299 body += r'Number of unmoved markers: &' + str(total_not_moved) +\ |
299 " (" + str(round(100.0*total_not_moved/number_of_fragments,2)) + r"\%)\\" | 300 " (" + str(round(100.0*total_not_moved/number_of_fragments,2)) + r"\%)\\"+'\n\t\t\t' |
300 body += r'Average time per page: &' + seconds2timestr(time_per_page_accum/number_of_pages) + r"\\" | 301 body += r'Average time per page: &' + seconds2timestr(time_per_page_accum/number_of_pages) + r"\\"+'\n\t\t' |
301 body += '\\end{tabular} \\vspace{1.5cm} \\\\ \n' | 302 body += '\\end{tabular} \\vspace{1.5cm} \\\\ \n' |
302 | 303 |
303 # Average duration for first, second, ... page | 304 # Average duration for first, second, ... page |
304 body += " \\vspace{.5cm} Average duration per page (see also Figure \\ref{fig:avgtimeperpage}): \\\\ \n" | 305 body += "\t\t\\vspace{.5cm} \n\n\t\tAverage duration per page (see also Figure \\ref{fig:avgtimeperpage}): \\\\ \n\t\t" |
305 body += r'''\begin{tabular}{lll} | 306 body += r'''\begin{tabular}{lll} |
306 \textbf{Page} & \textbf{Duration} & \textbf{\# subjects}\\ | 307 \textbf{Page} & \textbf{Duration} & \textbf{\# subjects}\\''' |
307 ''' | |
308 tpp_averages = [] # store average time per page | 308 tpp_averages = [] # store average time per page |
309 for page_number in range(len(duration_order)): | 309 for page_number in range(len(duration_order)): |
310 body += str(page_number+1) + "&" +\ | 310 body += '\n\t\t\t'+str(page_number+1) + "&" +\ |
311 seconds2timestr(sum(duration_order[page_number])/len(duration_order[page_number])) +\ | 311 seconds2timestr(sum(duration_order[page_number])/len(duration_order[page_number])) +\ |
312 "&"+str(len(duration_order[page_number]))+r"\\" | 312 "&"+str(len(duration_order[page_number]))+r"\\" |
313 tpp_averages.append(sum(duration_order[page_number])/len(duration_order[page_number])) | 313 tpp_averages.append(sum(duration_order[page_number])/len(duration_order[page_number])) |
314 | 314 |
315 body += '\\end{tabular} \\vspace{1.5cm} \\\\ \n' | 315 body += '\n\t\t\\end{tabular} \\vspace{1.5cm} \\\\ \n\n\t\t' |
316 | 316 |
317 # SHOW bar plot of average time per page | 317 # SHOW bar plot of average time per page |
318 plt.bar(range(1,len(duration_order)+1), np.array(tpp_averages)/60) | 318 plt.bar(range(1,len(duration_order)+1), np.array(tpp_averages)/60) |
319 plt.xlabel('Page order') | 319 plt.xlabel('Page order') |
320 plt.xlim(.8, len(duration_order)+1) | 320 plt.xlim(.8, len(duration_order)+1) |
337 # combine and sort in function of number of audioelements and duration | 337 # combine and sort in function of number of audioelements and duration |
338 combined_list = [page_names, average_duration_page, fragments_per_page, number_of_subjects_page] | 338 combined_list = [page_names, average_duration_page, fragments_per_page, number_of_subjects_page] |
339 combined_list = sorted(zip(*combined_list), key=operator.itemgetter(1, 2)) # sort | 339 combined_list = sorted(zip(*combined_list), key=operator.itemgetter(1, 2)) # sort |
340 | 340 |
341 # Show average duration for all songs | 341 # Show average duration for all songs |
342 body += r'''\vspace{.5cm} Average duration per audioholder (see also Figure \ref{fig:avgtimeperaudioholder}): \\ | 342 body += r'''\vspace{.5cm} |
343 \begin{tabular}{llll} | 343 Average duration per audioholder (see also Figure \ref{fig:avgtimeperaudioholder}): \\ |
344 \textbf{Audioholder} & \textbf{Duration} & \textbf{\# subjects} & \textbf{\# fragments} \\ | 344 \begin{tabular}{llll} |
345 ''' | 345 \textbf{Audioholder} & \textbf{Duration} & \textbf{\# subjects} & \textbf{\# fragments} \\''' |
346 audioholder_names_ordered = [] | 346 audioholder_names_ordered = [] |
347 average_duration_audioholder_ordered = [] | 347 average_duration_audioholder_ordered = [] |
348 number_of_subjects = [] | 348 number_of_subjects = [] |
349 for page_index in range(len(page_names)): | 349 for page_index in range(len(page_names)): |
350 audioholder_names_ordered.append(combined_list[page_index][0]) | 350 audioholder_names_ordered.append(combined_list[page_index][0]) |
351 average_duration_audioholder_ordered.append(combined_list[page_index][1]) | 351 average_duration_audioholder_ordered.append(combined_list[page_index][1]) |
352 number_of_subjects.append(combined_list[page_index][3]) | 352 number_of_subjects.append(combined_list[page_index][3]) |
353 body += combined_list[page_index][0] + "&" +\ | 353 body += '\n\t\t\t'+combined_list[page_index][0] + "&" +\ |
354 seconds2timestr(combined_list[page_index][1]) + "&" +\ | 354 seconds2timestr(combined_list[page_index][1]) + "&" +\ |
355 str(combined_list[page_index][3]) + "&" +\ | 355 str(combined_list[page_index][3]) + "&" +\ |
356 str(combined_list[page_index][2]) + r"\\" | 356 str(combined_list[page_index][2]) + r"\\" |
357 body += '\\end{tabular}\n' | 357 body += '\n\t\t\\end{tabular}\n' |
358 | 358 |
359 # SHOW bar plot of average time per page | 359 # SHOW bar plot of average time per page |
360 plt.bar(range(1,len(audioholder_names_ordered)+1), np.array(average_duration_audioholder_ordered)/60) | 360 plt.bar(range(1,len(audioholder_names_ordered)+1), np.array(average_duration_audioholder_ordered)/60) |
361 plt.xlabel('Audioholder') | 361 plt.xlabel('Audioholder') |
362 plt.xlim(.8, len(audioholder_names_ordered)+1) | 362 plt.xlim(.8, len(audioholder_names_ordered)+1) |
377 plt.yticks(yint) | 377 plt.yticks(yint) |
378 plt.savefig(folder_name+"/subjects_per_audioholder.pdf", bbox_inches='tight') | 378 plt.savefig(folder_name+"/subjects_per_audioholder.pdf", bbox_inches='tight') |
379 plt.close() | 379 plt.close() |
380 | 380 |
381 # SHOW both figures | 381 # SHOW both figures |
382 body += r'''\begin{figure}[htbp] | 382 body += r''' |
383 \begin{figure}[htbp] | |
383 \begin{center} | 384 \begin{center} |
384 \includegraphics[width=.65\textwidth]{'''+\ | 385 \includegraphics[width=.65\textwidth]{'''+\ |
385 folder_name+"/time_per_page.pdf"+\ | 386 folder_name+"/time_per_page.pdf"+\ |
386 r'''} | 387 r'''} |
387 \caption{Average time spent per page.} | 388 \caption{Average time spent per page.} |
388 \label{fig:avgtimeperpage} | 389 \label{fig:avgtimeperpage} |
389 \end{center} | 390 \end{center} |
390 \end{figure} | 391 \end{figure} |
392 | |
391 ''' | 393 ''' |
392 body += r'''\begin{figure}[htbp] | 394 body += r'''\begin{figure}[htbp] |
393 \begin{center} | 395 \begin{center} |
394 \includegraphics[width=.65\textwidth]{'''+\ | 396 \includegraphics[width=.65\textwidth]{'''+\ |
395 folder_name+"/time_per_audioholder.pdf"+\ | 397 folder_name+"/time_per_audioholder.pdf"+\ |
396 r'''} | 398 r'''} |
397 \caption{Average time spent per audioholder.} | 399 \caption{Average time spent per audioholder.} |
398 \label{fig:avgtimeperaudioholder} | 400 \label{fig:avgtimeperaudioholder} |
399 \end{center} | 401 \end{center} |
400 \end{figure} | 402 \end{figure} |
403 | |
401 ''' | 404 ''' |
402 body += r'''\begin{figure}[htbp] | 405 body += r'''\begin{figure}[htbp] |
403 \begin{center} | 406 \begin{center} |
404 \includegraphics[width=.65\textwidth]{'''+\ | 407 \includegraphics[width=.65\textwidth]{'''+\ |
405 folder_name+"/subjects_per_audioholder.pdf"+\ | 408 folder_name+"/subjects_per_audioholder.pdf"+\ |
406 r'''} | 409 r'''} |
407 \caption{Number of subjects per audioholder.} | 410 \caption{Number of subjects per audioholder.} |
408 \label{fig:avgtimeperaudioholder} | 411 \label{fig:avgtimeperaudioholder} |
409 \end{center} | 412 \end{center} |
410 \end{figure} | 413 \end{figure} |
414 | |
411 ''' | 415 ''' |
412 #TODO add error bars | 416 #TODO add error bars |
413 #TODO layout of figures | 417 #TODO layout of figures |
414 | 418 |
415 # SHOW boxplot per audioholder | 419 # SHOW boxplot per audioholder |
426 audioholder_name+' ('+str(subject_count[real_page_names.index(audioholder_name)])+\ | 430 audioholder_name+' ('+str(subject_count[real_page_names.index(audioholder_name)])+\ |
427 ''' participants).} | 431 ''' participants).} |
428 \label{fig:avgtimeperpage} | 432 \label{fig:avgtimeperpage} |
429 \end{center} | 433 \end{center} |
430 \end{figure} | 434 \end{figure} |
435 | |
431 ''' | 436 ''' |
432 | 437 |
433 # DEMO pie chart of gender distribution among subjects | 438 # DEMO pie chart of gender distribution among subjects |
434 genders = ['male', 'female', 'other', 'preferNotToSay', 'UNAVAILABLE'] | 439 genders = ['male', 'female', 'other', 'preferNotToSay', 'UNAVAILABLE'] |
435 # TODO: get the above automatically | 440 # TODO: get the above automatically |
439 if number>0: | 444 if number>0: |
440 gender_distribution += str("{:.2f}".format((100.0*number)/len(gender)))+\ | 445 gender_distribution += str("{:.2f}".format((100.0*number)/len(gender)))+\ |
441 '/'+item.capitalize()+' ('+str(number)+'),\n' | 446 '/'+item.capitalize()+' ('+str(number)+'),\n' |
442 | 447 |
443 body += r''' | 448 body += r''' |
449 % Pie chart of gender distribution | |
444 \def\angle{0} | 450 \def\angle{0} |
445 \def\radius{3} | 451 \def\radius{3} |
446 \def\cyclelist{{"orange","blue","red","green"}} | 452 \def\cyclelist{{"orange","blue","red","green"}} |
447 \newcount\cyclecount \cyclecount=-1 | 453 \newcount\cyclecount \cyclecount=-1 |
448 \newcount\ind \ind=-1 | 454 \newcount\ind \ind=-1 |
472 \end{tikzpicture} | 478 \end{tikzpicture} |
473 \caption{Representation of gender across subjects} | 479 \caption{Representation of gender across subjects} |
474 \label{default} | 480 \label{default} |
475 \end{center} | 481 \end{center} |
476 \end{figure} | 482 \end{figure} |
483 | |
477 ''' | 484 ''' |
478 # problem: some people entered twice? | 485 # problem: some people entered twice? |
479 | 486 |
480 #TODO | 487 #TODO |
481 # time per page in function of number of fragments (plot) | 488 # time per page in function of number of fragments (plot) |