| Tabs to spaces - warvox - VoIP based wardialing tool, forked from rapid7/warvox. | |
| Log | |
| Files | |
| Refs | |
| README | |
| --- | |
| commit 6fc7895fc496df7a5acdc2f22d6e60fb70f1ca65 | |
| parent 922c8b67c39f72646441de35353bb87401adea71 | |
| Author: HD Moore <[email protected]> | |
| Date: Wed, 2 Mar 2016 16:04:08 -0600 | |
| Tabs to spaces | |
| Diffstat: | |
| M app/controllers/application_contro… | 100 ++++++++++++++++-----------… | |
| M app/controllers/calls_controller.rb | 46 ++++++++++++++++-------------… | |
| M app/controllers/home_controller.rb | 38 ++++++++++++++++-------------… | |
| M app/controllers/jobs_controller.rb | 684 ++++++++++++++++-------------… | |
| M app/controllers/projects_controlle… | 266 ++++++++++++++++-----------… | |
| M app/controllers/providers_controll… | 28 ++++++++++++++-------------- | |
| M app/controllers/user_sessions_cont… | 36 ++++++++++++++++-----------… | |
| M app/helpers/analyze_helper.rb | 12 ++++++------ | |
| M app/helpers/application_helper.rb | 344 +++++++++++++++--------------- | |
| M app/models/call_medium.rb | 4 ++-- | |
| M app/models/job.rb | 284 ++++++++++++++++-------------… | |
| M app/models/line.rb | 24 ++++++++++++------------ | |
| M app/models/line_attribute.rb | 4 ++-- | |
| M app/models/project.rb | 18 +++++++++--------- | |
| M app/models/provider.rb | 10 +++++----- | |
| M app/models/settings.rb | 2 +- | |
| M app/models/signature.rb | 2 +- | |
| M app/models/signature_fp.rb | 2 +- | |
| M app/models/user.rb | 14 +++++++------- | |
| M app/models/user_session.rb | 2 +- | |
| M bin/adduser | 72 ++++++++++++++++-------------… | |
| M bin/analyze_result.rb | 14 +++++++------- | |
| M bin/audio_raw_to_fprint.rb | 16 ++++++++-------- | |
| M bin/audio_raw_to_wav.rb | 16 ++++++++-------- | |
| M bin/audio_trim.rb | 20 ++++++++++---------- | |
| M bin/cache_clear.rb | 2 +- | |
| M bin/export_audio.rb | 98 ++++++++++++++++-------------… | |
| M bin/export_list.rb | 46 ++++++++++++++++-------------… | |
| M bin/iaxrecord.rb | 62 ++++++++++++++++-------------… | |
| M bin/identify_matches.rb | 68 ++++++++++++++++-------------… | |
| M bin/import_audio.rb | 110 ++++++++++++++++-------------… | |
| M bin/resetpw | 40 ++++++++++++++++-------------… | |
| M bin/verify_install.rb | 34 ++++++++++++++++-------------… | |
| M bin/warvox.rb | 64 ++++++++++++++++-------------… | |
| M bin/worker.rb | 44 ++++++++++++++++-------------… | |
| M bin/worker_manager.rb | 220 ++++++++++++++++-------------… | |
| M config/classifiers/01.default.rb | 28 ++++++++++++++-------------- | |
| M config/classifiers/99.default.rb | 8 ++++---- | |
| M db/migrate/20121228171549_initial_… | 360 ++++++++++++++++-----------… | |
| M lib/warvox.rb | 10 +++++----- | |
| M lib/warvox/audio/raw.rb | 646 ++++++++++++++++-------------… | |
| M lib/warvox/config.rb | 310 ++++++++++++++++-------------… | |
| M lib/warvox/jobs.rb | 118 ++++++++++++++++-------------… | |
| M lib/warvox/jobs/analysis.rb | 828 +++++++++++++++--------------- | |
| M lib/warvox/jobs/base.rb | 52 ++++++++++++++++-------------… | |
| M lib/warvox/jobs/dialer.rb | 438 ++++++++++++++++-------------… | |
| M lib/warvox/phone.rb | 96 ++++++++++++++++-------------… | |
| M lib/warvox/proto/iax2/client.rb | 10 +++++----- | |
| M spec/factories/call_media.rb | 8 ++++---- | |
| M spec/factories/calls.rb | 12 ++++++------ | |
| M spec/factories/jobs.rb | 22 +++++++++++----------- | |
| M spec/factories/lines.rb | 8 ++++---- | |
| M spec/factories/projects.rb | 8 ++++---- | |
| M spec/factories/providers.rb | 18 +++++++++--------- | |
| M spec/factories/settings.rb | 6 +++--- | |
| M spec/factories/signature_fps.rb | 6 +++--- | |
| M spec/factories/signatures.rb | 16 ++++++++-------- | |
| M spec/factories/users.rb | 14 +++++++------- | |
| M spec/features/projects_spec.rb | 38 ++++++++++++++++-------------… | |
| M spec/features/visitor/logins_spec.… | 100 ++++++++++++++++-----------… | |
| M spec/models/call_medium_spec.rb | 10 +++++----- | |
| M spec/models/call_spec.rb | 14 +++++++------- | |
| M spec/models/job_spec.rb | 12 ++++++------ | |
| M spec/models/line_spec.rb | 10 +++++----- | |
| M spec/models/project_spec.rb | 20 ++++++++++---------- | |
| M spec/models/provider_spec.rb | 28 ++++++++++++++-------------- | |
| M spec/models/settings_spec.rb | 6 +++--- | |
| M spec/models/signature_spec.rb | 12 ++++++------ | |
| M spec/models/user_spec.rb | 10 +++++----- | |
| M spec/rails_helper.rb | 44 ++++++++++++++++-------------… | |
| M spec/support/auth_logic_helpers.rb | 28 ++++++++++++++-------------- | |
| 71 files changed, 3100 insertions(+), 3100 deletions(-) | |
| --- | |
| diff --git a/app/controllers/application_controller.rb b/app/controllers/applic… | |
| @@ -1,68 +1,68 @@ | |
| class ApplicationController < ActionController::Base | |
| - protect_from_forgery | |
| - helper :all | |
| + protect_from_forgery | |
| + helper :all | |
| - helper_method :current_user_session, :current_user | |
| - before_filter :require_user, :load_project | |
| - add_breadcrumb :projects, :root_path | |
| + helper_method :current_user_session, :current_user | |
| + before_filter :require_user, :load_project | |
| + add_breadcrumb :projects, :root_path | |
| - include ActionView::Helpers::NumberHelper | |
| + include ActionView::Helpers::NumberHelper | |
| private | |
| - def current_user_session | |
| - return @current_user_session if defined?(@current_user_session) | |
| - @current_user_session = UserSession.find | |
| - end | |
| + def current_user_session | |
| + return @current_user_session if defined?(@current_user_session) | |
| + @current_user_session = UserSession.find | |
| + end | |
| - def current_user | |
| - return @current_user if defined?(@current_user) | |
| - @current_user = current_user_session && current_user_session.r… | |
| - end | |
| + def current_user | |
| + return @current_user if defined?(@current_user) | |
| + @current_user = current_user_session && current_user_session.record | |
| + end | |
| - def require_user | |
| - unless current_user | |
| - store_location | |
| - flash.now[:notice] = "You must be logged in to access … | |
| - redirect_to '/login' | |
| - return false | |
| - end | |
| - end | |
| + def require_user | |
| + unless current_user | |
| + store_location | |
| + flash.now[:notice] = "You must be logged in to access this page" | |
| + redirect_to '/login' | |
| + return false | |
| + end | |
| + end | |
| - def require_no_user | |
| - if current_user | |
| - store_location | |
| - flash[:notice] = "You must be logged out to access thi… | |
| - redirect_to user_path(current_user) | |
| - return false | |
| - end | |
| - end | |
| + def require_no_user | |
| + if current_user | |
| + store_location | |
| + flash[:notice] = "You must be logged out to access this page" | |
| + redirect_to user_path(current_user) | |
| + return false | |
| + end | |
| + end | |
| - def store_location | |
| - session[:return_to] = request.fullpath | |
| - end | |
| + def store_location | |
| + session[:return_to] = request.fullpath | |
| + end | |
| - def redirect_back_or_default(default) | |
| - redirect_to(session[:return_to] || default) | |
| - session[:return_to] = nil | |
| - end | |
| + def redirect_back_or_default(default) | |
| + redirect_to(session[:return_to] || default) | |
| + session[:return_to] = nil | |
| + end | |
| - def load_project | |
| - # Only load this when we are logged in | |
| - return true unless current_user | |
| + def load_project | |
| + # Only load this when we are logged in | |
| + return true unless current_user | |
| - if params[:project_id] | |
| - @project = Project.where(:id => params[:project_id].to… | |
| - elsif session[:project_id] | |
| - @project = Project.where(:id => session[:project_id].t… | |
| - end | |
| + if params[:project_id] | |
| + @project = Project.where(:id => params[:project_id].to_i).first | |
| + elsif session[:project_id] | |
| + @project = Project.where(:id => session[:project_id].to_i).first | |
| + end | |
| - if @project and @project.id and not (session[:project_id] and … | |
| - session[:project_id] = @project.id | |
| - end | |
| + if @project and @project.id and not (session[:project_id] and session[:pro… | |
| + session[:project_id] = @project.id | |
| + end | |
| - true | |
| - end | |
| + true | |
| + end | |
| end | |
| diff --git a/app/controllers/calls_controller.rb b/app/controllers/calls_contro… | |
| @@ -4,9 +4,9 @@ class CallsController < ApplicationController | |
| # GET /calls.xml | |
| def index | |
| @jobs = @project.jobs.order('id DESC').where('task = ? AND completed_at IS… | |
| - :page => params[:page], | |
| - :per_page => 30 | |
| - ) | |
| + :page => params[:page], | |
| + :per_page => 30 | |
| + ) | |
| respond_to do |format| | |
| format.html # index.html.erb | |
| @@ -18,21 +18,21 @@ class CallsController < ApplicationController | |
| # GET /calls/1/view.xml | |
| def view | |
| @calls = Call.order('id DESC').where(:job_id => params[:id]).paginate( | |
| - :page => params[:page], | |
| - :per_page => 30 | |
| - ) | |
| - | |
| - unless @calls and @calls.length > 0 | |
| - redirect_to :action => :index | |
| - return | |
| - end | |
| - @call_results = { | |
| - :Timeout => Call.count(:conditions =>['job_id = ? and answere… | |
| - :Busy => Call.count(:conditions =>['job_id = ? and busy = … | |
| - :Answered => Call.count(:conditions =>['job_id = ? and answere… | |
| - } | |
| - | |
| - respond_to do |format| | |
| + :page => params[:page], | |
| + :per_page => 30 | |
| + ) | |
| + | |
| + unless @calls and @calls.length > 0 | |
| + redirect_to :action => :index | |
| + return | |
| + end | |
| + @call_results = { | |
| + :Timeout => Call.count(:conditions =>['job_id = ? and answered = ?', para… | |
| + :Busy => Call.count(:conditions =>['job_id = ? and busy = ?', params[:… | |
| + :Answered => Call.count(:conditions =>['job_id = ? and answered = ?', para… | |
| + } | |
| + | |
| + respond_to do |format| | |
| format.html # index.html.erb | |
| format.xml { render :xml => @calls } | |
| end | |
| @@ -43,10 +43,10 @@ class CallsController < ApplicationController | |
| def show | |
| @call = Call.find(params[:id]) | |
| - unless @call | |
| - redirect_to :action => :index | |
| - return | |
| - end | |
| + unless @call | |
| + redirect_to :action => :index | |
| + return | |
| + end | |
| respond_to do |format| | |
| format.html # show.html.erb | |
| @@ -109,7 +109,7 @@ class CallsController < ApplicationController | |
| def destroy | |
| @job = Job.find(params[:id]) | |
| - @job.destroy | |
| + @job.destroy | |
| respond_to do |format| | |
| format.html { redirect_to :action => 'index' } | |
| diff --git a/app/controllers/home_controller.rb b/app/controllers/home_controll… | |
| @@ -1,27 +1,27 @@ | |
| class HomeController < ApplicationController | |
| - def index | |
| + def index | |
| - end | |
| + end | |
| - def about | |
| - begin | |
| - @has_kissfft = "MISSING" | |
| - require 'kissfft' | |
| - @has_kissfft = $LOADED_FEATURES.grep(/kissfft/)[0] | |
| - rescue ::LoadError | |
| - end | |
| - end | |
| + def about | |
| + begin | |
| + @has_kissfft = "MISSING" | |
| + require 'kissfft' | |
| + @has_kissfft = $LOADED_FEATURES.grep(/kissfft/)[0] | |
| + rescue ::LoadError | |
| + end | |
| + end | |
| - def help | |
| - end | |
| + def help | |
| + end | |
| - def check | |
| - @has_project = ( Project.count > 0 ) | |
| - @has_provider = ( Provider.where(:enabled => true).count > 0 ) | |
| - @has_job = ( Job.where(:task => 'dialer').count > 0 ) | |
| - @has_result = ( Call.where(:answered => true ).count > 0 ) | |
| - @has_analysis = ( Call.where('analysis_completed_at IS NOT NUL… | |
| - end | |
| + def check | |
| + @has_project = ( Project.count > 0 ) | |
| + @has_provider = ( Provider.where(:enabled => true).count > 0 ) | |
| + @has_job = ( Job.where(:task => 'dialer').count > 0 ) | |
| + @has_result = ( Call.where(:answered => true ).count > 0 ) | |
| + @has_analysis = ( Call.where('analysis_completed_at IS NOT NULL').count > … | |
| + end | |
| end | |
| diff --git a/app/controllers/jobs_controller.rb b/app/controllers/jobs_controll… | |
| @@ -1,346 +1,346 @@ | |
| class JobsController < ApplicationController | |
| - require 'shellwords' | |
| - | |
| - def index | |
| - @reload_interval = 20000 | |
| - | |
| - @submitted_jobs = Job.where(:status => ['submitted', 'schedule… | |
| - @active_jobs = Job.where(:status => 'running', :completed_a… | |
| - @inactive_jobs = Job.order('id DESC').where('status NOT IN (?… | |
| - :page => params[:page], | |
| - :per_page => 30 | |
| - ) | |
| - | |
| - if @active_jobs.length > 0 | |
| - @reload_interval = 5000 | |
| - end | |
| - | |
| - if @submitted_jobs.length > 0 | |
| - @reload_interval = 3000 | |
| - end | |
| - | |
| - respond_to do |format| | |
| - format.html | |
| - end | |
| - end | |
| - | |
| - def results | |
| - @jobs = @project.jobs.order('id DESC').where('(task = ? OR tas… | |
| - :page => params[:page], | |
| - :per_page => 30 | |
| - ) | |
| - | |
| - respond_to do |format| | |
| - format.html | |
| - end | |
| - end | |
| - | |
| - def view_results | |
| - @job = Job.find(params[:id]) | |
| - | |
| - @call_results = { | |
| - :Timeout => @job.calls.count(:conditions => { :answer… | |
| - :Busy => @job.calls.count(:conditions => { :busy … | |
| - :Answered => @job.calls.count(:conditions => { :answer… | |
| - } | |
| - | |
| - sort_by = params[:sort_by] || 'number' | |
| - sort_dir = params[:sort_dir] || 'asc' | |
| - | |
| - @results = [] | |
| - @results_total_count = @job.calls.count() | |
| - | |
| - if request.format.json? | |
| - if params[:iDisplayLength] == '-1' | |
| - @results_per_page = nil | |
| - else | |
| - @results_per_page = (params[:iDisplayLength] |… | |
| - end | |
| - @results_offset = (params[:iDisplayStart] || 0).to_i | |
| - | |
| - calls_search | |
| - @results = @job.calls.includes(:provider).where(@searc… | |
| - @results_total_display_count = @job.calls.includes(:pr… | |
| - end | |
| - | |
| - respond_to do |format| | |
| - format.html | |
| - format.json { | |
| - render :content_type => 'application/json', :j… | |
| - } | |
| - end | |
| - end | |
| - | |
| - # Generate a SQL sort by option based on the incoming DataTables param… | |
| - # | |
| - # Returns the SQL String. | |
| - def calls_sort_option | |
| - column = case params[:iSortCol_0].to_s | |
| - when '1' | |
| - 'number' | |
| - when '2' | |
| - 'caller_id' | |
| - when '3' | |
| - 'providers.name' | |
| - when '4' | |
| - 'answered' | |
| - when '5' | |
| - 'busy' | |
| - when '6' | |
| - 'audio_length' | |
| - when '7' | |
| - 'ring_length' | |
| - end | |
| - column + ' ' + (params[:sSortDir_0] =~ /^A/i ? 'asc' : 'desc')… | |
| - end | |
| - | |
| - def calls_search | |
| - @search_conditions = [] | |
| - terms = params[:sSearch].to_s | |
| - terms = Shellword.shellwords(terms) rescue terms.split(/\s+/) | |
| - where = "" | |
| - param = [] | |
| - glue = "" | |
| - terms.each do |w| | |
| - next if w.downcase == 'undefined' | |
| - where << glue | |
| - case w | |
| - when 'answered' | |
| - where << "answered = ? " | |
| - param << true | |
| - when 'busy' | |
| - where << "busy = ? " | |
| - param << true | |
| - else | |
| - where << "( number ILIKE ? OR caller_i… | |
| - param << "%#{w}%" | |
| - param << "%#{w}%" | |
| - end | |
| - glue = "AND " if glue.empty? | |
| - @search_conditions = [ where, *param ] | |
| - end | |
| - end | |
| - | |
| - def new_dialer | |
| - @job = Job.new | |
| - if @project | |
| - @job.project = @project | |
| - else | |
| - @job.project = Project.last | |
| - end | |
| - | |
| - if params[:result_ids] | |
| - nums = "" | |
| - Call.find_each(:conditions => { :id => params[:result_… | |
| - nums << call.number + "\n" | |
| - end | |
| - @job.range = nums | |
| - end | |
| - | |
| - respond_to do |format| | |
| - format.html | |
| - end | |
| - end | |
| - | |
| - def purge_calls | |
| - Call.delete_all(:id => params[:result_ids]) | |
| - CallMedium.delete_all(:call_id => params[:result_ids]) | |
| - flash[:notice] = "Purged #{params[:result_ids].length} calls" | |
| - if params[:id] | |
| - @job = Job.find(params[:id]) | |
| - redirect_to view_results_path(@job.project_id, @job.id) | |
| - else | |
| - redirect_to analyze_path(@project) | |
| - end | |
| - end | |
| - | |
| - def dialer | |
| - @job = Job.new(params[:job]) | |
| - @job.created_by = @current_user.login | |
| - @job.task = 'dialer' | |
| - @job.range.to_s.gsub!(/[^0-9X:,\n]/, '') | |
| - @job.cid_mask.to_s.gsub!(/[^0-9X]/, '') if @job.cid_mask != "S… | |
| - | |
| - if @job.range_file.to_s != "" | |
| - @job.range = @job.range_file.read.gsub(/[^0-9X:,\n]/, … | |
| - end | |
| - | |
| - respond_to do |format| | |
| - if @job.schedule | |
| - flash[:notice] = 'Job was successfully created… | |
| - format.html { redirect_to :action => :index } | |
| - else | |
| - format.html { render :action => "new_dialer" } | |
| - end | |
| - end | |
| - end | |
| - | |
| - def new_analyze | |
| - @job = Job.new | |
| - if @project | |
| - @job.project = @project | |
| - else | |
| - @job.project = Project.last | |
| - end | |
| - | |
| - if params[:result_ids] | |
| - nums = "" | |
| - Call.find_each(:conditions => { :id => params[:result_… | |
| - nums << call.number + "\n" | |
| - end | |
| - @job.range = nums | |
| - end | |
| - | |
| - respond_to do |format| | |
| - format.html | |
| - end | |
| - end | |
| - | |
| - def new_identify | |
| - @job = Job.new | |
| - if @project | |
| - @job.project = @project | |
| - else | |
| - @job.project = Project.last | |
| - end | |
| - | |
| - if params[:result_ids] | |
| - nums = "" | |
| - Call.find_each(:conditions => { :id => params[:result_… | |
| - nums << call.number + "\n" | |
| - end | |
| - @job.range = nums | |
| - end | |
| - | |
| - respond_to do |format| | |
| - format.html | |
| - end | |
| - end | |
| - | |
| - def reanalyze_job | |
| - @job = Job.find(params[:id]) | |
| - @new = Job.new({ | |
| - :task => 'analysis', :scope => 'job', :target_id => @j… | |
| - :project_id => @project.id, :status => 'submitted' | |
| - }) | |
| - @new.created_by = @current_user.login | |
| - respond_to do |format| | |
| - if @new.schedule | |
| - flash[:notice] = 'Analysis job was successfull… | |
| - format.html { redirect_to jobs_path } | |
| - else | |
| - flash[:notice] = 'Analysis job could not run: … | |
| - format.html { redirect_to results_path(@projec… | |
| - end | |
| - end | |
| - end | |
| - | |
| - def analyze_job | |
| - @job = Job.find(params[:id]) | |
| - | |
| - # Handle analysis of specific call IDs via checkbox submission | |
| - if params[:result_ids] | |
| - @new = Job.new({ | |
| - :task => 'analysis', :scope => 'calls', :targe… | |
| - :project_id => @project.id, :status => 'submit… | |
| - }) | |
| - else | |
| - # Otherwise analyze the entire Job | |
| - @new = Job.new({ | |
| - :task => 'analysis', :scope => 'job', :target_… | |
| - :project_id => @project.id, :status => 'submit… | |
| - }) | |
| - end | |
| - | |
| - @new.created_by = @current_user.login | |
| - | |
| - respond_to do |format| | |
| - if @new.schedule | |
| - flash[:notice] = 'Analysis job was successfull… | |
| - format.html { redirect_to jobs_path } | |
| - else | |
| - flash[:notice] = 'Analysis job could not run: … | |
| - format.html { redirect_to results_path(@projec… | |
| - end | |
| - end | |
| - end | |
| - | |
| - | |
| - def analyze_project | |
| - | |
| - # Handle analysis of specific call IDs via checkbox submission | |
| - if params[:result_ids] | |
| - @new = Job.new({ | |
| - :task => 'analysis', :scope => 'calls', :targe… | |
| - :project_id => @project.id, :status => 'submit… | |
| - }) | |
| - else | |
| - # Otherwise analyze the entire Project | |
| - @new = Job.new({ | |
| - :task => 'analysis', :scope => 'project', :tar… | |
| - :project_id => @project.id, :status => 'submit… | |
| - }) | |
| - end | |
| - | |
| - @new.created_by = @current_user.login | |
| - | |
| - respond_to do |format| | |
| - if @new.schedule | |
| - flash[:notice] = 'Analysis job was successfull… | |
| - format.html { redirect_to jobs_path } | |
| - else | |
| - flash[:notice] = 'Analysis job could not run: … | |
| - format.html { redirect_to results_path(@projec… | |
| - end | |
| - end | |
| - end | |
| - | |
| - def identify_job | |
| - @job = Job.find(params[:id]) | |
| - | |
| - # Handle identification of specific lines via checkbox submiss… | |
| - if params[:result_ids] | |
| - @new = Job.new({ | |
| - :task => 'identify', :scope => 'calls', :targe… | |
| - :project_id => @project.id, :status => 'submit… | |
| - }) | |
| - else | |
| - # Otherwise analyze the entire Job | |
| - @new = Job.new({ | |
| - :task => 'identify', :scope => 'job', :target_… | |
| - :project_id => @project.id, :status => 'submit… | |
| - }) | |
| - end | |
| - | |
| - @new.created_by = @current_user.login | |
| - | |
| - respond_to do |format| | |
| - if @new.schedule | |
| - flash[:notice] = 'Identify job was successfull… | |
| - format.html { redirect_to jobs_path } | |
| - else | |
| - flash[:notice] = 'Identify job could not run: … | |
| - format.html { redirect_to results_path(@projec… | |
| - end | |
| - end | |
| - end | |
| - | |
| - def stop | |
| - @job = Job.find(params[:id]) | |
| - @job.stop | |
| - flash[:notice] = "Job has been cancelled" | |
| - redirect_to :action => 'index' | |
| - end | |
| - | |
| - def destroy | |
| - @job = Job.find(params[:id]) | |
| - @job.destroy | |
| - | |
| - respond_to do |format| | |
| - format.html { redirect_to(jobs_url) } | |
| - format.xml { head :ok } | |
| - end | |
| - end | |
| + require 'shellwords' | |
| + | |
| + def index | |
| + @reload_interval = 20000 | |
| + | |
| + @submitted_jobs = Job.where(:status => ['submitted', 'scheduled'], :comple… | |
| + @active_jobs = Job.where(:status => 'running', :completed_at => nil) | |
| + @inactive_jobs = Job.order('id DESC').where('status NOT IN (?)', ['submit… | |
| + :page => params[:page], | |
| + :per_page => 30 | |
| + ) | |
| + | |
| + if @active_jobs.length > 0 | |
| + @reload_interval = 5000 | |
| + end | |
| + | |
| + if @submitted_jobs.length > 0 | |
| + @reload_interval = 3000 | |
| + end | |
| + | |
| + respond_to do |format| | |
| + format.html | |
| + end | |
| + end | |
| + | |
| + def results | |
| + @jobs = @project.jobs.order('id DESC').where('(task = ? OR task = ?) AND c… | |
| + :page => params[:page], | |
| + :per_page => 30 | |
| + ) | |
| + | |
| + respond_to do |format| | |
| + format.html | |
| + end | |
| + end | |
| + | |
| + def view_results | |
| + @job = Job.find(params[:id]) | |
| + | |
| + @call_results = { | |
| + :Timeout => @job.calls.count(:conditions => { :answered => false }), | |
| + :Busy => @job.calls.count(:conditions => { :busy => true }), | |
| + :Answered => @job.calls.count(:conditions => { :answered => true }), | |
| + } | |
| + | |
| + sort_by = params[:sort_by] || 'number' | |
| + sort_dir = params[:sort_dir] || 'asc' | |
| + | |
| + @results = [] | |
| + @results_total_count = @job.calls.count() | |
| + | |
| + if request.format.json? | |
| + if params[:iDisplayLength] == '-1' | |
| + @results_per_page = nil | |
| + else | |
| + @results_per_page = (params[:iDisplayLength] || 20).to_i | |
| + end | |
| + @results_offset = (params[:iDisplayStart] || 0).to_i | |
| + | |
| + calls_search | |
| + @results = @job.calls.includes(:provider).where(@search_conditions).limi… | |
| + @results_total_display_count = @job.calls.includes(:provider).where(@sea… | |
| + end | |
| + | |
| + respond_to do |format| | |
| + format.html | |
| + format.json { | |
| + render :content_type => 'application/json', :json => render_to_string(… | |
| + } | |
| + end | |
| + end | |
| + | |
| + # Generate a SQL sort by option based on the incoming DataTables paramater. | |
| + # | |
| + # Returns the SQL String. | |
| + def calls_sort_option | |
| + column = case params[:iSortCol_0].to_s | |
| + when '1' | |
| + 'number' | |
| + when '2' | |
| + 'caller_id' | |
| + when '3' | |
| + 'providers.name' | |
| + when '4' | |
| + 'answered' | |
| + when '5' | |
| + 'busy' | |
| + when '6' | |
| + 'audio_length' | |
| + when '7' | |
| + 'ring_length' | |
| + end | |
| + column + ' ' + (params[:sSortDir_0] =~ /^A/i ? 'asc' : 'desc') if column | |
| + end | |
| + | |
| + def calls_search | |
| + @search_conditions = [] | |
| + terms = params[:sSearch].to_s | |
| + terms = Shellword.shellwords(terms) rescue terms.split(/\s+/) | |
| + where = "" | |
| + param = [] | |
| + glue = "" | |
| + terms.each do |w| | |
| + next if w.downcase == 'undefined' | |
| + where << glue | |
| + case w | |
| + when 'answered' | |
| + where << "answered = ? " | |
| + param << true | |
| + when 'busy' | |
| + where << "busy = ? " | |
| + param << true | |
| + else | |
| + where << "( number ILIKE ? OR caller_id ILIKE ? ) " | |
| + param << "%#{w}%" | |
| + param << "%#{w}%" | |
| + end | |
| + glue = "AND " if glue.empty? | |
| + @search_conditions = [ where, *param ] | |
| + end | |
| + end | |
| + | |
| + def new_dialer | |
| + @job = Job.new | |
| + if @project | |
| + @job.project = @project | |
| + else | |
| + @job.project = Project.last | |
| + end | |
| + | |
| + if params[:result_ids] | |
| + nums = "" | |
| + Call.find_each(:conditions => { :id => params[:result_ids] }) do |call| | |
| + nums << call.number + "\n" | |
| + end | |
| + @job.range = nums | |
| + end | |
| + | |
| + respond_to do |format| | |
| + format.html | |
| + end | |
| + end | |
| + | |
| + def purge_calls | |
| + Call.delete_all(:id => params[:result_ids]) | |
| + CallMedium.delete_all(:call_id => params[:result_ids]) | |
| + flash[:notice] = "Purged #{params[:result_ids].length} calls" | |
| + if params[:id] | |
| + @job = Job.find(params[:id]) | |
| + redirect_to view_results_path(@job.project_id, @job.id) | |
| + else | |
| + redirect_to analyze_path(@project) | |
| + end | |
| + end | |
| + | |
| + def dialer | |
| + @job = Job.new(params[:job]) | |
| + @job.created_by = @current_user.login | |
| + @job.task = 'dialer' | |
| + @job.range.to_s.gsub!(/[^0-9X:,\n]/, '') | |
| + @job.cid_mask.to_s.gsub!(/[^0-9X]/, '') if @job.cid_mask != "SELF" | |
| + | |
| + if @job.range_file.to_s != "" | |
| + @job.range = @job.range_file.read.gsub(/[^0-9X:,\n]/, '') | |
| + end | |
| + | |
| + respond_to do |format| | |
| + if @job.schedule | |
| + flash[:notice] = 'Job was successfully created.' | |
| + format.html { redirect_to :action => :index } | |
| + else | |
| + format.html { render :action => "new_dialer" } | |
| + end | |
| + end | |
| + end | |
| + | |
| + def new_analyze | |
| + @job = Job.new | |
| + if @project | |
| + @job.project = @project | |
| + else | |
| + @job.project = Project.last | |
| + end | |
| + | |
| + if params[:result_ids] | |
| + nums = "" | |
| + Call.find_each(:conditions => { :id => params[:result_ids] }) do |call| | |
| + nums << call.number + "\n" | |
| + end | |
| + @job.range = nums | |
| + end | |
| + | |
| + respond_to do |format| | |
| + format.html | |
| + end | |
| + end | |
| + | |
| + def new_identify | |
| + @job = Job.new | |
| + if @project | |
| + @job.project = @project | |
| + else | |
| + @job.project = Project.last | |
| + end | |
| + | |
| + if params[:result_ids] | |
| + nums = "" | |
| + Call.find_each(:conditions => { :id => params[:result_ids] }) do |call| | |
| + nums << call.number + "\n" | |
| + end | |
| + @job.range = nums | |
| + end | |
| + | |
| + respond_to do |format| | |
| + format.html | |
| + end | |
| + end | |
| + | |
| + def reanalyze_job | |
| + @job = Job.find(params[:id]) | |
| + @new = Job.new({ | |
| + :task => 'analysis', :scope => 'job', :target_id => @job.id, :force => t… | |
| + :project_id => @project.id, :status => 'submitted' | |
| + }) | |
| + @new.created_by = @current_user.login | |
| + respond_to do |format| | |
| + if @new.schedule | |
| + flash[:notice] = 'Analysis job was successfully created.' | |
| + format.html { redirect_to jobs_path } | |
| + else | |
| + flash[:notice] = 'Analysis job could not run: ' + @new.errors.inspect | |
| + format.html { redirect_to results_path(@project) } | |
| + end | |
| + end | |
| + end | |
| + | |
| + def analyze_job | |
| + @job = Job.find(params[:id]) | |
| + | |
| + # Handle analysis of specific call IDs via checkbox submission | |
| + if params[:result_ids] | |
| + @new = Job.new({ | |
| + :task => 'analysis', :scope => 'calls', :target_ids => params[:result_… | |
| + :project_id => @project.id, :status => 'submitted' | |
| + }) | |
| + else | |
| + # Otherwise analyze the entire Job | |
| + @new = Job.new({ | |
| + :task => 'analysis', :scope => 'job', :target_id => @job.id, | |
| + :project_id => @project.id, :status => 'submitted' | |
| + }) | |
| + end | |
| + | |
| + @new.created_by = @current_user.login | |
| + | |
| + respond_to do |format| | |
| + if @new.schedule | |
| + flash[:notice] = 'Analysis job was successfully created.' | |
| + format.html { redirect_to jobs_path } | |
| + else | |
| + flash[:notice] = 'Analysis job could not run: ' + @new.errors.inspect | |
| + format.html { redirect_to results_path(@project) } | |
| + end | |
| + end | |
| + end | |
| + | |
| + | |
| + def analyze_project | |
| + | |
| + # Handle analysis of specific call IDs via checkbox submission | |
| + if params[:result_ids] | |
| + @new = Job.new({ | |
| + :task => 'analysis', :scope => 'calls', :target_ids => params[:result_… | |
| + :project_id => @project.id, :status => 'submitted' | |
| + }) | |
| + else | |
| + # Otherwise analyze the entire Project | |
| + @new = Job.new({ | |
| + :task => 'analysis', :scope => 'project', :target_id => @project.id, | |
| + :project_id => @project.id, :status => 'submitted' | |
| + }) | |
| + end | |
| + | |
| + @new.created_by = @current_user.login | |
| + | |
| + respond_to do |format| | |
| + if @new.schedule | |
| + flash[:notice] = 'Analysis job was successfully created.' | |
| + format.html { redirect_to jobs_path } | |
| + else | |
| + flash[:notice] = 'Analysis job could not run: ' + @new.errors.inspect | |
| + format.html { redirect_to results_path(@project) } | |
| + end | |
| + end | |
| + end | |
| + | |
| + def identify_job | |
| + @job = Job.find(params[:id]) | |
| + | |
| + # Handle identification of specific lines via checkbox submission | |
| + if params[:result_ids] | |
| + @new = Job.new({ | |
| + :task => 'identify', :scope => 'calls', :target_ids => params[:result_… | |
| + :project_id => @project.id, :status => 'submitted' | |
| + }) | |
| + else | |
| + # Otherwise analyze the entire Job | |
| + @new = Job.new({ | |
| + :task => 'identify', :scope => 'job', :target_id => @job.id, | |
| + :project_id => @project.id, :status => 'submitted' | |
| + }) | |
| + end | |
| + | |
| + @new.created_by = @current_user.login | |
| + | |
| + respond_to do |format| | |
| + if @new.schedule | |
| + flash[:notice] = 'Identify job was successfully created.' | |
| + format.html { redirect_to jobs_path } | |
| + else | |
| + flash[:notice] = 'Identify job could not run: ' + @new.errors.inspect | |
| + format.html { redirect_to results_path(@project) } | |
| + end | |
| + end | |
| + end | |
| + | |
| + def stop | |
| + @job = Job.find(params[:id]) | |
| + @job.stop | |
| + flash[:notice] = "Job has been cancelled" | |
| + redirect_to :action => 'index' | |
| + end | |
| + | |
| + def destroy | |
| + @job = Job.find(params[:id]) | |
| + @job.destroy | |
| + | |
| + respond_to do |format| | |
| + format.html { redirect_to(jobs_url) } | |
| + format.xml { head :ok } | |
| + end | |
| + end | |
| end | |
| diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_… | |
| @@ -1,136 +1,136 @@ | |
| class ProjectsController < ApplicationController | |
| - def index | |
| - @projects = Project.order('id DESC').paginate( | |
| - :page => params[:page], | |
| - :per_page => 10 | |
| - ) | |
| - | |
| - @new_project = Project.new | |
| - | |
| - respond_to do |format| | |
| - format.html | |
| - format.xml { render :xml => @projects } | |
| - end | |
| - end | |
| - | |
| - def show | |
| - @project = Project.find(params[:id]) | |
| - @active_jobs = @project.jobs.where(:status => 'running', :comp… | |
| - @inactive_jobs = @project.jobs.order('id DESC').where('… | |
| - :page => params[:page], | |
| - :per_page => 30 | |
| - ) | |
| - | |
| - @boxes = { | |
| - :called => { :cnt => @project.calls.count }, | |
| - :answered => { :cnt => @project.calls.where(:answered… | |
| - :analyzed => { :cnt => @project.calls.where('analysis… | |
| - :voice => { :cnt => @project.lines.where(:line_typ… | |
| - :voicemail => { :cnt => @project.lines.where(:line_typ… | |
| - :fax => { :cnt => @project.lines.where(:line_typ… | |
| - :modem => { :cnt => @project.lines.where(:line_typ… | |
| - } | |
| - | |
| - if @boxes[:called][:cnt] == 0 | |
| - @boxes[:called][:txt] = '0' | |
| - @boxes[:called][:cls] = 'nodata' | |
| - | |
| - # No calls, so everything else is unknown | |
| - [ :answered, :analyzed, :voice, :voicemail, :fax, :mod… | |
| - @boxes[t][:txt] = '?' | |
| - @boxes[t][:cls] = 'nodata' | |
| - end | |
| - | |
| - else | |
| - | |
| - [ :called, :answered, :analyzed].each do |t| | |
| - @boxes[t][:txt] = number_with_delimiter(@boxes… | |
| - @boxes[t][:cls] = 'completed' | |
| - end | |
| - | |
| - if @boxes[:answered][:cnt] == 0 | |
| - @boxes[:answered][:txt] = '0' | |
| - @boxes[:answered][:cls] = 'nodata' | |
| - end | |
| - | |
| - if @boxes[:analyzed][:cnt] == 0 | |
| - [ :voice, :voicemail, :fax, :modem ].each do |… | |
| - @boxes[t][:txt] = '?' | |
| - @boxes[t][:cls] = 'nodata' | |
| - end | |
| - @boxes[:analyzed][:cls] = 'nodata' | |
| - else | |
| - | |
| - @boxes[:voice][:txt] = number_with_delimiter(@… | |
| - @boxes[:voice][:cls] = 'voice' | |
| - | |
| - @boxes[:voicemail][:txt] = number_with_delimit… | |
| - @boxes[:voicemail][:cls] = 'voicemail' | |
| - | |
| - @boxes[:fax][:txt] = number_with_delimiter(@bo… | |
| - @boxes[:fax][:cls] = 'fax' | |
| - | |
| - @boxes[:modem][:txt] = number_with_delimiter(@… | |
| - @boxes[:modem][:cls] = 'modem' | |
| - end | |
| - end | |
| - | |
| - respond_to do |format| | |
| - format.html | |
| - format.xml { render :xml => @project } | |
| - end | |
| - end | |
| - | |
| - def new | |
| - @new_project = Project.new | |
| - respond_to do |format| | |
| - format.html | |
| - format.xml { render :xml => @new_project } | |
| - end | |
| - end | |
| - | |
| - | |
| - def edit | |
| - @project = Project.find(params[:id]) | |
| - end | |
| - | |
| - def create | |
| - @new_project = Project.new(params[:project]) | |
| - @new_project.created_by = current_user.login | |
| - | |
| - respond_to do |format| | |
| - if @new_project.save | |
| - format.html { redirect_to(project_path(@new_pr… | |
| - format.xml { render :xml => @project, :… | |
| - else | |
| - format.html { render :action => "new" } | |
| - format.xml { render :xml => @new_projec… | |
| - end | |
| - end | |
| - end | |
| - | |
| - def update | |
| - @project = Project.find(params[:id]) | |
| - | |
| - respond_to do |format| | |
| - if @project.update_attributes(params[:project]) | |
| - format.html { redirect_to projects_path } | |
| - format.xml { head :ok } | |
| - else | |
| - format.html { render :action => "edit" } | |
| - format.xml { render :xml => @project.er… | |
| - end | |
| - end | |
| - end | |
| - | |
| - def destroy | |
| - @project = Project.find(params[:id]) | |
| - @project.destroy | |
| - | |
| - respond_to do |format| | |
| - format.html { redirect_to(projects_url) } | |
| - format.xml { head :ok } | |
| - end | |
| - end | |
| + def index | |
| + @projects = Project.order('id DESC').paginate( | |
| + :page => params[:page], | |
| + :per_page => 10 | |
| + ) | |
| + | |
| + @new_project = Project.new | |
| + | |
| + respond_to do |format| | |
| + format.html | |
| + format.xml { render :xml => @projects } | |
| + end | |
| + end | |
| + | |
| + def show | |
| + @project = Project.find(params[:id]) | |
| + @active_jobs = @project.jobs.where(:status => 'running', :completed_at => … | |
| + @inactive_jobs = @project.jobs.order('id DESC').where('status NOT IN (?)'… | |
| + :page => params[:page], | |
| + :per_page => 30 | |
| + ) | |
| + | |
| + @boxes = { | |
| + :called => { :cnt => @project.calls.count }, | |
| + :answered => { :cnt => @project.calls.where(:answered => true).count }, | |
| + :analyzed => { :cnt => @project.calls.where('analysis_completed_at IS N… | |
| + :voice => { :cnt => @project.lines.where(:line_type => 'voice').coun… | |
| + :voicemail => { :cnt => @project.lines.where(:line_type => 'voicemail').… | |
| + :fax => { :cnt => @project.lines.where(:line_type => 'fax').count … | |
| + :modem => { :cnt => @project.lines.where(:line_type => 'modem').coun… | |
| + } | |
| + | |
| + if @boxes[:called][:cnt] == 0 | |
| + @boxes[:called][:txt] = '0' | |
| + @boxes[:called][:cls] = 'nodata' | |
| + | |
| + # No calls, so everything else is unknown | |
| + [ :answered, :analyzed, :voice, :voicemail, :fax, :modem ].each do |t| | |
| + @boxes[t][:txt] = '?' | |
| + @boxes[t][:cls] = 'nodata' | |
| + end | |
| + | |
| + else | |
| + | |
| + [ :called, :answered, :analyzed].each do |t| | |
| + @boxes[t][:txt] = number_with_delimiter(@boxes[t][:cnt]) | |
| + @boxes[t][:cls] = 'completed' | |
| + end | |
| + | |
| + if @boxes[:answered][:cnt] == 0 | |
| + @boxes[:answered][:txt] = '0' | |
| + @boxes[:answered][:cls] = 'nodata' | |
| + end | |
| + | |
| + if @boxes[:analyzed][:cnt] == 0 | |
| + [ :voice, :voicemail, :fax, :modem ].each do |t| | |
| + @boxes[t][:txt] = '?' | |
| + @boxes[t][:cls] = 'nodata' | |
| + end | |
| + @boxes[:analyzed][:cls] = 'nodata' | |
| + else | |
| + | |
| + @boxes[:voice][:txt] = number_with_delimiter(@boxes[:voice][:cnt]) | |
| + @boxes[:voice][:cls] = 'voice' | |
| + | |
| + @boxes[:voicemail][:txt] = number_with_delimiter(@boxes[:voicemail][:c… | |
| + @boxes[:voicemail][:cls] = 'voicemail' | |
| + | |
| + @boxes[:fax][:txt] = number_with_delimiter(@boxes[:fax][:cnt]) | |
| + @boxes[:fax][:cls] = 'fax' | |
| + | |
| + @boxes[:modem][:txt] = number_with_delimiter(@boxes[:modem][:cnt]) | |
| + @boxes[:modem][:cls] = 'modem' | |
| + end | |
| + end | |
| + | |
| + respond_to do |format| | |
| + format.html | |
| + format.xml { render :xml => @project } | |
| + end | |
| + end | |
| + | |
| + def new | |
| + @new_project = Project.new | |
| + respond_to do |format| | |
| + format.html | |
| + format.xml { render :xml => @new_project } | |
| + end | |
| + end | |
| + | |
| + | |
| + def edit | |
| + @project = Project.find(params[:id]) | |
| + end | |
| + | |
| + def create | |
| + @new_project = Project.new(params[:project]) | |
| + @new_project.created_by = current_user.login | |
| + | |
| + respond_to do |format| | |
| + if @new_project.save | |
| + format.html { redirect_to(project_path(@new_project)) } | |
| + format.xml { render :xml => @project, :status => :created, :location … | |
| + else | |
| + format.html { render :action => "new" } | |
| + format.xml { render :xml => @new_project.errors, :status => :unproces… | |
| + end | |
| + end | |
| + end | |
| + | |
| + def update | |
| + @project = Project.find(params[:id]) | |
| + | |
| + respond_to do |format| | |
| + if @project.update_attributes(params[:project]) | |
| + format.html { redirect_to projects_path } | |
| + format.xml { head :ok } | |
| + else | |
| + format.html { render :action => "edit" } | |
| + format.xml { render :xml => @project.errors, :status => :unprocessabl… | |
| + end | |
| + end | |
| + end | |
| + | |
| + def destroy | |
| + @project = Project.find(params[:id]) | |
| + @project.destroy | |
| + | |
| + respond_to do |format| | |
| + format.html { redirect_to(projects_url) } | |
| + format.xml { head :ok } | |
| + end | |
| + end | |
| end | |
| diff --git a/app/controllers/providers_controller.rb b/app/controllers/provider… | |
| @@ -2,13 +2,13 @@ class ProvidersController < ApplicationController | |
| def index | |
| - @providers = Provider.order('id DESC').paginate( | |
| - :page => params[:page], | |
| - :per_page => 10 | |
| - ) | |
| + @providers = Provider.order('id DESC').paginate( | |
| + :page => params[:page], | |
| + :per_page => 10 | |
| + ) | |
| - @new_provider = Provider.new | |
| - @new_provider.enabled = true | |
| + @new_provider = Provider.new | |
| + @new_provider.enabled = true | |
| respond_to do |format| | |
| format.html # index.html.erb | |
| @@ -18,8 +18,8 @@ class ProvidersController < ApplicationController | |
| def new | |
| @provider = Provider.new | |
| - @provider.enabled = true | |
| - @provider.port = 4569 | |
| + @provider.enabled = true | |
| + @provider.port = 4569 | |
| respond_to do |format| | |
| format.html # new.html.erb | |
| @@ -29,12 +29,12 @@ class ProvidersController < ApplicationController | |
| def edit | |
| @provider = Provider.find(params[:id]) | |
| - @provider.pass = "********" | |
| + @provider.pass = "********" | |
| end | |
| def create | |
| @provider = Provider.new(params[:provider]) | |
| - @provider.enabled = true | |
| + @provider.enabled = true | |
| respond_to do |format| | |
| if @provider.save | |
| @@ -52,10 +52,10 @@ class ProvidersController < ApplicationController | |
| def update | |
| @provider = Provider.find(params[:id]) | |
| - # Dont set the password if its the placeholder | |
| - if params[:provider] and params[:provider][:pass] and params[:provider… | |
| - params[:provider].delete(:pass) | |
| - end | |
| + # Dont set the password if its the placeholder | |
| + if params[:provider] and params[:provider][:pass] and params[:provider][:pas… | |
| + params[:provider].delete(:pass) | |
| + end | |
| respond_to do |format| | |
| if @provider.update_attributes(params[:provider]) | |
| diff --git a/app/controllers/user_sessions_controller.rb b/app/controllers/user… | |
| @@ -1,23 +1,23 @@ | |
| class UserSessionsController < ApplicationController | |
| - before_filter :require_no_user, :only => [:new, :create] | |
| - before_filter :require_user, :only => :destroy | |
| - layout 'login' | |
| + before_filter :require_no_user, :only => [:new, :create] | |
| + before_filter :require_user, :only => :destroy | |
| + layout 'login' | |
| - def new | |
| - @user_session = UserSession.new | |
| - end | |
| + def new | |
| + @user_session = UserSession.new | |
| + end | |
| - def create | |
| - @user_session = UserSession.new(params[:user_session]) | |
| - if @user_session.save | |
| - redirect_back_or_default projects_path | |
| - else | |
| - render :action => :new | |
| - end | |
| - end | |
| + def create | |
| + @user_session = UserSession.new(params[:user_session]) | |
| + if @user_session.save | |
| + redirect_back_or_default projects_path | |
| + else | |
| + render :action => :new | |
| + end | |
| + end | |
| - def destroy | |
| - current_user_session.destroy | |
| - redirect_back_or_default login_url | |
| - end | |
| + def destroy | |
| + current_user_session.destroy | |
| + redirect_back_or_default login_url | |
| + end | |
| end | |
| diff --git a/app/helpers/analyze_helper.rb b/app/helpers/analyze_helper.rb | |
| @@ -2,21 +2,21 @@ module AnalyzeHelper | |
| def fwd_match_html(pct) | |
| - %Q|<span class="badge fwd_match_span" style='background-color: #{pct_t… | |
| + %Q|<span class="badge fwd_match_span" style='background-color: #{pct_to_colo… | |
| - #{"%.3f" % pct.to_f}% Match | |
| + #{"%.3f" % pct.to_f}% Match | |
| - </span> | |
| + </span> | |
| - | | |
| + | | |
| end | |
| def rev_match_html(pct) | |
| - %Q|<span class="rev_match_span" style='padding-left: #{ (pct.to_i * 2)… | |
| + %Q|<span class="rev_match_span" style='padding-left: #{ (pct.to_i * 2).to_i … | |
| end | |
| def pct_to_color(pct) | |
| - "#" + "20" + (pct.to_i * 2.00).to_i.to_s(16).rjust(2, "0") + "20" | |
| + "#" + "20" + (pct.to_i * 2.00).to_i.to_s(16).rjust(2, "0") + "20" | |
| end | |
| end | |
| diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper… | |
| @@ -1,178 +1,178 @@ | |
| # Methods added to this helper will be available to all templates in the appli… | |
| module ApplicationHelper | |
| - def select_tag_for_filter(nvpairs, params) | |
| - _url = ( url_for :overwrite_params => { }).split('?')[0] | |
| - _html = %{<span class="pull-left filter-label">Filter: </span> } | |
| - _html << %{<select name="show" class="filter-select" } | |
| - _html << %{onchange="window.location='#{_url}' + '?show=' + this.val… | |
| - nvpairs.each do |pair| | |
| - _html << %{<option value="#{h(pair[:scope])}" } | |
| - if params[:show] == pair[:scope] || ((params[:show].nil? || params… | |
| - _html << %{ selected="selected" } | |
| - end | |
| - _html << %{>#{pair[:label]} } | |
| - _html << %{</option>} | |
| - end | |
| - _html << %{</select>} | |
| - raw(_html) | |
| - end | |
| - | |
| - def select_match_scope(nvpairs, params) | |
| - _url = ( url_for :overwrite_params => { }).split('?')[0] | |
| - _html = %{<span class="pull-left filter-label">Matching Scope: </spa… | |
| - _html << %{<select name="match_scope" class="filter-select" } | |
| - _html << %{onchange="window.location='#{_url}' + '?match_scope=' + t… | |
| - nvpairs.each do |pair| | |
| - _html << %{<option value="#{h(pair[:scope])}" } | |
| - if params[:match_scope] == pair[:scope] || ((params[:match_scope].… | |
| - _html << %{ selected="selected" } | |
| - end | |
| - _html << %{>#{pair[:label]} } | |
| - _html << %{</option>} | |
| - end | |
| - _html << %{</select>} | |
| - raw(_html) | |
| - end | |
| - | |
| - def set_focus(element_id) | |
| - javascript_tag(" $elem = $(\"#{element_id}\"); if (null !== $e… | |
| - end | |
| - | |
| - def format_job_details(job) | |
| - begin | |
| - info = Marshal.load(job.args.to_s) | |
| - | |
| - ttip = raw("<div class='task_args_formatted'>") | |
| - info.each_pair do |k,v| | |
| - ttip << raw("<div class='task_args_var'>") + h… | |
| - ttip << raw("<div class='task_args_val'>") + h… | |
| - end | |
| - ttip << raw("</div>\n") | |
| - outp = raw("<span class='xpopover' rel='popover' data-… | |
| - outp | |
| - rescue ::Exception => e | |
| - job.status.to_s.capitalize | |
| - end | |
| - end | |
| - | |
| - def format_job_status(job) | |
| - case job.status | |
| - when 'error' | |
| - ttip = h(job.error.to_s) | |
| - outp = raw("<span class='xpopover' rel='popover' data-… | |
| - outp | |
| - else | |
| - job.status.to_s.capitalize | |
| - end | |
| - end | |
| - | |
| - def format_job_rate(job) | |
| - pluralize( (job.rate * 60.0).to_i, "call") + "/min" | |
| - end | |
| - | |
| - # | |
| - # Includes any javascripts specific to this view. The hosts/show view | |
| - # will automatically include any javascripts at public/javascripts/hos… | |
| - # | |
| - # @return [void] | |
| - def include_view_javascript | |
| - # | |
| - # Sprockets treats index.js as special, so the js for the inde… | |
| - # http://guides.rubyonrails.org/asset_pipeline.html#using-inde… | |
| - # | |
| - | |
| - controller_action_name = controller.action_name | |
| - | |
| - if controller_action_name == 'index' | |
| - safe_action_name = '_index' | |
| - else | |
| - safe_action_name = controller_action_name | |
| - end | |
| - | |
| - include_view_javascript_named(safe_action_name) | |
| - end | |
| - | |
| - # Includes the named javascript for this controller if it exists. | |
| - # | |
| - # @return [void] | |
| - def include_view_javascript_named(name) | |
| - | |
| - controller_path = controller.controller_path | |
| - extensions = ['.coffee', '.js.coffee'] | |
| - javascript_controller_pathname = Rails.root.join('app', 'asset… | |
| - pathnames = extensions.collect { |extension| | |
| - javascript_controller_pathname.join("#{name}#{extensio… | |
| - } | |
| - | |
| - if pathnames.any?(&:exist?) | |
| - path = File.join(controller_path, name) | |
| - content_for(:view_javascript) do | |
| - javascript_include_tag path | |
| - end | |
| - end | |
| - end | |
| - | |
| - def escape_javascript_dq(str) | |
| - escape_javascript(str.strip).gsub("\\'", "'").gsub("\t", " … | |
| - end | |
| - | |
| - def submit_checkboxes_to(name, path, html={}) | |
| - if html[:confirm] | |
| - confirm = html.delete(:confirm) | |
| - link_to(name, "#", html.merge({:onclick => "if(confirm… | |
| - else | |
| - link_to(name, "#", html.merge({:onclick => "submit_che… | |
| - end | |
| - end | |
| - | |
| - # Scrub out data that can break the JSON parser | |
| - # | |
| - # data - The String json to be scrubbed. | |
| - # | |
| - # Returns the String json with invalid data removed. | |
| - def json_data_scrub(data) | |
| - data.to_s.gsub(/[\x00-\x1f]/){ |x| "\\x%.2x" % x.unpack("C*")[… | |
| - end | |
| - | |
| - # Returns the properly escaped sEcho parameter that DataTables expects. | |
| - def echo_data_tables | |
| - h(params[:sEcho]).to_json.html_safe | |
| - end | |
| - | |
| - # Generate the markup for the call's row checkbox. | |
| - # Returns the String markup html, escaped for json. | |
| - def call_checkbox_tag(call) | |
| - check_box_tag("result_ids[]", call.id, false, :id => nil).to_j… | |
| - end | |
| - | |
| - def call_number_html(call) | |
| - json_data_scrub(h(call.number)).to_json.html_safe | |
| - end | |
| - | |
| - def call_caller_id_html(call) | |
| - json_data_scrub(h(call.caller_id)).to_json.html_safe | |
| - end | |
| - | |
| - def call_provider_html(call) | |
| - json_data_scrub(h(call.provider.name)).to_json.html_safe | |
| - end | |
| - | |
| - def call_answered_html(call) | |
| - json_data_scrub(h(call.answered ? "Yes" : "No")).to_json.html_… | |
| - end | |
| - | |
| - def call_busy_html(call) | |
| - json_data_scrub(h(call.busy ? "Yes" : "No")).to_json.html_safe | |
| - end | |
| - | |
| - def call_audio_length_html(call) | |
| - json_data_scrub(h(call.audio_length.to_s)).to_json.html_safe | |
| - end | |
| - | |
| - def call_ring_length_html(call) | |
| - json_data_scrub(h(call.ring_length.to_s)).to_json.html_safe | |
| - end | |
| + def select_tag_for_filter(nvpairs, params) | |
| + _url = ( url_for :overwrite_params => { }).split('?')[0] | |
| + _html = %{<span class="pull-left filter-label">Filter: </span> } | |
| + _html << %{<select name="show" class="filter-select" } | |
| + _html << %{onchange="window.location='#{_url}' + '?show=' + this.value"> } | |
| + nvpairs.each do |pair| | |
| + _html << %{<option value="#{h(pair[:scope])}" } | |
| + if params[:show] == pair[:scope] || ((params[:show].nil? || params[:show… | |
| + _html << %{ selected="selected" } | |
| + end | |
| + _html << %{>#{pair[:label]} } | |
| + _html << %{</option>} | |
| + end | |
| + _html << %{</select>} | |
| + raw(_html) | |
| + end | |
| + | |
| + def select_match_scope(nvpairs, params) | |
| + _url = ( url_for :overwrite_params => { }).split('?')[0] | |
| + _html = %{<span class="pull-left filter-label">Matching Scope: </span> } | |
| + _html << %{<select name="match_scope" class="filter-select" } | |
| + _html << %{onchange="window.location='#{_url}' + '?match_scope=' + this.va… | |
| + nvpairs.each do |pair| | |
| + _html << %{<option value="#{h(pair[:scope])}" } | |
| + if params[:match_scope] == pair[:scope] || ((params[:match_scope].nil? |… | |
| + _html << %{ selected="selected" } | |
| + end | |
| + _html << %{>#{pair[:label]} } | |
| + _html << %{</option>} | |
| + end | |
| + _html << %{</select>} | |
| + raw(_html) | |
| + end | |
| + | |
| + def set_focus(element_id) | |
| + javascript_tag(" $elem = $(\"#{element_id}\"); if (null !== $elem && $elem… | |
| + end | |
| + | |
| + def format_job_details(job) | |
| + begin | |
| + info = Marshal.load(job.args.to_s) | |
| + | |
| + ttip = raw("<div class='task_args_formatted'>") | |
| + info.each_pair do |k,v| | |
| + ttip << raw("<div class='task_args_var'>") + h(truncate(k.to_s, :lengt… | |
| + ttip << raw("<div class='task_args_val'>") + h(truncate((v.to_s), :len… | |
| + end | |
| + ttip << raw("</div>\n") | |
| + outp = raw("<span class='xpopover' rel='popover' data-title=\"#{job.task… | |
| + outp | |
| + rescue ::Exception => e | |
| + job.status.to_s.capitalize | |
| + end | |
| + end | |
| + | |
| + def format_job_status(job) | |
| + case job.status | |
| + when 'error' | |
| + ttip = h(job.error.to_s) | |
| + outp = raw("<span class='xpopover' rel='popover' data-title=\"Task Detai… | |
| + outp | |
| + else | |
| + job.status.to_s.capitalize | |
| + end | |
| + end | |
| + | |
| + def format_job_rate(job) | |
| + pluralize( (job.rate * 60.0).to_i, "call") + "/min" | |
| + end | |
| + | |
| + # | |
| + # Includes any javascripts specific to this view. The hosts/show view | |
| + # will automatically include any javascripts at public/javascripts/hosts/sho… | |
| + # | |
| + # @return [void] | |
| + def include_view_javascript | |
| + # | |
| + # Sprockets treats index.js as special, so the js for the index action mus… | |
| + # http://guides.rubyonrails.org/asset_pipeline.html#using-index-files | |
| + # | |
| + | |
| + controller_action_name = controller.action_name | |
| + | |
| + if controller_action_name == 'index' | |
| + safe_action_name = '_index' | |
| + else | |
| + safe_action_name = controller_action_name | |
| + end | |
| + | |
| + include_view_javascript_named(safe_action_name) | |
| + end | |
| + | |
| + # Includes the named javascript for this controller if it exists. | |
| + # | |
| + # @return [void] | |
| + def include_view_javascript_named(name) | |
| + | |
| + controller_path = controller.controller_path | |
| + extensions = ['.coffee', '.js.coffee'] | |
| + javascript_controller_pathname = Rails.root.join('app', 'assets', 'javascr… | |
| + pathnames = extensions.collect { |extension| | |
| + javascript_controller_pathname.join("#{name}#{extension}") | |
| + } | |
| + | |
| + if pathnames.any?(&:exist?) | |
| + path = File.join(controller_path, name) | |
| + content_for(:view_javascript) do | |
| + javascript_include_tag path | |
| + end | |
| + end | |
| + end | |
| + | |
| + def escape_javascript_dq(str) | |
| + escape_javascript(str.strip).gsub("\\'", "'").gsub("\t", " ") | |
| + end | |
| + | |
| + def submit_checkboxes_to(name, path, html={}) | |
| + if html[:confirm] | |
| + confirm = html.delete(:confirm) | |
| + link_to(name, "#", html.merge({:onclick => "if(confirm('#{h confirm}')){… | |
| + else | |
| + link_to(name, "#", html.merge({:onclick => "submit_checkboxes_to('#{path… | |
| + end | |
| + end | |
| + | |
| + # Scrub out data that can break the JSON parser | |
| + # | |
| + # data - The String json to be scrubbed. | |
| + # | |
| + # Returns the String json with invalid data removed. | |
| + def json_data_scrub(data) | |
| + data.to_s.gsub(/[\x00-\x1f]/){ |x| "\\x%.2x" % x.unpack("C*")[0] } | |
| + end | |
| + | |
| + # Returns the properly escaped sEcho parameter that DataTables expects. | |
| + def echo_data_tables | |
| + h(params[:sEcho]).to_json.html_safe | |
| + end | |
| + | |
| + # Generate the markup for the call's row checkbox. | |
| + # Returns the String markup html, escaped for json. | |
| + def call_checkbox_tag(call) | |
| + check_box_tag("result_ids[]", call.id, false, :id => nil).to_json.html_safe | |
| + end | |
| + | |
| + def call_number_html(call) | |
| + json_data_scrub(h(call.number)).to_json.html_safe | |
| + end | |
| + | |
| + def call_caller_id_html(call) | |
| + json_data_scrub(h(call.caller_id)).to_json.html_safe | |
| + end | |
| + | |
| + def call_provider_html(call) | |
| + json_data_scrub(h(call.provider.name)).to_json.html_safe | |
| + end | |
| + | |
| + def call_answered_html(call) | |
| + json_data_scrub(h(call.answered ? "Yes" : "No")).to_json.html_safe | |
| + end | |
| + | |
| + def call_busy_html(call) | |
| + json_data_scrub(h(call.busy ? "Yes" : "No")).to_json.html_safe | |
| + end | |
| + | |
| + def call_audio_length_html(call) | |
| + json_data_scrub(h(call.audio_length.to_s)).to_json.html_safe | |
| + end | |
| + | |
| + def call_ring_length_html(call) | |
| + json_data_scrub(h(call.ring_length.to_s)).to_json.html_safe | |
| + end | |
| end | |
| diff --git a/app/models/call_medium.rb b/app/models/call_medium.rb | |
| @@ -15,6 +15,6 @@ | |
| # | |
| class CallMedium < ActiveRecord::Base | |
| - belongs_to :call | |
| - belongs_to :project | |
| + belongs_to :call | |
| + belongs_to :project | |
| end | |
| diff --git a/app/models/job.rb b/app/models/job.rb | |
| @@ -20,152 +20,152 @@ | |
| class Job < ActiveRecord::Base | |
| - reportable :hourly, :aggregation => :count, :grouping => :hour, :date_… | |
| - reportable :daily, :aggregation => :count, :grouping => :day, :date_co… | |
| - reportable :weeky, :aggregation => :count, :grouping => :week, :date_c… | |
| - reportable :monthly, :aggregation => :count, :grouping => :month, :dat… | |
| - | |
| - class JobValidator < ActiveModel::Validator | |
| - def validate(record) | |
| - case record.task | |
| - when 'dialer' | |
| - | |
| - cracked_range = WarVOX::Phone.crack_mask(recor… | |
| - unless cracked_range.length > 0 | |
| - record.errors[:range] << "No valid ran… | |
| - end | |
| - | |
| - cracked_mask = WarVOX::Phone.crack_mask(record… | |
| - unless cracked_mask.length > 0 | |
| - record.errors[:cid_mask] << "No valid … | |
| - end | |
| - | |
| - unless record.seconds.to_i > 0 and record.seco… | |
| - record.errors[:seconds] << "Seconds sh… | |
| - end | |
| - | |
| - unless record.lines.to_i > 0 and record.lines.… | |
| - record.errors[:lines] << "Lines should… | |
| - end | |
| - | |
| - $stderr.puts "Errors: #{record.errors.map{|x| … | |
| - | |
| - when 'analysis' | |
| - unless ['calls', 'job', 'project', 'global'].i… | |
| - record.errors[:scope] << "Scope must b… | |
| - end | |
| - if record.scope == "job" and Job.where(:id => … | |
| - record.errors[:job_id] << "The job_id … | |
| - end | |
| - if record.scope == "project" and Project.where… | |
| - record.errors[:project_id] << "The pro… | |
| - end | |
| - if record.scope == "calls" and (record.target_… | |
| - record.errors[:target_ids] << "The tar… | |
| - end | |
| - when 'import' | |
| - else | |
| - record.errors[:base] << "Invalid task specifie… | |
| - end | |
| - end | |
| - end | |
| - | |
| - # XXX: Purging a single job will be slow, but deleting the project is … | |
| - has_many :calls, :dependent => :destroy | |
| - | |
| - belongs_to :project | |
| - | |
| - attr_accessible :task, :status, :progress | |
| - | |
| - validates_presence_of :project_id | |
| - | |
| - attr_accessible :project_id | |
| - | |
| - | |
| - # Allow the base Job class to be used for Dial Jobs | |
| - attr_accessor :range | |
| - attr_accessor :range_file | |
| - attr_accessor :lines | |
| - attr_accessor :seconds | |
| - attr_accessor :cid_mask | |
| - | |
| - attr_accessible :range, :seconds, :lines, :cid_mask, :range_file | |
| - | |
| - attr_accessor :scope | |
| - attr_accessor :force | |
| - attr_accessor :target_id | |
| - attr_accessor :target_ids | |
| - | |
| - attr_accessible :scope, :force, :target_id, :target_ids | |
| - | |
| - | |
| - validates_with JobValidator | |
| - | |
| - def stop | |
| - self.class.where(id: self.id).update_all(status: 'cancelled') | |
| - end | |
| - | |
| - def update_progress(pct) | |
| - if pct >= 100 | |
| - self.class.where(id: self.id).update_all(:progress => … | |
| - else | |
| - self.class.where(id: self.id).update_all(:progress => … | |
| - end | |
| - end | |
| - | |
| - def details | |
| - Marshal.load(self.args) rescue {} | |
| - end | |
| - | |
| - def schedule | |
| - case task | |
| - when 'dialer' | |
| - self.status = 'submitted' | |
| - self.args = Marshal.dump({ | |
| - :range => self.range, | |
| - :lines => self.lines.to_i, | |
| - :seconds => self.seconds.to_i, | |
| - :cid_mask => self.cid_mask | |
| - }) | |
| - | |
| - return self.save | |
| - | |
| - when 'analysis' | |
| - self.status = 'submitted' | |
| - d = { | |
| + reportable :hourly, :aggregation => :count, :grouping => :hour, :date_column… | |
| + reportable :daily, :aggregation => :count, :grouping => :day, :date_column =… | |
| + reportable :weeky, :aggregation => :count, :grouping => :week, :date_column … | |
| + reportable :monthly, :aggregation => :count, :grouping => :month, :date_colu… | |
| + | |
| + class JobValidator < ActiveModel::Validator | |
| + def validate(record) | |
| + case record.task | |
| + when 'dialer' | |
| + | |
| + cracked_range = WarVOX::Phone.crack_mask(record.range) rescue [] | |
| + unless cracked_range.length > 0 | |
| + record.errors[:range] << "No valid ranges were specified" | |
| + end | |
| + | |
| + cracked_mask = WarVOX::Phone.crack_mask(record.cid_mask) rescue [] | |
| + unless cracked_mask.length > 0 | |
| + record.errors[:cid_mask] << "No valid Caller ID mask was specified" | |
| + end | |
| + | |
| + unless record.seconds.to_i > 0 and record.seconds.to_i < 300 | |
| + record.errors[:seconds] << "Seconds should be between 1 and 300" | |
| + end | |
| + | |
| + unless record.lines.to_i > 0 and record.lines.to_i < 10000 | |
| + record.errors[:lines] << "Lines should be between 1 and 10,000" | |
| + end | |
| + | |
| + $stderr.puts "Errors: #{record.errors.map{|x| x.inspect}}" | |
| + | |
| + when 'analysis' | |
| + unless ['calls', 'job', 'project', 'global'].include?(record.scope) | |
| + record.errors[:scope] << "Scope must be calls, job, project, or glob… | |
| + end | |
| + if record.scope == "job" and Job.where(:id => record.target_id.to_i, :… | |
| + record.errors[:job_id] << "The job_id is not valid" | |
| + end | |
| + if record.scope == "project" and Project.where(:id => record.target_id… | |
| + record.errors[:project_id] << "The project_id is not valid" | |
| + end | |
| + if record.scope == "calls" and (record.target_ids.nil? or record.targe… | |
| + record.errors[:target_ids] << "The target_ids list is empty" | |
| + end | |
| + when 'import' | |
| + else | |
| + record.errors[:base] << "Invalid task specified" | |
| + end | |
| + end | |
| + end | |
| + | |
| + # XXX: Purging a single job will be slow, but deleting the project is fast | |
| + has_many :calls, :dependent => :destroy | |
| + | |
| + belongs_to :project | |
| + | |
| + attr_accessible :task, :status, :progress | |
| + | |
| + validates_presence_of :project_id | |
| + | |
| + attr_accessible :project_id | |
| + | |
| + | |
| + # Allow the base Job class to be used for Dial Jobs | |
| + attr_accessor :range | |
| + attr_accessor :range_file | |
| + attr_accessor :lines | |
| + attr_accessor :seconds | |
| + attr_accessor :cid_mask | |
| + | |
| + attr_accessible :range, :seconds, :lines, :cid_mask, :range_file | |
| + | |
| + attr_accessor :scope | |
| + attr_accessor :force | |
| + attr_accessor :target_id | |
| + attr_accessor :target_ids | |
| + | |
| + attr_accessible :scope, :force, :target_id, :target_ids | |
| + | |
| + | |
| + validates_with JobValidator | |
| + | |
| + def stop | |
| + self.class.where(id: self.id).update_all(status: 'cancelled') | |
| + end | |
| + | |
| + def update_progress(pct) | |
| + if pct >= 100 | |
| + self.class.where(id: self.id).update_all(:progress => pct, :completed_at… | |
| + else | |
| + self.class.where(id: self.id).update_all(:progress => pct) | |
| + end | |
| + end | |
| + | |
| + def details | |
| + Marshal.load(self.args) rescue {} | |
| + end | |
| + | |
| + def schedule | |
| + case task | |
| + when 'dialer' | |
| + self.status = 'submitted' | |
| + self.args = Marshal.dump({ | |
| + :range => self.range, | |
| + :lines => self.lines.to_i, | |
| + :seconds => self.seconds.to_i, | |
| + :cid_mask => self.cid_mask | |
| + }) | |
| + | |
| + return self.save | |
| + | |
| + when 'analysis' | |
| + self.status = 'submitted' | |
| + d = { | |
| :scope => self.scope, # job / pr… | |
| :force => !!(self.force), # true / f… | |
| :target_id => self.target_id.to_i, # job_id o… | |
| :target_ids => (self.target_ids || []).map{|x|… | |
| } | |
| - $stderr.puts d.inspect | |
| - | |
| - self.args = Marshal.dump({ | |
| - :scope => self.scope, # job / pr… | |
| - :force => !!(self.force), # true / f… | |
| - :target_id => self.target_id.to_i, # job_id o… | |
| - :target_ids => (self.target_ids || []).map{|x|… | |
| - }) | |
| - return self.save | |
| - else | |
| - raise ::RuntimeError, "Unsupported Job type" | |
| - end | |
| - end | |
| - | |
| - def rate | |
| - tend = (self.completed_at || Time.now) | |
| - tlen = tend.to_f - self.started_at.to_f | |
| - | |
| - case self.task | |
| - when 'dialer' | |
| - Call.where('job_id = ?', self.id).count() / tlen | |
| - when 'analysis' | |
| - Call.where('job_id = ? AND analysis_completed_at > ? A… | |
| - when 'import' | |
| - Call.where('job_id = ?', self.id).count() / tlen | |
| - else | |
| - 0 | |
| - end | |
| - end | |
| + $stderr.puts d.inspect | |
| + | |
| + self.args = Marshal.dump({ | |
| + :scope => self.scope, # job / project/ global | |
| + :force => !!(self.force), # true / false | |
| + :target_id => self.target_id.to_i, # job_id or project_id or nil | |
| + :target_ids => (self.target_ids || []).map{|x| x.to_i } | |
| + }) | |
| + return self.save | |
| + else | |
| + raise ::RuntimeError, "Unsupported Job type" | |
| + end | |
| + end | |
| + | |
| + def rate | |
| + tend = (self.completed_at || Time.now) | |
| + tlen = tend.to_f - self.started_at.to_f | |
| + | |
| + case self.task | |
| + when 'dialer' | |
| + Call.where('job_id = ?', self.id).count() / tlen | |
| + when 'analysis' | |
| + Call.where('job_id = ? AND analysis_completed_at > ? AND analysis_comple… | |
| + when 'import' | |
| + Call.where('job_id = ?', self.id).count() / tlen | |
| + else | |
| + 0 | |
| + end | |
| + end | |
| end | |
| diff --git a/app/models/line.rb b/app/models/line.rb | |
| @@ -12,18 +12,18 @@ | |
| # | |
| class Line < ActiveRecord::Base | |
| - has_many :line_attributes, :dependent => :delete_all | |
| - belongs_to :project | |
| + has_many :line_attributes, :dependent => :delete_all | |
| + belongs_to :project | |
| - def set_attribute(name, value, ctype='text/plain') | |
| - la = LineAttribute.where(line_id: self.id, project_id: self.pr… | |
| - la.value = value | |
| - la.ctype = ctype | |
| - la.save | |
| - la | |
| - end | |
| + def set_attribute(name, value, ctype='text/plain') | |
| + la = LineAttribute.where(line_id: self.id, project_id: self.project_id, na… | |
| + la.value = value | |
| + la.ctype = ctype | |
| + la.save | |
| + la | |
| + end | |
| - def get_attribute(name) | |
| - LineAttribute.where(:line_id => self[:id], :name => name).first | |
| - end | |
| + def get_attribute(name) | |
| + LineAttribute.where(:line_id => self[:id], :name => name).first | |
| + end | |
| end | |
| diff --git a/app/models/line_attribute.rb b/app/models/line_attribute.rb | |
| @@ -13,6 +13,6 @@ | |
| # | |
| class LineAttribute < ActiveRecord::Base | |
| - belongs_to :line | |
| - belongs_to :project | |
| + belongs_to :line | |
| + belongs_to :project | |
| end | |
| diff --git a/app/models/project.rb b/app/models/project.rb | |
| @@ -14,15 +14,15 @@ | |
| class Project < ActiveRecord::Base | |
| - validates_presence_of :name | |
| - validates_uniqueness_of :name | |
| + validates_presence_of :name | |
| + validates_uniqueness_of :name | |
| - attr_accessible :name, :description, :included, :excluded | |
| + attr_accessible :name, :description, :included, :excluded | |
| - # This is optimized for fast project deletion, even with thousands of … | |
| - has_many :lines, :dependent => :delete_all | |
| - has_many :line_attributes, :dependent => :delete_all | |
| - has_many :calls, :dependent => :delete_all | |
| - has_many :call_media, :dependent => :delete_all | |
| - has_many :jobs, :dependent => :delete_all | |
| + # This is optimized for fast project deletion, even with thousands of calls/… | |
| + has_many :lines, :dependent => :delete_all | |
| + has_many :line_attributes, :dependent => :delete_all | |
| + has_many :calls, :dependent => :delete_all | |
| + has_many :call_media, :dependent => :delete_all | |
| + has_many :jobs, :dependent => :delete_all | |
| end | |
| diff --git a/app/models/provider.rb b/app/models/provider.rb | |
| @@ -15,11 +15,11 @@ | |
| # | |
| class Provider < ActiveRecord::Base | |
| - has_many :dial_results | |
| + has_many :dial_results | |
| - validates_presence_of :name, :host, :port, :user, :pass, :lines | |
| - validates_numericality_of :port, :less_than => 65536, :greater_than =>… | |
| - validates_numericality_of :lines, :less_than => 255, :greater_than => 0 | |
| + validates_presence_of :name, :host, :port, :user, :pass, :lines | |
| + validates_numericality_of :port, :less_than => 65536, :greater_than => 0 | |
| + validates_numericality_of :lines, :less_than => 255, :greater_than => 0 | |
| - attr_accessible :enabled, :name, :host, :port, :user, :pass, :lines | |
| + attr_accessible :enabled, :name, :host, :port, :user, :pass, :lines | |
| end | |
| diff --git a/app/models/settings.rb b/app/models/settings.rb | |
| @@ -12,5 +12,5 @@ | |
| # | |
| class Settings < RailsSettings::CachedSettings | |
| - attr_accessible :var | |
| + attr_accessible :var | |
| end | |
| diff --git a/app/models/signature.rb b/app/models/signature.rb | |
| @@ -14,6 +14,6 @@ | |
| # | |
| class Signature < ActiveRecord::Base | |
| - has_many :signature_fps | |
| + has_many :signature_fps | |
| end | |
| diff --git a/app/models/signature_fp.rb b/app/models/signature_fp.rb | |
| @@ -1,4 +1,4 @@ | |
| class SignatureFp < ActiveRecord::Base | |
| - belongs_to :signature | |
| + belongs_to :signature | |
| end | |
| diff --git a/app/models/user.rb b/app/models/user.rb | |
| @@ -24,11 +24,11 @@ | |
| # | |
| class User < ActiveRecord::Base | |
| - include RailsSettings::Extend | |
| - acts_as_authentic do |c| | |
| - c.validate_email_field = false | |
| - c.merge_validates_length_of_password_field_options :minimum =>… | |
| - c.merge_validates_length_of_password_confirmation_field_option… | |
| - c.logged_in_timeout = 1.day | |
| - end | |
| + include RailsSettings::Extend | |
| + acts_as_authentic do |c| | |
| + c.validate_email_field = false | |
| + c.merge_validates_length_of_password_field_options :minimum => 8 | |
| + c.merge_validates_length_of_password_confirmation_field_options :minimum =… | |
| + c.logged_in_timeout = 1.day | |
| + end | |
| end | |
| diff --git a/app/models/user_session.rb b/app/models/user_session.rb | |
| @@ -1,3 +1,3 @@ | |
| class UserSession < Authlogic::Session::Base | |
| - logout_on_timeout true | |
| + logout_on_timeout true | |
| end | |
| diff --git a/bin/adduser b/bin/adduser | |
| @@ -17,19 +17,19 @@ require APP_PATH | |
| Rails.application.require_environment! | |
| def generate_password | |
| - set = ( [*(0x21 .. 0x2f)] + [*(0x3a .. 0x3F)] + [*(0x5b .. 0x60)] + [*… | |
| - set << "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" | |
| - str = '' | |
| - cnt = 0 | |
| - while not (str.length >= 8 and str =~ /[A-Za-z]/ and str =~ /[0-9]/ an… | |
| - if str.length > 12 | |
| - str = str[0,4] | |
| - next | |
| - end | |
| - str << set[ rand(set.length), 1] | |
| - cnt += 1 | |
| - end | |
| - str | |
| + set = ( [*(0x21 .. 0x2f)] + [*(0x3a .. 0x3F)] + [*(0x5b .. 0x60)] + [*(0x7b … | |
| + set << "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" | |
| + str = '' | |
| + cnt = 0 | |
| + while not (str.length >= 8 and str =~ /[A-Za-z]/ and str =~ /[0-9]/ and str … | |
| + if str.length > 12 | |
| + str = str[0,4] | |
| + next | |
| + end | |
| + str << set[ rand(set.length), 1] | |
| + cnt += 1 | |
| + end | |
| + str | |
| end | |
| @@ -40,32 +40,32 @@ user = username ? User.find_by_login(username) : nil | |
| if not user | |
| - if ! username | |
| - $stdout.write "[*] Please enter a username: " | |
| - $stdout.flush | |
| - username = $stdin.readline.strip | |
| - end | |
| + if ! username | |
| + $stdout.write "[*] Please enter a username: " | |
| + $stdout.flush | |
| + username = $stdin.readline.strip | |
| + end | |
| - if ! (username and username.strip.length > 0) | |
| - $stdout.puts "[-] Invalid username specified" | |
| - exit(0) | |
| - end | |
| + if ! (username and username.strip.length > 0) | |
| + $stdout.puts "[-] Invalid username specified" | |
| + exit(0) | |
| + end | |
| - if not password | |
| - randpass = generate_password | |
| - $stdout.puts "" | |
| - $stdout.puts "[*] Creating user '#{username}' with password '#… | |
| - $stdout.puts "" | |
| - password = randpass | |
| - end | |
| + if not password | |
| + randpass = generate_password | |
| + $stdout.puts "" | |
| + $stdout.puts "[*] Creating user '#{username}' with password '#{randpass}' … | |
| + $stdout.puts "" | |
| + password = randpass | |
| + end | |
| - user = User.new() | |
| - user.login = username | |
| - user.password = password | |
| - user.password_confirmation = password | |
| - user.save! | |
| + user = User.new() | |
| + user.login = username | |
| + user.password = password | |
| + user.password_confirmation = password | |
| + user.save! | |
| - $stdout.puts "[*] User #{user.login} has been created, please change y… | |
| + $stdout.puts "[*] User #{user.login} has been created, please change your pa… | |
| else | |
| - $stdout.puts "[*] That user account already exists, please try 'resetp… | |
| + $stdout.puts "[*] That user account already exists, please try 'resetpw' scr… | |
| end | |
| diff --git a/bin/analyze_result.rb b/bin/analyze_result.rb | |
| @@ -6,7 +6,7 @@ | |
| # | |
| base = __FILE__ | |
| while File.symlink?(base) | |
| - base = File.expand_path(File.readlink(base), File.dirname(base)) | |
| + base = File.expand_path(File.readlink(base), File.dirname(base)) | |
| end | |
| $:.unshift(File.join(File.expand_path(File.dirname(base)), '..', 'lib')) | |
| require 'warvox' | |
| @@ -23,13 +23,13 @@ $0 = "warvox(analyzer): #{inp} #{num}" | |
| begin | |
| $stdout.write( | |
| - Marshal.dump( | |
| - WarVOX::Jobs::Analysis.analyze_call( | |
| - inp, num | |
| - ) | |
| - ) | |
| + Marshal.dump( | |
| + WarVOX::Jobs::Analysis.analyze_call( | |
| + inp, num | |
| + ) | |
| + ) | |
| ) | |
| rescue ::Errno::EPIPE | |
| - # Hide pipe errors (parent is killed when task was cancelled) | |
| + # Hide pipe errors (parent is killed when task was cancelled) | |
| end | |
| diff --git a/bin/audio_raw_to_fprint.rb b/bin/audio_raw_to_fprint.rb | |
| @@ -6,15 +6,15 @@ | |
| # | |
| base = __FILE__ | |
| while File.symlink?(base) | |
| - base = File.expand_path(File.readlink(base), File.dirname(base)) | |
| + base = File.expand_path(File.readlink(base), File.dirname(base)) | |
| end | |
| $:.unshift(File.join(File.expand_path(File.dirname(base)), '..', 'lib')) | |
| require 'warvox' | |
| def usage | |
| - $stderr.puts "Usage: #{$0} <input.raw> <output.txt>" | |
| - exit | |
| + $stderr.puts "Usage: #{$0} <input.raw> <output.txt>" | |
| + exit | |
| end | |
| # | |
| @@ -25,14 +25,14 @@ inp = ARGV.shift | |
| out = ARGV.shift | |
| if (inp and inp == "-h") or not inp | |
| - usage() | |
| + usage() | |
| end | |
| raw = WarVOX::Audio::Raw.from_file(inp) | |
| if out | |
| - ::File.open(out, "wb") do |fd| | |
| - fd.write( "{" + raw.to_freq_sig.map{|x| x.to_s}.join(",") + "}… | |
| - end | |
| + ::File.open(out, "wb") do |fd| | |
| + fd.write( "{" + raw.to_freq_sig.map{|x| x.to_s}.join(",") + "}" ) | |
| + end | |
| else | |
| - $stdout.write( "{" + raw.to_freq_sig.map{|x| x.to_s}.join(",") + "}" ) | |
| + $stdout.write( "{" + raw.to_freq_sig.map{|x| x.to_s}.join(",") + "}" ) | |
| end | |
| diff --git a/bin/audio_raw_to_wav.rb b/bin/audio_raw_to_wav.rb | |
| @@ -6,15 +6,15 @@ | |
| # | |
| base = __FILE__ | |
| while File.symlink?(base) | |
| - base = File.expand_path(File.readlink(base), File.dirname(base)) | |
| + base = File.expand_path(File.readlink(base), File.dirname(base)) | |
| end | |
| $:.unshift(File.join(File.expand_path(File.dirname(base)), '..', 'lib')) | |
| require 'warvox' | |
| def usage | |
| - $stderr.puts "Usage: #{$0} <input.raw> <output.wav>" | |
| - exit | |
| + $stderr.puts "Usage: #{$0} <input.raw> <output.wav>" | |
| + exit | |
| end | |
| # | |
| @@ -25,14 +25,14 @@ inp = ARGV.shift | |
| out = ARGV.shift | |
| if (inp and inp == "-h") or not inp | |
| - usage() | |
| + usage() | |
| end | |
| raw = WarVOX::Audio::Raw.from_file(inp) | |
| if out | |
| - ::File.open(out, "wb") do |fd| | |
| - fd.write(raw.to_wav) | |
| - end | |
| + ::File.open(out, "wb") do |fd| | |
| + fd.write(raw.to_wav) | |
| + end | |
| else | |
| - $stdout.write(raw.to_wav) | |
| + $stdout.write(raw.to_wav) | |
| end | |
| diff --git a/bin/audio_trim.rb b/bin/audio_trim.rb | |
| @@ -6,15 +6,15 @@ | |
| # | |
| base = __FILE__ | |
| while File.symlink?(base) | |
| - base = File.expand_path(File.readlink(base), File.dirname(base)) | |
| + base = File.expand_path(File.readlink(base), File.dirname(base)) | |
| end | |
| $:.unshift(File.join(File.expand_path(File.dirname(base)), '..', 'lib')) | |
| require 'warvox' | |
| def usage | |
| - $stderr.puts "Usage: #{$0} [offset] [length] <input.raw> <output.raw>" | |
| - exit | |
| + $stderr.puts "Usage: #{$0} [offset] [length] <input.raw> <output.raw>" | |
| + exit | |
| end | |
| # | |
| @@ -27,26 +27,26 @@ inp = ARGV.shift | |
| out = ARGV.shift | |
| if (off and off == "-h") or not off | |
| - usage() | |
| + usage() | |
| end | |
| buf = '' | |
| ifd = nil | |
| if inp | |
| - ifd = ::File.open(inp, "rb") | |
| + ifd = ::File.open(inp, "rb") | |
| else | |
| - $stdin.binmode | |
| - ifd = $stdin | |
| + $stdin.binmode | |
| + ifd = $stdin | |
| end | |
| ofd = nil | |
| if out | |
| - ofd = ::File.open(out, "wb") | |
| + ofd = ::File.open(out, "wb") | |
| else | |
| - $stdout.binmode | |
| - ofd = $stdout | |
| + $stdout.binmode | |
| + ofd = $stdout | |
| end | |
| diff --git a/bin/cache_clear.rb b/bin/cache_clear.rb | |
| @@ -6,7 +6,7 @@ | |
| # | |
| base = __FILE__ | |
| while File.symlink?(base) | |
| - base = File.expand_path(File.readlink(base), File.dirname(base)) | |
| + base = File.expand_path(File.readlink(base), File.dirname(base)) | |
| end | |
| $:.unshift(File.join(File.expand_path(File.dirname(base)), '..', 'lib')) | |
| diff --git a/bin/export_audio.rb b/bin/export_audio.rb | |
| @@ -6,7 +6,7 @@ | |
| # | |
| base = __FILE__ | |
| while File.symlink?(base) | |
| - base = File.expand_path(File.readlink(base), File.dirname(base)) | |
| + base = File.expand_path(File.readlink(base), File.dirname(base)) | |
| end | |
| $:.unshift(File.join(File.expand_path(File.dirname(base)), '..', 'lib')) | |
| @@ -18,8 +18,8 @@ ENV['RAILS_ENV'] ||= 'production' | |
| $:.unshift(File.join(File.expand_path(File.dirname(base)), '..')) | |
| def usage | |
| - $stderr.puts "Usage: #{$0} [Output Dir] [Project ID] <Line Type>" | |
| - exit | |
| + $stderr.puts "Usage: #{$0} [Output Dir] [Project ID] <Line Type>" | |
| + exit | |
| end | |
| # | |
| @@ -31,61 +31,61 @@ project_id = ARGV.shift | |
| line_type = ARGV.shift | |
| if(output and output == "-h") or (! output) | |
| - usage() | |
| + usage() | |
| end | |
| require 'config/boot' | |
| require 'config/environment' | |
| if project_id.to_i == 0 | |
| - $stderr.puts "Listing all projects" | |
| - $stderr.puts "====================" | |
| - Project.all.each do |j| | |
| - puts "#{j.id}\t#{j.name}\t#{j.created_at}" | |
| - end | |
| - exit | |
| + $stderr.puts "Listing all projects" | |
| + $stderr.puts "====================" | |
| + Project.all.each do |j| | |
| + puts "#{j.id}\t#{j.name}\t#{j.created_at}" | |
| + end | |
| + exit | |
| end | |
| FileUtils.mkdir_p(output) | |
| begin | |
| - cond = { :project_id => project_id.to_i, :answered => true, :busy => f… | |
| - if line_type | |
| - cond[:line_type] = line_type.downcase | |
| - end | |
| - | |
| - Call.where(cond).order(number: :asc).each do |r| | |
| - m = r.media | |
| - if m and m.audio | |
| - | |
| - ::File.open(File.join(output, "#{r.number}.raw"), "wb"… | |
| - fd.write(m.audio) | |
| - end | |
| - | |
| - ::File.open(File.join(output, "#{r.number}.yml"), "wb"… | |
| - fd.write(r.to_yaml) | |
| - end | |
| - | |
| - if m.mp3 | |
| - ::File.open(File.join(output, "#{r.number}.mp3… | |
| - fd.write(m.mp3) | |
| - end | |
| - end | |
| - | |
| - if m.png_big | |
| - ::File.open(File.join(output, "#{r.number}_wav… | |
| - fd.write(m.png_big) | |
| - end | |
| - end | |
| - | |
| - if m.png_big_freq | |
| - ::File.open(File.join(output, "#{r.number}_fre… | |
| - fd.write(m.png_big_freq) | |
| - end | |
| - end | |
| - | |
| - $stderr.puts "[*] Exported #{r.number}..." | |
| - | |
| - end | |
| - end | |
| + cond = { :project_id => project_id.to_i, :answered => true, :busy => false } | |
| + if line_type | |
| + cond[:line_type] = line_type.downcase | |
| + end | |
| + | |
| + Call.where(cond).order(number: :asc).each do |r| | |
| + m = r.media | |
| + if m and m.audio | |
| + | |
| + ::File.open(File.join(output, "#{r.number}.raw"), "wb") do |fd| | |
| + fd.write(m.audio) | |
| + end | |
| + | |
| + ::File.open(File.join(output, "#{r.number}.yml"), "wb") do |fd| | |
| + fd.write(r.to_yaml) | |
| + end | |
| + | |
| + if m.mp3 | |
| + ::File.open(File.join(output, "#{r.number}.mp3"), "wb") do |fd| | |
| + fd.write(m.mp3) | |
| + end | |
| + end | |
| + | |
| + if m.png_big | |
| + ::File.open(File.join(output, "#{r.number}_wave.png"), "wb") do |fd| | |
| + fd.write(m.png_big) | |
| + end | |
| + end | |
| + | |
| + if m.png_big_freq | |
| + ::File.open(File.join(output, "#{r.number}_freq.png"), "wb") do |fd| | |
| + fd.write(m.png_big_freq) | |
| + end | |
| + end | |
| + | |
| + $stderr.puts "[*] Exported #{r.number}..." | |
| + | |
| + end | |
| + end | |
| end | |
| diff --git a/bin/export_list.rb b/bin/export_list.rb | |
| @@ -6,7 +6,7 @@ | |
| # | |
| base = __FILE__ | |
| while File.symlink?(base) | |
| - base = File.expand_path(File.readlink(base), File.dirname(base)) | |
| + base = File.expand_path(File.readlink(base), File.dirname(base)) | |
| end | |
| $:.unshift(File.join(File.expand_path(File.dirname(base)), '..', 'lib')) | |
| @@ -17,8 +17,8 @@ ENV['RAILS_ENV'] ||= 'production' | |
| $:.unshift(File.join(File.expand_path(File.dirname(base)), '..')) | |
| def usage | |
| - $stderr.puts "Usage: #{$0} [Job ID] <Type>" | |
| - exit | |
| + $stderr.puts "Usage: #{$0} [Job ID] <Type>" | |
| + exit | |
| end | |
| # | |
| @@ -29,37 +29,37 @@ project_id = ARGV.shift | |
| line_type = ARGV.shift | |
| if(project_id and project_id == "-h") | |
| - usage() | |
| + usage() | |
| end | |
| if project_id.to_i == 0 | |
| - usage() | |
| + usage() | |
| end | |
| require 'config/boot' | |
| require 'config/environment' | |
| if(not project_id) | |
| - $stderr.puts "Listing all projects" | |
| - $stderr.puts "====================" | |
| - Project.all.each do |j| | |
| - puts "#{j.id}\t#{j.name}\t#{j.created_at}" | |
| - end | |
| - exit | |
| + $stderr.puts "Listing all projects" | |
| + $stderr.puts "====================" | |
| + Project.all.each do |j| | |
| + puts "#{j.id}\t#{j.name}\t#{j.created_at}" | |
| + end | |
| + exit | |
| end | |
| fields = %W{ number line_type caller_id answered busy audio_length ring_length… | |
| begin | |
| - $stdout.puts fields.to_csv | |
| - cond = { :project_id => project_id.to_i } | |
| - if line_type | |
| - cond[:line_type] = line_type.downcase | |
| - end | |
| - Call.where(cond).order(number: :asc).each do |r| | |
| - out = [] | |
| - fields.each do |f| | |
| - out << r[f].to_s | |
| - end | |
| - $stdout.puts out.to_csv | |
| - end | |
| + $stdout.puts fields.to_csv | |
| + cond = { :project_id => project_id.to_i } | |
| + if line_type | |
| + cond[:line_type] = line_type.downcase | |
| + end | |
| + Call.where(cond).order(number: :asc).each do |r| | |
| + out = [] | |
| + fields.each do |f| | |
| + out << r[f].to_s | |
| + end | |
| + $stdout.puts out.to_csv | |
| + end | |
| end | |
| diff --git a/bin/iaxrecord.rb b/bin/iaxrecord.rb | |
| @@ -4,7 +4,7 @@ $:.unshift(::File.join(::File.dirname(__FILE__), "..", "lib")) | |
| def stop | |
| - exit(0) | |
| + exit(0) | |
| end | |
| trap("SIGINT") { stop() } | |
| @@ -16,56 +16,56 @@ require "optparse" | |
| parser = OptionParser.new | |
| opts = { | |
| - :recording_time => 52 | |
| + :recording_time => 52 | |
| } | |
| parser.banner = "Usage: #{$0} [options]" | |
| parser.on("-s server") do |v| | |
| - opts[:server_host] = v | |
| + opts[:server_host] = v | |
| end | |
| parser.on("-u user") do |v| | |
| - opts[:username] = v | |
| + opts[:username] = v | |
| end | |
| parser.on("-p pass") do |v| | |
| - opts[:password] = v | |
| + opts[:password] = v | |
| end | |
| parser.on("-o output") do |v| | |
| - opts[:output] = v | |
| + opts[:output] = v | |
| end | |
| parser.on("-n number") do |v| | |
| - opts[:called_number] = v | |
| + opts[:called_number] = v | |
| end | |
| parser.on("-c cid") do |v| | |
| - opts[:caller_number] = v | |
| + opts[:caller_number] = v | |
| end | |
| parser.on("-l seconds") do |v| | |
| - opts[:recording_time] = v.to_i | |
| + opts[:recording_time] = v.to_i | |
| end | |
| parser.on("-d") do |v| | |
| - opts[:debugging] = true | |
| + opts[:debugging] = true | |
| end | |
| parser.on("-k keys") do |v| | |
| - opts[:sendkeys] = v | |
| + opts[:sendkeys] = v | |
| end | |
| parser.on("-h") do | |
| - $stderr.puts parser | |
| - exit(1) | |
| + $stderr.puts parser | |
| + exit(1) | |
| end | |
| parser.parse!(ARGV) | |
| if not (opts[:server_host] and opts[:username] and opts[:password] and opts[:c… | |
| - $stderr.puts parser | |
| - exit(1) | |
| + $stderr.puts parser | |
| + exit(1) | |
| end | |
| @@ -74,33 +74,33 @@ cli = WarVOX::Proto::IAX2::Client.new(opts) | |
| reg = cli.create_call | |
| r = reg.register | |
| if not r | |
| - $stderr.puts "ERROR: Unable to register with the IAX server" | |
| - exit(0) | |
| + $stderr.puts "ERROR: Unable to register with the IAX server" | |
| + exit(0) | |
| end | |
| c = cli.create_call | |
| r = c.dial( opts[:called_number] ) | |
| if not r | |
| - $stderr.puts "ERROR: Unable to dial the requested number" | |
| - exit(0) | |
| + $stderr.puts "ERROR: Unable to dial the requested number" | |
| + exit(0) | |
| end | |
| begin | |
| ::Timeout.timeout( opts[:recording_time] ) do | |
| - while (c.state != :hangup) | |
| - case c.state | |
| - when :ringing | |
| - when :answered | |
| - when :hangup | |
| - break | |
| - end | |
| - select(nil,nil,nil, 0.25) | |
| - end | |
| + while (c.state != :hangup) | |
| + case c.state | |
| + when :ringing | |
| + when :answered | |
| + when :hangup | |
| + break | |
| + end | |
| + select(nil,nil,nil, 0.25) | |
| + end | |
| end | |
| rescue ::Timeout::Error | |
| ensure | |
| - c.hangup rescue nil | |
| + c.hangup rescue nil | |
| end | |
| cli.shutdown | |
| @@ -108,8 +108,8 @@ cli.shutdown | |
| cnt = 0 | |
| fd = ::File.open( opts[:output], "wb") | |
| c.each_audio_frame do |frame| | |
| - fd.write(frame) | |
| - cnt += frame.length | |
| + fd.write(frame) | |
| + cnt += frame.length | |
| end | |
| fd.close | |
| diff --git a/bin/identify_matches.rb b/bin/identify_matches.rb | |
| @@ -6,7 +6,7 @@ | |
| # | |
| base = __FILE__ | |
| while File.symlink?(base) | |
| - base = File.expand_path(File.readlink(base), File.dirname(base)) | |
| + base = File.expand_path(File.readlink(base), File.dirname(base)) | |
| end | |
| $:.unshift(File.join(File.expand_path(File.dirname(base)), '..', 'lib')) | |
| @@ -19,8 +19,8 @@ require 'config/boot' | |
| require 'config/environment' | |
| def usage | |
| - $stderr.puts "Usage: #{$0} [job|all] <fprint>" | |
| - exit | |
| + $stderr.puts "Usage: #{$0} [job|all] <fprint>" | |
| + exit | |
| end | |
| # | |
| @@ -31,49 +31,49 @@ job = ARGV.shift | |
| fp = ARGV.shift | |
| if(job and job == "-h") | |
| - usage() | |
| + usage() | |
| end | |
| if(not job) | |
| - $stderr.puts "Listing all available jobs" | |
| - $stderr.puts "==========================" | |
| - DialJob.find(:all).each do |j| | |
| - puts "#{j.id}\t#{j.started_at} --> #{j.completed_at}" | |
| - end | |
| - exit | |
| + $stderr.puts "Listing all available jobs" | |
| + $stderr.puts "==========================" | |
| + DialJob.find(:all).each do |j| | |
| + puts "#{j.id}\t#{j.started_at} --> #{j.completed_at}" | |
| + end | |
| + exit | |
| end | |
| fp = $stdin.read.strip if fp == "-" | |
| job = nil if job.downcase == "all" | |
| if not fp | |
| - usage() | |
| + usage() | |
| end | |
| begin | |
| - res = nil | |
| - job = DialJob.find(job.to_i) if job | |
| - if job | |
| - res = DialResult.find_by_sql "SELECT dial_results.*, " + | |
| - " (( icount('#{fp}'::int[] & dial_results.fprint::int[… | |
| - "FROM dial_results " + | |
| - "WHERE " + | |
| - " icount(dial_results.fprint) > 0 AND " + | |
| - " dial_results.dial_job_id = '#{job.id}' " + | |
| - "ORDER BY matchscore DESC" | |
| - else | |
| - res = DialResult.find_by_sql "SELECT dial_results.*, " + | |
| - " (( icount('#{fp}'::int[] & dial_results.fprint::int[… | |
| - "FROM dial_results " + | |
| - "WHERE " + | |
| - " icount(dial_results.fprint) > 0 " + | |
| - "ORDER BY matchscore DESC" | |
| - end | |
| - res.each do |r| | |
| - $stdout.puts "#{"%.2f" % r.matchscore}\t#{r.dial_job_id}\t#{r.… | |
| - end | |
| + res = nil | |
| + job = DialJob.find(job.to_i) if job | |
| + if job | |
| + res = DialResult.find_by_sql "SELECT dial_results.*, " + | |
| + " (( icount('#{fp}'::int[] & dial_results.fprint::int[]) / icount('#{fp}… | |
| + "FROM dial_results " + | |
| + "WHERE " + | |
| + " icount(dial_results.fprint) > 0 AND " + | |
| + " dial_results.dial_job_id = '#{job.id}' " + | |
| + "ORDER BY matchscore DESC" | |
| + else | |
| + res = DialResult.find_by_sql "SELECT dial_results.*, " + | |
| + " (( icount('#{fp}'::int[] & dial_results.fprint::int[]) / icount('#{fp}… | |
| + "FROM dial_results " + | |
| + "WHERE " + | |
| + " icount(dial_results.fprint) > 0 " + | |
| + "ORDER BY matchscore DESC" | |
| + end | |
| + res.each do |r| | |
| + $stdout.puts "#{"%.2f" % r.matchscore}\t#{r.dial_job_id}\t#{r.number}" | |
| + end | |
| rescue ActiveRecord::RecordNotFound | |
| - $stderr.puts "Job not found" | |
| - exit | |
| + $stderr.puts "Job not found" | |
| + exit | |
| end | |
| diff --git a/bin/import_audio.rb b/bin/import_audio.rb | |
| @@ -6,7 +6,7 @@ | |
| # | |
| base = __FILE__ | |
| while File.symlink?(base) | |
| - base = File.expand_path(File.readlink(base), File.dirname(base)) | |
| + base = File.expand_path(File.readlink(base), File.dirname(base)) | |
| end | |
| $:.unshift(File.join(File.expand_path(File.dirname(base)), '..', 'lib')) | |
| @@ -19,8 +19,8 @@ ENV['RAILS_ENV'] ||= 'production' | |
| $:.unshift(File.join(File.expand_path(File.dirname(base)), '..')) | |
| def usage | |
| - $stderr.puts "Usage: #{$0} [Input Directory] <Project ID> <Provider ID… | |
| - exit(1) | |
| + $stderr.puts "Usage: #{$0} [Input Directory] <Project ID> <Provider ID>" | |
| + exit(1) | |
| end | |
| # | |
| @@ -29,7 +29,7 @@ end | |
| dir = ARGV.shift() || usage() | |
| if (dir and dir =="-h") or (! dir) | |
| - usage() | |
| + usage() | |
| end | |
| require 'config/boot' | |
| @@ -41,47 +41,47 @@ provider_id = ARGV.shift | |
| todo = Dir["#{dir}/**/*.raw"].to_a | |
| if todo.empty? | |
| - $stderr.puts "Error: No raw audio files found within #{dir}" | |
| - exit(1) | |
| + $stderr.puts "Error: No raw audio files found within #{dir}" | |
| + exit(1) | |
| end | |
| project = nil | |
| provider = nil | |
| if project_id | |
| - project = Project.where(:id => project_id).first | |
| - unless project | |
| - $stderr.puts "Error: Specified Project ID not found" | |
| - exit(1) | |
| - end | |
| + project = Project.where(:id => project_id).first | |
| + unless project | |
| + $stderr.puts "Error: Specified Project ID not found" | |
| + exit(1) | |
| + end | |
| end | |
| if provider_id | |
| - provider = Provider.where(:id => provider_id).first | |
| - unless provider | |
| - $stderr.puts "Error: Specified Provider ID not found" | |
| - exit(1) | |
| - end | |
| + provider = Provider.where(:id => provider_id).first | |
| + unless provider | |
| + $stderr.puts "Error: Specified Provider ID not found" | |
| + exit(1) | |
| + end | |
| end | |
| unless project | |
| - project = Project.create( | |
| - :name => "Import from #{dir} at #{Time.now.utc.to_s}", | |
| - :created_by => "importer" | |
| - ) | |
| + project = Project.create( | |
| + :name => "Import from #{dir} at #{Time.now.utc.to_s}", | |
| + :created_by => "importer" | |
| + ) | |
| end | |
| provider = Provider.first | |
| unless provider | |
| - provider = Provider.create( | |
| - :name => 'Import Provider', | |
| - :host => 'localhost', | |
| - :port => 4369, | |
| - :user => "null", | |
| - :pass => "null", | |
| - :lines => 1, | |
| - :enabled => false | |
| - ) | |
| + provider = Provider.create( | |
| + :name => 'Import Provider', | |
| + :host => 'localhost', | |
| + :port => 4369, | |
| + :user => "null", | |
| + :pass => "null", | |
| + :lines => 1, | |
| + :enabled => false | |
| + ) | |
| end | |
| @@ -100,32 +100,32 @@ pct = 0 | |
| cnt = 0 | |
| todo.each do |rfile| | |
| - num, ext = File.basename(rfile).split(".", 2) | |
| - dr = Call.new | |
| - dr.number = num | |
| - dr.job_id = job.id | |
| - dr.project_id = project.id | |
| - dr.provider_id = provider.id | |
| - dr.answered = true | |
| - dr.busy = false | |
| - dr.audio_length = File.size(rfile) / 16000.0 | |
| - dr.ring_length = 0 | |
| - dr.caller_id = num | |
| - dr.save | |
| - | |
| - mr = dr.media | |
| - ::File.open(rfile, "rb") do |fd| | |
| - mr.audio = fd.read(fd.stat.size) | |
| - mr.save | |
| - end | |
| - | |
| - cnt += 1 | |
| - pct = (cnt / todo.length.to_f) * 100.0 | |
| - if cnt % 10 == 0 | |
| - job.update_progress(pct) | |
| - end | |
| - | |
| - $stdout.puts "[ %#{"%.3d" % pct.to_i} ] Imported #{num} into project '… | |
| + num, ext = File.basename(rfile).split(".", 2) | |
| + dr = Call.new | |
| + dr.number = num | |
| + dr.job_id = job.id | |
| + dr.project_id = project.id | |
| + dr.provider_id = provider.id | |
| + dr.answered = true | |
| + dr.busy = false | |
| + dr.audio_length = File.size(rfile) / 16000.0 | |
| + dr.ring_length = 0 | |
| + dr.caller_id = num | |
| + dr.save | |
| + | |
| + mr = dr.media | |
| + ::File.open(rfile, "rb") do |fd| | |
| + mr.audio = fd.read(fd.stat.size) | |
| + mr.save | |
| + end | |
| + | |
| + cnt += 1 | |
| + pct = (cnt / todo.length.to_f) * 100.0 | |
| + if cnt % 10 == 0 | |
| + job.update_progress(pct) | |
| + end | |
| + | |
| + $stdout.puts "[ %#{"%.3d" % pct.to_i} ] Imported #{num} into project '#{proj… | |
| end | |
| job.update_progress(100) | |
| diff --git a/bin/resetpw b/bin/resetpw | |
| @@ -20,31 +20,31 @@ uname = ARGV.shift | |
| upass = ARGV.shift | |
| def generate_password | |
| - set = ( [*(0x21 .. 0x2f)] + [*(0x3a .. 0x3F)] + [*(0x5b .. 0x60)] + [*… | |
| - set << "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" | |
| - str = '' | |
| - cnt = 0 | |
| - while not (str.length >= 8 and str =~ /[A-Za-z]/ and str =~ /[0-9]/ an… | |
| - if str.length > 12 | |
| - str = str[0,4] | |
| - next | |
| - end | |
| - str << set[ rand(set.length), 1] | |
| - cnt += 1 | |
| - end | |
| - str | |
| + set = ( [*(0x21 .. 0x2f)] + [*(0x3a .. 0x3F)] + [*(0x5b .. 0x60)] + [*(0x7b … | |
| + set << "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" | |
| + str = '' | |
| + cnt = 0 | |
| + while not (str.length >= 8 and str =~ /[A-Za-z]/ and str =~ /[0-9]/ and str … | |
| + if str.length > 12 | |
| + str = str[0,4] | |
| + next | |
| + end | |
| + str << set[ rand(set.length), 1] | |
| + cnt += 1 | |
| + end | |
| + str | |
| end | |
| user = uname ? User.find_by_login(uname) : User.first | |
| if uname and not user | |
| - $stderr.puts "[-] User #{uname} was not found" | |
| - exit(1) | |
| + $stderr.puts "[-] User #{uname} was not found" | |
| + exit(1) | |
| end | |
| if not user | |
| - $stderr.puts "[-] No user account has been created" | |
| - exit(1) | |
| + $stderr.puts "[-] No user account has been created" | |
| + exit(1) | |
| end | |
| randpass = upass || generate_password() | |
| @@ -69,9 +69,9 @@ $stdout.flush | |
| inp = $stdin.readline | |
| if inp.strip.downcase != 'yes' | |
| - $stdout.puts "[*] Reset cancelled, hit enter to exit" | |
| - $stdin.readline | |
| - exit(0) | |
| + $stdout.puts "[*] Reset cancelled, hit enter to exit" | |
| + $stdin.readline | |
| + exit(0) | |
| end | |
| diff --git a/bin/verify_install.rb b/bin/verify_install.rb | |
| @@ -6,7 +6,7 @@ | |
| # | |
| base = __FILE__ | |
| while File.symlink?(base) | |
| - base = File.expand_path(File.readlink(base), File.dirname(base)) | |
| + base = File.expand_path(File.readlink(base), File.dirname(base)) | |
| end | |
| $:.unshift(File.join(File.expand_path(File.dirname(base)), '..', 'lib')) | |
| require 'warvox' | |
| @@ -24,34 +24,34 @@ puts(" ") | |
| begin | |
| - require 'rubygems' | |
| - puts "[*] RubyGems have been installed" | |
| + require 'rubygems' | |
| + puts "[*] RubyGems have been installed" | |
| rescue ::LoadError | |
| - puts "[*] ERROR: The RubyGems package has not been installed:" | |
| - puts " $ sudo apt-get install rubygems" | |
| - exit | |
| + puts "[*] ERROR: The RubyGems package has not been installed:" | |
| + puts " $ sudo apt-get install rubygems" | |
| + exit | |
| end | |
| begin | |
| - require 'bundler' | |
| - puts "[*] The Bundler gem has been installed" | |
| + require 'bundler' | |
| + puts "[*] The Bundler gem has been installed" | |
| rescue ::LoadError | |
| - puts "[*] ERROR: The Bundler gem has not been installed:" | |
| - puts " $ sudo gem install bundler" | |
| - exit | |
| + puts "[*] ERROR: The Bundler gem has not been installed:" | |
| + puts " $ sudo gem install bundler" | |
| + exit | |
| end | |
| if(not WarVOX::Config.tool_path('gnuplot')) | |
| - puts "[*] ERROR: The 'gnuplot' binary could not be installed" | |
| - puts "[*] $ sudo apt-get install gnuplot" | |
| - exit | |
| + puts "[*] ERROR: The 'gnuplot' binary could not be installed" | |
| + puts "[*] $ sudo apt-get install gnuplot" | |
| + exit | |
| end | |
| puts "[*] The GNUPlot binary appears to be available" | |
| if(not WarVOX::Config.tool_path('lame')) | |
| - puts "[*] ERROR: The 'lame' binary could not be installed" | |
| - puts "[*] $ sudo apt-get install lame" | |
| - exit | |
| + puts "[*] ERROR: The 'lame' binary could not be installed" | |
| + puts "[*] $ sudo apt-get install lame" | |
| + exit | |
| end | |
| puts "[*] The LAME binary appears to be available" | |
| diff --git a/bin/warvox.rb b/bin/warvox.rb | |
| @@ -9,7 +9,7 @@ require 'open3' | |
| # | |
| base = __FILE__ | |
| while File.symlink?(base) | |
| - base = File.expand_path(File.readlink(base), File.dirname(base)) | |
| + base = File.expand_path(File.readlink(base), File.dirname(base)) | |
| end | |
| $:.unshift(File.join(File.expand_path(File.dirname(base)), '..', 'lib')) | |
| @@ -24,52 +24,52 @@ require 'warvox' | |
| Dir.chdir(voxroot) | |
| def stop | |
| - $stderr.puts "[-] Interrupt received, shutting down workers and web se… | |
| - Process.kill("TERM", @manager_pid) if @manager_pid | |
| - exit(0) | |
| + $stderr.puts "[-] Interrupt received, shutting down workers and web server..… | |
| + Process.kill("TERM", @manager_pid) if @manager_pid | |
| + exit(0) | |
| end | |
| def usage | |
| - $stderr.puts "#{$0} [--address IP] [--port PORT] --background" | |
| - exit(0) | |
| + $stderr.puts "#{$0} [--address IP] [--port PORT] --background" | |
| + exit(0) | |
| end | |
| opts = | |
| { | |
| - 'ServerPort' => 7777, | |
| - 'ServerHost' => '127.0.0.1', | |
| - 'Background' => false, | |
| + 'ServerPort' => 7777, | |
| + 'ServerHost' => '127.0.0.1', | |
| + 'Background' => false, | |
| } | |
| args = GetoptLong.new( | |
| - ["--address", "-a", GetoptLong::REQUIRED_ARGUMENT ], | |
| - ["--port", "-p", GetoptLong::REQUIRED_ARGUMENT ], | |
| - ["--daemon", "-d", GetoptLong::NO_ARGUMENT ], | |
| - ["--help", "-h", GetoptLong::NO_ARGUMENT] | |
| + ["--address", "-a", GetoptLong::REQUIRED_ARGUMENT ], | |
| + ["--port", "-p", GetoptLong::REQUIRED_ARGUMENT ], | |
| + ["--daemon", "-d", GetoptLong::NO_ARGUMENT ], | |
| + ["--help", "-h", GetoptLong::NO_ARGUMENT] | |
| ) | |
| args.each do |opt,arg| | |
| - case opt | |
| - when '--address' | |
| - opts['ServerHost'] = arg | |
| - when '--port' | |
| - opts['ServerPort'] = arg | |
| - when '--daemon' | |
| - opts['Background'] = true | |
| - when '--help' | |
| - usage() | |
| - end | |
| + case opt | |
| + when '--address' | |
| + opts['ServerHost'] = arg | |
| + when '--port' | |
| + opts['ServerPort'] = arg | |
| + when '--daemon' | |
| + opts['Background'] = true | |
| + when '--help' | |
| + usage() | |
| + end | |
| end | |
| args = [ | |
| - 'server', | |
| - '-p', opts['ServerPort'].to_s, | |
| - '-b', opts['ServerHost'], | |
| - '-e', 'production', | |
| + 'server', | |
| + '-p', opts['ServerPort'].to_s, | |
| + '-b', opts['ServerHost'], | |
| + '-e', 'production', | |
| ] | |
| if opts['Background'] | |
| - args.push("-d") | |
| + args.push("-d") | |
| end | |
| @@ -86,10 +86,10 @@ WarVOX::Log.info("WarVOX is starting up...") | |
| @manager_pid = Process.fork() | |
| if not @manager_pid | |
| - while ARGV.shift do | |
| - end | |
| - load(manager) | |
| - exit(0) | |
| + while ARGV.shift do | |
| + end | |
| + load(manager) | |
| + exit(0) | |
| end | |
| WarVOX::Log.info("Worker Manager has PID #{@manager_pid}") | |
| diff --git a/bin/worker.rb b/bin/worker.rb | |
| @@ -6,7 +6,7 @@ | |
| # | |
| base = __FILE__ | |
| while File.symlink?(base) | |
| - base = File.expand_path(File.readlink(base), File.dirname(base)) | |
| + base = File.expand_path(File.readlink(base), File.dirname(base)) | |
| end | |
| $:.unshift(File.join(File.expand_path(File.dirname(base)), '..', 'lib')) | |
| @@ -21,18 +21,18 @@ $:.unshift(File.join(File.expand_path(File.dirname(base)), … | |
| @job = nil | |
| def usage | |
| - $stderr.puts "Usage: #{$0} [JID]" | |
| - exit(1) | |
| + $stderr.puts "Usage: #{$0} [JID]" | |
| + exit(1) | |
| end | |
| def stop | |
| - if @task | |
| - @task.stop() rescue nil | |
| - end | |
| - if @job | |
| - Job.where(id: @job_id).update_all({ status: 'stopped', complet… | |
| - end | |
| - exit(0) | |
| + if @task | |
| + @task.stop() rescue nil | |
| + end | |
| + if @job | |
| + Job.where(id: @job_id).update_all({ status: 'stopped', completed_at: Time.… | |
| + end | |
| + exit(0) | |
| end | |
| # | |
| @@ -41,7 +41,7 @@ end | |
| jid = ARGV.shift() || usage() | |
| if (jid and jid =="-h") or (! jid) | |
| - usage() | |
| + usage() | |
| end | |
| require 'config/boot' | |
| @@ -54,9 +54,9 @@ jid = jid.to_i | |
| @job = Job.where(id: jid).first | |
| unless @job | |
| - $stderr.puts "Error: Specified job not found" | |
| - WarVOX::Log.warn("Worker rejected invalid Job #{jid}") | |
| - exit(1) | |
| + $stderr.puts "Error: Specified job not found" | |
| + WarVOX::Log.warn("Worker rejected invalid Job #{jid}") | |
| + exit(1) | |
| end | |
| $0 = "warvox worker: #{jid} " | |
| @@ -72,20 +72,20 @@ begin | |
| case @job.task | |
| when 'dialer' | |
| - @task = WarVOX::Jobs::Dialer.new(@job.id, args) | |
| - @task.start | |
| + @task = WarVOX::Jobs::Dialer.new(@job.id, args) | |
| + @task.start | |
| when 'analysis' | |
| - @task = WarVOX::Jobs::Analysis.new(@job.id, args) | |
| - @task.start | |
| + @task = WarVOX::Jobs::Analysis.new(@job.id, args) | |
| + @task.start | |
| else | |
| - Job.where(id: @job.id).update_all({ error: 'unsupported', status: 'err… | |
| + Job.where(id: @job.id).update_all({ error: 'unsupported', status: 'error' }) | |
| end | |
| @job.update_progress(100) | |
| rescue ::SignalException, ::SystemExit | |
| - raise $! | |
| + raise $! | |
| rescue ::Exception => e | |
| - WarVOX::Log.warn("Worker #{@job.id} #{@job.task} threw an exception: #… | |
| - Job.where(id: @job.id).update_all({ error: "Exception: #{e.class} #{e}… | |
| + WarVOX::Log.warn("Worker #{@job.id} #{@job.task} threw an exception: #{e.cla… | |
| + Job.where(id: @job.id).update_all({ error: "Exception: #{e.class} #{e}", sta… | |
| end | |
| diff --git a/bin/worker_manager.rb b/bin/worker_manager.rb | |
| @@ -6,7 +6,7 @@ | |
| # | |
| base = __FILE__ | |
| while File.symlink?(base) | |
| - base = File.expand_path(File.readlink(base), File.dirname(base)) | |
| + base = File.expand_path(File.readlink(base), File.dirname(base)) | |
| end | |
| $:.unshift(File.join(File.expand_path(File.dirname(base)), '..', 'lib')) | |
| @@ -25,136 +25,136 @@ require 'config/environment' | |
| @jobs = [] | |
| def stop | |
| - WarVOX::Log.info("Worker Manager is terminating due to signal") | |
| + WarVOX::Log.info("Worker Manager is terminating due to signal") | |
| - unless @jobs.length > 0 | |
| - exit(0) | |
| - end | |
| + unless @jobs.length > 0 | |
| + exit(0) | |
| + end | |
| - # Update the database | |
| - Job.update_all({ :status => "stopped", :completed_at => Time.now.utc},… | |
| + # Update the database | |
| + Job.update_all({ :status => "stopped", :completed_at => Time.now.utc}, { :id… | |
| - # Signal running jobs to shut down | |
| - @jobs.map{|j| Process.kill("TERM", j[:pid]) rescue nil } | |
| + # Signal running jobs to shut down | |
| + @jobs.map{|j| Process.kill("TERM", j[:pid]) rescue nil } | |
| - # Sleep for five seconds | |
| - sleep(5) | |
| + # Sleep for five seconds | |
| + sleep(5) | |
| - # Forcibly kill any remaining job processes | |
| - @jobs.map{|j| Process.kill("KILL", j[:pid]) rescue nil } | |
| + # Forcibly kill any remaining job processes | |
| + @jobs.map{|j| Process.kill("KILL", j[:pid]) rescue nil } | |
| - exit(0) | |
| + exit(0) | |
| end | |
| def clear_zombies | |
| - while ( r = Process.waitpid(-1, Process::WNOHANG) rescue nil ) do | |
| - end | |
| + while ( r = Process.waitpid(-1, Process::WNOHANG) rescue nil ) do | |
| + end | |
| end | |
| def schedule_job(j) | |
| - WarVOX::Log.debug("Worker Manager is launching job #{j.id}") | |
| - @jobs << { | |
| - :id => j.id, | |
| - :pid => Process.fork { exec("#{@worker_path} #{j.id}") } | |
| - } | |
| + WarVOX::Log.debug("Worker Manager is launching job #{j.id}") | |
| + @jobs << { | |
| + :id => j.id, | |
| + :pid => Process.fork { exec("#{@worker_path} #{j.id}") } | |
| + } | |
| end | |
| def stop_cancelled_jobs | |
| - jids = [] | |
| - @jobs.each do |x| | |
| - jids << x[:id] | |
| - end | |
| - | |
| - return if jids.length == 0 | |
| - Job.where(:status => 'cancelled', :id => jids).find_each do |j| | |
| - job = @jobs.select{ |o| o[:id] == j.id }.first | |
| - next unless job and job[:pid] | |
| - pid = job[:pid] | |
| - | |
| - WarVOX::Log.debug("Worker Manager is killing job #{j.id} with … | |
| - Process.kill('TERM', pid) | |
| - end | |
| + jids = [] | |
| + @jobs.each do |x| | |
| + jids << x[:id] | |
| + end | |
| + | |
| + return if jids.length == 0 | |
| + Job.where(:status => 'cancelled', :id => jids).find_each do |j| | |
| + job = @jobs.select{ |o| o[:id] == j.id }.first | |
| + next unless job and job[:pid] | |
| + pid = job[:pid] | |
| + | |
| + WarVOX::Log.debug("Worker Manager is killing job #{j.id} with PID #{pid}") | |
| + Process.kill('TERM', pid) | |
| + end | |
| end | |
| def clear_completed_jobs | |
| - dead_pids = [] | |
| - dead_jids = [] | |
| + dead_pids = [] | |
| + dead_jids = [] | |
| - @jobs.each do |j| | |
| - alive = Process.kill(0, j[:pid]) rescue nil | |
| - next if alive | |
| - dead_pids << j[:pid] | |
| - dead_jids << j[:id] | |
| - end | |
| + @jobs.each do |j| | |
| + alive = Process.kill(0, j[:pid]) rescue nil | |
| + next if alive | |
| + dead_pids << j[:pid] | |
| + dead_jids << j[:id] | |
| + end | |
| - return unless dead_jids.length > 0 | |
| + return unless dead_jids.length > 0 | |
| - WarVOX::Log.debug("Worker Manager is clearing #{dead_pids.length} comp… | |
| + WarVOX::Log.debug("Worker Manager is clearing #{dead_pids.length} completed … | |
| - @jobs = @jobs.reject{|x| dead_pids.include?( x[:pid] ) } | |
| + @jobs = @jobs.reject{|x| dead_pids.include?( x[:pid] ) } | |
| - # Mark failed/crashed jobs as completed | |
| - Job.where(id: dead_jids, completed_at: nil).update_all({completed_at: … | |
| + # Mark failed/crashed jobs as completed | |
| + Job.where(id: dead_jids, completed_at: nil).update_all({completed_at: Time.n… | |
| end | |
| def clear_stale_jobs | |
| - jids = @jobs.map{|x| x[:id] } | |
| - stale = nil | |
| - | |
| - if jids.length > 0 | |
| - stale = Job.where("completed_at IS NULL AND locked_by LIKE ? A… | |
| - else | |
| - stale = Job.where("completed_at IS NULL AND locked_by LIKE ?",… | |
| - end | |
| - | |
| - dead = [] | |
| - pids = {} | |
| - | |
| - # Extract the PID from the locked_by cookie for each job | |
| - stale.each do |j| | |
| - host, pid, uniq = j.locked_by.to_s.split("^", 3) | |
| - next unless (pid and uniq) | |
| - pids[pid] ||= [] | |
| - pids[pid] << j | |
| - end | |
| - | |
| - # Identify dead processes (must be same user or root) | |
| - pids.keys.each do |pid| | |
| - alive = Process.kill(0, pid.to_i) rescue nil | |
| - next if alive | |
| - pids[pid].each do |j| | |
| - dead << j.id | |
| - end | |
| - end | |
| - | |
| - # Mark these jobs as abandoned | |
| - if dead.length > 0 | |
| - WarVOX::Log.debug("Worker Manager is marking #{dead.length} jo… | |
| - Job.where(:id => dead).update_all({locked_by: nil, status: 'ab… | |
| - end | |
| + jids = @jobs.map{|x| x[:id] } | |
| + stale = nil | |
| + | |
| + if jids.length > 0 | |
| + stale = Job.where("completed_at IS NULL AND locked_by LIKE ? AND id NOT IN… | |
| + else | |
| + stale = Job.where("completed_at IS NULL AND locked_by LIKE ?", Socket.geth… | |
| + end | |
| + | |
| + dead = [] | |
| + pids = {} | |
| + | |
| + # Extract the PID from the locked_by cookie for each job | |
| + stale.each do |j| | |
| + host, pid, uniq = j.locked_by.to_s.split("^", 3) | |
| + next unless (pid and uniq) | |
| + pids[pid] ||= [] | |
| + pids[pid] << j | |
| + end | |
| + | |
| + # Identify dead processes (must be same user or root) | |
| + pids.keys.each do |pid| | |
| + alive = Process.kill(0, pid.to_i) rescue nil | |
| + next if alive | |
| + pids[pid].each do |j| | |
| + dead << j.id | |
| + end | |
| + end | |
| + | |
| + # Mark these jobs as abandoned | |
| + if dead.length > 0 | |
| + WarVOX::Log.debug("Worker Manager is marking #{dead.length} jobs as abando… | |
| + Job.where(:id => dead).update_all({locked_by: nil, status: 'abandoned'}) | |
| + end | |
| end | |
| def schedule_submitted_jobs | |
| - loop do | |
| - # Look for a candidate job with no current owner | |
| - j = Job.where(status: 'submitted', locked_by: nil).limit(1).f… | |
| - return unless j | |
| + loop do | |
| + # Look for a candidate job with no current owner | |
| + j = Job.where(status: 'submitted', locked_by: nil).limit(1).first | |
| + return unless j | |
| - # Try to get a lock on this job | |
| - Job.where(id: j.id, locked_by: nil).update_all({locked_by: @co… | |
| + # Try to get a lock on this job | |
| + Job.where(id: j.id, locked_by: nil).update_all({locked_by: @cookie, locked… | |
| - # See if we actually got the lock | |
| - j = Job.where(id: j.id, status: 'scheduled', locked_by: @cook… | |
| + # See if we actually got the lock | |
| + j = Job.where(id: j.id, status: 'scheduled', locked_by: @cookie).limit(1)… | |
| - # Try again if we lost the race, | |
| - next unless j | |
| + # Try again if we lost the race, | |
| + next unless j | |
| - # Hurray, we got a job, run it | |
| - schedule_job(j) | |
| + # Hurray, we got a job, run it | |
| + schedule_job(j) | |
| - return true | |
| - end | |
| + return true | |
| + end | |
| end | |
| # | |
| @@ -171,24 +171,24 @@ trap("SIGTERM") { stop() } | |
| WarVOX::Log.info("Worker Manager initialized with cookie #{@cookie}") | |
| loop do | |
| - $0 = "warvox manager: #{@jobs.length} active jobs (cookie : #{@cookie}… | |
| + $0 = "warvox manager: #{@jobs.length} active jobs (cookie : #{@cookie})" | |
| - # Clear any zombie processes | |
| - clear_zombies() | |
| + # Clear any zombie processes | |
| + clear_zombies() | |
| - # Clear any completed jobs | |
| - clear_completed_jobs() | |
| + # Clear any completed jobs | |
| + clear_completed_jobs() | |
| - # Stop any jobs cancelled by the user | |
| - stop_cancelled_jobs() | |
| + # Stop any jobs cancelled by the user | |
| + stop_cancelled_jobs() | |
| - # Clear locks on any stale jobs from this host | |
| - clear_stale_jobs() | |
| + # Clear locks on any stale jobs from this host | |
| + clear_stale_jobs() | |
| - while @jobs.length < @max_jobs | |
| - break unless schedule_submitted_jobs | |
| - end | |
| + while @jobs.length < @max_jobs | |
| + break unless schedule_submitted_jobs | |
| + end | |
| - # Sleep between 3-8 seconds before re-entering the loop | |
| - sleep(rand(5) + 3) | |
| + # Sleep between 3-8 seconds before re-entering the loop | |
| + sleep(rand(5) + 3) | |
| end | |
| diff --git a/config/classifiers/01.default.rb b/config/classifiers/01.default.rb | |
| @@ -29,24 +29,24 @@ maxf = data[:maxf] | |
| # Look for modems by detecting a 2100hz answer + 2250hz tone | |
| # | |
| if( (fcnt[2100] > 1.0 or fcnt[2230] > 1.0) and fcnt[2250] > 0.5) | |
| - @line_type = 'modem' | |
| - raise Completed | |
| + @line_type = 'modem' | |
| + raise Completed | |
| end | |
| # | |
| # Look for modems by detecting a peak frequency of 2250hz | |
| # | |
| if(fcnt[2100] > 1.0 and (maxf > 2245.0 and maxf < 2255.0)) | |
| - @line_type = 'modem' | |
| - raise Completed | |
| + @line_type = 'modem' | |
| + raise Completed | |
| end | |
| # | |
| # Look for modems by detecting a peak frequency of 3000hz | |
| # | |
| if(fcnt[2100] > 1.0 and (maxf > 2995.0 and maxf < 3005.0)) | |
| - @line_type = 'modem' | |
| - raise Completed | |
| + @line_type = 'modem' | |
| + raise Completed | |
| end | |
| # | |
| @@ -54,22 +54,22 @@ end | |
| # | |
| fax_sum = 0 | |
| [ | |
| - fcnt[1625], fcnt[1660], fcnt[1825], fcnt[2100], | |
| - fcnt[600], fcnt[1855], fcnt[1100], fcnt[2250], | |
| - fcnt[2230], fcnt[2220], fcnt[1800], fcnt[2095], | |
| - fcnt[2105] | |
| + fcnt[1625], fcnt[1660], fcnt[1825], fcnt[2100], | |
| + fcnt[600], fcnt[1855], fcnt[1100], fcnt[2250], | |
| + fcnt[2230], fcnt[2220], fcnt[1800], fcnt[2095], | |
| + fcnt[2105] | |
| ].map{|x| fax_sum += [x,1.0].min } | |
| if(fax_sum >= 2.0) | |
| - @line_type = 'fax' | |
| - raise Completed | |
| + @line_type = 'fax' | |
| + raise Completed | |
| end | |
| # | |
| # Dial tone detection (440hz + 350hz) | |
| # | |
| if(fcnt[440] > 1.0 and fcnt[350] > 1.0) | |
| - @line_type = 'dialtone' | |
| - raise Completed | |
| + @line_type = 'dialtone' | |
| + raise Completed | |
| end | |
| # | |
| diff --git a/config/classifiers/99.default.rb b/config/classifiers/99.default.rb | |
| @@ -15,16 +15,16 @@ maxf = data[:maxf] | |
| # this signature can fail. For non-US numbers, the beep | |
| # is often a different frequency entirely. | |
| if(fcnt[1000] >= 1.0) | |
| - @line_type = 'voicemail' | |
| - raise Completed | |
| + @line_type = 'voicemail' | |
| + raise Completed | |
| end | |
| # Look for voicemail by detecting a peak frequency of | |
| # 1000hz. Not as accurate, but thats why this is in | |
| # the fallback script. | |
| if(maxf > 995 and maxf < 1005) | |
| - @line_type = 'voicemail' | |
| - raise Completed | |
| + @line_type = 'voicemail' | |
| + raise Completed | |
| end | |
| diff --git a/db/migrate/20121228171549_initial_schema.rb b/db/migrate/201212281… | |
| @@ -1,182 +1,182 @@ | |
| class InitialSchema < ActiveRecord::Migration | |
| - def up | |
| - | |
| - # Require the intarray extension | |
| - execute("CREATE EXTENSION IF NOT EXISTS intarray") | |
| - | |
| - create_table :settings do |t| | |
| - t.string :var, :null => false | |
| - t.text :value, :null => true | |
| - t.integer :thing_id, :null => true | |
| - t.string :thing_type, :limit => 30, :null => true | |
| - t.timestamps | |
| - end | |
| - | |
| - add_index :settings, [ :thing_type, :thing_id, :var ], :unique… | |
| - | |
| - create_table 'users' do |t| | |
| - t.string :login, :null => false … | |
| - t.string :email, :null => true … | |
| - t.string :crypted_password, :null => false … | |
| - t.string :password_salt, :null => false … | |
| - t.string :persistence_token, :null => false … | |
| - t.string :single_access_token, :null => false … | |
| - t.string :perishable_token, :null => false … | |
| - | |
| - # Magic columns, just like ActiveRecord's created_at a… | |
| - t.integer :login_count, :null => false, :def… | |
| - t.integer :failed_login_count, :null => false, :def… | |
| - t.datetime :last_request_at … | |
| - t.datetime :current_login_at … | |
| - t.datetime :last_login_at … | |
| - t.string :current_login_ip … | |
| - t.string :last_login_ip … | |
| - | |
| - t.timestamps | |
| - t.boolean "enabled", :default => true | |
| - t.boolean "admin", :default => true | |
| - end | |
| - | |
| - create_table 'projects' do |t| | |
| - t.timestamps | |
| - t.text "name", :null => false | |
| - t.text "description" | |
| - t.text "included" | |
| - t.text "excluded" | |
| - t.string "created_by" | |
| - end | |
| - | |
| - create_table "jobs" do |t| | |
| - t.timestamps | |
| - t.integer "project_id", :null => false | |
| - t.string "locked_by" | |
| - t.timestamp "locked_at" | |
| - t.timestamp "started_at" | |
| - t.timestamp "completed_at" | |
| - t.string "created_by" | |
| - t.string "task", :null => false | |
| - t.binary "args" | |
| - t.string "status" | |
| - t.text "error" | |
| - t.integer "progress", :default => 0 | |
| - end | |
| - | |
| - create_table "lines" do |t| | |
| - t.timestamps | |
| - t.text "number", :null => false | |
| - t.integer "project_id", :null => false | |
| - t.text "line_type" | |
| - t.text "notes" | |
| - end | |
| - | |
| - create_table "line_attributes" do |t| | |
| - t.timestamps | |
| - t.integer "line_id", :null => false | |
| - t.integer "project_id", :null => false | |
| - t.text "name", :null => false | |
| - t.binary "value", :null => false | |
| - t.string "content_type", :default => "t… | |
| - end | |
| - | |
| - create_table "calls" do |t| | |
| - # Created by the dialer job | |
| - t.timestamps | |
| - t.text "number", :null => false | |
| - t.integer "project_id", :null => false | |
| - t.integer "job_id", :null => false | |
| - t.integer "provider_id", :null => false | |
| - t.boolean "answered" | |
| - t.boolean "busy" | |
| - t.text "error" | |
| - t.integer "audio_length" | |
| - t.integer "ring_length" | |
| - t.text "caller_id" | |
| - | |
| - # Generated by the analysis job | |
| - t.integer "analysis_job_id" | |
| - t.timestamp "analysis_started_at" | |
| - t.timestamp "analysis_completed_at" | |
| - t.float "peak_freq" | |
| - t.text "peak_freq_data" | |
| - t.text "line_type" | |
| - t.integer "fprint", :array => true | |
| - end | |
| - | |
| - create_table "call_media" do |t| | |
| - t.integer "call_id", :null => false | |
| - t.integer "project_id", :null => false | |
| - t.binary "audio" | |
| - t.binary "mp3" | |
| - t.binary "png_big" | |
| - t.binary "png_big_dots" | |
| - t.binary "png_big_freq" | |
| - t.binary "png_sig" | |
| - t.binary "png_sig_freq" | |
| - end | |
| - | |
| - create_table "signatures" do |t| | |
| - t.timestamps | |
| - t.text "name", :null => false | |
| - t.string "source" | |
| - t.text "description" | |
| - t.string "category" | |
| - t.string "line_type" | |
| - t.integer "risk" | |
| - end | |
| - | |
| - create_table "signature_fp" do |t| | |
| - t.integer "signature_id", :null => false | |
| - t.integer "fprint", :array => true | |
| - end | |
| - | |
| - create_table "providers" do |t| | |
| - t.timestamps | |
| - t.text "name", :null => false | |
| - t.text "host", :null => false | |
| - t.integer "port", :null => false | |
| - t.text "user" | |
| - t.text "pass" | |
| - t.integer "lines", :null => false, :def… | |
| - t.boolean "enabled", :default => true | |
| - end | |
| - | |
| - add_index :jobs, :project_id | |
| - add_index :lines, :number | |
| - add_index :lines, :project_id | |
| - add_index :line_attributes, :line_id | |
| - add_index :line_attributes, :project_id | |
| - add_index :calls, :number | |
| - add_index :calls, :job_id | |
| - add_index :calls, :provider_id | |
| - add_index :call_media, :call_id | |
| - add_index :call_media, :project_id | |
| - add_index :signature_fp, :signature_id | |
| - | |
| - end | |
| - | |
| - def down | |
| - remove_index :jobs, :project_id | |
| - remove_index :lines, :number | |
| - remove_index :lines, :project_id | |
| - remove_index :line_attributes, :line_id | |
| - remove_index :line_attributes, :project_id | |
| - remove_index :calls, :number | |
| - remove_index :calls, :job_id | |
| - remove_index :calls, :provider_id | |
| - remove_index :call_media, :call_id | |
| - remove_index :call_media, :project_id | |
| - remove_index :signature_fp, :signature_id | |
| - | |
| - drop_table "providers" | |
| - drop_table "signature_fp" | |
| - drop_table "signatures" | |
| - drop_table "call_media" | |
| - drop_table "calls" | |
| - drop_table "line_attributes" | |
| - drop_table "lines" | |
| - drop_table "jobs" | |
| - drop_table "projects" | |
| - drop_table "users" | |
| - drop_table "settings" | |
| - end | |
| + def up | |
| + | |
| + # Require the intarray extension | |
| + execute("CREATE EXTENSION IF NOT EXISTS intarray") | |
| + | |
| + create_table :settings do |t| | |
| + t.string :var, :null => false | |
| + t.text :value, :null => true | |
| + t.integer :thing_id, :null => true | |
| + t.string :thing_type, :limit => 30, :null => true | |
| + t.timestamps | |
| + end | |
| + | |
| + add_index :settings, [ :thing_type, :thing_id, :var ], :unique => true | |
| + | |
| + create_table 'users' do |t| | |
| + t.string :login, :null => false # option… | |
| + t.string :email, :null => true # option… | |
| + t.string :crypted_password, :null => false # option… | |
| + t.string :password_salt, :null => false # option… | |
| + t.string :persistence_token, :null => false # requir… | |
| + t.string :single_access_token, :null => false # option… | |
| + t.string :perishable_token, :null => false # option… | |
| + | |
| + # Magic columns, just like ActiveRecord's created_at and updated_at. The… | |
| + t.integer :login_count, :null => false, :default => 0 # option… | |
| + t.integer :failed_login_count, :null => false, :default => 0 # option… | |
| + t.datetime :last_request_at # option… | |
| + t.datetime :current_login_at # option… | |
| + t.datetime :last_login_at # option… | |
| + t.string :current_login_ip # option… | |
| + t.string :last_login_ip # option… | |
| + | |
| + t.timestamps | |
| + t.boolean "enabled", :default => true | |
| + t.boolean "admin", :default => true | |
| + end | |
| + | |
| + create_table 'projects' do |t| | |
| + t.timestamps | |
| + t.text "name", :null => false | |
| + t.text "description" | |
| + t.text "included" | |
| + t.text "excluded" | |
| + t.string "created_by" | |
| + end | |
| + | |
| + create_table "jobs" do |t| | |
| + t.timestamps | |
| + t.integer "project_id", :null => false | |
| + t.string "locked_by" | |
| + t.timestamp "locked_at" | |
| + t.timestamp "started_at" | |
| + t.timestamp "completed_at" | |
| + t.string "created_by" | |
| + t.string "task", :null => false | |
| + t.binary "args" | |
| + t.string "status" | |
| + t.text "error" | |
| + t.integer "progress", :default => 0 | |
| + end | |
| + | |
| + create_table "lines" do |t| | |
| + t.timestamps | |
| + t.text "number", :null => false | |
| + t.integer "project_id", :null => false | |
| + t.text "line_type" | |
| + t.text "notes" | |
| + end | |
| + | |
| + create_table "line_attributes" do |t| | |
| + t.timestamps | |
| + t.integer "line_id", :null => false | |
| + t.integer "project_id", :null => false | |
| + t.text "name", :null => false | |
| + t.binary "value", :null => false | |
| + t.string "content_type", :default => "text" | |
| + end | |
| + | |
| + create_table "calls" do |t| | |
| + # Created by the dialer job | |
| + t.timestamps | |
| + t.text "number", :null => false | |
| + t.integer "project_id", :null => false | |
| + t.integer "job_id", :null => false | |
| + t.integer "provider_id", :null => false | |
| + t.boolean "answered" | |
| + t.boolean "busy" | |
| + t.text "error" | |
| + t.integer "audio_length" | |
| + t.integer "ring_length" | |
| + t.text "caller_id" | |
| + | |
| + # Generated by the analysis job | |
| + t.integer "analysis_job_id" | |
| + t.timestamp "analysis_started_at" | |
| + t.timestamp "analysis_completed_at" | |
| + t.float "peak_freq" | |
| + t.text "peak_freq_data" | |
| + t.text "line_type" | |
| + t.integer "fprint", :array => true | |
| + end | |
| + | |
| + create_table "call_media" do |t| | |
| + t.integer "call_id", :null => false | |
| + t.integer "project_id", :null => false | |
| + t.binary "audio" | |
| + t.binary "mp3" | |
| + t.binary "png_big" | |
| + t.binary "png_big_dots" | |
| + t.binary "png_big_freq" | |
| + t.binary "png_sig" | |
| + t.binary "png_sig_freq" | |
| + end | |
| + | |
| + create_table "signatures" do |t| | |
| + t.timestamps | |
| + t.text "name", :null => false | |
| + t.string "source" | |
| + t.text "description" | |
| + t.string "category" | |
| + t.string "line_type" | |
| + t.integer "risk" | |
| + end | |
| + | |
| + create_table "signature_fp" do |t| | |
| + t.integer "signature_id", :null => false | |
| + t.integer "fprint", :array => true | |
| + end | |
| + | |
| + create_table "providers" do |t| | |
| + t.timestamps | |
| + t.text "name", :null => false | |
| + t.text "host", :null => false | |
| + t.integer "port", :null => false | |
| + t.text "user" | |
| + t.text "pass" | |
| + t.integer "lines", :null => false, :default => 1 | |
| + t.boolean "enabled", :default => true | |
| + end | |
| + | |
| + add_index :jobs, :project_id | |
| + add_index :lines, :number | |
| + add_index :lines, :project_id | |
| + add_index :line_attributes, :line_id | |
| + add_index :line_attributes, :project_id | |
| + add_index :calls, :number | |
| + add_index :calls, :job_id | |
| + add_index :calls, :provider_id | |
| + add_index :call_media, :call_id | |
| + add_index :call_media, :project_id | |
| + add_index :signature_fp, :signature_id | |
| + | |
| + end | |
| + | |
| + def down | |
| + remove_index :jobs, :project_id | |
| + remove_index :lines, :number | |
| + remove_index :lines, :project_id | |
| + remove_index :line_attributes, :line_id | |
| + remove_index :line_attributes, :project_id | |
| + remove_index :calls, :number | |
| + remove_index :calls, :job_id | |
| + remove_index :calls, :provider_id | |
| + remove_index :call_media, :call_id | |
| + remove_index :call_media, :project_id | |
| + remove_index :signature_fp, :signature_id | |
| + | |
| + drop_table "providers" | |
| + drop_table "signature_fp" | |
| + drop_table "signatures" | |
| + drop_table "call_media" | |
| + drop_table "calls" | |
| + drop_table "line_attributes" | |
| + drop_table "lines" | |
| + drop_table "jobs" | |
| + drop_table "projects" | |
| + drop_table "users" | |
| + drop_table "settings" | |
| + end | |
| end | |
| diff --git a/lib/warvox.rb b/lib/warvox.rb | |
| @@ -11,10 +11,10 @@ require 'logger' | |
| # Global configuration | |
| module WarVOX | |
| - VERSION = '2.0.0-dev' | |
| - Base = File.expand_path(File.join(File.dirname(__FILE__), '..')) | |
| - Conf = File.expand_path(File.join(Base, 'config', 'warvox.conf')) | |
| - Log = Logger.new( WarVOX::Config.log_file ) | |
| - Log.level = WarVOX::Config.log_level | |
| + VERSION = '2.0.0-dev' | |
| + Base = File.expand_path(File.join(File.dirname(__FILE__), '..')) | |
| + Conf = File.expand_path(File.join(Base, 'config', 'warvox.conf')) | |
| + Log = Logger.new( WarVOX::Config.log_file ) | |
| + Log.level = WarVOX::Config.log_level | |
| end | |
| diff --git a/lib/warvox/audio/raw.rb b/lib/warvox/audio/raw.rb | |
| @@ -2,329 +2,329 @@ module WarVOX | |
| module Audio | |
| class Raw | |
| - @@kissfft_loaded = false | |
| - begin | |
| - require 'kissfft' | |
| - @@kissfft_loaded = true | |
| - rescue ::LoadError | |
| - end | |
| - | |
| - require 'zlib' | |
| - | |
| - ## | |
| - # RAW AUDIO - 8khz little-endian 16-bit signed | |
| - ## | |
| - | |
| - ## | |
| - # Static methods | |
| - ## | |
| - | |
| - def self.from_str(str) | |
| - self.class.new(str) | |
| - end | |
| - | |
| - def self.from_file(path) | |
| - if(not path) | |
| - raise Error, "No audio path specified" | |
| - end | |
| - | |
| - if(path == "-") | |
| - return self.new($stdin.read) | |
| - end | |
| - | |
| - if(not File.readable?(path)) | |
| - raise Error, "The specified audio file does not exist" | |
| - end | |
| - | |
| - if(path =~ /\.gz$/) | |
| - return self.new(Zlib::GzipReader.open(path).read) | |
| - end | |
| - | |
| - self.new(File.read(path, File.size(path))) | |
| - end | |
| - | |
| - ## | |
| - # Class methods | |
| - ## | |
| - | |
| - attr_accessor :samples | |
| - | |
| - def initialize(data) | |
| - self.samples = data.unpack('v*').map do |s| | |
| - (s > 0x7fff) ? (0x10000 - s) * -1 : s | |
| - end | |
| - end | |
| - | |
| - def to_raw | |
| - self.samples.pack("v*") | |
| - end | |
| - | |
| - def to_wav | |
| - raw = self.to_raw | |
| - wav = | |
| - "RIFF" + | |
| - [raw.length + 36].pack("V") + | |
| - "WAVE" + | |
| - "fmt " + | |
| - [16, 1, 1, 8000, 16000, 2, 16 ].pack("VvvVVvv"… | |
| - "data" + | |
| - [ raw.length ].pack("V") + | |
| - raw | |
| - end | |
| - | |
| - def to_flow(opts={}) | |
| - | |
| - lo_lim = (opts[:lo_lim] || 100).to_i | |
| - lo_min = (opts[:lo_min] || 5).to_i | |
| - hi_min = (opts[:hi_min] || 5).to_i | |
| - lo_cnt = 0 | |
| - hi_cnt = 0 | |
| - | |
| - data = self.samples.map {|c| c.abs} | |
| - | |
| - # | |
| - # Granular hi/low state change list | |
| - # | |
| - fprint = [] | |
| - state = :lo | |
| - idx = 0 | |
| - buff = [] | |
| - | |
| - while (idx < data.length) | |
| - case state | |
| - when :lo | |
| - while(idx < data.length and data[idx] <= lo_li… | |
| - buff << data[idx] | |
| - idx += 1 | |
| - end | |
| - | |
| - # Ignore any sequence that is too small | |
| - fprint << [:lo, buff.length, buff - [0]] if bu… | |
| - state = :hi | |
| - buff = [] | |
| - next | |
| - when :hi | |
| - while(idx < data.length and data[idx] > lo_lim) | |
| - buff << data[idx] | |
| - idx += 1 | |
| - end | |
| - | |
| - # Ignore any sequence that is too small | |
| - fprint << [:hi, buff.length, buff] if buff.len… | |
| - state = :lo | |
| - buff = [] | |
| - next | |
| - end | |
| - end | |
| - | |
| - # | |
| - # Merge similar blocks | |
| - # | |
| - final = [] | |
| - prev = fprint[0] | |
| - idx = 1 | |
| - | |
| - while(idx < fprint.length) | |
| - | |
| - if(fprint[idx][0] == prev[0]) | |
| - prev[1] += fprint[idx][1] | |
| - prev[2] += fprint[idx][2] | |
| - else | |
| - final << prev | |
| - prev = fprint[idx] | |
| - end | |
| - | |
| - idx += 1 | |
| - end | |
| - final << prev | |
| - | |
| - # | |
| - # Process results | |
| - # | |
| - sig = "" | |
| - | |
| - final.each do |f| | |
| - sum = 0 | |
| - f[2].each {|i| sum += i } | |
| - avg = (sum == 0) ? 0 : sum / f[2].length | |
| - sig << "#{f[0].to_s.upcase[0,1]},#{f[1]},#{avg} " | |
| - end | |
| - | |
| - # Return the results | |
| - return sig | |
| - end | |
| - | |
| - def to_freq(opts={}) | |
| - | |
| - if(not @@kissfft_loaded) | |
| - raise RuntimeError, "The KissFFT module is not availab… | |
| - end | |
| - | |
| - freq_cnt = opts[:frequency_count] || 20 | |
| - | |
| - # Perform a DFT on the samples | |
| - ffts = KissFFT.fftr(8192, 8000, 1, self.samples) | |
| - | |
| - self.class.fft_to_freq_sig(ffts, freq_cnt) | |
| - end | |
| - | |
| - def to_freq_sig(opts={}) | |
| - fcnt = opts[:frequency_count] || 5 | |
| - | |
| - ffts = [] | |
| - | |
| - # Obtain 20 DFTs for the sample, at 1/20th second offsets into… | |
| - 0.upto(19) do |i| | |
| - ffts[i] = KissFFT.fftr(8192, 8000, 1, self.samples[ i … | |
| - end | |
| - | |
| - # Create a frequency table at 100hz boundaries | |
| - f = [ *(0.step(4000, 100)) ] | |
| - | |
| - # Create a worker method to find the closest frequency | |
| - barker = Proc.new do |t| | |
| - t = t.to_i | |
| - f.sort { |a,b| | |
| - (a-t).abs <=> (b-t).abs | |
| - }.first | |
| - end | |
| - | |
| - # Purge any empty fft slices | |
| - ffts.delete(nil) | |
| - | |
| - # Map each slice of the audio's FFT with each FFT chunk (8k sa… | |
| - tops = ffts.map{|x| x.map{|y| y.map{|z| | |
| - | |
| - frq,pwr = z | |
| - | |
| - # Toss any signals with a strength under 100 | |
| - if pwr < 100.0 | |
| - frq,pwr = [0,0] | |
| - # Map the signal to the closest offset of 100hz | |
| - else | |
| - frq = barker.call(frq) | |
| - end | |
| - | |
| - # Make sure the strength is an integer | |
| - pwr = pwr.to_i | |
| - | |
| - # Sort by signal strength and take the top fcnt items | |
| - [frq, pwr]}.sort{|a,b| | |
| - b[1] <=> a[1] | |
| - }[0, fcnt].map{|w| | |
| - # Grab just the frequency (drop the strength) | |
| - w[0] | |
| - # Remove any duplicates due to hz mapping | |
| - }.uniq | |
| - | |
| - } } | |
| - | |
| - # Track the generated 4-second chunk signatures | |
| - sigs = [] | |
| - | |
| - # Expand the list of top frequencies per sample into a flat li… | |
| - tops.each do |t| | |
| - next if t.length < 4 | |
| - 0.upto(t.length - 4) { |i| t[i].each { |a| t[i+1].each… | |
| - end | |
| - | |
| - # Dump any duplicate signatures | |
| - sigs = sigs.uniq | |
| - | |
| - # Convert each signature into a single 32-bit integer | |
| - # This is essentially [0-40, 0-40, 0-40, 0-40] | |
| - sigs.map{|x| x.map{|y| y / 100}.pack("C4").unpack("N")[0] } | |
| - end | |
| - | |
| - # Converts a signature to a postgresql integer array (text) format | |
| - def to_freq_sig_txt(opts={}) | |
| - "{" + to_freq_sig(opts).sort.join(",") + "}" | |
| - end | |
| - | |
| - def to_freq_sig_arr(opts={}) | |
| - to_freq_sig(opts) | |
| - end | |
| - | |
| - def self.fft_to_freq_sig(ffts, freq_cnt) | |
| - sig = [] | |
| - ffts.each do |s| | |
| - res = [] | |
| - maxp = 0 | |
| - maxf = 0 | |
| - s.each do |f| | |
| - if( f[1] > maxp ) | |
| - maxf,maxp = f | |
| - end | |
| - | |
| - if(maxf > 0 and f[1] < maxp and (maxf + 4.5 < … | |
| - res << [maxf, maxp] | |
| - maxf,maxp = [0,0] | |
| - end | |
| - end | |
| - | |
| - sig << res.sort{ |a,b| # s… | |
| - a[1] <=> b[1] | |
| - }.reverse[0,freq_cnt].sort { |a,b| # t… | |
| - a[0] <=> b[0] | |
| - }.map {|a| [a[0].round, a[1].round ] } # r… | |
| - end | |
| - | |
| - sig | |
| - end | |
| - | |
| - # Find pattern inside of sample | |
| - def self.compare_freq_sig(pat, zam, opts) | |
| - | |
| - fuzz_f = opts[:fuzz_f] || 7 | |
| - fuzz_p = opts[:fuzz_p] || 10 | |
| - final = [] | |
| - | |
| - 0.upto(zam.length - 1) do |si| | |
| - res = [] | |
| - sam = zam[si, zam.length] | |
| - | |
| - 0.upto(pat.length - 1) do |pi| | |
| - diff = [] | |
| - next if not pat[pi] | |
| - next if pat[pi].length == 0 | |
| - pat[pi].each do |x| | |
| - next if not sam[pi] | |
| - next if sam[pi].length == 0 | |
| - sam[pi].each do |y| | |
| - if( | |
| - (x[0] - fuzz_f) < y[0]… | |
| - (x[0] + fuzz_f) > y[0]… | |
| - (x[1] - fuzz_p) < y[1]… | |
| - (x[1] + fuzz_p) > y[1] | |
| - ) | |
| - diff << x | |
| - break | |
| - end | |
| - end | |
| - end | |
| - res << diff | |
| - end | |
| - next if res.length == 0 | |
| - | |
| - prev = 0 | |
| - rsum = 0 | |
| - ridx = 0 | |
| - res.each_index do |xi| | |
| - len = res[xi].length | |
| - if(xi == 0) | |
| - rsum += (len < 2) ? -40 : +20 | |
| - else | |
| - rsum += 20 if(prev > 11 and len > 11) | |
| - rsum += len | |
| - end | |
| - prev = len | |
| - end | |
| - | |
| - final << [ (rsum / res.length.to_f), res.map {|x| x.le… | |
| - end | |
| - | |
| - final | |
| - end | |
| + @@kissfft_loaded = false | |
| + begin | |
| + require 'kissfft' | |
| + @@kissfft_loaded = true | |
| + rescue ::LoadError | |
| + end | |
| + | |
| + require 'zlib' | |
| + | |
| + ## | |
| + # RAW AUDIO - 8khz little-endian 16-bit signed | |
| + ## | |
| + | |
| + ## | |
| + # Static methods | |
| + ## | |
| + | |
| + def self.from_str(str) | |
| + self.class.new(str) | |
| + end | |
| + | |
| + def self.from_file(path) | |
| + if(not path) | |
| + raise Error, "No audio path specified" | |
| + end | |
| + | |
| + if(path == "-") | |
| + return self.new($stdin.read) | |
| + end | |
| + | |
| + if(not File.readable?(path)) | |
| + raise Error, "The specified audio file does not exist" | |
| + end | |
| + | |
| + if(path =~ /\.gz$/) | |
| + return self.new(Zlib::GzipReader.open(path).read) | |
| + end | |
| + | |
| + self.new(File.read(path, File.size(path))) | |
| + end | |
| + | |
| + ## | |
| + # Class methods | |
| + ## | |
| + | |
| + attr_accessor :samples | |
| + | |
| + def initialize(data) | |
| + self.samples = data.unpack('v*').map do |s| | |
| + (s > 0x7fff) ? (0x10000 - s) * -1 : s | |
| + end | |
| + end | |
| + | |
| + def to_raw | |
| + self.samples.pack("v*") | |
| + end | |
| + | |
| + def to_wav | |
| + raw = self.to_raw | |
| + wav = | |
| + "RIFF" + | |
| + [raw.length + 36].pack("V") + | |
| + "WAVE" + | |
| + "fmt " + | |
| + [16, 1, 1, 8000, 16000, 2, 16 ].pack("VvvVVvv") + | |
| + "data" + | |
| + [ raw.length ].pack("V") + | |
| + raw | |
| + end | |
| + | |
| + def to_flow(opts={}) | |
| + | |
| + lo_lim = (opts[:lo_lim] || 100).to_i | |
| + lo_min = (opts[:lo_min] || 5).to_i | |
| + hi_min = (opts[:hi_min] || 5).to_i | |
| + lo_cnt = 0 | |
| + hi_cnt = 0 | |
| + | |
| + data = self.samples.map {|c| c.abs} | |
| + | |
| + # | |
| + # Granular hi/low state change list | |
| + # | |
| + fprint = [] | |
| + state = :lo | |
| + idx = 0 | |
| + buff = [] | |
| + | |
| + while (idx < data.length) | |
| + case state | |
| + when :lo | |
| + while(idx < data.length and data[idx] <= lo_lim) | |
| + buff << data[idx] | |
| + idx += 1 | |
| + end | |
| + | |
| + # Ignore any sequence that is too small | |
| + fprint << [:lo, buff.length, buff - [0]] if buff.length > lo_min | |
| + state = :hi | |
| + buff = [] | |
| + next | |
| + when :hi | |
| + while(idx < data.length and data[idx] > lo_lim) | |
| + buff << data[idx] | |
| + idx += 1 | |
| + end | |
| + | |
| + # Ignore any sequence that is too small | |
| + fprint << [:hi, buff.length, buff] if buff.length > hi_min | |
| + state = :lo | |
| + buff = [] | |
| + next | |
| + end | |
| + end | |
| + | |
| + # | |
| + # Merge similar blocks | |
| + # | |
| + final = [] | |
| + prev = fprint[0] | |
| + idx = 1 | |
| + | |
| + while(idx < fprint.length) | |
| + | |
| + if(fprint[idx][0] == prev[0]) | |
| + prev[1] += fprint[idx][1] | |
| + prev[2] += fprint[idx][2] | |
| + else | |
| + final << prev | |
| + prev = fprint[idx] | |
| + end | |
| + | |
| + idx += 1 | |
| + end | |
| + final << prev | |
| + | |
| + # | |
| + # Process results | |
| + # | |
| + sig = "" | |
| + | |
| + final.each do |f| | |
| + sum = 0 | |
| + f[2].each {|i| sum += i } | |
| + avg = (sum == 0) ? 0 : sum / f[2].length | |
| + sig << "#{f[0].to_s.upcase[0,1]},#{f[1]},#{avg} " | |
| + end | |
| + | |
| + # Return the results | |
| + return sig | |
| + end | |
| + | |
| + def to_freq(opts={}) | |
| + | |
| + if(not @@kissfft_loaded) | |
| + raise RuntimeError, "The KissFFT module is not availabale, raw.to_freq()… | |
| + end | |
| + | |
| + freq_cnt = opts[:frequency_count] || 20 | |
| + | |
| + # Perform a DFT on the samples | |
| + ffts = KissFFT.fftr(8192, 8000, 1, self.samples) | |
| + | |
| + self.class.fft_to_freq_sig(ffts, freq_cnt) | |
| + end | |
| + | |
| + def to_freq_sig(opts={}) | |
| + fcnt = opts[:frequency_count] || 5 | |
| + | |
| + ffts = [] | |
| + | |
| + # Obtain 20 DFTs for the sample, at 1/20th second offsets into the stream | |
| + 0.upto(19) do |i| | |
| + ffts[i] = KissFFT.fftr(8192, 8000, 1, self.samples[ i * 400, self.sample… | |
| + end | |
| + | |
| + # Create a frequency table at 100hz boundaries | |
| + f = [ *(0.step(4000, 100)) ] | |
| + | |
| + # Create a worker method to find the closest frequency | |
| + barker = Proc.new do |t| | |
| + t = t.to_i | |
| + f.sort { |a,b| | |
| + (a-t).abs <=> (b-t).abs | |
| + }.first | |
| + end | |
| + | |
| + # Purge any empty fft slices | |
| + ffts.delete(nil) | |
| + | |
| + # Map each slice of the audio's FFT with each FFT chunk (8k samples) and t… | |
| + tops = ffts.map{|x| x.map{|y| y.map{|z| | |
| + | |
| + frq,pwr = z | |
| + | |
| + # Toss any signals with a strength under 100 | |
| + if pwr < 100.0 | |
| + frq,pwr = [0,0] | |
| + # Map the signal to the closest offset of 100hz | |
| + else | |
| + frq = barker.call(frq) | |
| + end | |
| + | |
| + # Make sure the strength is an integer | |
| + pwr = pwr.to_i | |
| + | |
| + # Sort by signal strength and take the top fcnt items | |
| + [frq, pwr]}.sort{|a,b| | |
| + b[1] <=> a[1] | |
| + }[0, fcnt].map{|w| | |
| + # Grab just the frequency (drop the strength) | |
| + w[0] | |
| + # Remove any duplicates due to hz mapping | |
| + }.uniq | |
| + | |
| + } } | |
| + | |
| + # Track the generated 4-second chunk signatures | |
| + sigs = [] | |
| + | |
| + # Expand the list of top frequencies per sample into a flat list of each p… | |
| + tops.each do |t| | |
| + next if t.length < 4 | |
| + 0.upto(t.length - 4) { |i| t[i].each { |a| t[i+1].each { |b| t[i+2].each… | |
| + end | |
| + | |
| + # Dump any duplicate signatures | |
| + sigs = sigs.uniq | |
| + | |
| + # Convert each signature into a single 32-bit integer | |
| + # This is essentially [0-40, 0-40, 0-40, 0-40] | |
| + sigs.map{|x| x.map{|y| y / 100}.pack("C4").unpack("N")[0] } | |
| + end | |
| + | |
| + # Converts a signature to a postgresql integer array (text) format | |
| + def to_freq_sig_txt(opts={}) | |
| + "{" + to_freq_sig(opts).sort.join(",") + "}" | |
| + end | |
| + | |
| + def to_freq_sig_arr(opts={}) | |
| + to_freq_sig(opts) | |
| + end | |
| + | |
| + def self.fft_to_freq_sig(ffts, freq_cnt) | |
| + sig = [] | |
| + ffts.each do |s| | |
| + res = [] | |
| + maxp = 0 | |
| + maxf = 0 | |
| + s.each do |f| | |
| + if( f[1] > maxp ) | |
| + maxf,maxp = f | |
| + end | |
| + | |
| + if(maxf > 0 and f[1] < maxp and (maxf + 4.5 < f[0])) | |
| + res << [maxf, maxp] | |
| + maxf,maxp = [0,0] | |
| + end | |
| + end | |
| + | |
| + sig << res.sort{ |a,b| # sort by signal stre… | |
| + a[1] <=> b[1] | |
| + }.reverse[0,freq_cnt].sort { |a,b| # take the top 20 and… | |
| + a[0] <=> b[0] | |
| + }.map {|a| [a[0].round, a[1].round ] } # round to whole numb… | |
| + end | |
| + | |
| + sig | |
| + end | |
| + | |
| + # Find pattern inside of sample | |
| + def self.compare_freq_sig(pat, zam, opts) | |
| + | |
| + fuzz_f = opts[:fuzz_f] || 7 | |
| + fuzz_p = opts[:fuzz_p] || 10 | |
| + final = [] | |
| + | |
| + 0.upto(zam.length - 1) do |si| | |
| + res = [] | |
| + sam = zam[si, zam.length] | |
| + | |
| + 0.upto(pat.length - 1) do |pi| | |
| + diff = [] | |
| + next if not pat[pi] | |
| + next if pat[pi].length == 0 | |
| + pat[pi].each do |x| | |
| + next if not sam[pi] | |
| + next if sam[pi].length == 0 | |
| + sam[pi].each do |y| | |
| + if( | |
| + (x[0] - fuzz_f) < y[0] and | |
| + (x[0] + fuzz_f) > y[0] and | |
| + (x[1] - fuzz_p) < y[1] and | |
| + (x[1] + fuzz_p) > y[1] | |
| + ) | |
| + diff << x | |
| + break | |
| + end | |
| + end | |
| + end | |
| + res << diff | |
| + end | |
| + next if res.length == 0 | |
| + | |
| + prev = 0 | |
| + rsum = 0 | |
| + ridx = 0 | |
| + res.each_index do |xi| | |
| + len = res[xi].length | |
| + if(xi == 0) | |
| + rsum += (len < 2) ? -40 : +20 | |
| + else | |
| + rsum += 20 if(prev > 11 and len > 11) | |
| + rsum += len | |
| + end | |
| + prev = len | |
| + end | |
| + | |
| + final << [ (rsum / res.length.to_f), res.map {|x| x.length}] | |
| + end | |
| + | |
| + final | |
| + end | |
| end | |
| diff --git a/lib/warvox/config.rb b/lib/warvox/config.rb | |
| @@ -1,160 +1,160 @@ | |
| module WarVOX | |
| module Config | |
| - require 'yaml' | |
| - | |
| - def self.authentication_creds | |
| - user = nil | |
| - pass = nil | |
| - info = YAML.load_file(WarVOX::Conf) | |
| - if( info and | |
| - info['authentication'] and | |
| - info['authentication']['user'] and | |
| - info['authentication']['pass'] | |
| - ) | |
| - user = info['authentication']['user'] | |
| - pass = info['authentication']['pass'] | |
| - end | |
| - [user,pass] | |
| - end | |
| - | |
| - def self.authenticate(user,pass) | |
| - wuser,wpass = authentication_creds | |
| - (wuser == user and wpass == pass) ? true : false | |
| - end | |
| - | |
| - def self.tool_path(name) | |
| - info = YAML.load_file(WarVOX::Conf) | |
| - return nil if not info | |
| - return nil if not info['tools'] | |
| - return nil if not info['tools'][name] | |
| - find_full_path( | |
| - info['tools'][name].gsub('%BASE%', WarVOX::Base) | |
| - ) | |
| - end | |
| - | |
| - def self.analysis_threads | |
| - core_count = File.read("/proc/cpuinfo").scan(/^processor\s+:/)… | |
| - | |
| - info = YAML.load_file(WarVOX::Conf) | |
| - return core_count if not info | |
| - return core_count if not info['analysis_threads'] | |
| - return core_count if info['analysis_threads'] == 0 | |
| - [ info['analysis_threads'].to_i, core_count ].min | |
| - end | |
| - | |
| - def self.blacklist_path | |
| - info = YAML.load_file(WarVOX::Conf) | |
| - return nil if not info | |
| - return nil if not info['blacklist'] | |
| - File.expand_path(info['blacklist'].gsub('%BASE%', WarVOX::Base… | |
| - end | |
| - | |
| - def self.blacklist_load | |
| - path = blacklist_path | |
| - return if not path | |
| - data = File.read(path, File.size(path)) | |
| - sigs = [] | |
| - | |
| - File.open(path, 'r') do |fd| | |
| - lno = 0 | |
| - fd.each_line do |line| | |
| - lno += 1 | |
| - next if line =~ /^#/ | |
| - next if line =~ /^\s+$/ | |
| - line.strip! | |
| - sigs << [lno, line] | |
| - end | |
| - sigs | |
| - end | |
| - | |
| - end | |
| - | |
| - def self.signatures_path | |
| - info = YAML.load_file(WarVOX::Conf) | |
| - return nil if not info | |
| - return nil if not info['signatures'] | |
| - File.expand_path(info['signatures'].gsub('%BASE%', WarVOX::Bas… | |
| - end | |
| - | |
| - def self.classifiers_path | |
| - info = YAML.load_file(WarVOX::Conf) | |
| - return nil if not info | |
| - return nil if not info['classifiers'] | |
| - File.expand_path(info['classifiers'].gsub('%BASE%', WarVOX::Ba… | |
| - end | |
| - | |
| - def self.log_file | |
| - STDOUT | |
| - end | |
| - | |
| - def self.log_level | |
| - Logger::DEBUG | |
| - end | |
| - | |
| - def self.classifiers_load | |
| - path = classifiers_path | |
| - sigs = [] | |
| - return sigs if not path | |
| - | |
| - Dir.new(path).entries.sort{ |a,b| | |
| - a.to_i <=> b.to_i | |
| - }.map{ |ent| | |
| - File.join(path, ent) | |
| - }.each do |ent| | |
| - sigs << ent if File.file?(ent) | |
| - end | |
| - | |
| - sigs | |
| - end | |
| - | |
| - # This method searches the PATH environment variable for | |
| - # a fully qualified path to the supplied file name. | |
| - # Stolen from Rex | |
| - def self.find_full_path(file_name) | |
| - | |
| - # Return absolute paths unmodified | |
| - if(file_name[0,1] == ::File::SEPARATOR) | |
| - return file_name | |
| - end | |
| - | |
| - path = ENV['PATH'] | |
| - if (path) | |
| - path.split(::File::PATH_SEPARATOR).each { |base| | |
| - begin | |
| - path = base + ::File::SEPARATOR + file… | |
| - if (::File::Stat.new(path)) | |
| - return path | |
| - end | |
| - rescue | |
| - end | |
| - } | |
| - end | |
| - return nil | |
| - end | |
| - | |
| - # This method prevents two installations of WarVOX from using the same | |
| - # rails session key. The first time this method is called, it generates | |
| - # a new key and stores it in the rails directory, afterwards this key | |
| - # will be used every time. | |
| - def self.load_session_key | |
| - kfile = File.join(WarVOX::Base, 'config', 'session.key') | |
| - if(not File.exists?(kfile)) | |
| - # XXX: assume /dev/urandom exists | |
| - kdata = File.read('/dev/urandom', 64).unpack("H*")[0] | |
| - | |
| - # Create the new session key file | |
| - fd = File.new(kfile, 'w') | |
| - | |
| - # Make this file mode 0600 | |
| - File.chmod(0600, kfile) | |
| - | |
| - # Write it and close | |
| - fd.write(kdata) | |
| - fd.close | |
| - return kdata | |
| - end | |
| - File.read(kfile) | |
| - end | |
| + require 'yaml' | |
| + | |
| + def self.authentication_creds | |
| + user = nil | |
| + pass = nil | |
| + info = YAML.load_file(WarVOX::Conf) | |
| + if( info and | |
| + info['authentication'] and | |
| + info['authentication']['user'] and | |
| + info['authentication']['pass'] | |
| + ) | |
| + user = info['authentication']['user'] | |
| + pass = info['authentication']['pass'] | |
| + end | |
| + [user,pass] | |
| + end | |
| + | |
| + def self.authenticate(user,pass) | |
| + wuser,wpass = authentication_creds | |
| + (wuser == user and wpass == pass) ? true : false | |
| + end | |
| + | |
| + def self.tool_path(name) | |
| + info = YAML.load_file(WarVOX::Conf) | |
| + return nil if not info | |
| + return nil if not info['tools'] | |
| + return nil if not info['tools'][name] | |
| + find_full_path( | |
| + info['tools'][name].gsub('%BASE%', WarVOX::Base) | |
| + ) | |
| + end | |
| + | |
| + def self.analysis_threads | |
| + core_count = File.read("/proc/cpuinfo").scan(/^processor\s+:/).length resc… | |
| + | |
| + info = YAML.load_file(WarVOX::Conf) | |
| + return core_count if not info | |
| + return core_count if not info['analysis_threads'] | |
| + return core_count if info['analysis_threads'] == 0 | |
| + [ info['analysis_threads'].to_i, core_count ].min | |
| + end | |
| + | |
| + def self.blacklist_path | |
| + info = YAML.load_file(WarVOX::Conf) | |
| + return nil if not info | |
| + return nil if not info['blacklist'] | |
| + File.expand_path(info['blacklist'].gsub('%BASE%', WarVOX::Base)) | |
| + end | |
| + | |
| + def self.blacklist_load | |
| + path = blacklist_path | |
| + return if not path | |
| + data = File.read(path, File.size(path)) | |
| + sigs = [] | |
| + | |
| + File.open(path, 'r') do |fd| | |
| + lno = 0 | |
| + fd.each_line do |line| | |
| + lno += 1 | |
| + next if line =~ /^#/ | |
| + next if line =~ /^\s+$/ | |
| + line.strip! | |
| + sigs << [lno, line] | |
| + end | |
| + sigs | |
| + end | |
| + | |
| + end | |
| + | |
| + def self.signatures_path | |
| + info = YAML.load_file(WarVOX::Conf) | |
| + return nil if not info | |
| + return nil if not info['signatures'] | |
| + File.expand_path(info['signatures'].gsub('%BASE%', WarVOX::Base)) | |
| + end | |
| + | |
| + def self.classifiers_path | |
| + info = YAML.load_file(WarVOX::Conf) | |
| + return nil if not info | |
| + return nil if not info['classifiers'] | |
| + File.expand_path(info['classifiers'].gsub('%BASE%', WarVOX::Base)) | |
| + end | |
| + | |
| + def self.log_file | |
| + STDOUT | |
| + end | |
| + | |
| + def self.log_level | |
| + Logger::DEBUG | |
| + end | |
| + | |
| + def self.classifiers_load | |
| + path = classifiers_path | |
| + sigs = [] | |
| + return sigs if not path | |
| + | |
| + Dir.new(path).entries.sort{ |a,b| | |
| + a.to_i <=> b.to_i | |
| + }.map{ |ent| | |
| + File.join(path, ent) | |
| + }.each do |ent| | |
| + sigs << ent if File.file?(ent) | |
| + end | |
| + | |
| + sigs | |
| + end | |
| + | |
| + # This method searches the PATH environment variable for | |
| + # a fully qualified path to the supplied file name. | |
| + # Stolen from Rex | |
| + def self.find_full_path(file_name) | |
| + | |
| + # Return absolute paths unmodified | |
| + if(file_name[0,1] == ::File::SEPARATOR) | |
| + return file_name | |
| + end | |
| + | |
| + path = ENV['PATH'] | |
| + if (path) | |
| + path.split(::File::PATH_SEPARATOR).each { |base| | |
| + begin | |
| + path = base + ::File::SEPARATOR + file_name | |
| + if (::File::Stat.new(path)) | |
| + return path | |
| + end | |
| + rescue | |
| + end | |
| + } | |
| + end | |
| + return nil | |
| + end | |
| + | |
| + # This method prevents two installations of WarVOX from using the same | |
| + # rails session key. The first time this method is called, it generates | |
| + # a new key and stores it in the rails directory, afterwards this key | |
| + # will be used every time. | |
| + def self.load_session_key | |
| + kfile = File.join(WarVOX::Base, 'config', 'session.key') | |
| + if(not File.exists?(kfile)) | |
| + # XXX: assume /dev/urandom exists | |
| + kdata = File.read('/dev/urandom', 64).unpack("H*")[0] | |
| + | |
| + # Create the new session key file | |
| + fd = File.new(kfile, 'w') | |
| + | |
| + # Make this file mode 0600 | |
| + File.chmod(0600, kfile) | |
| + | |
| + # Write it and close | |
| + fd.write(kdata) | |
| + fd.close | |
| + return kdata | |
| + end | |
| + File.read(kfile) | |
| + end | |
| end | |
| diff --git a/lib/warvox/jobs.rb b/lib/warvox/jobs.rb | |
| @@ -1,75 +1,75 @@ | |
| module WarVOX | |
| class JobQueue | |
| - attr_accessor :active_job, :active_thread, :queue, :queue_thread | |
| + attr_accessor :active_job, :active_thread, :queue, :queue_thread | |
| - require "thread" | |
| + require "thread" | |
| - def initialize | |
| - @mutex = ::Mutex.new | |
| - @queue = [] | |
| - @queue_thread = Thread.new{ manage_queue } | |
| + def initialize | |
| + @mutex = ::Mutex.new | |
| + @queue = [] | |
| + @queue_thread = Thread.new{ manage_queue } | |
| - super | |
| - end | |
| + super | |
| + end | |
| - 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 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 == job_id) | |
| + end | |
| + end | |
| + false | |
| + end | |
| - def schedule(klass, job_id) | |
| - begin | |
| - return false if scheduled?(klass, job_id) | |
| - @queue.push(klass.new(job_id)) | |
| - rescue ::Exception => e | |
| - $stderr.puts "ERROR!!!!!: #{e} #{e.backtrace}" | |
| - false | |
| - end | |
| - end | |
| + def schedule(klass, job_id) | |
| + begin | |
| + return false if scheduled?(klass, job_id) | |
| + @queue.push(klass.new(job_id)) | |
| + rescue ::Exception => e | |
| + $stderr.puts "ERROR!!!!!: #{e} #{e.backtrace}" | |
| + false | |
| + end | |
| + end | |
| - def stop(job_id) | |
| - @mutex.synchronize do | |
| - [@active_job, *(@queue)].each do |c| | |
| - next if not c | |
| - if c.name == job_id | |
| - # Actively running | |
| - if @active_job == c | |
| + def stop(job_id) | |
| + @mutex.synchronize do | |
| + [@active_job, *(@queue)].each do |c| | |
| + next if not c | |
| + if c.name == job_id | |
| + # Actively running | |
| + if @active_job == c | |
| - else | |
| + else | |
| - end | |
| - end | |
| - end | |
| - end | |
| - end | |
| + end | |
| + end | |
| + end | |
| + end | |
| + end | |
| - def manage_queue | |
| - begin | |
| - while(true) | |
| - @mutex.synchronize do | |
| - if(@active_job and @active_job.status == 'comp… | |
| - @active_job = nil | |
| - @active_thread = nil | |
| - end | |
| + def manage_queue | |
| + begin | |
| + while(true) | |
| + @mutex.synchronize do | |
| + 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_… | |
| - end | |
| - end | |
| + if(not @active_job and @queue.length > 0) | |
| + @active_job = @queue.shift | |
| + @active_thread = Thread.new { @active_job.start } | |
| + end | |
| + end | |
| - Kernel.select(nil, nil, nil, 1) | |
| - end | |
| - rescue ::Exception | |
| - $stderr.puts "QUEUE MANAGER:#{$!.class} #{$!}" | |
| - $stderr.flush | |
| - end | |
| - end | |
| + Kernel.select(nil, nil, nil, 1) | |
| + end | |
| + rescue ::Exception | |
| + $stderr.puts "QUEUE MANAGER:#{$!.class} #{$!}" | |
| + $stderr.flush | |
| + end | |
| + end | |
| end | |
| end | |
| diff --git a/lib/warvox/jobs/analysis.rb b/lib/warvox/jobs/analysis.rb | |
| @@ -2,420 +2,420 @@ module WarVOX | |
| module Jobs | |
| class Analysis < Base | |
| - require 'fileutils' | |
| - require 'tempfile' | |
| - require 'open3' | |
| - | |
| - # This is required by the verify_install.rb script, so dont error | |
| - # out if the gem is not yet available | |
| - begin | |
| - require 'kissfft' | |
| - rescue ::LoadError | |
| - end | |
| - | |
| - class Classifier | |
| - | |
| - class Completed < RuntimeError | |
| - end | |
| - | |
| - attr_accessor :line_type | |
| - attr_accessor :signatures | |
| - attr_accessor :data | |
| - | |
| - def initialize | |
| - @signatures = [] | |
| - @data = {} | |
| - end | |
| - | |
| - def proc(str) | |
| - begin | |
| - eval(str) | |
| - rescue Completed | |
| - end | |
| - end | |
| - end | |
| - | |
| - def type | |
| - 'analysis' | |
| - end | |
| - | |
| - def initialize(job_id, conf) | |
| - @job_id = job_id | |
| - @conf = conf | |
| - @tasks = [] | |
| - @calls = [] | |
| - end | |
| - | |
| - def stop | |
| - @calls = [] | |
| - @tasks.each do |t| | |
| - t.kill rescue nil | |
| - end | |
| - @tasks = [] | |
| - end | |
| - | |
| - def start | |
| - | |
| - @calls = [] | |
| - | |
| - query = nil | |
| - | |
| - ::ActiveRecord::Base.connection_pool.with_connection { | |
| - | |
| - begin | |
| - | |
| - job = Job.find(@job_id) | |
| - if not job | |
| - raise RuntimeError, "The parent job no longer exists" | |
| - end | |
| - | |
| - case @conf[:scope] | |
| - when 'calls' | |
| - if @conf[:force] | |
| - query = {:id => @conf[:target_ids], :answered … | |
| - else | |
| - query = {:id => @conf[:target_ids], :answered … | |
| - end | |
| - when 'job' | |
| - if @conf[:force] | |
| - query = {:job_id => @conf[:target_id], :answer… | |
| - else | |
| - query = {:job_id => @conf[:target_id], :answer… | |
| - end | |
| - when 'project' | |
| - if @conf[:force] | |
| - query = {:project_id => @conf[:target_id], :an… | |
| - else | |
| - query = {:project_id => @conf[:target_id], :an… | |
| - end | |
| - when 'global' | |
| - if @conf[:force] | |
| - query = {:answered => true, :busy => false} | |
| - else | |
| - query = {:answered => true, :busy => false, :a… | |
| - end | |
| - else | |
| - # Bail if we don't have a valid scope | |
| - return | |
| - end | |
| - | |
| - # Build a list of call IDs, as find_each() gets confused if th… | |
| - calls = Call.where(query).map{|c| c.id } | |
| - | |
| - @total_calls = calls.length | |
| - @completed_calls = 0 | |
| - | |
| - max_threads = WarVOX::Config.analysis_threads | |
| - last_update = Time.now | |
| - | |
| - while(calls.length > 0) | |
| - if @tasks.length < max_threads | |
| - @tasks << Thread.new(calls.shift, job.id) { |c… | |
| - else | |
| - clear_stale_tasks | |
| - | |
| - # Update progress every 10 seconds or so | |
| - if Time.now.to_f - last_update.to_f > 10 | |
| - update_progress((@completed_calls / @t… | |
| - last_update = Time.now | |
| - end | |
| - | |
| - clear_zombies | |
| - end | |
| - end | |
| - | |
| - @tasks.map {|t| t.join } | |
| - clear_stale_tasks | |
| - clear_zombies | |
| - | |
| - rescue ::Exception => e | |
| - WarVOX::Log.error("Exception: #{e.class} #{e} #{e.back… | |
| - end | |
| - | |
| - } | |
| - end | |
| - | |
| - def clear_stale_tasks | |
| - @tasks = @tasks.select{ |x| x.status } | |
| - IO.select(nil, nil, nil, 0.25) | |
| - end | |
| - | |
| - def update_progress(pct) | |
| - ::ActiveRecord::Base.connection_pool.with_connection { | |
| - Job.where(id: @job_id).update_all(progress: pct) | |
| - } | |
| - end | |
| - | |
| - def run_analyze_call(cid, jid) | |
| - | |
| - dr = Call.includes(:job).where(id: cid).first | |
| - dr.analysis_started_at = Time.now | |
| - dr.analysis_job_id = jid | |
| - dr.save! | |
| - | |
| - WarVOX::Log.debug("Worker processing audio for #{dr.number}...… | |
| - | |
| - bin = File.join(WarVOX::Base, 'bin', 'analyze_result.rb') | |
| - tmp = Tempfile.new("Analysis") | |
| - begin | |
| - | |
| - mr = dr.media | |
| - ::File.open(tmp.path, "wb") do |fd| | |
| - fd.write(mr.audio) | |
| - end | |
| - | |
| - pfd = IO.popen("nice #{bin} '#{tmp.path}' '#{ dr.number.gsub(/… | |
| - out = Marshal.load(pfd.read) rescue nil | |
| - pfd.close | |
| - | |
| - return if not out | |
| - | |
| - mf = dr.media_fields | |
| - out.each_key do |k| | |
| - if mf.include?(k.to_s) | |
| - mr[k] = out[k] | |
| - else | |
| - dr[k] = out[k] | |
| - end | |
| - end | |
| - | |
| - dr.analysis_completed_at = Time.now | |
| - | |
| - rescue ::Interrupt | |
| - ensure | |
| - tmp.close | |
| - tmp.unlink | |
| - end | |
| - | |
| - mr.save | |
| - dr.save | |
| - | |
| - @completed_calls += 1 | |
| - end | |
| - | |
| - # Takes the raw file path as an argument, returns a hash | |
| - def self.analyze_call(input, num=nil) | |
| - | |
| - return if not input | |
| - return if not File.exist?(input) | |
| - | |
| - bname = File.expand_path(File.dirname(input)) | |
| - num ||= File.basename(input) | |
| - res = {} | |
| - | |
| - # | |
| - # Create the signature database | |
| - # | |
| - raw = WarVOX::Audio::Raw.from_file(input) | |
| - fft = KissFFT.fftr(8192, 8000, 1, raw.samples) || [] | |
| - | |
| - freq = raw.to_freq_sig_arr() | |
| - | |
| - # Save the signature data | |
| - res[:fprint] = freq | |
| - | |
| - # | |
| - # Create a raw decompressed file | |
| - # | |
| - | |
| - # Decompress the audio file | |
| - rawfile = Tempfile.new("rawfile") | |
| - datfile = Tempfile.new("datfile") | |
| - | |
| - # Data files for audio processing and signal graph | |
| - cnt = 0 | |
| - rawfile.write(raw.samples.pack('v*')) | |
| - datfile.write(raw.samples.map{|val| cnt +=1; "#{cnt/8000.0} #{… | |
| - rawfile.flush | |
| - datfile.flush | |
| - | |
| - # Data files for spectrum plotting | |
| - frefile = Tempfile.new("frefile") | |
| - | |
| - # Calculate the peak frequencies for the sample | |
| - maxf = 0 | |
| - maxp = 0 | |
| - tones = {} | |
| - fft.each do |x| | |
| - rank = x.sort{|a,b| a[1].to_i <=> b[1].to_i }.reverse | |
| - rank[0..10].each do |t| | |
| - f = t[0].round | |
| - p = t[1].round | |
| - next if f == 0 | |
| - next if p < 1 | |
| - tones[ f ] ||= [] | |
| - tones[ f ] << t | |
| - if(t[1] > maxp) | |
| - maxf = t[0] | |
| - maxp = t[1] | |
| - end | |
| - end | |
| - end | |
| - | |
| - # Save the peak frequency | |
| - res[:peak_freq] = maxf | |
| - | |
| - # Calculate average frequency and peaks over time | |
| - avg = {} | |
| - pks = [] | |
| - pkz = [] | |
| - fft.each do |slot| | |
| - pks << slot.sort{|a,b| a[1] <=> b[1] }.reverse[0] | |
| - pkz << slot.sort{|a,b| a[1] <=> b[1] }.reverse[0..9] | |
| - slot.each do |f| | |
| - avg[ f[0] ] ||= 0 | |
| - avg[ f[0] ] += f[1] | |
| - end | |
| - end | |
| - | |
| - # Save the peak frequencies over time | |
| - res[:peak_freq_data] = pks.map{|f| "#{f[0]}-#{f[1]}" }.join(" … | |
| - | |
| - # Generate the frequency file | |
| - avg.keys.sort.each do |k| | |
| - avg[k] = avg[k] / fft.length | |
| - frefile.write("#{k} #{avg[k]}\n") | |
| - end | |
| - frefile.flush | |
| - | |
| - # Count significant frequencies across the sample | |
| - fcnt = {} | |
| - 0.step(4000, 5) {|f| fcnt[f] = 0 } | |
| - pkz.each do |fb| | |
| - fb.each do |f| | |
| - fdx = ((f[0] / 5.0).round * 5.0).to_i | |
| - fcnt[fdx] += 0.1 | |
| - end | |
| - end | |
| - | |
| - # | |
| - # Classifier processing | |
| - # | |
| - | |
| - sproc = Classifier.new | |
| - sproc.data = | |
| - { | |
| - :raw => raw, | |
| - :freq => freq, | |
| - :fcnt => fcnt, | |
| - :fft => fft, | |
| - :pks => pks, | |
| - :pkz => pkz, | |
| - :maxf => maxf, | |
| - :maxp => maxp | |
| - } | |
| - | |
| - WarVOX::Config.classifiers_load.each do |sigfile| | |
| - begin | |
| - str = File.read(sigfile, File.size(sigfile)) | |
| - sproc.proc(str) | |
| - rescue ::Exception => e | |
| - $stderr.puts "DEBUG: Caught exception in #{sig… | |
| - end | |
| - break if sproc.line_type | |
| - end | |
| - | |
| - # Save the guessed line type | |
| - res[:line_type] = sproc.line_type | |
| - | |
| - png_big = Tempfile.new("big") | |
| - png_big_dots = Tempfile.new("bigdots") | |
| - png_big_freq = Tempfile.new("bigfreq") | |
| - png_sig = Tempfile.new("signal") | |
| - png_sig_freq = Tempfile.new("sigfreq") | |
| - | |
| - # Plot samples to a graph | |
| - plotter = Tempfile.new("gnuplot") | |
| - | |
| - | |
| - plotter.puts("set autoscale") | |
| - plotter.puts("set yrange [-15000:15000]") | |
| - plotter.puts("set ylabel \"Signal\"") | |
| - plotter.puts("set xlabel \"Seconds\"") | |
| - plotter.puts("set terminal png medium size 640,480 transparent… | |
| - plotter.puts("set output \"#{png_big.path}\"") | |
| - plotter.puts("plot \"#{datfile.path}\" using 1:2 title \"#{num… | |
| - plotter.puts("set output \"#{png_big_dots.path}\"") | |
| - plotter.puts("plot \"#{datfile.path}\" using 1:2 title \"#{num… | |
| - | |
| - | |
| - plotter.puts("unset yrange") | |
| - plotter.puts("set autoscale") | |
| - plotter.puts("set xrange [0:4000]") | |
| - plotter.puts("set terminal png medium size 640,480 transparent… | |
| - plotter.puts("set ylabel \"Power\"") | |
| - plotter.puts("set xlabel \"Frequency\"") | |
| - plotter.puts("set output \"#{png_big_freq.path}\"") | |
| - plotter.puts("plot \"#{frefile.path}\" using 1:2 title \"#{num… | |
| - | |
| - | |
| - plotter.puts("unset xrange") | |
| - plotter.puts("set autoscale") | |
| - plotter.puts("set yrange [-15000:15000]") | |
| - plotter.puts("unset border") | |
| - plotter.puts("unset xtics") | |
| - plotter.puts("unset ytics") | |
| - plotter.puts("set ylabel \"\"") | |
| - plotter.puts("set xlabel \"\"") | |
| - plotter.puts("set terminal png small size 80,60 transparent") | |
| - plotter.puts("set format x ''") | |
| - plotter.puts("set format y ''") | |
| - plotter.puts("set output \"#{png_sig.path}\"") | |
| - plotter.puts("plot \"#{datfile.path}\" using 1:2 notitle with … | |
| - | |
| - plotter.puts("unset yrange") | |
| - plotter.puts("set autoscale") | |
| - plotter.puts("set xrange [0:4000]") | |
| - plotter.puts("unset border") | |
| - plotter.puts("unset xtics") | |
| - plotter.puts("unset ytics") | |
| - plotter.puts("set ylabel \"\"") | |
| - plotter.puts("set xlabel \"\"") | |
| - plotter.puts("set terminal png small size 80,60 transparent") | |
| - plotter.puts("set format x ''") | |
| - plotter.puts("set format y ''") | |
| - plotter.puts("set output \"#{png_sig_freq.path}\"") | |
| - plotter.puts("plot \"#{frefile.path}\" using 1:2 notitle with … | |
| - plotter.flush | |
| - | |
| - system("#{WarVOX::Config.tool_path('gnuplot')} #{plotter.path}… | |
| - File.unlink(plotter.path) | |
| - File.unlink(datfile.path) | |
| - File.unlink(frefile.path) | |
| - plotter.close | |
| - datfile.close | |
| - frefile.path | |
| - | |
| - ::File.open(png_big.path, 'rb') { |fd| res[:png_big] … | |
| - ::File.open(png_big_dots.path, 'rb') { |fd| res[:png_big_dots]… | |
| - ::File.open(png_big_freq.path, 'rb') { |fd| res[:png_big_freq]… | |
| - ::File.open(png_sig.path, 'rb') { |fd| res[:png_sig] … | |
| - ::File.open(png_sig_freq.path, 'rb') { |fd| res[:png_sig_freq]… | |
| - | |
| - [png_big, png_big_dots, png_big_freq, png_sig, png_sig_freq ].… | |
| - | |
| - tmp_wav = Tempfile.new("wav") | |
| - tmp_mp3 = Tempfile.new("mp3") | |
| - | |
| - # Generate a WAV file from raw linear PCM | |
| - ::File.open(tmp_wav.path, "wb") do |fd| | |
| - fd.write(raw.to_wav) | |
| - end | |
| - | |
| - # Default samples at 8k, bump it to 32k to get better quality | |
| - system("#{WarVOX::Config.tool_path('lame')} -b 32 #{tmp_wav.pa… | |
| - | |
| - File.unlink(rawfile.path) | |
| - rawfile.close | |
| - | |
| - ::File.open(tmp_mp3.path, "rb") { |fd| res[:mp3] = fd.read } | |
| - | |
| - [tmp_wav, tmp_mp3].map {|x| x.unlink; x.close } | |
| - | |
| - clear_zombies() | |
| - | |
| - res | |
| - end | |
| + require 'fileutils' | |
| + require 'tempfile' | |
| + require 'open3' | |
| + | |
| + # This is required by the verify_install.rb script, so dont error | |
| + # out if the gem is not yet available | |
| + begin | |
| + require 'kissfft' | |
| + rescue ::LoadError | |
| + end | |
| + | |
| + class Classifier | |
| + | |
| + class Completed < RuntimeError | |
| + end | |
| + | |
| + attr_accessor :line_type | |
| + attr_accessor :signatures | |
| + attr_accessor :data | |
| + | |
| + def initialize | |
| + @signatures = [] | |
| + @data = {} | |
| + end | |
| + | |
| + def proc(str) | |
| + begin | |
| + eval(str) | |
| + rescue Completed | |
| + end | |
| + end | |
| + end | |
| + | |
| + def type | |
| + 'analysis' | |
| + end | |
| + | |
| + def initialize(job_id, conf) | |
| + @job_id = job_id | |
| + @conf = conf | |
| + @tasks = [] | |
| + @calls = [] | |
| + end | |
| + | |
| + def stop | |
| + @calls = [] | |
| + @tasks.each do |t| | |
| + t.kill rescue nil | |
| + end | |
| + @tasks = [] | |
| + end | |
| + | |
| + def start | |
| + | |
| + @calls = [] | |
| + | |
| + query = nil | |
| + | |
| + ::ActiveRecord::Base.connection_pool.with_connection { | |
| + | |
| + begin | |
| + | |
| + job = Job.find(@job_id) | |
| + if not job | |
| + raise RuntimeError, "The parent job no longer exists" | |
| + end | |
| + | |
| + case @conf[:scope] | |
| + when 'calls' | |
| + if @conf[:force] | |
| + query = {:id => @conf[:target_ids], :answered => true, :busy => false} | |
| + else | |
| + query = {:id => @conf[:target_ids], :answered => true, :busy => false,… | |
| + end | |
| + when 'job' | |
| + if @conf[:force] | |
| + query = {:job_id => @conf[:target_id], :answered => true, :busy => fal… | |
| + else | |
| + query = {:job_id => @conf[:target_id], :answered => true, :busy => fal… | |
| + end | |
| + when 'project' | |
| + if @conf[:force] | |
| + query = {:project_id => @conf[:target_id], :answered => true, :busy =>… | |
| + else | |
| + query = {:project_id => @conf[:target_id], :answered => true, :busy =>… | |
| + end | |
| + when 'global' | |
| + if @conf[:force] | |
| + query = {:answered => true, :busy => false} | |
| + else | |
| + query = {:answered => true, :busy => false, :analysis_started_at => ni… | |
| + end | |
| + else | |
| + # Bail if we don't have a valid scope | |
| + return | |
| + end | |
| + | |
| + # Build a list of call IDs, as find_each() gets confused if the DB changes… | |
| + calls = Call.where(query).map{|c| c.id } | |
| + | |
| + @total_calls = calls.length | |
| + @completed_calls = 0 | |
| + | |
| + max_threads = WarVOX::Config.analysis_threads | |
| + last_update = Time.now | |
| + | |
| + while(calls.length > 0) | |
| + if @tasks.length < max_threads | |
| + @tasks << Thread.new(calls.shift, job.id) { |c,j| ::ActiveRecord::Base… | |
| + else | |
| + clear_stale_tasks | |
| + | |
| + # Update progress every 10 seconds or so | |
| + if Time.now.to_f - last_update.to_f > 10 | |
| + update_progress((@completed_calls / @total_calls.to_f) * 100) | |
| + last_update = Time.now | |
| + end | |
| + | |
| + clear_zombies | |
| + end | |
| + end | |
| + | |
| + @tasks.map {|t| t.join } | |
| + clear_stale_tasks | |
| + clear_zombies | |
| + | |
| + rescue ::Exception => e | |
| + WarVOX::Log.error("Exception: #{e.class} #{e} #{e.backtrace}") | |
| + end | |
| + | |
| + } | |
| + end | |
| + | |
| + def clear_stale_tasks | |
| + @tasks = @tasks.select{ |x| x.status } | |
| + IO.select(nil, nil, nil, 0.25) | |
| + end | |
| + | |
| + def update_progress(pct) | |
| + ::ActiveRecord::Base.connection_pool.with_connection { | |
| + Job.where(id: @job_id).update_all(progress: pct) | |
| + } | |
| + end | |
| + | |
| + def run_analyze_call(cid, jid) | |
| + | |
| + dr = Call.includes(:job).where(id: cid).first | |
| + dr.analysis_started_at = Time.now | |
| + dr.analysis_job_id = jid | |
| + dr.save! | |
| + | |
| + WarVOX::Log.debug("Worker processing audio for #{dr.number}...") | |
| + | |
| + bin = File.join(WarVOX::Base, 'bin', 'analyze_result.rb') | |
| + tmp = Tempfile.new("Analysis") | |
| + begin | |
| + | |
| + mr = dr.media | |
| + ::File.open(tmp.path, "wb") do |fd| | |
| + fd.write(mr.audio) | |
| + end | |
| + | |
| + pfd = IO.popen("nice #{bin} '#{tmp.path}' '#{ dr.number.gsub(/[^0-9a-zA-Z\… | |
| + out = Marshal.load(pfd.read) rescue nil | |
| + pfd.close | |
| + | |
| + return if not out | |
| + | |
| + mf = dr.media_fields | |
| + out.each_key do |k| | |
| + if mf.include?(k.to_s) | |
| + mr[k] = out[k] | |
| + else | |
| + dr[k] = out[k] | |
| + end | |
| + end | |
| + | |
| + dr.analysis_completed_at = Time.now | |
| + | |
| + rescue ::Interrupt | |
| + ensure | |
| + tmp.close | |
| + tmp.unlink | |
| + end | |
| + | |
| + mr.save | |
| + dr.save | |
| + | |
| + @completed_calls += 1 | |
| + end | |
| + | |
| + # Takes the raw file path as an argument, returns a hash | |
| + def self.analyze_call(input, num=nil) | |
| + | |
| + return if not input | |
| + return if not File.exist?(input) | |
| + | |
| + bname = File.expand_path(File.dirname(input)) | |
| + num ||= File.basename(input) | |
| + res = {} | |
| + | |
| + # | |
| + # Create the signature database | |
| + # | |
| + raw = WarVOX::Audio::Raw.from_file(input) | |
| + fft = KissFFT.fftr(8192, 8000, 1, raw.samples) || [] | |
| + | |
| + freq = raw.to_freq_sig_arr() | |
| + | |
| + # Save the signature data | |
| + res[:fprint] = freq | |
| + | |
| + # | |
| + # Create a raw decompressed file | |
| + # | |
| + | |
| + # Decompress the audio file | |
| + rawfile = Tempfile.new("rawfile") | |
| + datfile = Tempfile.new("datfile") | |
| + | |
| + # Data files for audio processing and signal graph | |
| + cnt = 0 | |
| + rawfile.write(raw.samples.pack('v*')) | |
| + datfile.write(raw.samples.map{|val| cnt +=1; "#{cnt/8000.0} #{val}"}.join(… | |
| + rawfile.flush | |
| + datfile.flush | |
| + | |
| + # Data files for spectrum plotting | |
| + frefile = Tempfile.new("frefile") | |
| + | |
| + # Calculate the peak frequencies for the sample | |
| + maxf = 0 | |
| + maxp = 0 | |
| + tones = {} | |
| + fft.each do |x| | |
| + rank = x.sort{|a,b| a[1].to_i <=> b[1].to_i }.reverse | |
| + rank[0..10].each do |t| | |
| + f = t[0].round | |
| + p = t[1].round | |
| + next if f == 0 | |
| + next if p < 1 | |
| + tones[ f ] ||= [] | |
| + tones[ f ] << t | |
| + if(t[1] > maxp) | |
| + maxf = t[0] | |
| + maxp = t[1] | |
| + end | |
| + end | |
| + end | |
| + | |
| + # Save the peak frequency | |
| + res[:peak_freq] = maxf | |
| + | |
| + # Calculate average frequency and peaks over time | |
| + avg = {} | |
| + pks = [] | |
| + pkz = [] | |
| + fft.each do |slot| | |
| + pks << slot.sort{|a,b| a[1] <=> b[1] }.reverse[0] | |
| + pkz << slot.sort{|a,b| a[1] <=> b[1] }.reverse[0..9] | |
| + slot.each do |f| | |
| + avg[ f[0] ] ||= 0 | |
| + avg[ f[0] ] += f[1] | |
| + end | |
| + end | |
| + | |
| + # Save the peak frequencies over time | |
| + res[:peak_freq_data] = pks.map{|f| "#{f[0]}-#{f[1]}" }.join(" ") | |
| + | |
| + # Generate the frequency file | |
| + avg.keys.sort.each do |k| | |
| + avg[k] = avg[k] / fft.length | |
| + frefile.write("#{k} #{avg[k]}\n") | |
| + end | |
| + frefile.flush | |
| + | |
| + # Count significant frequencies across the sample | |
| + fcnt = {} | |
| + 0.step(4000, 5) {|f| fcnt[f] = 0 } | |
| + pkz.each do |fb| | |
| + fb.each do |f| | |
| + fdx = ((f[0] / 5.0).round * 5.0).to_i | |
| + fcnt[fdx] += 0.1 | |
| + end | |
| + end | |
| + | |
| + # | |
| + # Classifier processing | |
| + # | |
| + | |
| + sproc = Classifier.new | |
| + sproc.data = | |
| + { | |
| + :raw => raw, | |
| + :freq => freq, | |
| + :fcnt => fcnt, | |
| + :fft => fft, | |
| + :pks => pks, | |
| + :pkz => pkz, | |
| + :maxf => maxf, | |
| + :maxp => maxp | |
| + } | |
| + | |
| + WarVOX::Config.classifiers_load.each do |sigfile| | |
| + begin | |
| + str = File.read(sigfile, File.size(sigfile)) | |
| + sproc.proc(str) | |
| + rescue ::Exception => e | |
| + $stderr.puts "DEBUG: Caught exception in #{sigfile}: #{e} #{e.backtrac… | |
| + end | |
| + break if sproc.line_type | |
| + end | |
| + | |
| + # Save the guessed line type | |
| + res[:line_type] = sproc.line_type | |
| + | |
| + png_big = Tempfile.new("big") | |
| + png_big_dots = Tempfile.new("bigdots") | |
| + png_big_freq = Tempfile.new("bigfreq") | |
| + png_sig = Tempfile.new("signal") | |
| + png_sig_freq = Tempfile.new("sigfreq") | |
| + | |
| + # Plot samples to a graph | |
| + plotter = Tempfile.new("gnuplot") | |
| + | |
| + | |
| + plotter.puts("set autoscale") | |
| + plotter.puts("set yrange [-15000:15000]") | |
| + plotter.puts("set ylabel \"Signal\"") | |
| + plotter.puts("set xlabel \"Seconds\"") | |
| + plotter.puts("set terminal png medium size 640,480 transparent") | |
| + plotter.puts("set output \"#{png_big.path}\"") | |
| + plotter.puts("plot \"#{datfile.path}\" using 1:2 title \"#{num}\" with lin… | |
| + plotter.puts("set output \"#{png_big_dots.path}\"") | |
| + plotter.puts("plot \"#{datfile.path}\" using 1:2 title \"#{num}\" with dot… | |
| + | |
| + | |
| + plotter.puts("unset yrange") | |
| + plotter.puts("set autoscale") | |
| + plotter.puts("set xrange [0:4000]") | |
| + plotter.puts("set terminal png medium size 640,480 transparent") | |
| + plotter.puts("set ylabel \"Power\"") | |
| + plotter.puts("set xlabel \"Frequency\"") | |
| + plotter.puts("set output \"#{png_big_freq.path}\"") | |
| + plotter.puts("plot \"#{frefile.path}\" using 1:2 title \"#{num} - Peak #{m… | |
| + | |
| + | |
| + plotter.puts("unset xrange") | |
| + plotter.puts("set autoscale") | |
| + plotter.puts("set yrange [-15000:15000]") | |
| + plotter.puts("unset border") | |
| + plotter.puts("unset xtics") | |
| + plotter.puts("unset ytics") | |
| + plotter.puts("set ylabel \"\"") | |
| + plotter.puts("set xlabel \"\"") | |
| + plotter.puts("set terminal png small size 80,60 transparent") | |
| + plotter.puts("set format x ''") | |
| + plotter.puts("set format y ''") | |
| + plotter.puts("set output \"#{png_sig.path}\"") | |
| + plotter.puts("plot \"#{datfile.path}\" using 1:2 notitle with lines") | |
| + | |
| + plotter.puts("unset yrange") | |
| + plotter.puts("set autoscale") | |
| + plotter.puts("set xrange [0:4000]") | |
| + plotter.puts("unset border") | |
| + plotter.puts("unset xtics") | |
| + plotter.puts("unset ytics") | |
| + plotter.puts("set ylabel \"\"") | |
| + plotter.puts("set xlabel \"\"") | |
| + plotter.puts("set terminal png small size 80,60 transparent") | |
| + plotter.puts("set format x ''") | |
| + plotter.puts("set format y ''") | |
| + plotter.puts("set output \"#{png_sig_freq.path}\"") | |
| + plotter.puts("plot \"#{frefile.path}\" using 1:2 notitle with lines") | |
| + plotter.flush | |
| + | |
| + system("#{WarVOX::Config.tool_path('gnuplot')} #{plotter.path}") | |
| + File.unlink(plotter.path) | |
| + File.unlink(datfile.path) | |
| + File.unlink(frefile.path) | |
| + plotter.close | |
| + datfile.close | |
| + frefile.path | |
| + | |
| + ::File.open(png_big.path, 'rb') { |fd| res[:png_big] = fd.read } | |
| + ::File.open(png_big_dots.path, 'rb') { |fd| res[:png_big_dots] = fd.read } | |
| + ::File.open(png_big_freq.path, 'rb') { |fd| res[:png_big_freq] = fd.read } | |
| + ::File.open(png_sig.path, 'rb') { |fd| res[:png_sig] = fd.read } | |
| + ::File.open(png_sig_freq.path, 'rb') { |fd| res[:png_sig_freq] = fd.read } | |
| + | |
| + [png_big, png_big_dots, png_big_freq, png_sig, png_sig_freq ].map {|x| x.u… | |
| + | |
| + tmp_wav = Tempfile.new("wav") | |
| + tmp_mp3 = Tempfile.new("mp3") | |
| + | |
| + # Generate a WAV file from raw linear PCM | |
| + ::File.open(tmp_wav.path, "wb") do |fd| | |
| + fd.write(raw.to_wav) | |
| + end | |
| + | |
| + # Default samples at 8k, bump it to 32k to get better quality | |
| + system("#{WarVOX::Config.tool_path('lame')} -b 32 #{tmp_wav.path} #{tmp_mp… | |
| + | |
| + File.unlink(rawfile.path) | |
| + rawfile.close | |
| + | |
| + ::File.open(tmp_mp3.path, "rb") { |fd| res[:mp3] = fd.read } | |
| + | |
| + [tmp_wav, tmp_mp3].map {|x| x.unlink; x.close } | |
| + | |
| + clear_zombies() | |
| + | |
| + res | |
| + end | |
| end | |
| diff --git a/lib/warvox/jobs/base.rb b/lib/warvox/jobs/base.rb | |
| @@ -1,38 +1,38 @@ | |
| module WarVOX | |
| module Jobs | |
| class Base | |
| - attr_accessor :name, :status | |
| + attr_accessor :name, :status | |
| - def type | |
| - 'base' | |
| - end | |
| + def type | |
| + 'base' | |
| + end | |
| - def stop | |
| - @status = 'active' | |
| - end | |
| + def stop | |
| + @status = 'active' | |
| + end | |
| - def start | |
| - @status = 'completed' | |
| - end | |
| + def start | |
| + @status = 'completed' | |
| + end | |
| - def db_save(obj) | |
| - max_tries = 100 | |
| - cur_tries = 0 | |
| - obj.save! | |
| - end | |
| + def db_save(obj) | |
| + max_tries = 100 | |
| + cur_tries = 0 | |
| + obj.save! | |
| + end | |
| - def clear_zombies | |
| - self.class.clear_zombies | |
| - end | |
| + def clear_zombies | |
| + self.class.clear_zombies | |
| + end | |
| - def self.clear_zombies | |
| - begin | |
| - # Clear zombies just in case... | |
| - while(Process.waitpid(-1, Process::WNOHANG)) | |
| - end | |
| - rescue ::Exception | |
| - end | |
| - end | |
| + def self.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 | |
| @@ -2,225 +2,225 @@ module WarVOX | |
| module Jobs | |
| class Dialer < Base | |
| - require 'fileutils' | |
| - | |
| - def type | |
| - 'dialer' | |
| - end | |
| - | |
| - def initialize(job_id, conf) | |
| - @job_id = job_id | |
| - @conf = conf | |
| - @range = @conf[:range] | |
| - @seconds = @conf[:seconds] | |
| - @lines = @conf[:lines] | |
| - @nums = shuffle_a(WarVOX::Phone.crack_mask(@range)) | |
| - | |
| - @tasks = [] | |
| - @provs = get_providers | |
| - | |
| - # CallerID modes (SELF or a mask) | |
| - @cid_self = @conf[:cid_mask] == 'SELF' | |
| - if(not @cid_self) | |
| - @cid_range = WarVOX::Phone.crack_mask(@conf[:cid_mask]) | |
| - end | |
| - end | |
| - | |
| - # | |
| - # Performs a Fisher-Yates shuffle on an array | |
| - # | |
| - def shuffle_a(arr) | |
| - len = arr.length | |
| - max = len - 1 | |
| - cyc = [* (0..max) ] | |
| - for d in cyc | |
| - e = rand(d+1) | |
| - next if e == d | |
| - f = arr[d]; | |
| - g = arr[e]; | |
| - arr[d] = g; | |
| - arr[e] = f; | |
| - end | |
| - return arr | |
| - end | |
| - | |
| - def get_providers | |
| - res = [] | |
| - | |
| - ::ActiveRecord::Base.connection_pool.with_connection { | |
| - ::Provider.where(:enabled => true).all.each do |prov| | |
| - info = { | |
| - :name => prov.name, | |
| - :id => prov.id, | |
| - :port => prov.port, | |
| - :host => prov.host, | |
| - :user => prov.user, | |
| - :pass => prov.pass, | |
| - :lines => prov.lines | |
| - } | |
| - 1.upto(prov.lines) {|i| res.push(info) } | |
| - end | |
| - } | |
| - | |
| - shuffle_a(res) | |
| - end | |
| - | |
| - | |
| - def stop | |
| - @nums = [] | |
| - @tasks.each do |t| | |
| - t.kill rescue nil | |
| - end | |
| - @tasks = [] | |
| - end | |
| - | |
| - def start | |
| - # Scrub all numbers matching the blacklist | |
| - list = WarVOX::Config.blacklist_load | |
| - list.each do |b| | |
| - lno,reg = b | |
| - @nums.each do |num| | |
| - if(num =~ /#{reg}/) | |
| - $stderr.puts "DEBUG: Skipping #{num} d… | |
| - @nums.delete(num) | |
| - end | |
| - end | |
| - end | |
| - | |
| - last_update = Time.now | |
| - @nums_total = @nums.length | |
| - | |
| - max_tasks = [@provs.length, @lines].min | |
| - | |
| - while(@nums.length > 0) | |
| - while( @tasks.length < max_tasks ) do | |
| - tnum = @nums.shift | |
| - break unless tnum | |
| - | |
| - tprov = allocate_provider | |
| - | |
| - @tasks << Thread.new(tnum,tprov) do |num,prov| | |
| - | |
| - out_fd = Tempfile.new("rawfile") | |
| - out = out_fd.path | |
| - | |
| - begin | |
| - # Execute and read the output | |
| - busy = 0 | |
| - ring = 0 | |
| - fail = 1 | |
| - byte = 0 | |
| - path = '' | |
| - cid = @cid_self ? num : @cid_range[ r… | |
| - | |
| - IO.popen( | |
| - [ | |
| - WarVOX::Config.tool_pa… | |
| - "-s", | |
| - prov[:host], | |
| - "-u", | |
| - prov[:user], | |
| - "-p", | |
| - prov[:pass], | |
| - "-c", | |
| - cid, | |
| - "-o", | |
| - out, | |
| - "-n", | |
| - num, | |
| - "-l", | |
| - @seconds | |
| - ].map{|i| | |
| - "'" + i.to_s.gsub("'",… | |
| - }.join(" ")).each_line do |line| | |
| - $stderr.puts "DEBUG: #{line.st… | |
| - if(line =~ /^COMPLETED/) | |
| - line.split(/\s+/).map{… | |
| - busy = info[1]… | |
| - fail = info[1]… | |
| - ring = info[1]… | |
| - byte = info[1]… | |
| - path = info[1]… | |
| - end | |
| - end | |
| - end | |
| - | |
| - ::ActiveRecord::Base.connection_pool.w… | |
| - job = Job.find(@job_id) | |
| - if not job | |
| - raise RuntimeError, "T… | |
| - end | |
| - | |
| - res = ::Call.new | |
| - res.number = num | |
| - res.job_id = job.id | |
| - res.project_id = job.projec… | |
| - res.provider_id = prov[:id] | |
| - res.answered = (fail == 0… | |
| - res.busy = (busy == 1… | |
| - res.audio_length = (byte / 160… | |
| - res.ring_length = ring | |
| - res.caller_id = cid | |
| - | |
| - res.save | |
| - | |
| - if(File.exists?(out)) | |
| - File.open(out, "rb") d… | |
| - med = res.media | |
| - med.audio = fd… | |
| - med.save | |
| - end | |
| - end | |
| - | |
| - out_fd.close | |
| - ::FileUtils.rm_f(out) | |
| - end | |
| - | |
| - rescue ::Exception => e | |
| - $stderr.puts "ERROR: #{e.class… | |
| - end | |
| - end | |
| - | |
| - # END NEW THREAD | |
| - end | |
| - # END SPAWN THREADS | |
| - | |
| - clear_stale_tasks | |
| - | |
| - # Update progress every 10 seconds or so | |
| - if Time.now.to_f - last_update.to_f > 10 | |
| - update_progress(((@nums_total - @nums.length) … | |
| - last_update = Time.now.to_f | |
| - end | |
| - | |
| - clear_zombies() | |
| - end | |
| - | |
| - while @tasks.length > 0 | |
| - clear_stale_tasks | |
| - end | |
| - | |
| - # ALL DONE | |
| - end | |
| - | |
| - def clear_stale_tasks | |
| - # Remove dead threads from the task list | |
| - @tasks = @tasks.select{ |x| x.status } | |
| - IO.select(nil, nil, nil, 0.25) | |
| - end | |
| - | |
| - def update_progress(pct) | |
| - ::ActiveRecord::Base.connection_pool.with_connection { | |
| - Job.where(id: @job_id).update_all(progress: pct) | |
| - } | |
| - end | |
| - | |
| - def allocate_provider | |
| - @prov_idx ||= 0 | |
| - prov = @provs[ @prov_idx % @provs.length ] | |
| - @prov_idx += 1 | |
| - prov | |
| - end | |
| + require 'fileutils' | |
| + | |
| + def type | |
| + 'dialer' | |
| + end | |
| + | |
| + def initialize(job_id, conf) | |
| + @job_id = job_id | |
| + @conf = conf | |
| + @range = @conf[:range] | |
| + @seconds = @conf[:seconds] | |
| + @lines = @conf[:lines] | |
| + @nums = shuffle_a(WarVOX::Phone.crack_mask(@range)) | |
| + | |
| + @tasks = [] | |
| + @provs = get_providers | |
| + | |
| + # CallerID modes (SELF or a mask) | |
| + @cid_self = @conf[:cid_mask] == 'SELF' | |
| + if(not @cid_self) | |
| + @cid_range = WarVOX::Phone.crack_mask(@conf[:cid_mask]) | |
| + end | |
| + end | |
| + | |
| + # | |
| + # Performs a Fisher-Yates shuffle on an array | |
| + # | |
| + def shuffle_a(arr) | |
| + len = arr.length | |
| + max = len - 1 | |
| + cyc = [* (0..max) ] | |
| + for d in cyc | |
| + e = rand(d+1) | |
| + next if e == d | |
| + f = arr[d]; | |
| + g = arr[e]; | |
| + arr[d] = g; | |
| + arr[e] = f; | |
| + end | |
| + return arr | |
| + end | |
| + | |
| + def get_providers | |
| + res = [] | |
| + | |
| + ::ActiveRecord::Base.connection_pool.with_connection { | |
| + ::Provider.where(:enabled => true).all.each do |prov| | |
| + info = { | |
| + :name => prov.name, | |
| + :id => prov.id, | |
| + :port => prov.port, | |
| + :host => prov.host, | |
| + :user => prov.user, | |
| + :pass => prov.pass, | |
| + :lines => prov.lines | |
| + } | |
| + 1.upto(prov.lines) {|i| res.push(info) } | |
| + end | |
| + } | |
| + | |
| + shuffle_a(res) | |
| + end | |
| + | |
| + | |
| + def stop | |
| + @nums = [] | |
| + @tasks.each do |t| | |
| + t.kill rescue nil | |
| + end | |
| + @tasks = [] | |
| + end | |
| + | |
| + def start | |
| + # Scrub all numbers matching the blacklist | |
| + list = WarVOX::Config.blacklist_load | |
| + list.each do |b| | |
| + lno,reg = b | |
| + @nums.each do |num| | |
| + if(num =~ /#{reg}/) | |
| + $stderr.puts "DEBUG: Skipping #{num} due to blacklist (line: #{lno})" | |
| + @nums.delete(num) | |
| + end | |
| + end | |
| + end | |
| + | |
| + last_update = Time.now | |
| + @nums_total = @nums.length | |
| + | |
| + max_tasks = [@provs.length, @lines].min | |
| + | |
| + while(@nums.length > 0) | |
| + while( @tasks.length < max_tasks ) do | |
| + tnum = @nums.shift | |
| + break unless tnum | |
| + | |
| + tprov = allocate_provider | |
| + | |
| + @tasks << Thread.new(tnum,tprov) do |num,prov| | |
| + | |
| + out_fd = Tempfile.new("rawfile") | |
| + out = out_fd.path | |
| + | |
| + begin | |
| + # Execute and read the output | |
| + busy = 0 | |
| + ring = 0 | |
| + fail = 1 | |
| + byte = 0 | |
| + path = '' | |
| + cid = @cid_self ? num : @cid_range[ rand(@cid_range.length) ] | |
| + | |
| + IO.popen( | |
| + [ | |
| + WarVOX::Config.tool_path('iaxrecord'), | |
| + "-s", | |
| + prov[:host], | |
| + "-u", | |
| + prov[:user], | |
| + "-p", | |
| + prov[:pass], | |
| + "-c", | |
| + cid, | |
| + "-o", | |
| + out, | |
| + "-n", | |
| + num, | |
| + "-l", | |
| + @seconds | |
| + ].map{|i| | |
| + "'" + i.to_s.gsub("'",'') +"'" | |
| + }.join(" ")).each_line do |line| | |
| + $stderr.puts "DEBUG: #{line.strip}" | |
| + if(line =~ /^COMPLETED/) | |
| + line.split(/\s+/).map{|b| b.split('=', 2) }.each do |info| | |
| + busy = info[1].to_i if info[0] == 'BUSY' | |
| + fail = info[1].to_i if info[0] == 'FAIL' | |
| + ring = info[1].to_i if info[0] == 'RINGTIME' | |
| + byte = info[1].to_i if info[0] == 'BYTES' | |
| + path = info[1] if info[0] == 'FILE' | |
| + end | |
| + end | |
| + end | |
| + | |
| + ::ActiveRecord::Base.connection_pool.with_connection do | |
| + job = Job.find(@job_id) | |
| + if not job | |
| + raise RuntimeError, "The parent job is not available" | |
| + end | |
| + | |
| + res = ::Call.new | |
| + res.number = num | |
| + res.job_id = job.id | |
| + res.project_id = job.project_id | |
| + res.provider_id = prov[:id] | |
| + res.answered = (fail == 0) ? true : false | |
| + res.busy = (busy == 1) ? true : false | |
| + res.audio_length = (byte / 16000) # 8khz @ 16-bit | |
| + res.ring_length = ring | |
| + res.caller_id = cid | |
| + | |
| + res.save | |
| + | |
| + if(File.exists?(out)) | |
| + File.open(out, "rb") do |fd| | |
| + med = res.media | |
| + med.audio = fd.read(fd.stat.size) | |
| + med.save | |
| + end | |
| + end | |
| + | |
| + out_fd.close | |
| + ::FileUtils.rm_f(out) | |
| + end | |
| + | |
| + rescue ::Exception => e | |
| + $stderr.puts "ERROR: #{e.class} #{e} #{e.backtrace} #{num} #{prov.… | |
| + end | |
| + end | |
| + | |
| + # END NEW THREAD | |
| + end | |
| + # END SPAWN THREADS | |
| + | |
| + clear_stale_tasks | |
| + | |
| + # Update progress every 10 seconds or so | |
| + if Time.now.to_f - last_update.to_f > 10 | |
| + update_progress(((@nums_total - @nums.length) / @nums_total.to_f) * 10… | |
| + last_update = Time.now.to_f | |
| + end | |
| + | |
| + clear_zombies() | |
| + end | |
| + | |
| + while @tasks.length > 0 | |
| + clear_stale_tasks | |
| + end | |
| + | |
| + # ALL DONE | |
| + end | |
| + | |
| + def clear_stale_tasks | |
| + # Remove dead threads from the task list | |
| + @tasks = @tasks.select{ |x| x.status } | |
| + IO.select(nil, nil, nil, 0.25) | |
| + end | |
| + | |
| + def update_progress(pct) | |
| + ::ActiveRecord::Base.connection_pool.with_connection { | |
| + Job.where(id: @job_id).update_all(progress: pct) | |
| + } | |
| + end | |
| + | |
| + def allocate_provider | |
| + @prov_idx ||= 0 | |
| + prov = @provs[ @prov_idx % @provs.length ] | |
| + @prov_idx += 1 | |
| + prov | |
| + end | |
| end | |
| end | |
| diff --git a/lib/warvox/phone.rb b/lib/warvox/phone.rb | |
| @@ -1,58 +1,58 @@ | |
| module WarVOX | |
| class Phone | |
| - # Convert 123456XXXX to an array of expanded numbers | |
| - def self.crack_mask(mask) | |
| - masks = mask.split(/[\s,]+/) || [ ] | |
| - masks.delete(nil) | |
| - self.crack_masks(masks) | |
| - end | |
| - | |
| - def self.crack_masks(masks) | |
| - res = {} | |
| - masks.each do |mask| | |
| - mask = mask.strip | |
| - | |
| - if(mask.index(':')) | |
| - next if mask.index('X') | |
| + # Convert 123456XXXX to an array of expanded numbers | |
| + def self.crack_mask(mask) | |
| + masks = mask.split(/[\s,]+/) || [ ] | |
| + masks.delete(nil) | |
| + self.crack_masks(masks) | |
| + end | |
| + | |
| + def self.crack_masks(masks) | |
| + res = {} | |
| + masks.each do |mask| | |
| + mask = mask.strip | |
| + | |
| + if(mask.index(':')) | |
| + next if mask.index('X') | |
| - # Quick hack to fix leading 0s | |
| - prefix = "" | |
| - if mask =~ /^(0+)/ | |
| - prefix = $1 | |
| + # Quick hack to fix leading 0s | |
| + prefix = "" | |
| + if mask =~ /^(0+)/ | |
| + prefix = $1 | |
| end | |
| - rbeg,rend = mask.split(':').map{|c| c.gsub(/[^… | |
| - rbeg.upto(rend) do |n| | |
| - res[prefix + n.to_s] = {} | |
| - end | |
| - next | |
| - end | |
| - | |
| - incdigits = 0 | |
| - mask.each_char do |c| | |
| - incdigits += 1 if c =~ /^[X#]$/i | |
| - end | |
| - | |
| - max = (10**incdigits)-1 | |
| - | |
| - (0..max).each do |num| | |
| - number = mask.dup # copy the mask | |
| - numstr = sprintf("%0#{incdigits}d", num) # str… | |
| - j = 0 # index for numstr | |
| - for i in 0..number.length-1 do # step through … | |
| - if number[i].chr =~ /^[X#]$/i | |
| - number[i] = numstr[j] # replac… | |
| - j += 1 | |
| - end | |
| - end | |
| - res[number] = {} | |
| - end | |
| - | |
| - end | |
| + rbeg,rend = mask.split(':').map{|c| c.gsub(/[^\d]/, '').to_i } | |
| + rbeg.upto(rend) do |n| | |
| + res[prefix + n.to_s] = {} | |
| + end | |
| + next | |
| + end | |
| + | |
| + incdigits = 0 | |
| + mask.each_char do |c| | |
| + incdigits += 1 if c =~ /^[X#]$/i | |
| + end | |
| + | |
| + max = (10**incdigits)-1 | |
| + | |
| + (0..max).each do |num| | |
| + number = mask.dup # copy the mask | |
| + numstr = sprintf("%0#{incdigits}d", num) # stringify our incrementing … | |
| + j = 0 # index for numstr | |
| + for i in 0..number.length-1 do # step through the number (mask) | |
| + if number[i].chr =~ /^[X#]$/i | |
| + number[i] = numstr[j] # replaced masked indexes with digits from i… | |
| + j += 1 | |
| + end | |
| + end | |
| + res[number] = {} | |
| + end | |
| + | |
| + end | |
| - return res.keys.sort | |
| - end | |
| + return res.keys.sort | |
| + end | |
| end | |
| end | |
| diff --git a/lib/warvox/proto/iax2/client.rb b/lib/warvox/proto/iax2/client.rb | |
| @@ -162,27 +162,27 @@ class Client | |
| end | |
| def send_ack(call) | |
| - data = [ IAX_SUBTYPE_ACK ].pack('C') | |
| + data = [ IAX_SUBTYPE_ACK ].pack('C') | |
| send_data( call, create_pkt( call.scall, call.dcall, call.timestamp, call.… | |
| end | |
| def send_pong(call, stamp) | |
| - data = [ IAX_SUBTYPE_PONG ].pack('C') | |
| + data = [ IAX_SUBTYPE_PONG ].pack('C') | |
| send_data( call, create_pkt( call.scall, call.dcall, stamp, call.oseq, cal… | |
| end | |
| def send_lagrp(call, stamp) | |
| - data = [ IAX_SUBTYPE_LAGRP ].pack('C') | |
| + data = [ IAX_SUBTYPE_LAGRP ].pack('C') | |
| send_data( call, create_pkt( call.scall, call.dcall, stamp, call.oseq, cal… | |
| end | |
| def send_invalid(call) | |
| - data = [ IAX_SUBTYPE_INVAL ].pack('C') | |
| + data = [ IAX_SUBTYPE_INVAL ].pack('C') | |
| send_data( call, create_pkt( call.scall, call.dcall, call.timestamp, call.… | |
| end | |
| def send_hangup(call) | |
| - data = [ IAX_SUBTYPE_HANGUP ].pack('C') | |
| + data = [ IAX_SUBTYPE_HANGUP ].pack('C') | |
| send_data( call, create_pkt( call.scall, call.dcall, call.timestamp, call.… | |
| end | |
| diff --git a/spec/factories/call_media.rb b/spec/factories/call_media.rb | |
| @@ -15,9 +15,9 @@ | |
| # | |
| FactoryGirl.define do | |
| - factory :call_medium do | |
| - call | |
| - project | |
| - end | |
| + factory :call_medium do | |
| + call | |
| + project | |
| + end | |
| end | |
| diff --git a/spec/factories/calls.rb b/spec/factories/calls.rb | |
| @@ -25,11 +25,11 @@ | |
| # | |
| FactoryGirl.define do | |
| - factory :call do | |
| - project | |
| - job | |
| - provider | |
| - number { Faker::PhoneNumber.phone_number } | |
| - end | |
| + factory :call do | |
| + project | |
| + job | |
| + provider | |
| + number { Faker::PhoneNumber.phone_number } | |
| + end | |
| end | |
| diff --git a/spec/factories/jobs.rb b/spec/factories/jobs.rb | |
| @@ -19,16 +19,16 @@ | |
| # | |
| FactoryGirl.define do | |
| - factory :job do | |
| - project | |
| - task 'dialer' | |
| - args "\x04\b{\t:\nrangeI\"\x0F7632458942\x06:\x06ET:\nlinesi\x… | |
| - status 'submitted' | |
| - error nil | |
| - range { Faker::PhoneNumber.phone_number } | |
| - cid_mask { Faker::PhoneNumber.phone_number } | |
| - seconds { Faker::Number.between(1, 299) } | |
| - lines { Faker::Number.between(1, 10000) } | |
| - end | |
| + factory :job do | |
| + project | |
| + task 'dialer' | |
| + args "\x04\b{\t:\nrangeI\"\x0F7632458942\x06:\x06ET:\nlinesi\x0F:\fseconds… | |
| + status 'submitted' | |
| + error nil | |
| + range { Faker::PhoneNumber.phone_number } | |
| + cid_mask { Faker::PhoneNumber.phone_number } | |
| + seconds { Faker::Number.between(1, 299) } | |
| + lines { Faker::Number.between(1, 10000) } | |
| + end | |
| end | |
| diff --git a/spec/factories/lines.rb b/spec/factories/lines.rb | |
| @@ -12,9 +12,9 @@ | |
| # | |
| FactoryGirl.define do | |
| - factory :line do | |
| - project | |
| - number { Faker::PhoneNumber.phone_number } | |
| - end | |
| + factory :line do | |
| + project | |
| + number { Faker::PhoneNumber.phone_number } | |
| + end | |
| end | |
| diff --git a/spec/factories/projects.rb b/spec/factories/projects.rb | |
| @@ -13,9 +13,9 @@ | |
| # | |
| FactoryGirl.define do | |
| - factory :project do | |
| - name { Faker::Lorem.sentence } | |
| - description { Faker::Lorem.sentence } | |
| - end | |
| + factory :project do | |
| + name { Faker::Lorem.sentence } | |
| + description { Faker::Lorem.sentence } | |
| + end | |
| end | |
| diff --git a/spec/factories/providers.rb b/spec/factories/providers.rb | |
| @@ -15,14 +15,14 @@ | |
| # | |
| FactoryGirl.define do | |
| - factory :provider do | |
| - name { Faker::Company.name } | |
| - host { Faker::Internet.ip_v4_address } | |
| - port { Faker::Number.between(1, 65535) } | |
| - user { Faker::Internet.user_name } | |
| - pass { Faker::Internet.password(10, 20) } | |
| - lines { Faker::Number.between(1, 254) } | |
| - enabled true | |
| - end | |
| + factory :provider do | |
| + name { Faker::Company.name } | |
| + host { Faker::Internet.ip_v4_address } | |
| + port { Faker::Number.between(1, 65535) } | |
| + user { Faker::Internet.user_name } | |
| + pass { Faker::Internet.password(10, 20) } | |
| + lines { Faker::Number.between(1, 254) } | |
| + enabled true | |
| + end | |
| end | |
| diff --git a/spec/factories/settings.rb b/spec/factories/settings.rb | |
| @@ -12,8 +12,8 @@ | |
| # | |
| FactoryGirl.define do | |
| - factory :setting, :class => 'Settings' do | |
| - var "CachedStuff" | |
| - end | |
| + factory :setting, :class => 'Settings' do | |
| + var "CachedStuff" | |
| + end | |
| end | |
| diff --git a/spec/factories/signature_fps.rb b/spec/factories/signature_fps.rb | |
| @@ -1,6 +1,6 @@ | |
| FactoryGirl.define do | |
| - factory :signature_fp do | |
| - | |
| - end | |
| + factory :signature_fp do | |
| + | |
| + end | |
| end | |
| diff --git a/spec/factories/signatures.rb b/spec/factories/signatures.rb | |
| @@ -14,13 +14,13 @@ | |
| # | |
| FactoryGirl.define do | |
| - factory :signature do | |
| - name { Faker::Commerce.product_name } | |
| - source { Faker::PhoneNumber.cell_phone } | |
| - description { Faker::Lorem.sentence } | |
| - category { Faker::Lorem.word } | |
| - line_type { Faker::Lorem.word } | |
| - risk { Faker::Lorem.word } | |
| - end | |
| + factory :signature do | |
| + name { Faker::Commerce.product_name } | |
| + source { Faker::PhoneNumber.cell_phone } | |
| + description { Faker::Lorem.sentence } | |
| + category { Faker::Lorem.word } | |
| + line_type { Faker::Lorem.word } | |
| + risk { Faker::Lorem.word } | |
| + end | |
| end | |
| diff --git a/spec/factories/users.rb b/spec/factories/users.rb | |
| @@ -24,12 +24,12 @@ | |
| # | |
| FactoryGirl.define do | |
| - factory :user do | |
| - login { Faker::Internet.user_name } | |
| - password 'RandomPass' | |
| - password_confirmation 'RandomPass' | |
| - enabled true | |
| - admin true | |
| - end | |
| + factory :user do | |
| + login { Faker::Internet.user_name } | |
| + password 'RandomPass' | |
| + password_confirmation 'RandomPass' | |
| + enabled true | |
| + admin true | |
| + end | |
| end | |
| diff --git a/spec/features/projects_spec.rb b/spec/features/projects_spec.rb | |
| @@ -2,24 +2,24 @@ require 'rails_helper' | |
| RSpec.feature "Projects", type: :feature do | |
| - before(:each) do | |
| - @user = create(:user) | |
| - create_user_session(@user) | |
| - end | |
| + before(:each) do | |
| + @user = create(:user) | |
| + create_user_session(@user) | |
| + end | |
| - it "list all existing projects" do | |
| - project = create(:project) | |
| - visit projects_path | |
| - expect(page).to have_content "WarVOX Projects" | |
| - within "#projects-table" do | |
| - expect(page).to have_content "Name" | |
| - expect(page).to have_content "Description" | |
| - expect(page).to have_content "Jobs" | |
| - expect(page).to have_content "Calls" | |
| - expect(page).to have_content "Analyzed" | |
| - expect(page).to have_content "Created" | |
| - expect(page).to have_content "Actions" | |
| - expect(page).to have_content project.name | |
| - end | |
| - end | |
| + it "list all existing projects" do | |
| + project = create(:project) | |
| + visit projects_path | |
| + expect(page).to have_content "WarVOX Projects" | |
| + within "#projects-table" do | |
| + expect(page).to have_content "Name" | |
| + expect(page).to have_content "Description" | |
| + expect(page).to have_content "Jobs" | |
| + expect(page).to have_content "Calls" | |
| + expect(page).to have_content "Analyzed" | |
| + expect(page).to have_content "Created" | |
| + expect(page).to have_content "Actions" | |
| + expect(page).to have_content project.name | |
| + end | |
| + end | |
| end | |
| diff --git a/spec/features/visitor/logins_spec.rb b/spec/features/visitor/login… | |
| @@ -1,58 +1,58 @@ | |
| require 'rails_helper' | |
| RSpec.feature "Logins", type: :feature do | |
| - it "login with valid credentials" do | |
| - user = create(:user) | |
| - visit login_path | |
| - within "#new_user_session" do | |
| - expect(page).to have_content "Username" | |
| - expect(page).to have_content "Password" | |
| - fill_in "user_session_login", with: user.login | |
| - fill_in "user_session_password", with: 'RandomPass' | |
| - click_button "Sign in" | |
| - end | |
| - within "div.content" do | |
| - expect(page).to have_content "WarVOX Projects" | |
| - end | |
| - end | |
| + it "login with valid credentials" do | |
| + user = create(:user) | |
| + visit login_path | |
| + within "#new_user_session" do | |
| + expect(page).to have_content "Username" | |
| + expect(page).to have_content "Password" | |
| + fill_in "user_session_login", with: user.login | |
| + fill_in "user_session_password", with: 'RandomPass' | |
| + click_button "Sign in" | |
| + end | |
| + within "div.content" do | |
| + expect(page).to have_content "WarVOX Projects" | |
| + end | |
| + end | |
| - it "failed login with invalid password valid username" do | |
| - user = create(:user) | |
| - visit login_path | |
| - within "#new_user_session" do | |
| - fill_in "user_session_login", with: user.login | |
| - fill_in "user_session_password", with: 'WrongPassword' | |
| - click_button "Sign in" | |
| - end | |
| - expect(page).to have_content "Password is not valid" | |
| - end | |
| + it "failed login with invalid password valid username" do | |
| + user = create(:user) | |
| + visit login_path | |
| + within "#new_user_session" do | |
| + fill_in "user_session_login", with: user.login | |
| + fill_in "user_session_password", with: 'WrongPassword' | |
| + click_button "Sign in" | |
| + end | |
| + expect(page).to have_content "Password is not valid" | |
| + end | |
| - it "failed login with invalid username valid password" do | |
| - user = create(:user) | |
| - visit login_path | |
| - within "#new_user_session" do | |
| - fill_in "user_session_login", with: user.login + "Wron… | |
| - fill_in "user_session_password", with: 'RandomPass' | |
| - click_button "Sign in" | |
| - end | |
| - expect(page).to have_content "Login is not valid" | |
| - end | |
| + it "failed login with invalid username valid password" do | |
| + user = create(:user) | |
| + visit login_path | |
| + within "#new_user_session" do | |
| + fill_in "user_session_login", with: user.login + "Wrong" | |
| + fill_in "user_session_password", with: 'RandomPass' | |
| + click_button "Sign in" | |
| + end | |
| + expect(page).to have_content "Login is not valid" | |
| + end | |
| - it "failed login with no input entered" do | |
| - visit login_path | |
| - within "#new_user_session" do | |
| - click_button "Sign in" | |
| - end | |
| - expect(page).to have_content "You did not provide any details … | |
| - end | |
| + it "failed login with no input entered" do | |
| + visit login_path | |
| + within "#new_user_session" do | |
| + click_button "Sign in" | |
| + end | |
| + expect(page).to have_content "You did not provide any details for authenti… | |
| + end | |
| - it "failed login with no password entered" do | |
| - user = create(:user) | |
| - visit login_path | |
| - within "#new_user_session" do | |
| - fill_in "user_session_login", with: user.login | |
| - click_button "Sign in" | |
| - end | |
| - expect(page).to have_content "Password cannot be blank" | |
| - end | |
| + it "failed login with no password entered" do | |
| + user = create(:user) | |
| + visit login_path | |
| + within "#new_user_session" do | |
| + fill_in "user_session_login", with: user.login | |
| + click_button "Sign in" | |
| + end | |
| + expect(page).to have_content "Password cannot be blank" | |
| + end | |
| end | |
| diff --git a/spec/models/call_medium_spec.rb b/spec/models/call_medium_spec.rb | |
| @@ -17,10 +17,10 @@ | |
| require 'rails_helper' | |
| RSpec.describe CallMedium, type: :model do | |
| - it { should belong_to(:call) } | |
| - it { should belong_to(:project) } | |
| + it { should belong_to(:call) } | |
| + it { should belong_to(:project) } | |
| - it "valid record" do | |
| - expect(build(:call_medium)).to be_valid | |
| - end | |
| + it "valid record" do | |
| + expect(build(:call_medium)).to be_valid | |
| + end | |
| end | |
| diff --git a/spec/models/call_spec.rb b/spec/models/call_spec.rb | |
| @@ -27,12 +27,12 @@ | |
| require 'rails_helper' | |
| RSpec.describe Call, type: :model do | |
| - it { should belong_to(:project) } | |
| - it { should belong_to(:provider) } | |
| - it { should belong_to(:job) } | |
| - it { should have_one(:call_medium).dependent(:delete) } | |
| + it { should belong_to(:project) } | |
| + it { should belong_to(:provider) } | |
| + it { should belong_to(:job) } | |
| + it { should have_one(:call_medium).dependent(:delete) } | |
| - it "valid record" do | |
| - expect(build(:call)).to be_valid | |
| - end | |
| + it "valid record" do | |
| + expect(build(:call)).to be_valid | |
| + end | |
| end | |
| diff --git a/spec/models/job_spec.rb b/spec/models/job_spec.rb | |
| @@ -21,12 +21,12 @@ | |
| require 'rails_helper' | |
| RSpec.describe Job, type: :model do | |
| - it { should belong_to(:project) } | |
| - it { should have_many(:calls) } | |
| + it { should belong_to(:project) } | |
| + it { should have_many(:calls) } | |
| - it { should validate_presence_of(:project_id) } | |
| + it { should validate_presence_of(:project_id) } | |
| - it "valid record" do | |
| - expect(build(:job)).to be_valid | |
| - end | |
| + it "valid record" do | |
| + expect(build(:job)).to be_valid | |
| + end | |
| end | |
| diff --git a/spec/models/line_spec.rb b/spec/models/line_spec.rb | |
| @@ -14,10 +14,10 @@ | |
| require 'rails_helper' | |
| RSpec.describe Line, type: :model do | |
| - it { should belong_to(:project) } | |
| - it { should have_many(:line_attributes).dependent(:delete_all) } | |
| + it { should belong_to(:project) } | |
| + it { should have_many(:line_attributes).dependent(:delete_all) } | |
| - it "valid record" do | |
| - expect(build(:line)).to be_valid | |
| - end | |
| + it "valid record" do | |
| + expect(build(:line)).to be_valid | |
| + end | |
| end | |
| diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb | |
| @@ -15,16 +15,16 @@ | |
| require 'rails_helper' | |
| RSpec.describe Project, type: :model do | |
| - it { should have_many(:lines).dependent(:delete_all) } | |
| - it { should have_many(:line_attributes).dependent(:delete_all) } | |
| - it { should have_many(:calls).dependent(:delete_all) } | |
| - it { should have_many(:call_media).dependent(:delete_all) } | |
| - it { should have_many(:jobs).dependent(:delete_all) } | |
| + it { should have_many(:lines).dependent(:delete_all) } | |
| + it { should have_many(:line_attributes).dependent(:delete_all) } | |
| + it { should have_many(:calls).dependent(:delete_all) } | |
| + it { should have_many(:call_media).dependent(:delete_all) } | |
| + it { should have_many(:jobs).dependent(:delete_all) } | |
| - it { should validate_presence_of(:name) } | |
| - it { should validate_uniqueness_of(:name) } | |
| + it { should validate_presence_of(:name) } | |
| + it { should validate_uniqueness_of(:name) } | |
| - it "valid record" do | |
| - expect(build(:project)).to be_valid | |
| - end | |
| + it "valid record" do | |
| + expect(build(:project)).to be_valid | |
| + end | |
| end | |
| diff --git a/spec/models/provider_spec.rb b/spec/models/provider_spec.rb | |
| @@ -17,20 +17,20 @@ | |
| require 'rails_helper' | |
| RSpec.describe Provider, type: :model do | |
| - ## TODO determine if association is unecessary | |
| - # the DialResult model does not exist | |
| - #it { should have_many(:dial_results) } | |
| + ## TODO determine if association is unecessary | |
| + # the DialResult model does not exist | |
| + #it { should have_many(:dial_results) } | |
| - it { should validate_presence_of(:name) } | |
| - it { should validate_presence_of(:host) } | |
| - it { should validate_presence_of(:port) } | |
| - it { should validate_presence_of(:user) } | |
| - it { should validate_presence_of(:pass) } | |
| - it { should validate_presence_of(:lines) } | |
| - it { should validate_numericality_of(:port).is_less_than(65536).is_gre… | |
| - it { should validate_numericality_of(:lines).is_less_than(255).is_grea… | |
| + it { should validate_presence_of(:name) } | |
| + it { should validate_presence_of(:host) } | |
| + it { should validate_presence_of(:port) } | |
| + it { should validate_presence_of(:user) } | |
| + it { should validate_presence_of(:pass) } | |
| + it { should validate_presence_of(:lines) } | |
| + it { should validate_numericality_of(:port).is_less_than(65536).is_greater_t… | |
| + it { should validate_numericality_of(:lines).is_less_than(255).is_greater_th… | |
| - it "valid record" do | |
| - expect(build(:provider)).to be_valid | |
| - end | |
| + it "valid record" do | |
| + expect(build(:provider)).to be_valid | |
| + end | |
| end | |
| diff --git a/spec/models/settings_spec.rb b/spec/models/settings_spec.rb | |
| @@ -14,7 +14,7 @@ | |
| require 'rails_helper' | |
| RSpec.describe Settings, type: :model do | |
| - it "valid record" do | |
| - expect(build(:setting)).to be_valid | |
| - end | |
| + it "valid record" do | |
| + expect(build(:setting)).to be_valid | |
| + end | |
| end | |
| diff --git a/spec/models/signature_spec.rb b/spec/models/signature_spec.rb | |
| @@ -16,11 +16,11 @@ | |
| require 'rails_helper' | |
| RSpec.describe Signature, type: :model do | |
| - ## TODO association may not be needed | |
| - # causes crash: PG::UndefinedTable: ERROR: relation "signature_fps" … | |
| - #it { should have_many(:signature_fps) } | |
| + ## TODO association may not be needed | |
| + # causes crash: PG::UndefinedTable: ERROR: relation "signature_fps" does n… | |
| + #it { should have_many(:signature_fps) } | |
| - it "valid record" do | |
| - expect(build(:signature)).to be_valid | |
| - end | |
| + it "valid record" do | |
| + expect(build(:signature)).to be_valid | |
| + end | |
| end | |
| diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb | |
| @@ -26,10 +26,10 @@ | |
| require 'rails_helper' | |
| RSpec.describe User, type: :model do | |
| - it { should validate_length_of(:password).is_at_least(8) } | |
| - it { should validate_length_of(:password_confirmation).is_at_least(8) } | |
| + it { should validate_length_of(:password).is_at_least(8) } | |
| + it { should validate_length_of(:password_confirmation).is_at_least(8) } | |
| - it 'valid record' do | |
| - expect(build(:user)).to be_valid | |
| - end | |
| + it 'valid record' do | |
| + expect(build(:user)).to be_valid | |
| + end | |
| end | |
| diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb | |
| @@ -27,29 +27,29 @@ Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| req… | |
| ActiveRecord::Migration.maintain_test_schema! | |
| RSpec.configure do |config| | |
| - # FactoryGirl Syntax | |
| - config.include FactoryGirl::Syntax::Methods | |
| + # FactoryGirl Syntax | |
| + config.include FactoryGirl::Syntax::Methods | |
| - # Remove this line if you're not using ActiveRecord or ActiveRecord fi… | |
| - config.fixture_path = "#{::Rails.root}/spec/fixtures" | |
| + # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures | |
| + config.fixture_path = "#{::Rails.root}/spec/fixtures" | |
| - # If you're not using ActiveRecord, or you'd prefer not to run each of… | |
| - # examples within a transaction, remove the following line or assign f… | |
| - # instead of true. | |
| - config.use_transactional_fixtures = true | |
| + # If you're not using ActiveRecord, or you'd prefer not to run each of your | |
| + # examples within a transaction, remove the following line or assign false | |
| + # instead of true. | |
| + config.use_transactional_fixtures = true | |
| - # RSpec Rails can automatically mix in different behaviours to your te… | |
| - # based on their file location, for example enabling you to call `get`… | |
| - # `post` in specs under `spec/controllers`. | |
| - # | |
| - # You can disable this behaviour by removing the line below, and inste… | |
| - # explicitly tag your specs with their type, e.g.: | |
| - # | |
| - # RSpec.describe UsersController, :type => :controller do | |
| - # # ... | |
| - # end | |
| - # | |
| - # The different available types are documented in the features, such a… | |
| - # https://relishapp.com/rspec/rspec-rails/docs | |
| - config.infer_spec_type_from_file_location! | |
| + # RSpec Rails can automatically mix in different behaviours to your tests | |
| + # based on their file location, for example enabling you to call `get` and | |
| + # `post` in specs under `spec/controllers`. | |
| + # | |
| + # You can disable this behaviour by removing the line below, and instead | |
| + # explicitly tag your specs with their type, e.g.: | |
| + # | |
| + # RSpec.describe UsersController, :type => :controller do | |
| + # # ... | |
| + # end | |
| + # | |
| + # The different available types are documented in the features, such as in | |
| + # https://relishapp.com/rspec/rspec-rails/docs | |
| + config.infer_spec_type_from_file_location! | |
| end | |
| diff --git a/spec/support/auth_logic_helpers.rb b/spec/support/auth_logic_helpe… | |
| @@ -1,20 +1,20 @@ | |
| module Authlogic | |
| - module TestHelper | |
| - def create_user_session(user) | |
| - visit login_path | |
| - within "#new_user_session" do | |
| - expect(page).to have_content "Username" | |
| - expect(page).to have_content "Password" | |
| - fill_in "user_session_login", with: user.login | |
| - fill_in "user_session_password", with: user.pa… | |
| - click_button "Sign in" | |
| - end | |
| - end | |
| - end | |
| + module TestHelper | |
| + def create_user_session(user) | |
| + visit login_path | |
| + within "#new_user_session" do | |
| + expect(page).to have_content "Username" | |
| + expect(page).to have_content "Password" | |
| + fill_in "user_session_login", with: user.login | |
| + fill_in "user_session_password", with: user.password | |
| + click_button "Sign in" | |
| + end | |
| + end | |
| + end | |
| end | |
| # Make this available to just the request and feature specs | |
| RSpec.configure do |config| | |
| - config.include Authlogic::TestHelper, type: :request | |
| - config.include Authlogic::TestHelper, type: :feature | |
| + config.include Authlogic::TestHelper, type: :request | |
| + config.include Authlogic::TestHelper, type: :feature | |
| end | |
| \ No newline at end of file |