class StepsRecorderAnalyser
Attributes
duration[R]
start_time[R]
steps[RW]
Public Class Methods
new(s, debug: false, savepath: '/tmp', title: 'Untitled', working_dir: '/tmp')
click to toggle source
# File lib/sra2019.rb, line 48 def initialize(s, debug: false, savepath: '/tmp', title: 'Untitled', working_dir: '/tmp') @savepath, @title, @working_dir, @debug = savepath, title, working_dir, debug raw_content, type = RXFHelper.read(s) content = if type == :file and File.extname(s) == '.zip' then Zip::ZipFile.new(s).instance_eval {read(to_a[0].name)} else raw_content end puts ('content: ' + content.inspect).debug if @debug @actions = parse_report content @all_steps = parse_steps content @doc = build @all_steps steps = @all_steps.select {|x| x[:user_comment]} @steps = steps.any? ? steps : @all_steps # find the duration for each step @steps.each.with_index do |x, i| x.duration = if i < @steps.length - 1 then @steps[i+1].time - x.time else @duration - x.time end end end
Public Instance Methods
import(s)
click to toggle source
# File lib/sra2019.rb, line 84 def import(s) @dx = Dynarex.new @dx.import s end
remove_steps(a)
click to toggle source
# File lib/sra2019.rb, line 91 def remove_steps(a) a.each {|n| @steps[n] = nil } @steps.compact! end
tidy!()
click to toggle source
# File lib/sra2019.rb, line 97 def tidy!() verbose_level = 0 @steps.each do |x| x.desc.gsub!(/\s*\([^\)]+\)\s*/,'') x.desc.sub!(/ in "\w+"$/,'') x.desc.sub!(/"User account for [^"]+"/,'the User account icon.') if x.desc =~ /User left click/ and verbose_level == 0 then x.desc.sub!(/User left click/, 'Using the mouse, left click') verbose_level = 1 elsif x.desc =~ /User left click/ and verbose_level == 1 x.desc.sub!(/User left click/, 'Left click') verbose_level = 2 elsif x.desc =~ /User left click/ and verbose_level == 2 x.desc.sub!(/User left click/, 'Click') verbose_level = 3 elsif x.desc =~ /User left click/ and verbose_level == 3 x.desc.sub!(/User left click on/, 'Select') else verbose_level = 0 end end end
to_dx()
click to toggle source
Returns a Dynarex object
# File lib/sra2019.rb, line 137 def to_dx() @dx = Dynarex.new 'instructions/instruction(step, imgsrc, description)' @dx.summary[:rawdoc_type] = 'rowx' @steps.each.with_index do |h, i| @dx.create step: i+1, imgsrc: "screenshot#{i+1}.jpg", description: h[:user_comment] || h[:desc] end @dx end
to_html(filepath=File.join(@savepath, 'sra' + Time.now.to_i.to_s))
click to toggle source
Writes the steps to an HTML file
# File lib/sra2019.rb, line 162 def to_html(filepath=File.join(@savepath, 'sra' + Time.now.to_i.to_s)) # save the image files to a file directory. # name the sub-directory using the date and time? imgpath = File.join(filepath, 'images') csspath = File.join(filepath, 'css') [filepath, imgpath, csspath].each {|x| FileUtils.mkdir x} @steps.each.with_index do |x, i| File.open(File.join(imgpath, "screenshot#{i}.jpg" ), 'wb') \ {|f| f.write(x[:screenshot]) } end rows = @steps.map.with_index do |x, i| li = " li markdown: #{x[:user_comment]}  " end @sliml=<<EOF html head title #{@title}: #{@steps.length} Steps (with Pictures) link rel='stylesheet' type='text/css' href='css/layout.css' media='screen, projection, tv' link rel='stylesheet' type='text/css' href='css/style.css' media='screen, projection, tv' link rel='stylesheet' type='text/css' href='css/print.css' media='print' body h1 #{@title} ol #{rows.join("\n").lstrip} EOF html = Rexle.new(Hlt.new(@sliml).to_html)\ .root.element('html').xml pretty: true File.write File.join(filepath, 'index.html'), html %w(layout style print).each \ {|x| File.write File.join(csspath, "%s.css" % x), ''} 'saved' end
to_kbml(options={})
click to toggle source
# File lib/sra2019.rb, line 153 def to_kbml(options={}) @doc.xml options end
to_pdf(pdf_file=File.join(@savepath, 'sra' + Time.now.to_i.to_s, @title.gsub(/ /,'-') + '.pdf'))
click to toggle source
Writes the steps to a PDF file
# File lib/sra2019.rb, line 242 def to_pdf(pdf_file=File.join(@savepath, 'sra' + Time.now.to_i.to_s, @title.gsub(/ /,'-') + '.pdf')) dir = File.dirname(pdf_file) html_file = File.join(dir, 'index.html') to_html(dir) pdf = WickedPdf.new.pdf_from_html_file(html_file) File.write pdf_file, pdf end
to_sliml()
click to toggle source
# File lib/sra2019.rb, line 217 def to_sliml() @sliml end
to_subtitles(offset=-(@steps.first.time - 1))
click to toggle source
# File lib/sra2019.rb, line 223 def to_subtitles(offset=-(@steps.first.time - 1)) @steps.map do |x| secs = x.time.to_i+offset if secs < 1 then raise "to_subtitles: time can't be negative. Reduce the offset value" end a = Subunit.new(units={minutes:60}, seconds: secs).to_h.to_a time = a.map {|x|"%d%s" % [x[1], x[0][0]] }.join('') "%s %s " % [time, x.desc] end end
to_zip()
click to toggle source
Compresses the HTML file directory to a ZIP file
# File lib/sra2019.rb, line 256 def to_zip() project = 'sra' + Time.now.to_i.to_s newdir = File.join(@savepath, project) zipfile = newdir + '.zip' to_html(File.join(newdir)) #to_pdf(File.join(newdir, @title.gsub(/ /,'-') + '.pdf')) Archive::Zip.archive(zipfile, newdir) zipfile end
Private Instance Methods
build(steps)
click to toggle source
# File lib/sra2019.rb, line 273 def build(steps) xml = RexleBuilder.new xml.kbml do steps.each do |h| comment = h[:user_comment] if comment then xml.comment! comment # is there text to be typed? typed = comment.scan(/\*[^*]+\*/) typed.each { |x| xml.type x[1..-2] } xml.sleep if typed else h[:keys].each do |keyx| key, modifier = keyx.split('-').reverse if modifier then xml.send(modifier.downcase.to_sym, {key: key.downcase}) else xml.send(key.downcase.to_sym) end end xml.sleep end end end Rexle.new(xml.to_a) end
extract_image(s, n)
click to toggle source
# File lib/sra2019.rb, line 318 def extract_image(s, n) action = @actions[n-1] puts 'action: ' + action.inspect if @debug e = action.element('HighlightXYWH') return unless e y, x, w, h = e.text.split(',').map(&:to_i) jpg_file = action.element('ScreenshotFileName/text()') return unless jpg_file img_data = s[/(?<=Content-Location: #{jpg_file}\r\n\r\n)[^--]+/m] puts ('img_data: ' + img_data.inspect).debug if @debug image = MiniMagick::Image.read Base64.decode64(img_data.rstrip) # crop along the red boundary and remove the red boundary image.crop "%sx%s+%s+%s" % [w - 5, h - 5, x + 3, y + 3] image.to_blob end
parse_report(s)
click to toggle source
# File lib/sra2019.rb, line 340 def parse_report(s) report = Rexle.new s[/<Report>.*<\/Report>/m] session = report.root.element('UserActionData/RecordSession') puts 'session: ' + session.inspect if @debug puts 'attributes: ' + session.attributes.inspect if @debug @start_time, stop_time = %w(Start Stop).map do |x| v = session.attributes[(x + 'Time').to_sym] puts 'v: ' + v.inspect if @debug v.to_time end @duration = stop_time - @start_time session.xpath('EachAction') end
parse_steps(s)
click to toggle source
# File lib/sra2019.rb, line 359 def parse_steps(s) s2 = s[/Recording Session.*(?=<)/] puts ('s: ' + s.inspect).debug if @debug raw_steps = s2.split(/(?=Step \d+:)/) summary = raw_steps.shift raw_steps.map do |entry| a = entry.split(/<br \/>/) raw_keys = a[0][/(?<=\[)[^\]]+/] keys = raw_keys ? raw_keys.gsub('...','').split : [] raw_comment = a[0][/User Comment: (.*)/,1] n = a[0][/(?<=Step )\d+/].to_i puts ('n: ' + n.inspect).debug if @debug time = (@actions[n-1].attributes[:Time].to_time - @start_time).to_i h = if raw_comment then { user_comment: raw_comment.gsub(""",'') } else { desc: a[0][/(?:Step \d+: )(.*)/,1].gsub('&','&')\ .gsub(""",'"').sub(/\s+\[[^\[]+\]$/,''), keys: keys , program: a[1][/(?<=Program: ).*/], ui_elements: a[2][/(?<=UI Elements: ).*/].split(/,\s+/) } end steps = {step: n, time: time, screenshot: extract_image(s, n)}.merge(h) OpenStruct.new(steps) end end