annotate scripts/generate_report.py @ 1150:2674d80c66ff

Confirmed working (using examples) on OSX (Chrome/Safari/Firefox)
author Nicholas Jillings <n.g.r.jillings@se14.qmul.ac.uk>
date Thu, 14 Jan 2016 16:00:52 +0000
parents 3edcbbea168b
children 8540d153caec b5bf2f57187c
rev   line source
n@1118 1 #!/usr/bin/python
n@1118 2 # -*- coding: utf-8 -*-
n@1118 3
n@1118 4 import xml.etree.ElementTree as ET
n@1118 5 import os # for getting files from directory
n@1118 6 import operator # for sorting data with multiple keys
n@1118 7 import sys # for accessing command line arguments
n@1118 8 import subprocess # for calling pdflatex
n@1118 9 import shlex # for calling pdflatex
n@1118 10 import matplotlib.pyplot as plt # plots
n@1118 11 import numpy as np # numbers
n@1118 12
n@1118 13 # Command line arguments
n@1118 14 assert len(sys.argv)<4, "generate_report takes at most 2 command line arguments\n"+\
n@1118 15 "Use: python generate_report.py [results_folder] [no_render | -nr]"
n@1118 16
n@1118 17 render_figures = True
n@1118 18
n@1118 19 # XML results files location
n@1118 20 if len(sys.argv) == 1:
n@1118 21 folder_name = "../saves/" # Looks in 'saves/' folder from 'scripts/' folder
n@1118 22 print "Use: python generate_report.py [results_folder] [no_render | -nr]"
n@1118 23 print "Using default path: " + folder_name
n@1118 24 elif len(sys.argv) == 2:
n@1118 25 folder_name = sys.argv[1] # First command line argument is folder
n@1118 26 elif len(sys.argv) == 3:
n@1118 27 folder_name = sys.argv[1] # First command line argument is folder
n@1118 28 assert sys.argv[2] in ('no_render','-nr'), "Second argument not recognised. \n" +\
n@1118 29 "Use: python generate_report.py [results_folder] [no_render | -nr]"
n@1118 30 # Second command line argument is [no_render | -nr]
n@1118 31 render_figures = False
n@1118 32
n@1118 33 def isNaN(num):
n@1118 34 return num != num
n@1118 35
n@1118 36 # Turn number of seconds (int) to '[minutes] min [seconds] s' (string)
n@1118 37 def seconds2timestr(time_in_seconds):
n@1118 38 if time_in_seconds is not None and not isNaN(time_in_seconds):
n@1118 39 time_in_minutes = int(time_in_seconds/60)
n@1118 40 remaining_seconds = int(time_in_seconds%60)
n@1118 41 return str(time_in_minutes) + " min " + str(remaining_seconds) + " s"
n@1118 42 else:
n@1118 43 return 'N/A'
n@1118 44
n@1118 45 # stats initialisation
n@1118 46 number_of_XML_files = 0
n@1118 47 number_of_pages = 0
n@1118 48 number_of_fragments = 0
n@1118 49 total_empty_comments = 0
n@1118 50 total_not_played = 0
n@1118 51 total_not_moved = 0
n@1118 52 time_per_page_accum = 0
n@1118 53
n@1118 54 # arrays initialisation
n@1118 55 page_names = []
n@1118 56 real_page_names = [] # regardless of differing numbers of fragments
n@1118 57 subject_count = [] # subjects per audioholder name
n@1118 58 page_count = []
n@1118 59 duration_page = [] # duration of experiment in function of page content
n@1118 60 duration_order = [] # duration of experiment in function of page number
n@1118 61 fragments_per_page = [] # number of fragments for corresponding page
n@1118 62
n@1118 63 # survey stats
n@1118 64 gender = []
n@1118 65 age = []
n@1118 66
n@1118 67 # get username if available
n@1118 68 for name in ('LOGNAME', 'USER', 'LNAME', 'USERNAME'):
n@1118 69 user = os.environ.get(name)
n@1118 70 if user:
n@1118 71 break
n@1118 72 else:
n@1118 73 user = ''
n@1118 74
n@1118 75
n@1118 76 # begin LaTeX document
n@1118 77 header = r'''\documentclass[11pt, oneside]{article}
n@1118 78 \usepackage{geometry}
n@1118 79 \geometry{a4paper}
n@1118 80 \usepackage[parfill]{parskip} % empty line instead of indent
n@1118 81 \usepackage{graphicx} % figures
n@1118 82 \usepackage[space]{grffile} % include figures with spaces in paths
n@1118 83 \usepackage{hyperref}
n@1118 84 \usepackage{tikz} % pie charts
n@1118 85 \title{Report}
n@1118 86 \author{'''+\
n@1118 87 user+\
n@1118 88 r'''}
n@1118 89 \graphicspath{{'''+\
n@1118 90 folder_name+\
n@1118 91 r'''}}
n@1118 92 %\setcounter{section}{-1} % Summary section 0 so number of sections equals number of files
n@1118 93 \begin{document}
n@1118 94 \maketitle
n@1118 95 This is an automatically generated report using the `generate\_report.py' Python script
n@1118 96 included with the Web Audio Evaluation Tool \cite{WAET} distribution which can be found
n@1118 97 at \texttt{code.soundsoftware.ac.uk/projects/webaudioevaluationtool}.
n@1118 98 \tableofcontents
n@1118 99
n@1118 100 '''
n@1118 101
n@1118 102 footer = '\n\t\t'+r'''\begin{thebibliography}{9}
n@1118 103 \bibitem{WAET} % reference to accompanying publication
n@1118 104 Nicholas Jillings, Brecht De Man, David Moffat and Joshua D. Reiss,
n@1118 105 ``Web Audio Evaluation Tool: A browser-based listening test environment,''
n@1118 106 presented at the 12th Sound and Music Computing Conference, July 2015.
n@1118 107 \end{thebibliography}
n@1118 108 \end{document}'''
n@1118 109
n@1118 110 body = ''
n@1118 111
n@1118 112 # make sure folder_name ends in '/'
n@1118 113 folder_name = os.path.join(folder_name, '')
n@1118 114
n@1118 115 # generate images for later use
n@1118 116 if render_figures:
n@1118 117 subprocess.call("python timeline_view_movement.py '"+folder_name+"'", shell=True)
n@1118 118 subprocess.call("python score_parser.py '"+folder_name+"'", shell=True)
n@1118 119 subprocess.call("python score_plot.py '"+folder_name+"ratings/'", shell=True)
n@1118 120
n@1118 121 # get every XML file in folder
n@1118 122 files_list = os.listdir(folder_name)
n@1118 123 for file in files_list: # iterate over all files in files_list
n@1118 124 if file.endswith(".xml"): # check if XML file
n@1118 125 number_of_XML_files += 1
n@1118 126 tree = ET.parse(folder_name + file)
n@1118 127 root = tree.getroot()
n@1118 128
n@1118 129 # PRINT name as section
n@1118 130 body+= '\n\section{'+file[:-4].capitalize()+'}\n' # make section header from name without extension
n@1118 131
n@1118 132 # reset for new subject
n@1118 133 total_duration = 0
n@1118 134 page_number = 0
n@1118 135
n@1118 136 individual_table = '\n' # table with stats for this individual test file
n@1118 137 timeline_plots = '' # plots of timeline (movements and plays)
n@1118 138
n@1118 139 # DEMO survey stats
n@1118 140 # get gender
n@1118 141 this_subjects_gender = root.find("./posttest/radio/[@id='gender']")
n@1118 142 if this_subjects_gender is not None:
n@1118 143 gender.append(this_subjects_gender.get("name"))
n@1118 144 else:
n@1118 145 gender.append('UNAVAILABLE')
n@1118 146 # get age
n@1118 147 this_subjects_age = root.find("./posttest/number/[@id='age']")
n@1118 148 if this_subjects_age is not None:
n@1118 149 age.append(this_subjects_age.text)
n@1118 150 #TODO add plot of age
n@1118 151
n@1118 152 # get list of all page names
n@1118 153 for audioholder in root.findall("./audioholder"): # iterate over pages
n@1118 154 page_name = audioholder.get('id') # get page name
n@1118 155
n@1118 156 if page_name is None: # ignore 'empty' audio_holders
n@1118 157 print "WARNING: " + file + " contains empty audio holder. (evaluation_stats.py)"
n@1118 158 break # move on to next
n@1118 159
n@1118 160 number_of_comments = 0 # for this page
n@1118 161 number_of_missing_comments = 0 # for this page
n@1118 162 not_played = [] # for this page
n@1118 163 not_moved = [] # for this page
n@1118 164
n@1118 165 if audioholder.find("./metric/metricresult[@id='testTime']") is not None: # check if time is included
n@1118 166 # 'testTime' keeps total duration: subtract time so far for duration of this audioholder
n@1118 167 duration = float(audioholder.find("./metric/metricresult[@id='testTime']").text) - total_duration
n@1118 168
n@1118 169 # total duration of test
n@1118 170 total_duration += duration
n@1118 171 else:
n@1118 172 duration = float('nan')
n@1118 173 total_duration = float('nan')
n@1118 174
n@1118 175 # number of audio elements
n@1118 176 audioelements = audioholder.findall("./audioelement") # get audioelements
n@1118 177 number_of_fragments += len(audioelements) # add length of this list to total
n@1118 178
n@1118 179 # number of comments (interesting if comments not mandatory)
n@1118 180 for audioelement in audioelements:
n@1118 181 response = audioelement.find("./comment/response")
n@1118 182 was_played = audioelement.find("./metric/metricresult/[@name='elementFlagListenedTo']")
n@1118 183 was_moved = audioelement.find("./metric/metricresult/[@name='elementFlagMoved']")
n@1118 184 if response.text is not None and len(response.text) > 1:
n@1118 185 number_of_comments += 1
n@1118 186 else:
n@1118 187 number_of_missing_comments += 1
n@1118 188 if was_played is not None and was_played.text == 'false':
n@1118 189 not_played.append(audioelement.get('id'))
n@1118 190 if was_moved is not None and was_moved.text == 'false':
n@1118 191 not_moved.append(audioelement.get('id'))
n@1118 192
n@1118 193 # update global counters
n@1118 194 total_empty_comments += number_of_missing_comments
n@1118 195 total_not_played += len(not_played)
n@1118 196 total_not_moved += len(not_moved)
n@1118 197
n@1118 198 # PRINT alerts when elements not played or markers not moved
n@1118 199 # number of audio elements not played
n@1118 200 if len(not_played) > 1:
n@1118 201 body += '\t\t\\emph{\\textbf{ATTENTION: '+str(len(not_played))+\
n@1118 202 ' fragments were not listened to in '+page_name+'! }}'+\
n@1118 203 ', '.join(not_played)+'\\\\ \n'
n@1118 204 if len(not_played) == 1:
n@1118 205 body += '\t\t\\emph{\\textbf{ATTENTION: one fragment was not listened to in '+page_name+'! }}'+\
n@1118 206 not_played[0]+'\\\\ \n'
n@1118 207
n@1118 208 # number of audio element markers not moved
n@1118 209 if len(not_moved) > 1:
n@1118 210 body += '\t\t\\emph{\\textbf{ATTENTION: '+str(len(not_moved))+\
n@1118 211 ' markers were not moved in '+page_name+'! }}'+\
n@1118 212 ', '.join(not_moved)+'\\\\ \n'
n@1118 213 if len(not_moved) == 1:
n@1118 214 body += '\t\t\\emph{\\textbf{ATTENTION: one marker was not moved in '+page_name+'! }}'+\
n@1118 215 not_moved[0]+'\\\\ \n'
n@1118 216
n@1118 217 # PRINT song-specific statistic
n@1118 218 individual_table += '\t\t'+page_name+'&'+\
n@1118 219 str(number_of_comments) + '/' +\
n@1118 220 str(number_of_comments+number_of_missing_comments)+'&'+\
n@1118 221 seconds2timestr(duration)+'\\\\\n'
n@1118 222
n@1118 223 # get timeline for this audioholder
n@1118 224 img_path = 'timelines_movement/'+file[:-4]+'-'+page_name+'.pdf'
n@1118 225
n@1118 226 # check if available
n@1118 227 if os.path.isfile(folder_name+img_path):
n@1118 228 # SHOW timeline image
n@1118 229 timeline_plots += '\\includegraphics[width=\\textwidth]{'+\
n@1118 230 folder_name+img_path+'}\n\t\t'
n@1118 231
n@1118 232 # keep track of duration in function of page index
n@1118 233 if len(duration_order)>page_number:
n@1118 234 duration_order[page_number].append(duration)
n@1118 235 else:
n@1118 236 duration_order.append([duration])
n@1118 237
n@1118 238 # keep list of audioholder ids and count how many times each audioholder id
n@1118 239 # was tested, how long it took, and how many fragments there were
n@1118 240 # (if number of fragments is different, store as different audioholder id)
n@1118 241 if page_name in page_names:
n@1118 242 page_index = page_names.index(page_name) # get index
n@1118 243 # check if number of audioelements the same
n@1118 244 if len(audioelements) == fragments_per_page[page_index]:
n@1118 245 page_count[page_index] += 1
n@1118 246 duration_page[page_index].append(duration)
n@1118 247 else: # make new entry
n@1118 248 alt_page_name = page_name+"("+str(len(audioelements))+")"
n@1118 249 if alt_page_name in page_names: # if already there
n@1118 250 alt_page_index = page_names.index(alt_page_name) # get index
n@1118 251 page_count[alt_page_index] += 1
n@1118 252 duration_page[alt_page_index].append(duration)
n@1118 253 else:
n@1118 254 page_names.append(alt_page_name)
n@1118 255 page_count.append(1)
n@1118 256 duration_page.append([duration])
n@1118 257 fragments_per_page.append(len(audioelements))
n@1118 258 else:
n@1118 259 page_names.append(page_name)
n@1118 260 page_count.append(1)
n@1118 261 duration_page.append([duration])
n@1118 262 fragments_per_page.append(len(audioelements))
n@1118 263
n@1118 264 # number of subjects per audioholder regardless of differing numbers of
n@1118 265 # fragments (for inclusion in box plots)
n@1118 266 if page_name in real_page_names:
n@1118 267 page_index = real_page_names.index(page_name) # get index
n@1118 268 subject_count[page_index] += 1
n@1118 269 else:
n@1118 270 real_page_names.append(page_name)
n@1118 271 subject_count.append(1)
n@1118 272
n@1118 273 # bookkeeping
n@1118 274 page_number += 1 # increase page count for this specific test
n@1118 275 number_of_pages += 1 # increase total number of pages
n@1118 276 time_per_page_accum += duration # total duration (for average time spent per page)
n@1118 277
n@1118 278 # PRINT table with statistics about this test
n@1118 279 body += '\t\t'+r'''\begin{tabular}{|p{3.5cm}|c|p{2.5cm}|}
n@1118 280 \hline
n@1118 281 \textbf{Song name} & \textbf{Comments} & \textbf{Duration} \\ \hline '''+\
n@1118 282 individual_table+'\t\t'+\
n@1118 283 r'''\hline
n@1118 284 \textbf{TOTAL} & & \textbf{'''+\
n@1118 285 seconds2timestr(total_duration)+\
n@1118 286 r'''}\\
n@1118 287 \hline
n@1118 288 \end{tabular}
n@1118 289
n@1118 290 '''
n@1118 291 # PRINT timeline plots
n@1118 292 body += timeline_plots
n@1118 293
n@1118 294 # join to footer
n@1118 295 footer = body + footer
n@1118 296
n@1118 297 # empty body again
n@1118 298 body = ''
n@1118 299
n@1118 300 # PRINT summary of everything (at start)
n@1118 301 # unnumbered so that number of sections equals number of files
n@1118 302 body += '\section*{Summary}\n\t\t\\addcontentsline{toc}{section}{Summary}\n'
n@1118 303
n@1118 304 # PRINT table with statistics
n@1118 305 body += '\t\t\\begin{tabular}{ll}\n\t\t\t'
n@1118 306 body += r'Number of XML files: &' + str(number_of_XML_files) + r'\\'+'\n\t\t\t'
n@1118 307 body += r'Number of pages: &' + str(number_of_pages) + r'\\'+'\n\t\t\t'
n@1118 308 body += r'Number of fragments: &' + str(number_of_fragments) + r'\\'+'\n\t\t\t'
n@1118 309 body += r'Number of empty comments: &' + str(total_empty_comments) +\
n@1118 310 " (" + str(round(100.0*total_empty_comments/number_of_fragments,2)) + r"\%)\\"+'\n\t\t\t'
n@1118 311 body += r'Number of unplayed fragments: &' + str(total_not_played) +\
n@1118 312 " (" + str(round(100.0*total_not_played/number_of_fragments,2)) + r"\%)\\"+'\n\t\t\t'
n@1118 313 body += r'Number of unmoved markers: &' + str(total_not_moved) +\
n@1118 314 " (" + str(round(100.0*total_not_moved/number_of_fragments,2)) + r"\%)\\"+'\n\t\t\t'
n@1118 315 body += r'Average time per page: &' + seconds2timestr(time_per_page_accum/number_of_pages) + r"\\"+'\n\t\t'
n@1118 316 body += '\\end{tabular} \\vspace{1.5cm} \\\\ \n'
n@1118 317
n@1118 318 # Average duration for first, second, ... page
n@1118 319 body += "\t\t\\vspace{.5cm} \n\n\t\tAverage duration per page (see also Figure \\ref{fig:avgtimeperpage}): \\\\ \n\t\t"
n@1118 320 body += r'''\begin{tabular}{lll}
n@1118 321 \textbf{Page} & \textbf{Duration} & \textbf{\# subjects}\\'''
n@1118 322 tpp_averages = [] # store average time per page
n@1118 323 for page_number in range(len(duration_order)):
n@1118 324 body += '\n\t\t\t'+str(page_number+1) + "&" +\
n@1118 325 seconds2timestr(sum(duration_order[page_number])/len(duration_order[page_number])) +\
n@1118 326 "&"+str(len(duration_order[page_number]))+r"\\"
n@1118 327 tpp_averages.append(sum(duration_order[page_number])/len(duration_order[page_number]))
n@1118 328
n@1118 329 body += '\n\t\t\\end{tabular} \\vspace{1.5cm} \\\\ \n\n\t\t'
n@1118 330
n@1118 331 # SHOW bar plot of average time per page
n@1118 332 plt.bar(range(1,len(duration_order)+1), np.array(tpp_averages)/60)
n@1118 333 plt.xlabel('Page order')
n@1118 334 plt.xlim(.8, len(duration_order)+1)
n@1118 335 plt.xticks(np.arange(1,len(duration_order)+1)+.4, range(1,len(duration_order)+1))
n@1118 336 plt.ylabel('Average time [minutes]')
n@1118 337 plt.savefig(folder_name+"time_per_page.pdf", bbox_inches='tight')
n@1118 338 plt.close()
n@1118 339 #TODO add error bars
n@1118 340
n@1118 341
n@1118 342 # Sort pages by number of audioelements, then by duration
n@1118 343
n@1118 344 # average duration and number of subjects per page
n@1118 345 average_duration_page = []
n@1118 346 number_of_subjects_page = []
n@1118 347 for line in duration_page:
n@1118 348 number_of_subjects_page.append(len(line))
n@1118 349 average_duration_page.append(sum(line)/len(line))
n@1118 350
n@1118 351 # combine and sort in function of number of audioelements and duration
n@1118 352 combined_list = [page_names, average_duration_page, fragments_per_page, number_of_subjects_page]
n@1118 353 combined_list = sorted(zip(*combined_list), key=operator.itemgetter(1, 2)) # sort
n@1118 354
n@1118 355 # Show average duration for all songs
n@1118 356 body += r'''\vspace{.5cm}
n@1118 357 Average duration per audioholder (see also Figure \ref{fig:avgtimeperaudioholder}): \\
n@1118 358 \begin{tabular}{llll}
n@1118 359 \textbf{Audioholder} & \textbf{Duration} & \textbf{\# subjects} & \textbf{\# fragments} \\'''
n@1118 360 audioholder_names_ordered = []
n@1118 361 average_duration_audioholder_ordered = []
n@1118 362 number_of_subjects = []
n@1118 363 for page_index in range(len(page_names)):
n@1118 364 audioholder_names_ordered.append(combined_list[page_index][0])
n@1118 365 average_duration_audioholder_ordered.append(combined_list[page_index][1])
n@1118 366 number_of_subjects.append(combined_list[page_index][3])
n@1118 367 body += '\n\t\t\t'+combined_list[page_index][0] + "&" +\
n@1118 368 seconds2timestr(combined_list[page_index][1]) + "&" +\
n@1118 369 str(combined_list[page_index][3]) + "&" +\
n@1118 370 str(combined_list[page_index][2]) + r"\\"
n@1118 371 body += '\n\t\t\\end{tabular}\n'
n@1118 372
n@1118 373 # SHOW bar plot of average time per page
n@1118 374 plt.bar(range(1,len(audioholder_names_ordered)+1), np.array(average_duration_audioholder_ordered)/60)
n@1118 375 plt.xlabel('Audioholder')
n@1118 376 plt.xlim(.8, len(audioholder_names_ordered)+1)
n@1118 377 plt.xticks(np.arange(1,len(audioholder_names_ordered)+1)+.4, audioholder_names_ordered, rotation=90)
n@1118 378 plt.ylabel('Average time [minutes]')
n@1118 379 plt.savefig(folder_name+"time_per_audioholder.pdf", bbox_inches='tight')
n@1118 380 plt.close()
n@1118 381
n@1118 382 # SHOW bar plot of average time per page
n@1118 383 plt.bar(range(1,len(audioholder_names_ordered)+1), number_of_subjects)
n@1118 384 plt.xlabel('Audioholder')
n@1118 385 plt.xlim(.8, len(audioholder_names_ordered)+1)
n@1118 386 plt.xticks(np.arange(1,len(audioholder_names_ordered)+1)+.4, audioholder_names_ordered, rotation=90)
n@1118 387 plt.ylabel('Number of subjects')
n@1118 388 ax = plt.gca()
n@1118 389 ylims = ax.get_ylim()
n@1118 390 yint = np.arange(int(np.floor(ylims[0])), int(np.ceil(ylims[1]))+1)
n@1118 391 plt.yticks(yint)
n@1118 392 plt.savefig(folder_name+"subjects_per_audioholder.pdf", bbox_inches='tight')
n@1118 393 plt.close()
n@1118 394
n@1118 395 # SHOW both figures
n@1118 396 body += r'''
n@1118 397 \begin{figure}[htbp]
n@1118 398 \begin{center}
n@1118 399 \includegraphics[width=.65\textwidth]{'''+\
n@1118 400 folder_name+'time_per_page.pdf'+\
n@1118 401 r'''}
n@1118 402 \caption{Average time spent per page.}
n@1118 403 \label{fig:avgtimeperpage}
n@1118 404 \end{center}
n@1118 405 \end{figure}
n@1118 406
n@1118 407 '''
n@1118 408 body += r'''\begin{figure}[htbp]
n@1118 409 \begin{center}
n@1118 410 \includegraphics[width=.65\textwidth]{'''+\
n@1118 411 folder_name+'time_per_audioholder.pdf'+\
n@1118 412 r'''}
n@1118 413 \caption{Average time spent per audioholder.}
n@1118 414 \label{fig:avgtimeperaudioholder}
n@1118 415 \end{center}
n@1118 416 \end{figure}
n@1118 417
n@1118 418 '''
n@1118 419 body += r'''\begin{figure}[htbp]
n@1118 420 \begin{center}
n@1118 421 \includegraphics[width=.65\textwidth]{'''+\
n@1118 422 folder_name+'subjects_per_audioholder.pdf'+\
n@1118 423 r'''}
n@1118 424 \caption{Number of subjects per audioholder.}
n@1118 425 \label{fig:subjectsperaudioholder}
n@1118 426 \end{center}
n@1118 427 \end{figure}
n@1118 428
n@1118 429 '''
n@1118 430 #TODO add error bars
n@1118 431 #TODO layout of figures
n@1118 432
n@1118 433 # SHOW boxplot per audioholder
n@1118 434 #TODO order in decreasing order of participants
n@1118 435 for audioholder_name in page_names: # get each name
n@1118 436 # plot boxplot if exists (not so for the 'alt' names)
n@1118 437 if os.path.isfile(folder_name+'ratings/'+audioholder_name+'-ratings-box.pdf'):
n@1118 438 body += r'''\begin{figure}[htbp]
n@1118 439 \begin{center}
n@1118 440 \includegraphics[width=.65\textwidth]{'''+\
n@1118 441 folder_name+"ratings/"+audioholder_name+'-ratings-box.pdf'+\
n@1118 442 r'''}
n@1118 443 \caption{Box plot of ratings for audioholder '''+\
n@1118 444 audioholder_name+' ('+str(subject_count[real_page_names.index(audioholder_name)])+\
n@1118 445 ''' participants).}
n@1118 446 \label{fig:boxplot'''+audioholder_name.replace(" ", "")+'''}
n@1118 447 \end{center}
n@1118 448 \end{figure}
n@1118 449
n@1118 450 '''
n@1118 451
n@1118 452 # DEMO pie chart of gender distribution among subjects
n@1118 453 genders = ['male', 'female', 'other', 'preferNotToSay', 'UNAVAILABLE']
n@1118 454 # TODO: get the above automatically
n@1118 455 gender_distribution = ''
n@1118 456 for item in genders:
n@1118 457 number = gender.count(item)
n@1118 458 if number>0:
n@1118 459 gender_distribution += str("{:.2f}".format((100.0*number)/len(gender)))+\
n@1118 460 '/'+item.capitalize()+' ('+str(number)+'),\n'
n@1118 461
n@1118 462 body += r'''
n@1118 463 % Pie chart of gender distribution
n@1118 464 \def\angle{0}
n@1118 465 \def\radius{3}
n@1118 466 \def\cyclelist{{"orange","blue","red","green"}}
n@1118 467 \newcount\cyclecount \cyclecount=-1
n@1118 468 \newcount\ind \ind=-1
n@1118 469 \begin{figure}[htbp]
n@1118 470 \begin{center}\begin{tikzpicture}[nodes = {font=\sffamily}]
n@1118 471 \foreach \percent/\name in {'''+\
n@1118 472 gender_distribution+\
n@1118 473 r'''} {\ifx\percent\empty\else % If \percent is empty, do nothing
n@1118 474 \global\advance\cyclecount by 1 % Advance cyclecount
n@1118 475 \global\advance\ind by 1 % Advance list index
n@1118 476 \ifnum6<\cyclecount % If cyclecount is larger than list
n@1118 477 \global\cyclecount=0 % reset cyclecount and
n@1118 478 \global\ind=0 % reset list index
n@1118 479 \fi
n@1118 480 \pgfmathparse{\cyclelist[\the\ind]} % Get color from cycle list
n@1118 481 \edef\color{\pgfmathresult} % and store as \color
n@1118 482 % Draw angle and set labels
n@1118 483 \draw[fill={\color!50},draw={\color}] (0,0) -- (\angle:\radius)
n@1118 484 arc (\angle:\angle+\percent*3.6:\radius) -- cycle;
n@1118 485 \node at (\angle+0.5*\percent*3.6:0.7*\radius) {\percent\,\%};
n@1118 486 \node[pin=\angle+0.5*\percent*3.6:\name]
n@1118 487 at (\angle+0.5*\percent*3.6:\radius) {};
n@1118 488 \pgfmathparse{\angle+\percent*3.6} % Advance angle
n@1118 489 \xdef\angle{\pgfmathresult} % and store in \angle
n@1118 490 \fi
n@1118 491 };
n@1118 492 \end{tikzpicture}
n@1118 493 \caption{Representation of gender across subjects}
n@1118 494 \label{default}
n@1118 495 \end{center}
n@1118 496 \end{figure}
n@1118 497
n@1118 498 '''
n@1118 499 # problem: some people entered twice?
n@1118 500
n@1118 501 #TODO
n@1118 502 # time per page in function of number of fragments (plot)
n@1118 503 # time per participant in function of number of pages
n@1118 504 # plot total time for each participant
n@1118 505 # show 'count' per page (in order)
n@1118 506
n@1118 507 # clear up page_index <> page_count <> page_number confusion
n@1118 508
n@1118 509
n@1118 510 texfile = header+body+footer # add bits together
n@1118 511
n@1118 512 print 'pdflatex -output-directory="'+folder_name+'"" "'+ folder_name + 'Report.tex"' # DEBUG
n@1118 513
n@1118 514 # write TeX file
n@1118 515 with open(folder_name + 'Report.tex','w') as f:
n@1118 516 f.write(texfile)
n@1118 517 proc=subprocess.Popen(shlex.split('pdflatex -output-directory="'+folder_name+'" "'+ folder_name + 'Report.tex"'))
n@1118 518 proc.communicate()
n@1118 519 # run again
n@1118 520 proc=subprocess.Popen(shlex.split('pdflatex -output-directory="'+folder_name+'" "'+ folder_name + 'Report.tex"'))
n@1118 521 proc.communicate()
n@1118 522
n@1118 523 #TODO remove auxiliary LaTeX files
n@1118 524 try:
n@1118 525 os.remove(folder_name + 'Report.aux')
n@1118 526 os.remove(folder_name + 'Report.log')
n@1118 527 os.remove(folder_name + 'Report.out')
n@1118 528 os.remove(folder_name + 'Report.toc')
n@1118 529 except OSError:
n@1118 530 pass
n@1118 531