class SARVEvents::Recording
Attributes
duration[RW]
files[RW]
finish[RW]
meeting_id[RW]
metadata[RW]
start[RW]
timestamp[RW]
Public Class Methods
new(events_xml)
click to toggle source
# File lib/sarvevents/recording.rb, line 14 def initialize(events_xml) filename = File.basename(events_xml) raise "#{filename} is not a file or does not exist." unless File.file?(events_xml) # The Hash.from_xml automatically converts keys with dashes '-' to snake_case # (i.e canvas-recording-ready-url becomes canvas_recording_ready_url) # see https://www.rubydoc.info/github/datamapper/extlib/Hash.from_xml raw_recording_data = Hash.from_xml(File.read(events_xml)) raise "#{filename} is not a valid xml file (unable to parse)." if raw_recording_data.nil? raise "#{filename} is missing recording key." unless raw_recording_data.key?("recording") recording_data = raw_recording_data["recording"] events = recording_data["event"] events = [] if events.nil? events = [events] unless events.is_a?(Array) @metadata = recording_data["metadata"] @meeting_id = recording_data["metadata"]["meetingId"] internal_meeting_id = recording_data["meeting"]["id"] @timestamp = extract_timestamp(internal_meeting_id) @start = Time.at(@timestamp / 1000) if events.length > 0 @first_event = events.first["timestamp"].to_i @last_event = events.last["timestamp"].to_i @finish = Time.at(timestamp_conversion(@last_event)) else @finish = @start end @duration = (@finish - @start).to_i @attendees = {} @polls = {} @files = [] # Map to look up external user id (for @data[:attendees]) from the # internal user id in most recording events @externalUserId = {} process_events(events) @attendees.values.each do |att| att.leaves << @finish if att.joins.length > att.leaves.length att.duration = total_duration(@finish, att) end end
Public Instance Methods
attendees()
click to toggle source
Take only the values since we no longer need to index.
# File lib/sarvevents/recording.rb, line 65 def attendees @attendees.values end
build_join_left_tuples(joins_lefts_arr_sorted)
click to toggle source
# File lib/sarvevents/recording.rb, line 231 def build_join_left_tuples(joins_lefts_arr_sorted) jl_tuples = [] jl_tuple = {:join => nil, :left => nil} loop_state = :find_join events_length = joins_lefts_arr_sorted.length - 1 for i in 0..events_length cur_event = joins_lefts_arr_sorted[i] if loop_state == :find_join and cur_event[:event] == :join jl_tuple[:join] = cur_event loop_state = :find_left end next_event = nil if i < events_length next_event = joins_lefts_arr_sorted[i + 1] end if loop_state == :find_left if next_event != nil and next_event[:event] == :left # skip the current event to get to the next event elsif (cur_event[:event] == :left and next_event != nil and next_event[:event] == :join) or (i == events_length) jl_tuple[:left] = cur_event jl_tuples.append(jl_tuple) jl_tuple = {:join => nil, :left => nil} loop_state = :find_join end end end jl_tuples end
build_join_lefts_array(last_event_timestamp, user_session)
click to toggle source
# File lib/sarvevents/recording.rb, line 266 def build_join_lefts_array(last_event_timestamp, user_session) joins_leaves_arr = [] lefts_count = 0 joins_count = 0 user_session.each do | userid, joins_lefts | lefts_count += joins_lefts[:lefts].length joins_count += joins_lefts[:joins].length joins_lefts[:joins].each { |j| joins_leaves_arr.append(j)} joins_lefts[:lefts].each { |j| joins_leaves_arr.append(j)} end if joins_count > lefts_count last_event = joins_leaves_arr[-1] joins_leaves_arr.append({:timestamp => last_event_timestamp, :userid => " system ", :ext_userid=> last_event[:ext_userid], :event => :left}) end joins_leaves_arr end
build_tuples_of_kept_events(kept_events)
click to toggle source
# File lib/sarvevents/recording.rb, line 315 def build_tuples_of_kept_events(kept_events) odd_events = [] even_events = [] for i in 0..kept_events.length - 1 odd_even = i + 1 if odd_even.even? even_events.append(kept_events[i]) else odd_events.append(kept_events[i]) end end tuples = [] for i in 0..odd_events.length - 1 tuple = {:start => odd_events[i], :end => even_events[i]} tuples.append(tuple) end tuples end
calculate_user_duration(join_events, left_events)
click to toggle source
# File lib/sarvevents/recording.rb, line 127 def calculate_user_duration(join_events, left_events) joins_leaves_arr = [] join_events.each { |j| joins_leaves_arr.append({:time => j.to_i, :datetime => j, :event => :join})} left_events.each { |j| joins_leaves_arr.append({:time => j.to_i, :datetime => j, :event => :left})} joins_leaves_arr_sorted = joins_leaves_arr.sort_by { |event| event[:time] } partial_duration = 0 prev_event = nil joins_leaves_arr_sorted.each do |cur_event| duration = 0 if prev_event != nil and cur_event[:event] == :join and prev_event[:event] == :left # user left and rejoining, don't update duration prev_event = cur_event elsif prev_event != nil duration = cur_event[:time] - prev_event[:time] partial_duration += duration prev_event = cur_event else prev_event = cur_event end end return partial_duration end
calculate_user_duration_based_on_userid(last_event_ts, sessions)
click to toggle source
# File lib/sarvevents/recording.rb, line 154 def calculate_user_duration_based_on_userid(last_event_ts, sessions) # combine join and left events into an array joins_lefts_arr = build_join_lefts_array(last_event_ts, sessions) # sort the events joins_lefts_arr_sorted = joins_lefts_arr.sort_by { |event| event[:timestamp] } combined_tuples = combine_tuples_by_userid(sessions) combined_tuples_sorted = fill_missing_left_events(combined_tuples) prepare_joins_lefts_for_overlap_checks(joins_lefts_arr_sorted) mark_overlapping_events(combined_tuples_sorted, joins_lefts_arr_sorted) removed_overlap_events = remove_overlapping_events(joins_lefts_arr_sorted) duration_tuples = build_join_left_tuples(removed_overlap_events) partial_duration = 0 duration_tuples.each do |tuple| duration = tuple[:left][:timestamp].to_i - tuple[:join][:timestamp].to_i partial_duration += duration end partial_duration end
combine_tuples_by_userid(user_sessions)
click to toggle source
# File lib/sarvevents/recording.rb, line 193 def combine_tuples_by_userid(user_sessions) combined_tuples = [] user_sessions.each do | userid, joins_lefts | joins_lefts_arr = [] joins_lefts[:joins].each { |j| joins_lefts_arr.append(j)} joins_lefts[:lefts].each { |j| joins_lefts_arr.append(j)} tuples = tuples_by_userid(joins_lefts[:joins], joins_lefts[:lefts]) tuples.each do |tuple| combined_tuples.append(tuple) end end combined_tuples end
create_csv(filepath)
click to toggle source
Export recording data to a CSV file.
# File lib/sarvevents/recording.rb, line 95 def create_csv(filepath) CSV.open(filepath, "wb") do |csv| csv << CSV_HEADER.map(&:capitalize) + (1..polls.length).map { |i| "Poll #{i}" } @attendees.each do |id, att| csv << att.csv_row + polls.map { |poll| poll.votes[id] || NO_VOTE_SYMBOL } end end end
fill_missing_left_events(combined_tuples)
click to toggle source
# File lib/sarvevents/recording.rb, line 211 def fill_missing_left_events(combined_tuples) joins_lefts_arr_sorted = combined_tuples.sort_by { |event| event[:join][:timestamp]} joins_lefts_arr_sorted_length = joins_lefts_arr_sorted.length - 1 for i in 0..joins_lefts_arr_sorted_length cur_event = joins_lefts_arr_sorted[i] if cur_event[:left].nil? unless joins_lefts_arr_sorted_length == i # Take the next event as the left event for this current event next_event = joins_lefts_arr_sorted[i + 1] left_event = {:timestamp => next_event[:timestamp], :userid => cur_event[:userid], :event => :left} cur_event[:left] = left_event end end end joins_lefts_arr_sorted end
mark_overlapping_events(combined_tuples_sorted, joins_leaves_arr_sorted)
click to toggle source
# File lib/sarvevents/recording.rb, line 292 def mark_overlapping_events(combined_tuples_sorted, joins_leaves_arr_sorted) combined_tuples_sorted.each do |ce| joins_leaves_arr_sorted.each do |jl| event_ts = jl[:timestamp].to_i ce_join = ce[:join][:timestamp].to_i if event_ts > ce_join and not ce[:left].nil? and event_ts < ce[:left][:timestamp].to_i jl[:remove] = true end end end end
moderators()
click to toggle source
Retrieve a list of all the moderators.
# File lib/sarvevents/recording.rb, line 70 def moderators attendees.select(&:moderator?) end
polls()
click to toggle source
Take only the values since we no longer need to index.
# File lib/sarvevents/recording.rb, line 80 def polls @polls.values end
prepare_joins_lefts_for_overlap_checks(joins_leaves_arr_sorted)
click to toggle source
# File lib/sarvevents/recording.rb, line 286 def prepare_joins_lefts_for_overlap_checks(joins_leaves_arr_sorted) joins_leaves_arr_sorted.each do |event| event[:remove] = false end end
published_polls()
click to toggle source
Retrieve a list of published polls.
# File lib/sarvevents/recording.rb, line 85 def published_polls polls.select(&:published?) end
remove_overlapping_events(joins_leaves_arr_sorted)
click to toggle source
# File lib/sarvevents/recording.rb, line 305 def remove_overlapping_events(joins_leaves_arr_sorted) keep_events = [] joins_leaves_arr_sorted.each do |ev| if not ev[:remove] keep_events.append(ev) end end keep_events end
to_h()
click to toggle source
# File lib/sarvevents/recording.rb, line 104 def to_h # Transform any CamelCase keys to snake_case. @metadata.deep_transform_keys! do |key| k = key.to_s.underscore rescue key k.to_sym rescue key end { metadata: @metadata, meeting_id: @meeting_id, duration: @duration, start: @start, finish: @finish, attendees: attendees.map(&:to_h), files: @files, polls: polls.map(&:to_h) } end
to_json()
click to toggle source
# File lib/sarvevents/recording.rb, line 123 def to_json to_h.to_json end
tuples_by_userid(joins_arr, lefts_arr)
click to toggle source
# File lib/sarvevents/recording.rb, line 179 def tuples_by_userid(joins_arr, lefts_arr) joins_length = joins_arr.length - 1 tuples = [] for i in 0..joins_length tuple = {:join => joins_arr[i], :left => nil} if i <= lefts_arr.length - 1 tuple[:left] = lefts_arr[i] end tuples.append(tuple) end tuples end
unpublished_polls()
click to toggle source
Retrieve a list of unpublished polls.
# File lib/sarvevents/recording.rb, line 90 def unpublished_polls polls.reject(&:published?) end
viewers()
click to toggle source
Retrieve a list of all the viewers.
# File lib/sarvevents/recording.rb, line 75 def viewers attendees.reject(&:moderator?) end
Private Instance Methods
extract_timestamp(meeting_id)
click to toggle source
Extracts the timestamp from a meeting id.
# File lib/sarvevents/recording.rb, line 347 def extract_timestamp(meeting_id) meeting_id.split("-").last.to_i end
process_events(events)
click to toggle source
Process all the events in the events.xml file.
# File lib/sarvevents/recording.rb, line 339 def process_events(events) events.each do |e| event = e["eventname"].underscore send(event, e) if RECORDABLE_EVENTS.include?(event) end end
timestamp_conversion(base)
click to toggle source
Converts the sarvwave timestamps to proper time.
# File lib/sarvevents/recording.rb, line 352 def timestamp_conversion(base) (base.to_i - @first_event + @timestamp) / 1000 end
total_duration(last_event_ts, att)
click to toggle source
Calculates an attendee’s duration.
# File lib/sarvevents/recording.rb, line 357 def total_duration(last_event_ts, att) calculate_user_duration_based_on_userid(last_event_ts, att.sessions) end