| Tons of bug fixes after usability testing, still a few more to go - warvox - Vo… | |
| Log | |
| Files | |
| Refs | |
| README | |
| --- | |
| commit 4abcd8f39f245c31f30752f92c4979dbf18879e7 | |
| parent c617d707a5b387aa960188673371bbef88994ab9 | |
| Author: HD Moore <[email protected]> | |
| Date: Thu, 5 Mar 2009 07:36:47 +0000 | |
| Tons of bug fixes after usability testing, still a few more to go | |
| Diffstat: | |
| M lib/warvox/jobs.rb | 47 +++++++++++++++--------------… | |
| M lib/warvox/jobs/analysis.rb | 10 ++++++---- | |
| M lib/warvox/jobs/base.rb | 29 +++++++++++++++++++++++++---- | |
| M lib/warvox/jobs/dialer.rb | 38 ++++++++++-------------------… | |
| M web/app/controllers/analyze_contro… | 4 ++-- | |
| M web/app/controllers/dial_jobs_cont… | 3 +-- | |
| M web/app/controllers/dial_results_c… | 29 +++++++++++++++++++++++++++… | |
| M web/app/models/dial_job.rb | 8 ++++---- | |
| M web/app/views/dial_jobs/index.html… | 6 +++--- | |
| M web/app/views/dial_jobs/new.html.e… | 6 +++--- | |
| M web/app/views/dial_results/analyze… | 35 ++++++++-------------------… | |
| M web/app/views/home/index.html.erb | 4 ++-- | |
| M web/config/database.yml | 6 +++--- | |
| 13 files changed, 119 insertions(+), 106 deletions(-) | |
| --- | |
| diff --git a/lib/warvox/jobs.rb b/lib/warvox/jobs.rb | |
| @@ -3,44 +3,41 @@ class JobQueue | |
| attr_accessor :active_job, :active_thread, :queue, :queue_thread | |
| def initialize | |
| + @mutex = Mutex.new | |
| @queue = [] | |
| @queue_thread = Thread.new{ manage_queue } | |
| + | |
| super | |
| end | |
| - # XXX synchronize | |
| - def deschedule(job_id) | |
| - | |
| - if(@active_job and @active_job.name == job_id) | |
| - @active_thread.kill | |
| - @active_job = @active_thread = nil | |
| - end | |
| - | |
| - res = [] | |
| - @queue.each do |j| | |
| - res << j if j.name == job_id | |
| - end | |
| - | |
| - if(res.length > 0) | |
| - res.each {|j| @queue.delete(j) } | |
| + def scheduled?(klass, job_id) | |
| + @mutex.synchronize do | |
| + [@active_job, *(@queue)].each do |c| | |
| + next if not c | |
| + return true if (c.class == klass and c.name ==… | |
| + end | |
| end | |
| + false | |
| end | |
| - def schedule(job) | |
| - @queue.push(job) | |
| + def schedule(klass, job_id) | |
| + return false if scheduled?(klass, job_id) | |
| + @queue.push(klass.new(job_id)) | |
| end | |
| def manage_queue | |
| begin | |
| while(true) | |
| - if(@active_job and @active_job.status == 'completed') | |
| - @active_job = nil | |
| - @active_thread = nil | |
| - end | |
| - | |
| - if(not @active_job and @queue.length > 0) | |
| - @active_job = @queue.shift | |
| - @active_thread = Thread.new { @active_job.star… | |
| + @mutex.synchronize do | |
| + if(@active_job and @active_job.status == 'comp… | |
| + @active_job = nil | |
| + @active_thread = nil | |
| + end | |
| + | |
| + if(not @active_job and @queue.length > 0) | |
| + @active_job = @queue.shift | |
| + @active_thread = Thread.new { @active_… | |
| + end | |
| end | |
| Kernel.select(nil, nil, nil, 1) | |
| diff --git a/lib/warvox/jobs/analysis.rb b/lib/warvox/jobs/analysis.rb | |
| @@ -231,8 +231,10 @@ class Analysis < Base | |
| system("gnuplot #{plotter.path}") | |
| File.unlink(plotter.path) | |
| File.unlink(datfile.path) | |
| + File.unlink(frefile.path) | |
| plotter.close | |
| datfile.close | |
| + frefile.path | |
| # Generate a MP3 audio file | |
| system("sox -s -w -r 8000 -t raw -c 1 #{rawfile.path} … | |
| @@ -240,13 +242,13 @@ class Analysis < Base | |
| File.unlink("#{bname}.wav") | |
| File.unlink(rawfile.path) | |
| rawfile.close | |
| - | |
| - # XXX: Dump the frequencies | |
| - | |
| + | |
| # Save the changes | |
| r.processed = true | |
| r.processed_at = Time.now | |
| - r.save | |
| + db_save(r) | |
| + | |
| + clear_zombies() | |
| end | |
| end | |
| diff --git a/lib/warvox/jobs/base.rb b/lib/warvox/jobs/base.rb | |
| @@ -7,10 +7,6 @@ class Base | |
| 'base' | |
| end | |
| - def name | |
| - 'noname' | |
| - end | |
| - | |
| def stop | |
| @status = 'active' | |
| end | |
| @@ -18,6 +14,31 @@ class Base | |
| def start | |
| @status = 'completed' | |
| end | |
| + | |
| + def db_save(obj) | |
| + max_tries = 10 | |
| + cur_tries = 0 | |
| + begin | |
| + obj.save | |
| + rescue ::SQLite3::BusyException => e | |
| + cur_tries += 1 | |
| + if(cur_tries > max_tries) | |
| + raise e | |
| + return | |
| + end | |
| + Kernel.select(nil, nil, nil, 0.25) | |
| + retry | |
| + end | |
| + end | |
| + | |
| + def clear_zombies | |
| + begin | |
| + # Clear zombies just in case... | |
| + while(Process.waitpid(-1, Process::WNOHANG)) | |
| + end | |
| + rescue ::Exception | |
| + end | |
| + end | |
| end | |
| end | |
| end | |
| diff --git a/lib/warvox/jobs/dialer.rb b/lib/warvox/jobs/dialer.rb | |
| @@ -70,7 +70,7 @@ class Dialer < Base | |
| model = get_job | |
| model.status = 'active' | |
| model.started_at = Time.now | |
| - model.save | |
| + db_save(model) | |
| start_dialing() | |
| @@ -86,7 +86,7 @@ class Dialer < Base | |
| model = get_job | |
| model.status = 'completed' | |
| model.completed_at = Time.now | |
| - model.save | |
| + db_save(model) | |
| end | |
| def start_dialing | |
| @@ -177,32 +177,18 @@ class Dialer < Base | |
| end | |
| # END SPAWN THREADS | |
| tasks.map{|t| t.join if t} | |
| - | |
| - # Save data to the database | |
| - begin | |
| - | |
| - # Iterate through the results | |
| - @calls.each do |r| | |
| - tries = 0 | |
| - begin | |
| - r.save | |
| - rescue ::Exception => e | |
| - $stderr.puts "ERROR: #{r.inspe… | |
| - tries += 1 | |
| - Kernel.select(nil, nil, nil, 0… | |
| - retry if tries < 5 | |
| - end | |
| - end | |
| - | |
| - # Update the progress bar | |
| - model = get_job | |
| - model.progress = ((@nums_total - @nums.length)… | |
| - model.save | |
| - rescue ::SQLite3::BusyException => e | |
| - $stderr.puts "ERROR: Database lock hit trying … | |
| - retry | |
| + # Iterate through the results | |
| + @calls.each do |r| | |
| + db_save(r) | |
| end | |
| + | |
| + # Update the progress bar | |
| + model = get_job | |
| + model.progress = ((@nums_total - @nums.length) / @nums… | |
| + db_save(model) | |
| + | |
| + clear_zombies() | |
| end | |
| # ALL DONE | |
| diff --git a/web/app/controllers/analyze_controller.rb b/web/app/controllers/an… | |
| @@ -20,8 +20,8 @@ class AnalyzeController < ApplicationController | |
| :conditions => [ 'completed = ? and processed = ? and busy = ?… | |
| ) | |
| - @g1 = Ezgraphix::Graphic.new(:c_type => 'pie2d', :div_name => 'calls_p… | |
| - @g1.render_options(:caption => 'Line Types') | |
| + @g1 = Ezgraphix::Graphic.new(:c_type => 'col3d', :div_name => 'calls_p… | |
| + @g1.render_options(:caption => 'Detected Lines by Type', :y_name => 'L… | |
| @g2 = Ezgraphix::Graphic.new(:c_type => 'pie2d', :div_name => 'calls_p… | |
| @g2.render_options(:caption => 'Ring Time') | |
| diff --git a/web/app/controllers/dial_jobs_controller.rb b/web/app/controllers/… | |
| @@ -79,8 +79,7 @@ class DialJobsController < ApplicationController | |
| flash[:notice] = 'Job was successfully created.' | |
| # Launch it | |
| - dialer = WarVOX::Jobs::Dialer.new(@dial_job.id) | |
| - WarVOX::JobManager.schedule(dialer) | |
| + WarVOX::JobManager.schedule(::WarVOX::Jobs::Dialer, @dial_job.id) | |
| format.html { redirect_to(@dial_job) } | |
| format.xml { render :xml => @dial_job, :status => :created, :location… | |
| diff --git a/web/app/controllers/dial_results_controller.rb b/web/app/controlle… | |
| @@ -38,6 +38,32 @@ class DialResultsController < ApplicationController | |
| @job_id = params[:id] | |
| @job = DialJob.find(@job_id) | |
| + @dial_data_total = DialResult.find_all_by_dial_job_id( | |
| + @job_id, | |
| + :conditions => [ 'completed = ? and busy = ?', true, false ] | |
| + ).length | |
| + | |
| + @dial_data_done_set = DialResult.find_all_by_dial_job_id( | |
| + @job_id, | |
| + :conditions => [ 'processed = ?', true] | |
| + ) | |
| + @dial_data_done = @dial_data_done_set.length | |
| + | |
| + @g1 = Ezgraphix::Graphic.new(:c_type => 'col3d', :div_name => 'calls_p… | |
| + @g1.render_options(:caption => 'Detected Lines by Type', :y_name => 'L… | |
| + | |
| + @g2 = Ezgraphix::Graphic.new(:c_type => 'pie2d', :div_name => 'calls_p… | |
| + @g2.render_options(:caption => 'Analysis Progress') | |
| + | |
| + res_types = {} | |
| + @dial_data_done_set.each do |r| | |
| + res_types[ r.line_type.capitalize.to_sym ] ||= 0 | |
| + res_types[ r.line_type.capitalize.to_sym ] += 1 … | |
| + end | |
| + | |
| + @g1.data = res_types | |
| + @g2.data = {:Remaining => @dial_data_total-@dial_data_done, :Complete … | |
| + | |
| @dial_data_todo = DialResult.paginate_all_by_dial_job_id( | |
| @job_id, | |
| :page => params[:page], | |
| @@ -52,8 +78,7 @@ class DialResultsController < ApplicationController | |
| end | |
| if(@dial_data_todo.length > 0) | |
| - analyzer = WarVOX::Jobs::Analysis.new(@job_id) | |
| - WarVOX::JobManager.schedule(analyzer) | |
| + WarVOX::JobManager.schedule(::WarVOX::Jobs::Analysis, @job_id) | |
| end | |
| end | |
| diff --git a/web/app/models/dial_job.rb b/web/app/models/dial_job.rb | |
| @@ -6,16 +6,16 @@ class DialJob < ActiveRecord::Base | |
| validates_numericality_of :seconds, :less_than => 301, :greater_than =… | |
| def validate | |
| - if(range.gsub(/[^0-9X]/, '').length != 10) | |
| - errors.add(:range, "The range must be exactly 10 chara… | |
| + if(range.gsub(/[^0-9X]/, '').empty?) | |
| + errors.add(:range, "The range must be at least 1 chara… | |
| end | |
| if(range.scan(/X/).length > 5) | |
| errors.add(:range, "The range must contain no more tha… | |
| end | |
| - if(cid_mask != "SELF" and cid_mask.gsub(/[^0-9X]/, '').length … | |
| - errors.add(:range, "The Caller ID must be exactly 10 c… | |
| + if(cid_mask != "SELF" and cid_mask.gsub(/[^0-9X]/, '').empty?) | |
| + errors.add(:range, "The Caller ID must be at least 1 c… | |
| end | |
| if(cid_mask != "SELF" and cid_mask.scan(/X/).length > 5) | |
| diff --git a/web/app/views/dial_jobs/index.html.erb b/web/app/views/dial_jobs/i… | |
| @@ -77,7 +77,7 @@ | |
| <% form_for(@new_job) do |f| %> | |
| <%= f.error_messages %> | |
| <p> | |
| - <%= f.label :range, 'The target telephone range (123-456-XXXX)' %><br /> | |
| + <%= f.label :range, 'The target telephone range (1-123-456-XXXX)' %><br /> | |
| <%= f.text_field :range %> | |
| </p> | |
| <p> | |
| @@ -89,8 +89,8 @@ | |
| <%= f.text_field :lines, :value => 10 %> | |
| </p> | |
| <p> | |
| - <%= f.label :lines, 'The source Caller ID range (555-555-55XX)' %><br /> | |
| - <%= f.text_field :cid_mask, :value => '000-000-XXXX' %> | |
| + <%= f.label :lines, 'The source Caller ID range (1-555-555-55XX)' %><br /> | |
| + <%= f.text_field :cid_mask, :value => '1-123-456-XXXX' %> | |
| </p> | |
| <p> | |
| <%= f.submit "Create" %> | |
| diff --git a/web/app/views/dial_jobs/new.html.erb b/web/app/views/dial_jobs/new… | |
| @@ -3,7 +3,7 @@ | |
| <% form_for(@dial_job) do |f| %> | |
| <%= f.error_messages %> | |
| <p> | |
| - <%= f.label :range, 'The target telephone range (123-456-XXXX)' %><br /> | |
| + <%= f.label :range, 'The target telephone range (1-123-456-XXXX)' %><br /> | |
| <%= f.text_field :range %> | |
| </p> | |
| <p> | |
| @@ -15,8 +15,8 @@ | |
| <%= f.text_field :lines, :value => 10 %> | |
| </p> | |
| <p> | |
| - <%= f.label :lines, 'The source Caller ID range (555-555-55XX or SELF)' %>… | |
| - <%= f.text_field :cid_mask, :value => '000-000-XXXX' %> | |
| + <%= f.label :lines, 'The source Caller ID range (1-555-555-55XX or SELF)' … | |
| + <%= f.text_field :cid_mask, :value => '1-123-456-XXXX' %> | |
| </p> | |
| <p> | |
| <%= f.submit "Create" %> | |
| diff --git a/web/app/views/dial_results/analyze.html.rb b/web/app/views/dial_re… | |
| @@ -1,40 +1,23 @@ | |
| <% if @dial_data_todo.length > 0 %> | |
| -<h1 class='title'>Processing <%= @dial_data_todo.length %> Calls...</h1> | |
| +<h1 class='title'> | |
| + Analyzing Calls ( completed <%= @dial_data_done %> of <%= @dial_data_t… | |
| + - <%= @dial_data_total-@dial_data_done %> left )... | |
| +</h1> | |
| <table width='100%' align='center' border=0 cellspacing=0 cellpadding=6> | |
| <tr> | |
| - <td align='center'> </td> | |
| - <td align='center'> </td> | |
| -</tr> | |
| -</table> | |
| - | |
| -<table class='table_scaffold' width='100%'> | |
| - <tr> | |
| - <th>Number</th> | |
| - <th>CallerID</th> | |
| - <th>Provider</th> | |
| - <th>Call Time</th> | |
| - <th>Ring Time</th> | |
| - </tr> | |
| - | |
| -<% for dial_result in @dial_data_todo.sort{|a,b| a.number <=> b.number } %> | |
| - <tr> | |
| - <td><%=h dial_result.number %></td> | |
| - <td><%=h dial_result.cid %></td> | |
| - <td><%=h dial_result.provider.name %></td> | |
| - <td><%=h dial_result.seconds %></td> | |
| - <td><%=h dial_result.ringtime %></td> | |
| - </tr> | |
| +<% if @dial_data_done > 0 %> | |
| + <td align='center'><%= render_ezgraphix @g1 %></td> | |
| + <td align='center'><%= render_ezgraphix @g2 %></td> | |
| <% end %> | |
| +</tr> | |
| </table> | |
| <script language="javascript"> | |
| - setTimeout("location.reload(true);", 3000); | |
| + setTimeout("location.reload(true);", 5000); | |
| </script> | |
| -<%= will_paginate @dial_data_todo %> | |
| - | |
| <% else %> | |
| <h1 class='title'>No Completed Calls Found</h1> | |
| diff --git a/web/app/views/home/index.html.erb b/web/app/views/home/index.html.… | |
| @@ -16,7 +16,7 @@ In order to make phone calls, WarVOX needs to be configured w… | |
| <p>Once one or more service providers have been configured, click the <a href=… | |
| </p> | |
| -<p>The phone number range is specified by entering the full 10-digit phone num… | |
| +<p>The phone number range is specified by entering the phone number (including… | |
| </p> | |
| <p> | |
| @@ -27,7 +27,7 @@ The seconds field indicates the number of seconds to spend on… | |
| The outgoing line count is limited by the number of providers available and th… | |
| </p> | |
| -<p>The Caller ID is specified by entering the full 10-digit phone number, with… | |
| +<p>The Caller ID is specified by entering the phone number (including country … | |
| </p> | |
| <p> | |
| diff --git a/web/config/database.yml b/web/config/database.yml | |
| @@ -4,7 +4,7 @@ development: | |
| adapter: sqlite3 | |
| database: db/development.sqlite3 | |
| pool: 5 | |
| - timeout: 25000 | |
| + timeout: 5000 | |
| # Warning: The database defined as "test" will be erased and | |
| # re-generated from your development database when you run "rake". | |
| @@ -13,10 +13,10 @@ test: | |
| adapter: sqlite3 | |
| database: db/test.sqlite3 | |
| pool: 5 | |
| - timeout: 25000 | |
| + timeout: 5000 | |
| production: | |
| adapter: sqlite3 | |
| database: db/production.sqlite3 | |
| pool: 5 | |
| - timeout: 25000 | |
| + timeout: 5000 |