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)