annotate scripts/timeline_view_movement.py @ 1316:279930a008ca

All interfaces support comment boxes. Comment box identification matches presented tag (for instance, AB will be Comment on fragment A, rather than 1). Tighter buffer loading protocol, audioObjects register with the buffer rather than checking for buffer existence (which can be buggy depending on the buffer state). Buffers now have a state to ensure exact location in loading chain (downloading, decoding, LUFS, ready).
author Nicholas Jillings <nickjillings@users.noreply.github.com>
date Fri, 29 Jan 2016 11:11:57 +0000
parents
children 235594325b84 b5bf2f57187c
rev   line source
nickjillings@1316 1 #!/usr/bin/python
nickjillings@1316 2
nickjillings@1316 3 import xml.etree.ElementTree as ET
nickjillings@1316 4 import os # list files in directory
nickjillings@1316 5 import sys # command line arguments
nickjillings@1316 6 import matplotlib.pyplot as plt # plots
nickjillings@1316 7 import matplotlib.patches as patches # rectangles
nickjillings@1316 8
nickjillings@1316 9
nickjillings@1316 10 # COMMAND LINE ARGUMENTS
nickjillings@1316 11
nickjillings@1316 12 assert len(sys.argv)<3, "timeline_view_movement takes at most 1 command line argument\n"+\
nickjillings@1316 13 "Use: python timeline_view_movement.py [XML_files_location]"
nickjillings@1316 14
nickjillings@1316 15 # XML results files location
nickjillings@1316 16 if len(sys.argv) == 1:
nickjillings@1316 17 folder_name = "../saves" # Looks in 'saves/' folder from 'scripts/' folder
nickjillings@1316 18 print "Use: python timeline_view_movement.py [XML_files_location]"
nickjillings@1316 19 print "Using default path: " + folder_name
nickjillings@1316 20 elif len(sys.argv) == 2:
nickjillings@1316 21 folder_name = sys.argv[1] # First command line argument is folder
nickjillings@1316 22
nickjillings@1316 23 # check if folder_name exists
nickjillings@1316 24 if not os.path.exists(folder_name):
nickjillings@1316 25 #the file is not there
nickjillings@1316 26 print "Folder '"+folder_name+"' does not exist."
nickjillings@1316 27 sys.exit() # terminate script execution
nickjillings@1316 28 elif not os.access(os.path.dirname(folder_name), os.W_OK):
nickjillings@1316 29 #the file does exist but write privileges are not given
nickjillings@1316 30 print "No write privileges in folder '"+folder_name+"'."
nickjillings@1316 31
nickjillings@1316 32
nickjillings@1316 33 # CONFIGURATION
nickjillings@1316 34
nickjillings@1316 35 # Folder where to store timelines
nickjillings@1316 36 timeline_folder = folder_name + '/timelines_movement/' # Stores in 'saves/timelines_movement/' by default
nickjillings@1316 37
nickjillings@1316 38 # Font settings
nickjillings@1316 39 font = {'weight' : 'bold',
nickjillings@1316 40 'size' : 16}
nickjillings@1316 41 plt.rc('font', **font)
nickjillings@1316 42
nickjillings@1316 43 # Colormap for to cycle through
nickjillings@1316 44 colormap = ['b', 'g', 'c', 'm', 'y', 'k']
nickjillings@1316 45
nickjillings@1316 46 # figure size
nickjillings@1316 47 fig_width = 25
nickjillings@1316 48 fig_height = 10
nickjillings@1316 49
nickjillings@1316 50
nickjillings@1316 51 # CODE
nickjillings@1316 52
nickjillings@1316 53 # create timeline_folder if not yet created
nickjillings@1316 54 if not os.path.exists(timeline_folder):
nickjillings@1316 55 os.makedirs(timeline_folder)
nickjillings@1316 56
nickjillings@1316 57 # get every XML file in folder
nickjillings@1316 58 for file in os.listdir(folder_name):
nickjillings@1316 59 if file.endswith(".xml"):
nickjillings@1316 60 tree = ET.parse(folder_name + '/' + file)
nickjillings@1316 61 root = tree.getroot()
nickjillings@1316 62 subject_id = file[:-4] # drop '.xml'
nickjillings@1316 63
nickjillings@1316 64 previous_audioholder_time = 0 # time spent before current audioholder
nickjillings@1316 65 time_offset = 0 # test starts at zero
nickjillings@1316 66
nickjillings@1316 67 # ONE TIMELINE PER PAGE - make new plot per page
nickjillings@1316 68
nickjillings@1316 69 # get list of all page names
nickjillings@1316 70 for audioholder in root.findall("./audioholder"): # iterate over pages
nickjillings@1316 71 page_name = audioholder.get('id') # get page name
nickjillings@1316 72 plot_empty = True # check if any data is plotted
nickjillings@1316 73
nickjillings@1316 74 if page_name is None: # ignore 'empty' audio_holders
nickjillings@1316 75 print "Skipping empty audioholder name from "+subject_id+"."
nickjillings@1316 76 break
nickjillings@1316 77
nickjillings@1316 78 # subtract total audioholder length from subsequent audioholder event times
nickjillings@1316 79 audioholder_time_temp = audioholder.find("./metric/metricresult/[@id='testTime']")
nickjillings@1316 80 if audioholder_time_temp is not None:
nickjillings@1316 81 audioholder_time = float(audioholder_time_temp.text)
nickjillings@1316 82 else:
nickjillings@1316 83 print "Skipping audioholder without total time specified from "+subject_id+"."
nickjillings@1316 84 break
nickjillings@1316 85
nickjillings@1316 86 # get audioelements
nickjillings@1316 87 audioelements = audioholder.findall("./audioelement")
nickjillings@1316 88
nickjillings@1316 89 # sort alphabetically
nickjillings@1316 90 data = []
nickjillings@1316 91 for elem in audioelements: # from http://effbot.org/zone/element-sort.htm
nickjillings@1316 92 key = elem.get("id")
nickjillings@1316 93 data.append((key, elem))
nickjillings@1316 94 data.sort()
nickjillings@1316 95
nickjillings@1316 96 N_audioelements = len(audioelements) # number of audio elements for this page
nickjillings@1316 97 increment = 0 # increased for every new audioelement
nickjillings@1316 98
nickjillings@1316 99 # get axes handle
nickjillings@1316 100 fig = plt.figure(figsize=(fig_width, fig_height))
nickjillings@1316 101 ax = fig.add_subplot(111)
nickjillings@1316 102
nickjillings@1316 103 # for page [page_name], print comments related to fragment [id]
nickjillings@1316 104 #for tuple in data:
nickjillings@1316 105 # audioelement = tuple[1]
nickjillings@1316 106 for tuple in data:
nickjillings@1316 107 audioelement = tuple[1]
nickjillings@1316 108 if audioelement is not None: # Check it exists
nickjillings@1316 109 audio_id = str(audioelement.get('id'))
nickjillings@1316 110
nickjillings@1316 111 # break if no initial position or move events registered
nickjillings@1316 112 initial_position_temp = audioelement.find("./metric/metricresult/[@name='elementInitialPosition']")
nickjillings@1316 113 if initial_position_temp is None:
nickjillings@1316 114 print "Skipping "+page_name+" from "+subject_id+": does not have initial positions specified."
nickjillings@1316 115 break
nickjillings@1316 116
nickjillings@1316 117 # get move events, initial and eventual position
nickjillings@1316 118 initial_position = float(initial_position_temp.text)
nickjillings@1316 119 move_events = audioelement.findall("./metric/metricresult/[@name='elementTrackerFull']/timepos")
nickjillings@1316 120 final_position = float(audioelement.find("./value").text)
nickjillings@1316 121
nickjillings@1316 122 # get listen events
nickjillings@1316 123 start_times_global = []
nickjillings@1316 124 stop_times_global = []
nickjillings@1316 125 listen_events = audioelement.findall("./metric/metricresult/[@name='elementListenTracker']/event")
nickjillings@1316 126 for event in listen_events:
nickjillings@1316 127 # get testtime: start and stop
nickjillings@1316 128 start_times_global.append(float(event.find('testtime').get('start'))-time_offset)
nickjillings@1316 129 stop_times_global.append(float(event.find('testtime').get('stop'))-time_offset)
nickjillings@1316 130
nickjillings@1316 131 # display fragment name at start
nickjillings@1316 132 plt.text(0,initial_position+0.02,audio_id,color=colormap[increment%len(colormap)]) #,rotation=45
nickjillings@1316 133
nickjillings@1316 134 # previous position and time
nickjillings@1316 135 previous_position = initial_position
nickjillings@1316 136 previous_time = 0
nickjillings@1316 137
nickjillings@1316 138 # assume not playing at start
nickjillings@1316 139 currently_playing = False # keep track of whether fragment is playing during move event
nickjillings@1316 140
nickjillings@1316 141 # draw all segments except final one
nickjillings@1316 142 for event in move_events:
nickjillings@1316 143 # mark this plot as not empty
nickjillings@1316 144 plot_empty = False
nickjillings@1316 145
nickjillings@1316 146 # get time and final position of move event
nickjillings@1316 147 new_time = float(event.find("./time").text)-time_offset
nickjillings@1316 148 new_position = float(event.find("./position").text)
nickjillings@1316 149
nickjillings@1316 150 # get play/stop events since last move until current move event
nickjillings@1316 151 stop_times = []
nickjillings@1316 152 start_times = []
nickjillings@1316 153 # is there a play and/or stop event between previous_time and new_time?
nickjillings@1316 154 for time in start_times_global:
nickjillings@1316 155 if time>previous_time and time<new_time:
nickjillings@1316 156 start_times.append(time)
nickjillings@1316 157 for time in stop_times_global:
nickjillings@1316 158 if time>previous_time and time<new_time:
nickjillings@1316 159 stop_times.append(time)
nickjillings@1316 160 # if no play/stop events between move events, find out whether playing
nickjillings@1316 161
nickjillings@1316 162 segment_start = previous_time # first segment starts at previous move event
nickjillings@1316 163
nickjillings@1316 164 # draw segments (horizontal line)
nickjillings@1316 165 while len(start_times)+len(stop_times)>0: # while still play/stop events left
nickjillings@1316 166 if len(stop_times)<1: # upcoming event is 'play'
nickjillings@1316 167 # draw non-playing segment from segment_start to 'play'
nickjillings@1316 168 currently_playing = False
nickjillings@1316 169 segment_stop = start_times.pop(0) # remove and return first item
nickjillings@1316 170 elif len(start_times)<1: # upcoming event is 'stop'
nickjillings@1316 171 # draw playing segment (red) from segment_start to 'stop'
nickjillings@1316 172 currently_playing = True
nickjillings@1316 173 segment_stop = stop_times.pop(0) # remove and return first item
nickjillings@1316 174 elif start_times[0]<stop_times[0]: # upcoming event is 'play'
nickjillings@1316 175 # draw non-playing segment from segment_start to 'play'
nickjillings@1316 176 currently_playing = False
nickjillings@1316 177 segment_stop = start_times.pop(0) # remove and return first item
nickjillings@1316 178 else: # stop_times[0]<start_times[0]: upcoming event is 'stop'
nickjillings@1316 179 # draw playing segment (red) from segment_start to 'stop'
nickjillings@1316 180 currently_playing = True
nickjillings@1316 181 segment_stop = stop_times.pop(0) # remove and return first item
nickjillings@1316 182
nickjillings@1316 183 # draw segment
nickjillings@1316 184 plt.plot([segment_start, segment_stop], # x-values
nickjillings@1316 185 [previous_position, previous_position], # y-values
nickjillings@1316 186 color='r' if currently_playing else colormap[increment%len(colormap)],
nickjillings@1316 187 linewidth=3
nickjillings@1316 188 )
nickjillings@1316 189 segment_start = segment_stop # move on to next segment
nickjillings@1316 190 currently_playing = not currently_playing # toggle to draw final segment correctly
nickjillings@1316 191
nickjillings@1316 192 # draw final segment (horizontal line) from last 'segment_start' to current move event time
nickjillings@1316 193 plt.plot([segment_start, new_time], # x-values
nickjillings@1316 194 [previous_position, previous_position], # y-values
nickjillings@1316 195 # color depends on playing during move event or not:
nickjillings@1316 196 color='r' if currently_playing else colormap[increment%len(colormap)],
nickjillings@1316 197 linewidth=3
nickjillings@1316 198 )
nickjillings@1316 199
nickjillings@1316 200 # vertical line from previous to current position
nickjillings@1316 201 plt.plot([new_time, new_time], # x-values
nickjillings@1316 202 [previous_position, new_position], # y-values
nickjillings@1316 203 # color depends on playing during move event or not:
nickjillings@1316 204 color='r' if currently_playing else colormap[increment%len(colormap)],
nickjillings@1316 205 linewidth=3
nickjillings@1316 206 )
nickjillings@1316 207
nickjillings@1316 208 # update previous_position value
nickjillings@1316 209 previous_position = new_position
nickjillings@1316 210 previous_time = new_time
nickjillings@1316 211
nickjillings@1316 212
nickjillings@1316 213
nickjillings@1316 214 # draw final horizontal segment (or only segment if audioelement not moved)
nickjillings@1316 215 # horizontal line from previous time to end of audioholder
nickjillings@1316 216
nickjillings@1316 217 # get play/stop events since last move until current move event
nickjillings@1316 218 stop_times = []
nickjillings@1316 219 start_times = []
nickjillings@1316 220 # is there a play and/or stop event between previous_time and new_time?
nickjillings@1316 221 for time in start_times_global:
nickjillings@1316 222 if time>previous_time and time<audioholder_time-time_offset:
nickjillings@1316 223 start_times.append(time)
nickjillings@1316 224 for time in stop_times_global:
nickjillings@1316 225 if time>previous_time and time<audioholder_time-time_offset:
nickjillings@1316 226 stop_times.append(time)
nickjillings@1316 227 # if no play/stop events between move events, find out whether playing
nickjillings@1316 228
nickjillings@1316 229 segment_start = previous_time # first segment starts at previous move event
nickjillings@1316 230
nickjillings@1316 231 # draw segments (horizontal line)
nickjillings@1316 232 while len(start_times)+len(stop_times)>0: # while still play/stop events left
nickjillings@1316 233 # mark this plot as not empty
nickjillings@1316 234 plot_empty = False
nickjillings@1316 235 if len(stop_times)<1: # upcoming event is 'play'
nickjillings@1316 236 # draw non-playing segment from segment_start to 'play'
nickjillings@1316 237 currently_playing = False
nickjillings@1316 238 segment_stop = start_times.pop(0) # remove and return first item
nickjillings@1316 239 elif len(start_times)<1: # upcoming event is 'stop'
nickjillings@1316 240 # draw playing segment (red) from segment_start to 'stop'
nickjillings@1316 241 currently_playing = True
nickjillings@1316 242 segment_stop = stop_times.pop(0) # remove and return first item
nickjillings@1316 243 elif start_times[0]<stop_times[0]: # upcoming event is 'play'
nickjillings@1316 244 # draw non-playing segment from segment_start to 'play'
nickjillings@1316 245 currently_playing = False
nickjillings@1316 246 segment_stop = start_times.pop(0) # remove and return first item
nickjillings@1316 247 else: # stop_times[0]<start_times[0]: upcoming event is 'stop'
nickjillings@1316 248 # draw playing segment (red) from segment_start to 'stop'
nickjillings@1316 249 currently_playing = True
nickjillings@1316 250 segment_stop = stop_times.pop(0) # remove and return first item
nickjillings@1316 251
nickjillings@1316 252 # draw segment
nickjillings@1316 253 plt.plot([segment_start, segment_stop], # x-values
nickjillings@1316 254 [previous_position, previous_position], # y-values
nickjillings@1316 255 color='r' if currently_playing else colormap[increment%len(colormap)],
nickjillings@1316 256 linewidth=3
nickjillings@1316 257 )
nickjillings@1316 258 segment_start = segment_stop # move on to next segment
nickjillings@1316 259 currently_playing = not currently_playing # toggle to draw final segment correctly
nickjillings@1316 260
nickjillings@1316 261 # draw final segment (horizontal line) from last 'segment_start' to current move event time
nickjillings@1316 262 plt.plot([segment_start, audioholder_time-time_offset], # x-values
nickjillings@1316 263 [previous_position, previous_position], # y-values
nickjillings@1316 264 # color depends on playing during move event or not:
nickjillings@1316 265 color='r' if currently_playing else colormap[increment%len(colormap)],
nickjillings@1316 266 linewidth=3
nickjillings@1316 267 )
nickjillings@1316 268
nickjillings@1316 269 # plt.plot([previous_time, audioholder_time-time_offset], # x-values
nickjillings@1316 270 # [previous_position, previous_position], # y-values
nickjillings@1316 271 # color=colormap[increment%len(colormap)],
nickjillings@1316 272 # linewidth=3
nickjillings@1316 273 # )
nickjillings@1316 274
nickjillings@1316 275 # display fragment name at end
nickjillings@1316 276 plt.text(audioholder_time-time_offset,previous_position,\
nickjillings@1316 277 audio_id,color=colormap[increment%len(colormap)]) #,rotation=45
nickjillings@1316 278
nickjillings@1316 279 increment+=1 # to next audioelement
nickjillings@1316 280
nickjillings@1316 281 last_audioholder_duration = audioholder_time-time_offset
nickjillings@1316 282 time_offset = audioholder_time
nickjillings@1316 283
nickjillings@1316 284 if not plot_empty: # if plot is not empty, show or store
nickjillings@1316 285 # set plot parameters
nickjillings@1316 286 plt.title('Timeline ' + file + ": "+page_name)
nickjillings@1316 287 plt.xlabel('Time [seconds]')
nickjillings@1316 288 plt.xlim(0, last_audioholder_duration)
nickjillings@1316 289 plt.ylabel('Rating') # default
nickjillings@1316 290 plt.ylim(0, 1) # rating between 0 and 1
nickjillings@1316 291
nickjillings@1316 292 #y-ticks: labels on rating axis
nickjillings@1316 293 label_positions = []
nickjillings@1316 294 label_text = []
nickjillings@1316 295 scale_tags = root.findall("./BrowserEvalProjectDocument/audioHolder/interface/scale")
nickjillings@1316 296 scale_title = root.find("./BrowserEvalProjectDocument/audioHolder/interface/title")
nickjillings@1316 297 for tag in scale_tags:
nickjillings@1316 298 label_positions.append(float(tag.get('position'))/100) # on a scale from 0 to 100
nickjillings@1316 299 label_text.append(tag.text)
nickjillings@1316 300 if len(label_positions) > 0: # if any labels available
nickjillings@1316 301 plt.yticks(label_positions, label_text) # show rating axis labels
nickjillings@1316 302 # set label Y-axis
nickjillings@1316 303 if scale_title is not None:
nickjillings@1316 304 plt.ylabel(scale_title.text)
nickjillings@1316 305
nickjillings@1316 306 #plt.show() # uncomment to show plot; comment when just saving
nickjillings@1316 307 #exit()
nickjillings@1316 308
nickjillings@1316 309 plt.savefig(timeline_folder+subject_id+"-"+page_name+".pdf", bbox_inches='tight')
nickjillings@1316 310 plt.close()
nickjillings@1316 311