Tabs to spaces - warvox - VoIP based wardialing tool, forked from rapid7/warvox.
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) README
---
(DIR) commit 6fc7895fc496df7a5acdc2f22d6e60fb70f1ca65
(DIR) parent 922c8b67c39f72646441de35353bb87401adea71
(HTM) Author: HD Moore <x@hdm.io>
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(-)
---
(DIR) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
@@ -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.record
- 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 this page"
- 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 this page"
- 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_i).first
- elsif session[:project_id]
- @project = Project.where(:id => session[:project_id].to_i).first
- 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)
- session[:project_id] = @project.id
- end
+ if @project and @project.id and not (session[:project_id] and session[:project_id] == @project.id)
+ session[:project_id] = @project.id
+ end
- true
- end
+ true
+ end
end
(DIR) diff --git a/app/controllers/calls_controller.rb b/app/controllers/calls_controller.rb
@@ -4,9 +4,9 @@ class CallsController < ApplicationController
# GET /calls.xml
def index
@jobs = @project.jobs.order('id DESC').where('task = ? AND completed_at IS NOT NULL', 'dialer').paginate(
- :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 answered = ?', params[:id], false]),
- :Busy => Call.count(:conditions =>['job_id = ? and busy = ?', params[:id], true]),
- :Answered => Call.count(:conditions =>['job_id = ? and answered = ?', params[:id], true]),
- }
-
- 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 = ?', params[:id], false]),
+ :Busy => Call.count(:conditions =>['job_id = ? and busy = ?', params[:id], true]),
+ :Answered => Call.count(:conditions =>['job_id = ? and answered = ?', params[:id], true]),
+ }
+
+ 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' }
(DIR) diff --git a/app/controllers/home_controller.rb b/app/controllers/home_controller.rb
@@ -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 NULL').count > 0 )
- 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 > 0 )
+ end
end
(DIR) diff --git a/app/controllers/jobs_controller.rb b/app/controllers/jobs_controller.rb
@@ -1,346 +1,346 @@
class JobsController < ApplicationController
- require 'shellwords'
-
- def index
- @reload_interval = 20000
-
- @submitted_jobs = Job.where(:status => ['submitted', 'scheduled'], :completed_at => nil)
- @active_jobs = Job.where(:status => 'running', :completed_at => nil)
- @inactive_jobs = Job.order('id DESC').where('status NOT IN (?)', ['submitted', 'scheduled', 'running']).paginate(
- :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 completed_at IS NOT NULL', 'dialer', 'import').paginate(
- :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).limit(@results_per_page).offset(@results_offset).order(calls_sort_option)
- @results_total_display_count = @job.calls.includes(:provider).where(@search_conditions).count()
- end
-
- respond_to do |format|
- format.html
- format.json {
- render :content_type => 'application/json', :json => render_to_string(:partial => 'view_results', :results => @results, :call_results => @call_results )
- }
- 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 => true,
- :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_ids],
- :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_ids],
- :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_ids],
- :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
+ require 'shellwords'
+
+ def index
+ @reload_interval = 20000
+
+ @submitted_jobs = Job.where(:status => ['submitted', 'scheduled'], :completed_at => nil)
+ @active_jobs = Job.where(:status => 'running', :completed_at => nil)
+ @inactive_jobs = Job.order('id DESC').where('status NOT IN (?)', ['submitted', 'scheduled', 'running']).paginate(
+ :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 completed_at IS NOT NULL', 'dialer', 'import').paginate(
+ :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).limit(@results_per_page).offset(@results_offset).order(calls_sort_option)
+ @results_total_display_count = @job.calls.includes(:provider).where(@search_conditions).count()
+ end
+
+ respond_to do |format|
+ format.html
+ format.json {
+ render :content_type => 'application/json', :json => render_to_string(:partial => 'view_results', :results => @results, :call_results => @call_results )
+ }
+ 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 => true,
+ :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_ids],
+ :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_ids],
+ :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_ids],
+ :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
(DIR) diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
@@ -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', :completed_at => nil)
- @inactive_jobs = @project.jobs.order('id DESC').where('status NOT IN (?)', ['submitted', 'scheduled', 'running']).paginate(
- :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 NOT NULL').count },
- :voice => { :cnt => @project.lines.where(:line_type => 'voice').count },
- :voicemail => { :cnt => @project.lines.where(:line_type => 'voicemail').count },
- :fax => { :cnt => @project.lines.where(:line_type => 'fax').count },
- :modem => { :cnt => @project.lines.where(:line_type => 'modem').count }
- }
-
- 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][:cnt])
- @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 => @new_project }
- else
- format.html { render :action => "new" }
- format.xml { render :xml => @new_project.errors, :status => :unprocessable_entity }
- 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 => :unprocessable_entity }
- 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 => nil)
+ @inactive_jobs = @project.jobs.order('id DESC').where('status NOT IN (?)', ['submitted', 'scheduled', 'running']).paginate(
+ :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 NOT NULL').count },
+ :voice => { :cnt => @project.lines.where(:line_type => 'voice').count },
+ :voicemail => { :cnt => @project.lines.where(:line_type => 'voicemail').count },
+ :fax => { :cnt => @project.lines.where(:line_type => 'fax').count },
+ :modem => { :cnt => @project.lines.where(:line_type => 'modem').count }
+ }
+
+ 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][:cnt])
+ @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 => @new_project }
+ else
+ format.html { render :action => "new" }
+ format.xml { render :xml => @new_project.errors, :status => :unprocessable_entity }
+ 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 => :unprocessable_entity }
+ 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
(DIR) diff --git a/app/controllers/providers_controller.rb b/app/controllers/providers_controller.rb
@@ -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][:pass] == "********"
- params[:provider].delete(:pass)
- end
+ # Dont set the password if its the placeholder
+ if params[:provider] and params[:provider][:pass] and params[:provider][:pass] == "********"
+ params[:provider].delete(:pass)
+ end
respond_to do |format|
if @provider.update_attributes(params[:provider])
(DIR) diff --git a/app/controllers/user_sessions_controller.rb b/app/controllers/user_sessions_controller.rb
@@ -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
(DIR) 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_to_color(pct)};'>
+ %Q|<span class="badge fwd_match_span" style='background-color: #{pct_to_color(pct)};'>
- #{"%.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).to_i }px; background-color: #{pct_to_color(pct)};'>#{pct}%</span>|
+ %Q|<span class="rev_match_span" style='padding-left: #{ (pct.to_i * 2).to_i }px; background-color: #{pct_to_color(pct)};'>#{pct}%</span>|
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
(DIR) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
@@ -1,178 +1,178 @@
# Methods added to this helper will be available to all templates in the application.
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.value"> }
- nvpairs.each do |pair|
- _html << %{<option value="#{h(pair[:scope])}" }
- if params[:show] == pair[:scope] || ((params[:show].nil? || params[:show].empty?) && pair[:scope] == "all")
- _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.value"> }
- nvpairs.each do |pair|
- _html << %{<option value="#{h(pair[:scope])}" }
- if params[:match_scope] == pair[:scope] || ((params[:match_scope].nil? || params[:match_scope].empty?) && pair[:scope] == "job")
- _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.length > 0){$elem.focus()}")
- 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, :length => 20)) + raw(": </div> ")
- ttip << raw("<div class='task_args_val'>") + h(truncate((v.to_s), :length => 20)) + raw(" </div>")
- end
- ttip << raw("</div>\n")
- outp = raw("<span class='xpopover' rel='popover' data-title=\"#{job.task.capitalize} Task ##{job.id}\" data-content=\"#{ttip}\">#{h job.task.capitalize}</span>")
- 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 Details\" data-content=\"#{ttip}\">#{h job.status.capitalize}</span>")
- 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/show.js.
- #
- # @return [void]
- def include_view_javascript
- #
- # Sprockets treats index.js as special, so the js for the index action must be called _index.js instead.
- # 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', 'javascripts', controller_path)
- 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}')){ submit_checkboxes_to('#{path}','#{form_authenticity_token}')}else{return false;}" }))
- else
- link_to(name, "#", html.merge({:onclick => "submit_checkboxes_to('#{path}','#{form_authenticity_token}')" }))
- 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
+ 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].empty?) && pair[:scope] == "all")
+ _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.value"> }
+ nvpairs.each do |pair|
+ _html << %{<option value="#{h(pair[:scope])}" }
+ if params[:match_scope] == pair[:scope] || ((params[:match_scope].nil? || params[:match_scope].empty?) && pair[:scope] == "job")
+ _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.length > 0){$elem.focus()}")
+ 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, :length => 20)) + raw(": </div> ")
+ ttip << raw("<div class='task_args_val'>") + h(truncate((v.to_s), :length => 20)) + raw(" </div>")
+ end
+ ttip << raw("</div>\n")
+ outp = raw("<span class='xpopover' rel='popover' data-title=\"#{job.task.capitalize} Task ##{job.id}\" data-content=\"#{ttip}\">#{h job.task.capitalize}</span>")
+ 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 Details\" data-content=\"#{ttip}\">#{h job.status.capitalize}</span>")
+ 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/show.js.
+ #
+ # @return [void]
+ def include_view_javascript
+ #
+ # Sprockets treats index.js as special, so the js for the index action must be called _index.js instead.
+ # 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', 'javascripts', controller_path)
+ 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}')){ submit_checkboxes_to('#{path}','#{form_authenticity_token}')}else{return false;}" }))
+ else
+ link_to(name, "#", html.merge({:onclick => "submit_checkboxes_to('#{path}','#{form_authenticity_token}')" }))
+ 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
(DIR) 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
(DIR) 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_column => :created_at, :cacheable => false
- reportable :daily, :aggregation => :count, :grouping => :day, :date_column => :created_at, :cacheable => false
- reportable :weeky, :aggregation => :count, :grouping => :week, :date_column => :created_at, :cacheable => false
- reportable :monthly, :aggregation => :count, :grouping => :month, :date_column => :created_at, :cacheable => false
-
- 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 global"
- end
- if record.scope == "job" and Job.where(:id => record.target_id.to_i, :task => ['import', 'dialer']).count == 0
- record.errors[:job_id] << "The job_id is not valid"
- end
- if record.scope == "project" and Project.where(:id => record.target_id.to_i).count == 0
- record.errors[:project_id] << "The project_id is not valid"
- end
- if record.scope == "calls" and (record.target_ids.nil? or record.target_ids.length == 0)
- 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 => Time.now, :status => 'completed')
- 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 = {
+ reportable :hourly, :aggregation => :count, :grouping => :hour, :date_column => :created_at, :cacheable => false
+ reportable :daily, :aggregation => :count, :grouping => :day, :date_column => :created_at, :cacheable => false
+ reportable :weeky, :aggregation => :count, :grouping => :week, :date_column => :created_at, :cacheable => false
+ reportable :monthly, :aggregation => :count, :grouping => :month, :date_column => :created_at, :cacheable => false
+
+ 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 global"
+ end
+ if record.scope == "job" and Job.where(:id => record.target_id.to_i, :task => ['import', 'dialer']).count == 0
+ record.errors[:job_id] << "The job_id is not valid"
+ end
+ if record.scope == "project" and Project.where(:id => record.target_id.to_i).count == 0
+ record.errors[:project_id] << "The project_id is not valid"
+ end
+ if record.scope == "calls" and (record.target_ids.nil? or record.target_ids.length == 0)
+ 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 => Time.now, :status => 'completed')
+ 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 / 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 }
}
- $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_completed_at < ?', self.details[:target_id], self.created_at, tend).count() / tlen
- 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_completed_at < ?', self.details[:target_id], self.created_at, tend).count() / tlen
+ when 'import'
+ Call.where('job_id = ?', self.id).count() / tlen
+ else
+ 0
+ end
+ end
end
(DIR) 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.project_id, name: name).first_or_create
- 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, name: name).first_or_create
+ 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
(DIR) 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
(DIR) 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 calls/jobs/lines
- 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/jobs/lines
+ 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
(DIR) 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 => 0
- 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
(DIR) 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
(DIR) 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
(DIR) 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
(DIR) 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 => 8
- c.merge_validates_length_of_password_confirmation_field_options :minimum => 8
- 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 => 8
+ c.logged_in_timeout = 1.day
+ end
end
(DIR) 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
(DIR) 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)] + [*(0x7b .. 0x7e)] ).flatten.pack("C*")
- set << "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
- str = ''
- cnt = 0
- while not (str.length >= 8 and str =~ /[A-Za-z]/ and str =~ /[0-9]/ and str =~ /[\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x3a\x3b\x3c\x3d\x3e\x3f\x5b\x5c\x5d\x5e\x5f\x60\x7b\x7c\x7d\x7e]/)
- 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 .. 0x7e)] ).flatten.pack("C*")
+ set << "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ str = ''
+ cnt = 0
+ while not (str.length >= 8 and str =~ /[A-Za-z]/ and str =~ /[0-9]/ and str =~ /[\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x3a\x3b\x3c\x3d\x3e\x3f\x5b\x5c\x5d\x5e\x5f\x60\x7b\x7c\x7d\x7e]/)
+ 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 '#{randpass}' ..."
- $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 your password on login."
+ $stdout.puts "[*] User #{user.login} has been created, please change your password on login."
else
- $stdout.puts "[*] That user account already exists, please try 'resetpw' script"
+ $stdout.puts "[*] That user account already exists, please try 'resetpw' script"
end
(DIR) 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
(DIR) 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
(DIR) 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
(DIR) 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
(DIR) 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'))
(DIR) 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 => 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
+ 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
(DIR) 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 peak_freq }
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
(DIR) 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[:called_number] and opts[:output])
- $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
(DIR) 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[]) / icount('#{fp}'::int[])::float ) * 100.0 ) AS matchscore " +
- "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}'::int[])::float ) * 100.0 ) AS matchscore " +
- "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
+ 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}'::int[])::float ) * 100.0 ) AS matchscore " +
+ "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}'::int[])::float ) * 100.0 ) AS matchscore " +
+ "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
(DIR) 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 '#{project.name}' ##{project.id}"
+ 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 '#{project.name}' ##{project.id}"
end
job.update_progress(100)
(DIR) 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)] + [*(0x7b .. 0x7e)] ).flatten.pack("C*")
- set << "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
- str = ''
- cnt = 0
- while not (str.length >= 8 and str =~ /[A-Za-z]/ and str =~ /[0-9]/ and str =~ /[\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x3a\x3b\x3c\x3d\x3e\x3f\x5b\x5c\x5d\x5e\x5f\x60\x7b\x7c\x7d\x7e]/)
- 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 .. 0x7e)] ).flatten.pack("C*")
+ set << "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+ str = ''
+ cnt = 0
+ while not (str.length >= 8 and str =~ /[A-Za-z]/ and str =~ /[0-9]/ and str =~ /[\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x3a\x3b\x3c\x3d\x3e\x3f\x5b\x5c\x5d\x5e\x5f\x60\x7b\x7c\x7d\x7e]/)
+ 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
(DIR) 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"
(DIR) 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 server..."
- 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}")
(DIR) 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', completed_at: Time.now })
- end
- exit(0)
+ if @task
+ @task.stop() rescue nil
+ end
+ if @job
+ Job.where(id: @job_id).update_all({ status: 'stopped', completed_at: Time.now })
+ 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: 'error' })
+ 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: #{e.class} #{e} #{e.backtrace}")
- Job.where(id: @job.id).update_all({ error: "Exception: #{e.class} #{e}", status: 'error', completed_at: Time.now.utc })
+ WarVOX::Log.warn("Worker #{@job.id} #{@job.task} threw an exception: #{e.class} #{e} #{e.backtrace}")
+ Job.where(id: @job.id).update_all({ error: "Exception: #{e.class} #{e}", status: 'error', completed_at: Time.now.utc })
end
(DIR) 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}, { :id => @jobs.map{|j| j[:id] } })
+ # Update the database
+ Job.update_all({ :status => "stopped", :completed_at => Time.now.utc}, { :id => @jobs.map{|j| j[: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 PID #{pid}")
- 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} completed jobs")
+ WarVOX::Log.debug("Worker Manager is clearing #{dead_pids.length} completed jobs")
- @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: Time.now.utc})
+ # Mark failed/crashed jobs as completed
+ Job.where(id: dead_jids, completed_at: nil).update_all({completed_at: Time.now.utc})
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 ? AND id NOT IN (?)", Socket.gethostname + "^%", jids)
- else
- stale = Job.where("completed_at IS NULL AND locked_by LIKE ?", Socket.gethostname + "^%")
- 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 abandoned")
- Job.where(:id => dead).update_all({locked_by: nil, status: 'abandoned'})
- 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 (?)", Socket.gethostname + "^%", jids)
+ else
+ stale = Job.where("completed_at IS NULL AND locked_by LIKE ?", Socket.gethostname + "^%")
+ 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 abandoned")
+ 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).first
- 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: @cookie, locked_at: Time.now.utc, status: 'scheduled'})
+ # Try to get a lock on this job
+ Job.where(id: j.id, locked_by: nil).update_all({locked_by: @cookie, locked_at: Time.now.utc, status: 'scheduled'})
- # See if we actually got the lock
- j = Job.where(id: j.id, status: 'scheduled', locked_by: @cookie).limit(1).first
+ # See if we actually got the lock
+ j = Job.where(id: j.id, status: 'scheduled', locked_by: @cookie).limit(1).first
- # 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
(DIR) 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
#
(DIR) 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
(DIR) diff --git a/db/migrate/20121228171549_initial_schema.rb b/db/migrate/20121228171549_initial_schema.rb
@@ -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 => true
-
- create_table 'users' do |t|
- t.string :login, :null => false # optional, you can use email instead, or both
- t.string :email, :null => true # optional, you can use login instead, or both
- t.string :crypted_password, :null => false # optional, see below
- t.string :password_salt, :null => false # optional, but highly recommended
- t.string :persistence_token, :null => false # required
- t.string :single_access_token, :null => false # optional, see Authlogic::Session::Params
- t.string :perishable_token, :null => false # optional, see Authlogic::Session::Perishability
-
- # Magic columns, just like ActiveRecord's created_at and updated_at. These are automatically maintained by Authlogic if they are present.
- t.integer :login_count, :null => false, :default => 0 # optional, see Authlogic::Session::MagicColumns
- t.integer :failed_login_count, :null => false, :default => 0 # optional, see Authlogic::Session::MagicColumns
- t.datetime :last_request_at # optional, see Authlogic::Session::MagicColumns
- t.datetime :current_login_at # optional, see Authlogic::Session::MagicColumns
- t.datetime :last_login_at # optional, see Authlogic::Session::MagicColumns
- t.string :current_login_ip # optional, see Authlogic::Session::MagicColumns
- t.string :last_login_ip # optional, see Authlogic::Session::MagicColumns
-
- 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
+ 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 # optional, you can use email instead, or both
+ t.string :email, :null => true # optional, you can use login instead, or both
+ t.string :crypted_password, :null => false # optional, see below
+ t.string :password_salt, :null => false # optional, but highly recommended
+ t.string :persistence_token, :null => false # required
+ t.string :single_access_token, :null => false # optional, see Authlogic::Session::Params
+ t.string :perishable_token, :null => false # optional, see Authlogic::Session::Perishability
+
+ # Magic columns, just like ActiveRecord's created_at and updated_at. These are automatically maintained by Authlogic if they are present.
+ t.integer :login_count, :null => false, :default => 0 # optional, see Authlogic::Session::MagicColumns
+ t.integer :failed_login_count, :null => false, :default => 0 # optional, see Authlogic::Session::MagicColumns
+ t.datetime :last_request_at # optional, see Authlogic::Session::MagicColumns
+ t.datetime :current_login_at # optional, see Authlogic::Session::MagicColumns
+ t.datetime :last_login_at # optional, see Authlogic::Session::MagicColumns
+ t.string :current_login_ip # optional, see Authlogic::Session::MagicColumns
+ t.string :last_login_ip # optional, see Authlogic::Session::MagicColumns
+
+ 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
(DIR) 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
(DIR) 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_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() failed"
- 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.samples.length - (i * 400)])
- 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 then work on it
- 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 permutation
- 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 { |c| t[i+3].each { |d| sigs << [a,b,c,d] } } } } }
- 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 strength
- a[1] <=> b[1]
- }.reverse[0,freq_cnt].sort { |a,b| # take the top 20 and sort by frequency
- a[0] <=> b[0]
- }.map {|a| [a[0].round, a[1].round ] } # round to whole numbers
- 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
+ @@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() failed"
+ 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.samples.length - (i * 400)])
+ 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 then work on it
+ 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 permutation
+ 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 { |c| t[i+3].each { |d| sigs << [a,b,c,d] } } } } }
+ 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 strength
+ a[1] <=> b[1]
+ }.reverse[0,freq_cnt].sort { |a,b| # take the top 20 and sort by frequency
+ a[0] <=> b[0]
+ }.map {|a| [a[0].round, a[1].round ] } # round to whole numbers
+ 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
(DIR) 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+:/).length rescue 1
-
- 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
+ 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 rescue 1
+
+ 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
(DIR) 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 == job_id)
- 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 == 'completed')
- @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_job.start }
- 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
(DIR) 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 => true, :busy => false}
- else
- query = {:id => @conf[:target_ids], :answered => true, :busy => false, :analysis_started_at => nil}
- end
- when 'job'
- if @conf[:force]
- query = {:job_id => @conf[:target_id], :answered => true, :busy => false}
- else
- query = {:job_id => @conf[:target_id], :answered => true, :busy => false, :analysis_started_at => nil}
- end
- when 'project'
- if @conf[:force]
- query = {:project_id => @conf[:target_id], :answered => true, :busy => false}
- else
- query = {:project_id => @conf[:target_id], :answered => true, :busy => false, :analysis_started_at => nil}
- end
- when 'global'
- if @conf[:force]
- query = {:answered => true, :busy => false}
- else
- query = {:answered => true, :busy => false, :analysis_started_at => nil}
- 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 mid-iteration
- 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.connection_pool.with_connection { run_analyze_call(c,j) }}
- 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("\n"))
- 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.backtrace}"
- 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 lines")
- plotter.puts("set output \"#{png_big_dots.path}\"")
- plotter.puts("plot \"#{datfile.path}\" using 1:2 title \"#{num}\" with dots")
-
-
- 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 #{maxf.round}hz\" with lines")
-
-
- 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.unlink; x.close }
-
- 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_mp3.path} >/dev/null 2>&1")
-
- 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, :analysis_started_at => nil}
+ end
+ when 'job'
+ if @conf[:force]
+ query = {:job_id => @conf[:target_id], :answered => true, :busy => false}
+ else
+ query = {:job_id => @conf[:target_id], :answered => true, :busy => false, :analysis_started_at => nil}
+ end
+ when 'project'
+ if @conf[:force]
+ query = {:project_id => @conf[:target_id], :answered => true, :busy => false}
+ else
+ query = {:project_id => @conf[:target_id], :answered => true, :busy => false, :analysis_started_at => nil}
+ end
+ when 'global'
+ if @conf[:force]
+ query = {:answered => true, :busy => false}
+ else
+ query = {:answered => true, :busy => false, :analysis_started_at => nil}
+ 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 mid-iteration
+ 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.connection_pool.with_connection { run_analyze_call(c,j) }}
+ 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("\n"))
+ 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.backtrace}"
+ 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 lines")
+ plotter.puts("set output \"#{png_big_dots.path}\"")
+ plotter.puts("plot \"#{datfile.path}\" using 1:2 title \"#{num}\" with dots")
+
+
+ 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 #{maxf.round}hz\" with lines")
+
+
+ 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.unlink; x.close }
+
+ 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_mp3.path} >/dev/null 2>&1")
+
+ 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
(DIR) 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
(DIR) 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} 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.inspect}"
- 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) * 100)
- 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.inspect}"
+ 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) * 100)
+ 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
(DIR) 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(/[^\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 number
- 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 incrementing number
- 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 number
+ 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 incrementing number
+ j += 1
+ end
+ end
+ res[number] = {}
+ end
+
+ end
- return res.keys.sort
- end
+ return res.keys.sort
+ end
end
end
(DIR) 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.oseq, call.iseq, IAX_TYPE_IAX, data ), false )
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, call.iseq, IAX_TYPE_IAX, data ) )
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, call.iseq, IAX_TYPE_IAX, data ) )
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.oseq, call.iseq, IAX_TYPE_IAX, data ) )
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.oseq, call.iseq, IAX_TYPE_IAX, data ) )
end
(DIR) 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
(DIR) 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
(DIR) 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\x0F:\fsecondsi::\rcid_maskI\"\tSELF\x06;\x06T"
- 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:\fsecondsi::\rcid_maskI\"\tSELF\x06;\x06T"
+ 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
(DIR) 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
(DIR) 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
(DIR) 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
(DIR) 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
(DIR) 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
(DIR) 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
(DIR) 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
(DIR) 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
(DIR) diff --git a/spec/features/visitor/logins_spec.rb b/spec/features/visitor/logins_spec.rb
@@ -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 + "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 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 for authentication."
- 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 authentication."
+ 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
(DIR) 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
(DIR) 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
(DIR) 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
(DIR) 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
(DIR) 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
(DIR) 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_greater_than(0) }
- it { should validate_numericality_of(:lines).is_less_than(255).is_greater_than(0) }
+ 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_than(0) }
+ it { should validate_numericality_of(:lines).is_less_than(255).is_greater_than(0) }
- it "valid record" do
- expect(build(:provider)).to be_valid
- end
+ it "valid record" do
+ expect(build(:provider)).to be_valid
+ end
end
(DIR) 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
(DIR) 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" does not exist
- #it { should have_many(:signature_fps) }
+ ## TODO association may not be needed
+ # causes crash: PG::UndefinedTable: ERROR: relation "signature_fps" does not exist
+ #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
(DIR) 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
(DIR) 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| require f }
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 fixtures
- 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 your
- # examples within a transaction, remove the following line or assign false
- # 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 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!
+ # 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
(DIR) diff --git a/spec/support/auth_logic_helpers.rb b/spec/support/auth_logic_helpers.rb
@@ -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.password
- 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