Sync recent changes - warvox - VoIP based wardialing tool, forked from rapid7/warvox.
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
       ---
 (DIR) commit ef6496ad1da956df421e15ce6c2eadc06044f3d7
 (DIR) parent aa07abc9e7aaabc1723625d72daac6dd9e1edfcd
 (HTM) Author: HD Moore <hd_moore@rapid7.com>
       Date:   Tue,  1 Jan 2013 21:07:16 -0600
       
       Sync recent changes
       
       Diffstat:
         M app/assets/stylesheets/application… |       1 +
         M app/assets/stylesheets/bootstrap_a… |      32 +++++++++++++++++++++----------
         M app/controllers/analyze_controller… |      34 ++++++++++++++++----------------
         M app/controllers/application_contro… |       9 ++++++---
         D app/controllers/dial_jobs_controll… |     107 -------------------------------
         D app/controllers/dial_results_contr… |     179 -------------------------------
         M app/controllers/home_controller.rb  |       8 ++++----
         M app/controllers/projects_controlle… |      13 ++++++-------
         M app/controllers/providers_controll… |      41 ++++++++++---------------------
         M app/helpers/application_helper.rb   |      29 +++++++++++++++++++++++++++++
         D app/helpers/dial_jobs_helper.rb     |       2 --
         D app/helpers/dial_results_helper.rb  |       2 --
         M app/models/call.rb                  |       2 +-
         M app/models/call_medium.rb           |       1 +
         D app/models/dial_job.rb              |      49 -------------------------------
         M app/models/job.rb                   |      74 ++++++++++++++++++++++++++++++-
         M app/models/line.rb                  |      15 ++++++++++++++-
         M app/models/line_attribute.rb        |       1 +
         M app/models/project.rb               |      12 +++++++++---
         M app/views/analyze/index.html.erb    |      25 ++++++++++++-------------
         M app/views/analyze/view.html.erb     |      28 ++++++++++++++--------------
         M app/views/analyze/view_matches.htm… |      56 ++++++++++++++++----------------
         D app/views/dial_jobs/edit.html.erb   |      24 ------------------------
         D app/views/dial_jobs/index.html.erb  |      81 ------------------------------
         D app/views/dial_jobs/new.html.erb    |      35 -------------------------------
         D app/views/dial_jobs/run.html.erb    |       5 -----
         D app/views/dial_jobs/show.html.erb   |      39 -------------------------------
         D app/views/dial_results/analyze.htm… |      27 ---------------------------
         D app/views/dial_results/edit.html.e… |      48 -------------------------------
         D app/views/dial_results/index.html.… |      57 -------------------------------
         D app/views/dial_results/new.html.erb |      43 ------------------------------
         D app/views/dial_results/show.html.e… |      48 -------------------------------
         D app/views/dial_results/view.html.e… |      58 ------------------------------
         M app/views/home/about.html.erb       |       8 ++++----
         M app/views/home/check.html.erb       |       8 ++++----
         M app/views/home/index.html.erb       |       6 +++---
         M app/views/layouts/application.html… |      33 ++++++++++++++++++-------------
         M app/views/projects/edit.html.erb    |       9 ++++-----
         M app/views/projects/index.html.erb   |      10 ++++++++--
         M app/views/projects/new.html.erb     |      10 +---------
         M app/views/providers/edit.html.erb   |      47 ++++++++-----------------------
         M app/views/providers/index.html.erb  |      32 +++++++++++++------------------
         M app/views/providers/new.html.erb    |      41 ++++++++-----------------------
         D app/views/providers/show.html.erb   |      38 -------------------------------
         M app/views/user_sessions/new.html.e… |       2 +-
         M bin/export_audio.rb                 |      80 ++++++++++++++++++++-----------
         M bin/iaxrecord.rb                    |      37 ++++++++++++++++++-------------
         M bin/import_audio.rb                 |       9 ++++-----
         M bin/warvox.rb                       |      14 +++++++++++++-
         M config/routes.rb                    |      48 ++++++++++++++++---------------
         M db/migrate/20121228171549_initial_… |       3 ++-
         M db/schema.rb                        |       6 ++++--
         M lib/warvox.rb                       |       5 ++++-
         M lib/warvox/config.rb                |       8 ++++++++
         M lib/warvox/jobs/dialer.rb           |       8 ++++----
       
       55 files changed, 466 insertions(+), 1181 deletions(-)
       ---
 (DIR) diff --git a/app/assets/stylesheets/application.css.scss b/app/assets/stylesheets/application.css.scss
       @@ -6,5 +6,6 @@
         *= require_self
         *= require formtastic
         *= require formtastic-bootstrap
       + *= require formtastic-overrides
         *= require dataTables/jquery.dataTables.bootstrap
        */
 (DIR) diff --git a/app/assets/stylesheets/bootstrap_and_overrides.css.less b/app/assets/stylesheets/bootstrap_and_overrides.css.less
       @@ -39,6 +39,28 @@ body {
        @navbarBackgroundHighlight: #4A1C04;
        
        
       +.progress_pct {
       +        color: @darkGray;
       +        margin-left: 10px;
       +}
       +
       +.task_args_formatted {
       +        width: 400px;
       +        margin: 5px;
       +}
       +
       +.task_args_var {
       +        font-size: 14px;
       +        float: left;
       +        width: 100px;
       +        overflow: hidden;
       +        font-weight: bold;
       +        text-align: right;
       +}
       +.task_args_val {
       +        font-size: 14px;
       +        text-align: left;
       +}
        .check-item {
                font-size: 18px;
                margin-bottom: 10px;
       @@ -49,13 +71,6 @@ body {
                font-size: 16px;
        }
        
       -.project-title {
       -        font-size: 18px;
       -        color: @white;
       -        margin-top: 10px;
       -        margin-right: 50px;
       -
       -}
        
        // Hacks to override active drop-down item background color and hover
        .dropdown-menu .active > a {
       @@ -80,9 +95,6 @@ body {
                width: 400px;
        }
        
       -.fbtn {
       -
       -}
        
        .project_description {
                height: 100px;
 (DIR) diff --git a/app/controllers/analyze_controller.rb b/app/controllers/analyze_controller.rb
       @@ -1,7 +1,7 @@
        class AnalyzeController < ApplicationController
        
          def index
       -        @jobs = DialJob.paginate(
       +        @jobs = Job.paginate(
                        :page => params[:page],
                        :order => 'id DESC',
                        :per_page => 30
       @@ -10,30 +10,30 @@ class AnalyzeController < ApplicationController
        
          def view
                  @job_id   = params[:id]
       -          @dial_job = DialJob.find(@job_id)
       +          @job = Job.find(@job_id)
                @shown    = params[:show]
        
       -        ltypes = DialResult.find( :all, :select => 'DISTINCT line_type', :conditions => ["dial_job_id = ?", @job_id] ).map{|r| r.line_type}
       +        ltypes = Call.find( :all, :select => 'DISTINCT line_type', :conditions => ["job_id = ?", @job_id] ).map{|r| r.line_type}
                res_types = {}
        
                ltypes.each do |k|
                        next if not k
       -                res_types[k.capitalize.to_sym] = DialResult.count(
       -                        :conditions => ['dial_job_id = ? and line_type = ?', @job_id, k]
       +                res_types[k.capitalize.to_sym] = Call.count(
       +                        :conditions => ['job_id = ? and line_type = ?', @job_id, k]
                        )
                end
        
                @lines_by_type = res_types
        
                if(@shown and @shown != 'all')
       -                @results = DialResult.where(:dial_job_id => @job_id).paginate(
       +                @results = Call.where(:job_id => @job_id).paginate(
                                :page => params[:page],
                                :order => 'number ASC',
                                :per_page => 10,
                                :conditions => [ 'completed = ? and processed = ? and busy = ? and line_type = ?', true, true, false, @shown ]
                        )
                else
       -                @results = DialResult.where(:dial_job_id => @job_id).paginate(
       +                @results = Call.where(:job_id => @job_id).paginate(
                                :page => params[:page],
                                :order => 'number ASC',
                                :per_page => 10,
       @@ -51,30 +51,30 @@ class AnalyzeController < ApplicationController
        
         def show
                  @job_id   = params[:id]
       -          @dial_job = DialJob.find(@job_id)
       +          @job = Job.find(@job_id)
                @shown    = params[:show]
        
       -        ltypes = DialResult.find( :all, :select => 'DISTINCT line_type', :conditions => ["dial_job_id = ?", @job_id] ).map{|r| r.line_type}
       +        ltypes = Call.find( :all, :select => 'DISTINCT line_type', :conditions => ["job_id = ?", @job_id] ).map{|r| r.line_type}
                res_types = {}
        
                ltypes.each do |k|
                        next if not k
       -                res_types[k.capitalize.to_sym] = DialResult.count(
       -                        :conditions => ['dial_job_id = ? and line_type = ?', @job_id, k]
       +                res_types[k.capitalize.to_sym] = Call.count(
       +                        :conditions => ['job_id = ? and line_type = ?', @job_id, k]
                        )
                end
        
                @lines_by_type = res_types
        
                if(@shown and @shown != 'all')
       -                @results = DialJob.where(:id => @job_id).paginate(
       +                @results = Job.where(:id => @job_id).paginate(
                                :page => params[:page],
                                :order => 'number ASC',
                                :per_page => 20,
                                :conditions => [ 'completed = ? and processed = ? and busy = ? and line_type = ?', true, true, false, @shown ]
                        )
                else
       -                @results = DialJob.where(:id => @job_id).paginate(
       +                @results = Job.where(:id => @job_id).paginate(
                                :page => params[:page],
                                :order => 'number ASC',
                                :per_page => 20,
       @@ -91,13 +91,13 @@ class AnalyzeController < ApplicationController
          end
        
        
       -  # GET /dial_results/1/resource?id=XXX&type=YYY
       +  # GET /calls/1/resource?id=XXX&type=YYY
          def resource
                  ctype = 'text/html'
                cpath = nil
                cdata = "File not found"
        
       -        res = DialResultMedium.where(:dial_result_id => params[:result_id].to_i).first
       +        res = CallMedium.where(:call_id => params[:result_id].to_i).first
        
                if res
                        case params[:type]
       @@ -133,8 +133,8 @@ class AnalyzeController < ApplicationController
        
        
          def view_matches
       -          @result = DialResult.find(params[:dial_result_id])
       -          @job_id = @result.dial_job_id
       +          @result = Call.find(params[:call_id])
       +          @job_id = @result.job_id
                @results = @result.matches.select{|x| x.matchscore.to_f > 10.0 }
          end
        end
 (DIR) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
       @@ -45,14 +45,17 @@ private
                end
        
                def load_project
       +                # Only load this when we are logged in
       +                return true unless current_user
       +
                        if params[:project_id]
       -                        @project = Project.find(params[:project_id])
       +                        @project = Project.where(:id => params[:project_id].to_i).first
                        elsif session[:project_id]
       -                        @project = Project.find(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
       +                        session[:project_id] = @project.id
                        end
        
                        true
 (DIR) diff --git a/app/controllers/dial_jobs_controller.rb b/app/controllers/dial_jobs_controller.rb
       @@ -1,107 +0,0 @@
       -class DialJobsController < ApplicationController
       -
       -  # GET /dial_jobs
       -  # GET /dial_jobs.xml
       -  def index
       -          @submitted_jobs = DialJob.find_all_by_status('submitted')
       -        @active_jobs    = DialJob.find_all_by_status('active')
       -        @new_job        = DialJob.new
       -    respond_to do |format|
       -      format.html # index.html.erb
       -      format.xml  { render :xml => @active_jobs + @submitted_jobs }
       -    end
       -  end
       -
       -  # GET /dial_jobs/new
       -  # GET /dial_jobs/new.xml
       -  def new
       -    @dial_job = DialJob.new
       -    respond_to do |format|
       -      format.html # new.html.erb
       -      format.xml  { render :xml => @dial_job }
       -    end
       -  end
       -
       -  # GET /dial_jobs/1/run
       -  def run
       -    @dial_job = DialJob.find(params[:id])
       -
       -        if(@dial_job.status != 'submitted')
       -          flash[:notice] = 'Job is already running or completed'
       -          return
       -        end
       -
       -        WarVOX::JobManager.schedule(::WarVOX::Jobs::Dialer, @dial_job.id)
       -        redirect_to :action => 'index'
       -  end
       -
       -  def stop
       -    @dial_job = DialJob.find(params[:id])
       -
       -        @dial_job.stop
       -
       -        if(@dial_job.status != 'submitted')
       -          flash[:notice] = 'Job is already running or completed'
       -          return
       -        end
       -    format.html { redirect_to :action => 'index' }
       -  end
       -
       -
       -  # POST /dial_jobs
       -  # POST /dial_jobs.xml
       -  def create
       -
       -        @dial_job = DialJob.new(params[:dial_job])
       -
       -    if(Provider.find_all_by_enabled(true).length == 0)
       -                @dial_job.errors.add(:base, "No providers have been configured or enabled, this job cannot be run")
       -                respond_to do |format|
       -                        format.html { render :action => "new" }
       -                        format.xml  { render :xml => @dial_job.errors, :status => :unprocessable_entity }
       -                end
       -                return
       -        end
       -
       -        @dial_job.status       = 'submitted'
       -        @dial_job.progress     = 0
       -        @dial_job.started_at   = nil
       -        @dial_job.completed_at = nil
       -        @dial_job.range.gsub!(/[^0-9X:,\n]/, '')
       -        @dial_job.cid_mask.gsub!(/[^0-9X]/, '') if @dial_job.cid_mask != "SELF"
       -
       -        if(@dial_job.range_file.to_s != "")
       -                @dial_job.range = @dial_job.range_file.read.gsub!(/[^0-9X:,\n]/, '')
       -        end
       -
       -    respond_to do |format|
       -      if @dial_job.save
       -        flash[:notice] = 'Job was successfully created.'
       -
       -                res = @dial_job.schedule(:dialer)
       -                unless res
       -                        flash[:error] = "Unable to launch dialer job"
       -                end
       -
       -            format.html { redirect_to :action => 'index' }
       -        format.xml  { render :xml => @dial_job, :status => :created, :location => @dial_job }
       -      else
       -        format.html { render :action => "new" }
       -        format.xml  { render :xml => @dial_job.errors, :status => :unprocessable_entity }
       -      end
       -    end
       -  end
       -
       -  # DELETE /dial_jobs/1
       -  # DELETE /dial_jobs/1.xml
       -  def destroy
       -    @dial_job = DialJob.find(params[:id])
       -    @dial_job.destroy
       -
       -    respond_to do |format|
       -      format.html { redirect_to(dial_jobs_url) }
       -      format.xml  { head :ok }
       -    end
       -  end
       -
       -end
 (DIR) diff --git a/app/controllers/dial_results_controller.rb b/app/controllers/dial_results_controller.rb
       @@ -1,179 +0,0 @@
       -class DialResultsController < ApplicationController
       -
       -  # GET /dial_results
       -  # GET /dial_results.xml
       -  def index
       -    @jobs = DialJob.where(:status => 'completed').paginate(
       -                :page => params[:page],
       -                :order => 'id DESC',
       -                :per_page => 30
       -
       -        )
       -
       -    respond_to do |format|
       -      format.html # index.html.erb
       -      format.xml  { render :xml => @dial_results }
       -    end
       -  end
       -
       -  # GET /dial_results/1/reanalyze
       -  def reanalyze
       -          DialResult.update_all(['processed = ?', false], ['dial_job_id = ?', params[:id]])
       -        j = DialJob.find(params[:id])
       -        j.processed = false
       -        j.save
       -
       -        redirect_to :action => 'analyze'
       -  end
       -
       -  # GET /dial_results/1/process
       -  # GET /dial_results/1/process.xml
       -  def analyze
       -          @job_id = params[:id]
       -        @job    = DialJob.find(@job_id)
       -
       -        if(@job.processed)
       -                redirect_to :controller => 'analyze', :action => 'view', :id => @job_id
       -                return
       -        end
       -
       -        @dial_data_total = DialResult.count(
       -                :conditions => [ 'dial_job_id = ? and completed = ?', @job_id, true ]
       -        )
       -
       -        @dial_data_done = DialResult.count(
       -                :conditions => [ 'dial_job_id = ? and processed = ?', @job_id, true ]
       -        )
       -
       -        ltypes = DialResult.find( :all, :select => 'DISTINCT line_type', :conditions => ["dial_job_id = ?", @job_id] ).map{|r| r.line_type}
       -        res_types = {}
       -
       -        ltypes.each do |k|
       -                next if not k
       -                res_types[k.capitalize.to_sym] = DialResult.count(
       -                        :conditions => ['dial_job_id = ? and line_type = ?', @job_id, k]
       -                )
       -        end
       -
       -        @lines_by_type = res_types
       -
       -        @dial_data_todo = DialResult.where(:dial_job_id => @job_id).paginate(
       -                :page => params[:page],
       -                :order => 'number ASC',
       -                :per_page => 50,
       -                :conditions => [ 'completed = ? and processed = ? and busy = ?', true, false, false ]
       -        )
       -
       -        if @dial_data_todo.length > 0
       -        res = @job.schedule(:analysis)
       -                unless res
       -                        flash[:error] = "Unable to launch analysis job"
       -                end
       -        end
       -  end
       -
       -  # GET /dial_results/1/view
       -  # GET /dial_results/1/view.xml
       -  def view
       -    @dial_results = DialResult.where(:dial_job_id => params[:id]).paginate(
       -                :page => params[:page],
       -                :order => 'number ASC',
       -                :per_page => 30
       -        )
       -
       -        unless @dial_results and @dial_results.length > 0
       -                redirect_to :action => :index
       -                return
       -        end
       -        @call_results = {
       -                :Timeout  => DialResult.count(:conditions =>['dial_job_id = ? and completed = ?', params[:id], false]),
       -                :Busy     => DialResult.count(:conditions =>['dial_job_id = ? and busy = ?', params[:id], true]),
       -                :Answered => DialResult.count(:conditions =>['dial_job_id = ? and completed = ?', params[:id], true]),
       -        }
       -
       -        respond_to do |format|
       -      format.html # index.html.erb
       -      format.xml  { render :xml => @dial_results }
       -    end
       -  end
       -
       -  # GET /dial_results/1
       -  # GET /dial_results/1.xml
       -  def show
       -    @dial_result = DialResult.find(params[:id])
       -
       -        unless @dial_result
       -                redirect_to :action => :index
       -                return
       -        end
       -
       -    respond_to do |format|
       -      format.html # show.html.erb
       -      format.xml  { render :xml => @dial_result }
       -    end
       -  end
       -
       -  # GET /dial_results/new
       -  # GET /dial_results/new.xml
       -  def new
       -    @dial_result = DialResult.new
       -
       -    respond_to do |format|
       -      format.html # new.html.erb
       -      format.xml  { render :xml => @dial_result }
       -    end
       -  end
       -
       -  # GET /dial_results/1/edit
       -  def edit
       -    @dial_result = DialResult.find(params[:id])
       -  end
       -
       -  # POST /dial_results
       -  # POST /dial_results.xml
       -  def create
       -    @dial_result = DialResult.new(params[:dial_result])
       -
       -    respond_to do |format|
       -      if @dial_result.save
       -        flash[:notice] = 'DialResult was successfully created.'
       -        format.html { redirect_to(@dial_result) }
       -        format.xml  { render :xml => @dial_result, :status => :created, :location => @dial_result }
       -      else
       -        format.html { render :action => "new" }
       -        format.xml  { render :xml => @dial_result.errors, :status => :unprocessable_entity }
       -      end
       -    end
       -  end
       -
       -  # PUT /dial_results/1
       -  # PUT /dial_results/1.xml
       -  def update
       -    @dial_result = DialResult.find(params[:id])
       -
       -    respond_to do |format|
       -      if @dial_result.update_attributes(params[:dial_result])
       -        flash[:notice] = 'DialResult was successfully updated.'
       -        format.html { redirect_to(@dial_result) }
       -        format.xml  { head :ok }
       -      else
       -        format.html { render :action => "edit" }
       -        format.xml  { render :xml => @dial_result.errors, :status => :unprocessable_entity }
       -      end
       -    end
       -  end
       -
       -  # DELETE /dial_results/1
       -  # DELETE /dial_results/1.xml
       -  def destroy
       -
       -    @job = DialJob.find(params[:id])
       -        @job.destroy
       -
       -    respond_to do |format|
       -      format.html { redirect_to :action => 'index' }
       -      format.xml  { head :ok }
       -    end
       -  end
       -
       -end
 (DIR) diff --git a/app/controllers/home_controller.rb b/app/controllers/home_controller.rb
       @@ -18,10 +18,10 @@ class HomeController < ApplicationController
        
                def check
                        @has_project  = ( Project.count > 0 )
       -                @has_provider = ( Provider.count > 0 )
       -                @has_job      = ( DialJob.count > 0 )
       -                @has_result   = ( DialResult.where(:completed => true ).count > 0 )
       -                @has_analysis = ( DialResult.where(:processed => true ).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/projects_controller.rb b/app/controllers/projects_controller.rb
       @@ -29,7 +29,6 @@ class ProjectsController < ApplicationController
          # GET /projects/new.xml
          def new
            @new_project = Project.new
       -
            respond_to do |format|
              format.html # new.html.erb
              format.xml  { render :xml => @new_project }
       @@ -44,17 +43,17 @@ class ProjectsController < ApplicationController
          # POST /projects
          # POST /projects.xml
          def create
       -    @project = Project.new(params[:project])
       -    @project.created_by = current_user.login
       +    @new_project = Project.new(params[:project])
       +    @new_project.created_by = current_user.login
        
            respond_to do |format|
       -      if @project.save
       +      if @new_project.save
                flash[:notice] = 'Project was successfully created.'
       -        format.html { redirect_to(project_path(@project)) }
       -        format.xml  { render :xml => @project, :status => :created, :location => @project }
       +        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 => @project.errors, :status => :unprocessable_entity }
       +        format.xml  { render :xml => @new_project.errors, :status => :unprocessable_entity }
              end
            end
          end
 (DIR) diff --git a/app/controllers/providers_controller.rb b/app/controllers/providers_controller.rb
       @@ -1,9 +1,13 @@
        class ProvidersController < ApplicationController
        
       -  # GET /providers
       -  # GET /providers.xml
          def index
       -    @providers = Provider.find(:all)
       +
       +           @providers = Provider.paginate(
       +                :page => params[:page],
       +                :order => 'id DESC',
       +                :per_page => 10
       +        )
       +
                @new_provider = Provider.new
                @new_provider.enabled = true
        
       @@ -13,23 +17,10 @@ class ProvidersController < ApplicationController
            end
          end
        
       -  # GET /providers/1
       -  # GET /providers/1.xml
       -  def show
       -    @provider = Provider.find(params[:id])
       -        @provider.pass = "********"
       -
       -    respond_to do |format|
       -      format.html # show.html.erb
       -      format.xml  { render :xml => @provider }
       -    end
       -  end
       -
       -  # GET /providers/new
       -  # GET /providers/new.xml
          def new
            @provider = Provider.new
                @provider.enabled = true
       +        @provider.port = 4569
        
            respond_to do |format|
              format.html # new.html.erb
       @@ -37,14 +28,11 @@ class ProvidersController < ApplicationController
            end
          end
        
       -  # GET /providers/1/edit
          def edit
            @provider = Provider.find(params[:id])
                @provider.pass = "********"
          end
        
       -  # POST /providers
       -  # POST /providers.xml
          def create
            @provider = Provider.new(params[:provider])
                @provider.enabled = true
       @@ -52,8 +40,8 @@ class ProvidersController < ApplicationController
            respond_to do |format|
              if @provider.save
                flash[:notice] = 'Provider was successfully created.'
       -        format.html { redirect_to(@provider) }
       -        format.xml  { render :xml => @provider, :status => :created, :location => @provider }
       +        format.html { redirect_to providers_path }
       +        format.xml  { render :xml => @provider, :status => :created, :location => providers_path }
              else
                format.html { render :action => "new" }
                format.xml  { render :xml => @provider.errors, :status => :unprocessable_entity }
       @@ -61,8 +49,7 @@ class ProvidersController < ApplicationController
            end
          end
        
       -  # PUT /providers/1
       -  # PUT /providers/1.xml
       +
          def update
            @provider = Provider.find(params[:id])
        
       @@ -74,7 +61,7 @@ class ProvidersController < ApplicationController
            respond_to do |format|
              if @provider.update_attributes(params[:provider])
                flash[:notice] = 'Provider was successfully updated.'
       -        format.html { redirect_to(@provider) }
       +        format.html { redirect_to providers_path }
                format.xml  { head :ok }
              else
                format.html { render :action => "edit" }
       @@ -83,14 +70,12 @@ class ProvidersController < ApplicationController
            end
          end
        
       -  # DELETE /providers/1
       -  # DELETE /providers/1.xml
          def destroy
            @provider = Provider.find(params[:id])
            @provider.destroy
        
            respond_to do |format|
       -      format.html { redirect_to(providers_url) }
       +      format.html { redirect_to providers_path }
              format.xml  { head :ok }
            end
          end
 (DIR) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
       @@ -21,4 +21,33 @@ module ApplicationHelper
                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'>") + k.to_s.html_safe + raw(": </div> ")
       +                                ttip << raw("<div class='task_args_val'>") + v.to_s.html_safe + raw("&nbsp;</div>")
       +                        end
       +                        ttip << raw("</div>\n")
       +                        outp = raw("<a href='#' rel='tooltip' title=\"#{ttip}\" data-html='true'>#{job.task.capitalize.html_safe}</a>")
       +                        outp
       +                rescue ::Exception => e
       +                        job.status.to_s.capitalize
       +                end
       +        end
       +
       +        def format_job_status(job)
       +                case job.status
       +                when 'error'
       +                        ttip = job.error.to_s.html_safe
       +                        outp = raw("<a href='#' rel='tooltip' title=\"#{ttip}\" data-html='true'>#{job.status.capitalize.html_safe}</a>")
       +                        outp
       +                else
       +                        job.status.to_s.capitalize
       +                end
       +
       +        end
        end
 (DIR) diff --git a/app/helpers/dial_jobs_helper.rb b/app/helpers/dial_jobs_helper.rb
       @@ -1,2 +0,0 @@
       -module DialJobsHelper
       -end
 (DIR) diff --git a/app/helpers/dial_results_helper.rb b/app/helpers/dial_results_helper.rb
       @@ -1,2 +0,0 @@
       -module DialResultsHelper
       -end
 (DIR) diff --git a/app/models/call.rb b/app/models/call.rb
       @@ -27,7 +27,7 @@ class Call < ActiveRecord::Base
        
        
                def media
       -                CallMedium.find_or_create_by_call_id(self[:id])
       +                CallMedium.find_or_create_by_call_id_and_project_id(self[:id], self[:project_id])
                end
        
                def media_fields
 (DIR) diff --git a/app/models/call_medium.rb b/app/models/call_medium.rb
       @@ -1,3 +1,4 @@
        class CallMedium < ActiveRecord::Base
                belongs_to :call
       +        belongs_to :project
        end
 (DIR) diff --git a/app/models/dial_job.rb b/app/models/dial_job.rb
       @@ -1,49 +0,0 @@
       -class DialJob < ActiveRecord::Base
       -        attr_accessor :range_file
       -
       -        has_many :dial_results, :dependent => :destroy
       -
       -        validates_presence_of :range, :lines, :seconds
       -        validates_numericality_of :lines, :less_than => 256, :greater_than => 0
       -        validates_numericality_of :seconds, :less_than => 301, :greater_than => 0
       -
       -
       -        validate :validate_range
       -
       -        def validate_range
       -                return if range == "IMPORTED"
       -
       -                if range.gsub(/[^0-9X:,\n]/, '').empty?
       -                        errors.add(:range, "must be at least 1 character long and made up of 0-9 and X as the mask.")
       -                end
       -
       -                if(range.scan(/X/).length > 5)
       -                        errors.add(:range, "must contain no more than 5 mask digits.")
       -                end
       -
       -                if(cid_mask != "SELF" and cid_mask.gsub(/[^0-9X]/, '').empty?)
       -                        errors.add(:range, "The Caller ID must be at least 1 character long and made up of 0-9 and X as the mask.")
       -                end
       -
       -                if(cid_mask != "SELF" and cid_mask.scan(/X/).length > 5)
       -                        errors.add(:range, "The Caller ID must contain no more than 5 mask digits.")
       -                end
       -        end
       -
       -        attr_accessible :range, :seconds, :lines, :cid_mask
       -
       -
       -        def schedule(jtype)
       -                res = nil
       -                case jtype
       -                when :dialer
       -                        res = WarVOX::JobManager.schedule(::WarVOX::Jobs::Dialer, self[:id])
       -                when :analysis
       -                        res = WarVOX::JobManager.schedule(::WarVOX::Jobs::Analysis, self[:id])
       -                else
       -                        raise RuntimeError, "Unknown task type: #{jtype} for Job #{self[:id]}"
       -                end
       -                res
       -        end
       -
       -end
 (DIR) diff --git a/app/models/job.rb b/app/models/job.rb
       @@ -1,12 +1,84 @@
        class Job < ActiveRecord::Base
       +
       +        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
       +                        when 'analysis'
       +                        when 'import'
       +                        else
       +                                record.errors[:base] << "Invalid task specified"
       +                        end
       +                end
       +        end
       +
       +
                has_many :calls
                belongs_to :project
       +        validates_with JobValidator
       +
       +        def stop
       +                self.class.update_all({ :status => 'cancelled'}, { :id => self.id })
       +        end
        
                def update_progress(pct)
                        if pct >= 100
       -                        self.class.update_all({ :progress => pct, :completed_at => Time.now.utc }, { :id => self.id })
       +                        self.class.update_all({ :progress => pct, :completed_at => Time.now.utc, :status => 'completed' }, { :id => self.id })
                        else
                                self.class.update_all({ :progress => pct }, { :id => self.id })
                        end
                end
       +
       +
       +        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
       +
       +
       +        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'
       +                        #
       +                else
       +                        raise ::RuntimeError, "Unsupported Job type"
       +                end
       +        end
       +
        end
 (DIR) diff --git a/app/models/line.rb b/app/models/line.rb
       @@ -1,3 +1,16 @@
        class Line < ActiveRecord::Base
       -        has_many :line_attributes
       +        has_many :line_attributes, :dependent => :delete_all
       +        belongs_to :project
       +
       +        def set_attribute(name, value, ctype='text/plain')
       +                la = LineAttribute.find_or_create_by_line_id_and_project_id_and_name(self[:id], self[:project_id], name)
       +                la.value = value
       +                la.ctype = ctype
       +                la.save
       +                la
       +        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
       @@ -1,3 +1,4 @@
        class LineAttribute < ActiveRecord::Base
                belongs_to :line
       +        belongs_to :project
        end
 (DIR) diff --git a/app/models/project.rb b/app/models/project.rb
       @@ -1,9 +1,15 @@
        class Project < ActiveRecord::Base
       +
       +
       +        validates :name, :uniqueness => true
                validates_presence_of :name
        
                attr_accessible :name, :description, :included, :excluded
        
       -        has_many :lines
       -        has_many :calls
       -        has_many :jobs
       +        # 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/views/analyze/index.html.erb b/app/views/analyze/index.html.erb
       @@ -15,29 +15,28 @@
          </thead>
          <tbody>
        
       -<% @jobs.sort{|a,b| b.id <=> a.id}.each do |dial_job|  %>
       +<% @jobs.sort{|a,b| b.id <=> a.id}.each do |job|  %>
          <tr>
       -    <td><%=h dial_job.id %></td>
       -    <td><%=h dial_job.range %></td>
       -    <td><%=h dial_job.cid_mask %></td>
       +    <td><%=h job.id %></td>
       +    <td><%=h job.range %></td>
       +    <td><%=h job.cid_mask %></td>
            <td><%=h (
       -                DialResult.count(:conditions => ['dial_job_id = ? and processed = ?', dial_job.id, true]).to_s +
       +                job.calls.where("analysis_completed_at IS NOT NULL").count.to_s +
                        "/" +
       -                DialResult.count(:conditions => ['dial_job_id = ?', dial_job.id]).to_s
       +                job.calls.count.to_s
                )%></td>
       -    <td><%=h dial_job.started_at.localtime.strftime("%Y-%m-%d %H:%M:%S") %></td>
       +    <td><%=h job.started_at.localtime.strftime("%Y-%m-%d %H:%M:%S") %></td>
        
            <td>
       -        <a class="btn btn-mini" href="<%= view_dial_result_path(dial_job) %>"><i class="icon-bar-chart"></i></a>
        
       -                <% if(dial_job.processed) %>
       -                        <a class="btn btn-mini" href="<%= analyze_dial_result_path(dial_job) %>"><i class="icon-eye-open"></i></a>
       -                        <a class="btn btn-mini" href="<%= reanalyze_dial_result_path(dial_job) %>" data-confirm="Reprocess this job?" rel="nofollow"><i class="icon-refresh"></i></a>
       +                <% if job.calls.where("analysis_completed_at IS NOT NULL").count > 0 %>
       +                        <a class="btn btn-mini" href="<%= analyze_call_path(job) %>"><i class="icon-eye-open"></i></a>
       +                        <a class="btn btn-mini" href="<%= reanalyze_call_path(job) %>" data-confirm="Reprocess this job?" rel="nofollow"><i class="icon-refresh"></i></a>
                        <% else %>
       -                        <a class="btn btn-mini" href="<%= analyze_dial_result_path(dial_job) %>"><i class="icon-bolt"></i></a>
       +                        <a class="btn btn-mini" href="<%= analyze_call_path(job) %>"><i class="icon-bolt"></i></a>
                        <% end %>
        
       -            <a class="btn btn-mini" href="<%= dial_result_path(dial_job) %>" data-confirm="Delete all data for this job?" data-method="delete" rel="nofollow"><i class="icon-trash"></i></a>
       +            <a class="btn btn-mini" href="<%= call_path(job) %>" data-confirm="Delete all data for this job?" data-method="delete" rel="nofollow"><i class="icon-trash"></i></a>
                </td>
          </tr>
        
 (DIR) diff --git a/app/views/analyze/view.html.erb b/app/views/analyze/view.html.erb
       @@ -21,40 +21,40 @@
          </thead>
          <tbody>
        
       -<%  @results.each do |dial_result| %>
       +<%  @results.each do |call| %>
          <tr>
            <td align='center'>
        
                        <object
                                type="application/x-shockwave-flash"
       -                        data="/assets/musicplayer.swf?song_url=<%=resource_analyze_path(@job_id, dial_result.id, "mp3")%>"
       +                        data="/assets/musicplayer.swf?song_url=<%=resource_analyze_path(@job_id, call.id, "mp3")%>"
                                width="20"
                                height="17"
                                style="margin-bottom: -5px;"
                                >
       -                        <param name="movie" value="/assets/musicplayer.swf?song_url=<%=resource_analyze_path(@job_id, dial_result.id, "mp3")%>"></param>
       +                        <param name="movie" value="/assets/musicplayer.swf?song_url=<%=resource_analyze_path(@job_id, call.id, "mp3")%>"></param>
                                <param name="wmode" value="transparent"></param>
                        </object>
       -                <b><%= dial_result.number %></b>
       +                <b><%= call.number %></b>
                        <hr width='100%' size='1'/>
       -                CallerID: <%= dial_result.cid%><br/>
       -                Provider: <%=h dial_result.provider.name %><br/>
       -                Audio: <%=h dial_result.seconds %> Seconds<br/>
       -                Ringer: <%=h dial_result.ringtime %> Seconds<br/>
       +                CallerID: <%= call.cid%><br/>
       +                Provider: <%=h call.provider.name %><br/>
       +                Audio: <%=h call.seconds %> Seconds<br/>
       +                Ringer: <%=h call.ringtime %> Seconds<br/>
                </td>
                  <td align='center'>
       -                <b><%=h dial_result.line_type.upcase %></b><br/>
       -                <a href="<%=resource_analyze_path(@job_id, dial_result.id, "big_sig_dots")%>" class="lightbox"><img src="<%=resource_analyze_path(@job_id, dial_result.id, "small_sig")%>" /></a>
       -                <a href="<%=resource_analyze_path(@job_id, dial_result.id, "big_freq")%>" class="lightbox"><img src="<%=resource_analyze_path(@job_id, dial_result.id, "small_freq")%>" /></a><br/>
       -                <% (dial_result.signatures||"").split("\n").each do |s|
       +                <b><%=h call.line_type.upcase %></b><br/>
       +                <a href="<%=resource_analyze_path(@job_id, call.id, "big_sig_dots")%>" class="lightbox"><img src="<%=resource_analyze_path(@job_id, call.id, "small_sig")%>" /></a>
       +                <a href="<%=resource_analyze_path(@job_id, call.id, "big_freq")%>" class="lightbox"><img src="<%=resource_analyze_path(@job_id, call.id, "small_freq")%>" /></a><br/>
       +                <% (call.signatures||"").split("\n").each do |s|
                                sid,mat,name = s.split(':', 3)
                                str = [mat.to_i * 6.4, 255].min
                                col = ("%.2x" % (255 - str)) * 3
                        %>
                                <div style="color: #<%= col%>;"><%=h name%> (<%=h sid %>@<%=h mat %>)</div>
                        <% end %>
       -                <% if dial_result.fprint and dial_result.fprint.length > 0 %>
       -                        <a href="<%=view_matches_path(dial_result.id)%>">View Matches</a>
       +                <% if call.fprint and call.fprint.length > 0 %>
       +                        <a href="<%=view_matches_path(call.id)%>">View Matches</a>
                        <% end %>
                </td>
          </tr>
 (DIR) diff --git a/app/views/analyze/view_matches.html.erb b/app/views/analyze/view_matches.html.erb
       @@ -1,6 +1,6 @@
       -<%  dial_result = @result %>
       +<%  call = @result %>
        
       -<h1 class='title'><%= @result.number %> (<a href="<%=view_analyze_path(dial_result.dial_job_id)%>">Back to Job</a>)</h1>
       +<h1 class='title'><%= @result.number %> (<a href="<%=view_analyze_path(call.job_id)%>">Back to Job</a>)</h1>
        <table class='table table-striped table-bordered' width='90%'>
          <tr>
            <th>Number</th>
       @@ -12,27 +12,27 @@
        
                        <object
                                type="application/x-shockwave-flash"
       -                        data="/assets/musicplayer.swf?song_url=<%=resource_analyze_path(@job_id, dial_result.id, "mp3")%>"
       +                        data="/assets/musicplayer.swf?song_url=<%=resource_analyze_path(@job_id, call.id, "mp3")%>"
                                width="20"
                                height="17"
                                style="margin-bottom: -5px;"
                                >
       -                        <param name="movie" value="/assets/musicplayer.swf?song_url=<%=resource_analyze_path(@job_id, dial_result.id, "mp3")%>"></param>
       +                        <param name="movie" value="/assets/musicplayer.swf?song_url=<%=resource_analyze_path(@job_id, call.id, "mp3")%>"></param>
                                <param name="wmode" value="transparent"></param>
                        </object>
        
       -                <b><%= dial_result.number %></b>
       +                <b><%= call.number %></b>
                        <hr width='100%' size='1'/>
       -                CallerID: <%= dial_result.cid%><br/>
       -                Provider: <%=h dial_result.provider.name %><br/>
       -                Audio: <%=h dial_result.seconds %> Seconds<br/>
       -                Ringer: <%=h dial_result.ringtime %> Seconds<br/>
       +                CallerID: <%= call.cid%><br/>
       +                Provider: <%=h call.provider.name %><br/>
       +                Audio: <%=h call.seconds %> Seconds<br/>
       +                Ringer: <%=h call.ringtime %> Seconds<br/>
                </td>
                  <td align='center'>
       -                <b><%=h dial_result.line_type.upcase %></b><br/>
       -                <a href="<%=resource_analyze_path(@job_id, dial_result.id, "big_sig_dots")%>" rel="lightbox"><img src="<%=resource_analyze_path(@job_id, dial_result.id, "small_sig")%>" /></a>
       -                <a href="<%=resource_analyze_path(@job_id, dial_result.id, "big_freq")%>" rel="lightbox"><img src="<%=resource_analyze_path(@job_id, dial_result.id, "small_freq")%>" /></a><br/>
       -                <% (dial_result.signatures||"").split("\n").each do |s|
       +                <b><%=h call.line_type.upcase %></b><br/>
       +                <a href="<%=resource_analyze_path(@job_id, call.id, "big_sig_dots")%>" rel="lightbox"><img src="<%=resource_analyze_path(@job_id, call.id, "small_sig")%>" /></a>
       +                <a href="<%=resource_analyze_path(@job_id, call.id, "big_freq")%>" rel="lightbox"><img src="<%=resource_analyze_path(@job_id, call.id, "small_freq")%>" /></a><br/>
       +                <% (call.signatures||"").split("\n").each do |s|
                                sid,mat,name = s.split(':', 3)
                                str = [mat.to_i * 6.4, 255].min
                                col = ("%.2x" % (255 - str)) * 3
       @@ -55,43 +55,43 @@
          </thead>
          <tbody>
        
       -<%  @results.each do |dial_result| %>
       +<%  @results.each do |call| %>
          <tr>
            <td align='center'>
        
       -                <br/><%= raw(fwd_match_html(dial_result.matchscore)) %><br/><br/>
       +                <br/><%= raw(fwd_match_html(call.matchscore)) %><br/><br/>
        
                        <object
                                type="application/x-shockwave-flash"
       -                        data="/assets/musicplayer.swf?song_url=<%=resource_analyze_path(@job_id, dial_result.id, "mp3")%>"
       +                        data="/assets/musicplayer.swf?song_url=<%=resource_analyze_path(@job_id, call.id, "mp3")%>"
                                width="20"
                                height="17"
                                style="margin-bottom: -5px;"
                                >
       -                        <param name="movie" value="/assets/musicplayer.swf?song_url=<%=resource_analyze_path(@job_id, dial_result.id, "mp3")%>"></param>
       +                        <param name="movie" value="/assets/musicplayer.swf?song_url=<%=resource_analyze_path(@job_id, call.id, "mp3")%>"></param>
                                <param name="wmode" value="transparent"></param>
                        </object>
        
       -                <b><%= dial_result.number %></b>
       +                <b><%= call.number %></b>
                        <hr width='100%' size='1'/>
       -                CallerID: <%= dial_result.cid%><br/>
       -                Provider: <%=h dial_result.provider.name %><br/>
       -                Audio: <%=h dial_result.seconds %> Seconds<br/>
       -                Ringer: <%=h dial_result.ringtime %> Seconds<br/>
       +                CallerID: <%= call.cid%><br/>
       +                Provider: <%=h call.provider.name %><br/>
       +                Audio: <%=h call.seconds %> Seconds<br/>
       +                Ringer: <%=h call.ringtime %> Seconds<br/>
                </td>
                  <td align='center'>
       -                <b><%=h dial_result.line_type.upcase %></b><br/>
       -                <a href="<%=resource_analyze_path(@job_id, dial_result.id, "big_sig_dots")%>" class="lightbox"><img src="<%=resource_analyze_path(@job_id, dial_result.id, "small_sig")%>" /></a>
       -                <a href="<%=resource_analyze_path(@job_id, dial_result.id, "big_freq")%>" class="lightbox"><img src="<%=resource_analyze_path(@job_id, dial_result.id, "small_freq")%>" /></a><br/>
       -                <% (dial_result.signatures||"").split("\n").each do |s|
       +                <b><%=h call.line_type.upcase %></b><br/>
       +                <a href="<%=resource_analyze_path(@job_id, call.id, "big_sig_dots")%>" class="lightbox"><img src="<%=resource_analyze_path(@job_id, call.id, "small_sig")%>" /></a>
       +                <a href="<%=resource_analyze_path(@job_id, call.id, "big_freq")%>" class="lightbox"><img src="<%=resource_analyze_path(@job_id, call.id, "small_freq")%>" /></a><br/>
       +                <% (call.signatures||"").split("\n").each do |s|
                                sid,mat,name = s.split(':', 3)
                                str = [mat.to_i * 6.4, 255].min
                                col = ("%.2x" % (255 - str)) * 3
                        %>
                                <div style="color: #<%= col%>;"><%=h name%> (<%=h sid %>@<%=h mat %>)</div>
                        <% end %>
       -                <% if dial_result.fprint %>
       -                        <a href="<%=view_matches_path(dial_result.id)%>">View Matches</a>
       +                <% if call.fprint %>
       +                        <a href="<%=view_matches_path(call.id)%>">View Matches</a>
                        <% end %>
                </td>
          </tr>
 (DIR) diff --git a/app/views/dial_jobs/edit.html.erb b/app/views/dial_jobs/edit.html.erb
       @@ -1,24 +0,0 @@
       -<h1 class='title'>Modify Job</h1>
       -
       -<%= form_for(@dial_job) do |f| %>
       -  <%= f.error_messages %>
       -
       -  <p>
       -    <%= f.label :range %><br />
       -    <%= f.text_area :range, :size => "35x5" %>
       -  </p>
       -  <p>
       -    <%= f.label :seconds %><br />
       -    <%= f.text_field :seconds %>
       -  </p>
       -  <p>
       -    <%= f.label :lines %><br />
       -    <%= f.text_field :lines %>
       -  </p>
       -  <p>
       -    <%= f.submit "Update" %>
       -  </p>
       -<% end %>
       -
       -<%= link_to 'Show', @dial_job %> |
       -<%= link_to 'Back', dial_jobs_path(@project) %>
 (DIR) diff --git a/app/views/dial_jobs/index.html.erb b/app/views/dial_jobs/index.html.erb
       @@ -1,81 +0,0 @@
       -<% if(@submitted_jobs.length > 0) %>
       -
       -<h1 class='title'>Submitted Jobs</h1>
       -
       -<table class='table table-striped table-bordered' width='90%'>
       -  <tr>
       -    <th>ID</th>
       -    <th>Range</th>
       -    <th>CallerID</th>
       -    <th>Seconds</th>
       -    <th>Lines</th>
       -    <th>Submitted Time</th>
       -    <th>Actions</th>
       -  </tr>
       -
       -<% @submitted_jobs.each do |dial_job| %>
       -  <tr>
       -    <td><%=h dial_job.id %></td>
       -    <td><%=h dial_job.range %></td>
       -        <td><%=h dial_job.cid_mask %></td>
       -    <td><%=h dial_job.seconds %></td>
       -    <td><%=h dial_job.lines %></td>
       -    <td><%=h dial_job.created_at.localtime.strftime("%Y-%m-%d %H:%M:%S %Z") %></td>
       -
       -    <td>
       -            <a class="btn btn-mini" href="<%= run_dial_job_path(@project, dial_job) %>" data-confirm="Launch this job?" rel="nofollow tooltip" title="Launch Job"><i class="icon-play"></i></a>
       -                <a class="btn btn-mini" href="<%= dial_job %>" data-confirm="Remove this job?" data-method="delete" rel="nofollow tooltip" title="Remove Job"><i class="icon-trash"></i></a>
       -        </td>
       -  </tr>
       -<% end %>
       -</table>
       -<br />
       -<% end %>
       -
       -<% if(@active_jobs.length > 0) %>
       -
       -<h1 class='title'>Active Jobs</h1>
       -
       -<table class='table table-striped table-bordered' width='90%'>
       -  <tr>
       -    <th>ID</th>
       -    <th>Range</th>
       -    <th>CallerID</th>
       -    <th>Seconds</th>
       -    <th>Lines</th>
       -    <th>Status</th>
       -    <th>Progress</th>
       -    <th>Start Time</th>
       -    <th>Actions</th>
       -  </tr>
       -
       -<% @active_jobs.each do |dial_job| %>
       -  <tr class='active_job_row'>
       -    <td><%=h dial_job.id %></td>
       -        <td><%=h dial_job.range %></td>
       -        <td><%=h dial_job.cid_mask %></td>
       -    <td><%=h dial_job.seconds %></td>
       -    <td><%=h dial_job.lines %></td>
       -    <td><%=h dial_job.status %></td>
       -    <td><%=h dial_job.progress %>%</td>
       -    <td><%=h dial_job.started_at.localtime.strftime("%Y-%m-%d %H:%M:%S %Z") %></td>
       -    <td>
       -                <a class="btn btn-mini" href="<%= stop_dial_job_path(@project, dial_job) %>" data-confirm="Terminate this job?" rel="nofollow tooltip" title="Terminate Job"><i class="icon-stop"></i></a>
       -        </td>
       -  </tr>
       -<% end %>
       -</table>
       -<br />
       -
       -<script language="javascript">
       -        setTimeout("location.reload(true);", 20000);
       -</script>
       -
       -<% end %>
       -
       -<% if (@active_jobs.length + @submitted_jobs.length == 0) %>
       -<h1 class='title'>No Active Jobs</h1>
       -<br/>
       -<a class="btn" href="<%= new_dial_job_path(@project) %>"><i class="icon-plus"></i> Start Job </a>
       -
       -<% end %>
 (DIR) diff --git a/app/views/dial_jobs/new.html.erb b/app/views/dial_jobs/new.html.erb
       @@ -1,35 +0,0 @@
       -<h1 class='title'>Submit a New Job</h1>
       -
       -<%= form_for(@dial_job, :html => { :multipart => true }) do |f| %>
       -  <%= f.error_messages %>
       -  <p>
       -    <%= f.label :range, 'Specify target telephone range(s) (1-123-456-7890 or 1-123-456-XXXX or 1-123-300-1000:1-123-400-2000)' %><br />
       -    <%= f.text_area :range, :size => "35x5" %>
       -  </p>
       -
       -  <p>
       -    <%= f.label :range_file, 'Or upload a file containing the target ranges' %><br />
       -    <%= f.file_field :range_file %>
       -  </p>
       -
       -  <p>
       -    <%= f.label :seconds, 'Seconds of audio to capture' %><br />
       -    <%= f.text_field :seconds, :value => 53 %>
       -  </p>
       -
       -  <p>
       -    <%= f.label :lines, 'Maximum number of outgoing lines' %><br />
       -    <%= f.text_field :lines, :value => 10 %>
       -  </p>
       -
       -  <p>
       -    <%= f.label :lines, 'The source Caller ID range (1-555-555-55XX or SELF)' %><br />
       -    <%= f.text_field :cid_mask, :value => '1-123-456-XXXX' %>
       -  </p>
       -
       -  <p>
       -    <%= f.submit "Create" %>
       -  </p>
       -<% end %>
       -
       -<%= link_to 'Back', dial_jobs_path(@project) %>
 (DIR) diff --git a/app/views/dial_jobs/run.html.erb b/app/views/dial_jobs/run.html.erb
       @@ -1,5 +0,0 @@
       -<h1 class='title'>Run Job</h1>
       -
       -Running this job...<br/>
       -
       -<%= link_to 'Back', dial_jobs_path %>
 (DIR) diff --git a/app/views/dial_jobs/show.html.erb b/app/views/dial_jobs/show.html.erb
       @@ -1,39 +0,0 @@
       -<h1 class='title'>Show Job</h1>
       -<p>
       -  <b>Range:</b>
       -  <%=h @dial_job.range %>
       -</p>
       -
       -<p>
       -  <b>Seconds:</b>
       -  <%=h @dial_job.seconds %>
       -</p>
       -
       -<p>
       -  <b>Lines:</b>
       -  <%=h @dial_job.lines %>
       -</p>
       -
       -<p>
       -  <b>Status:</b>
       -  <%=h @dial_job.status %>
       -</p>
       -
       -<p>
       -  <b>Progress:</b>
       -  <%=h @dial_job.progress %>
       -</p>
       -
       -<p>
       -  <b>Started at:</b>
       -  <%=h @dial_job.started_at %>
       -</p>
       -
       -<p>
       -  <b>Completed at:</b>
       -  <%=h @dial_job.completed_at %>
       -</p>
       -
       -
       -<%= link_to 'Edit', edit_dial_job_path(@dial_job) %> |
       -<%= link_to 'Back', dial_jobs_path %>
 (DIR) diff --git a/app/views/dial_results/analyze.html.erb b/app/views/dial_results/analyze.html.erb
       @@ -1,27 +0,0 @@
       -<% if @dial_data_todo.length > 0 %>
       -
       -<h1 class='title'>
       -        Analyzing Audio for <%= @dial_data_total-@dial_data_done %> of <%= @dial_data_total %> Calls...
       -</h1>
       -
       -<table width='100%' align='center' border=0 cellspacing=0 cellpadding=6>
       -<tr>
       -<% if @dial_data_done > 0 %>
       -        <td align='center'>
       -                <%= render :partial => 'shared/graphs/lines_by_type' %>
       -        </td>
       -<% end %>
       -</tr>
       -</table>
       -
       -<script language="javascript">
       -        setTimeout("location.reload(true);", 10000);
       -</script>
       -
       -<% else %>
       -
       -<h1 class='title'>No Completed Calls Found</h1>
       -
       -<% end %>
       -
       -<br />
 (DIR) diff --git a/app/views/dial_results/edit.html.erb b/app/views/dial_results/edit.html.erb
       @@ -1,48 +0,0 @@
       -<h1>Editing dial_result</h1>
       -
       -<%= form_for(@dial_result) do |f| %>
       -  <%= f.error_messages %>
       -
       -  <p>
       -    <%= f.label :number %><br />
       -    <%= f.text_field :number %>
       -  </p>
       -  <p>
       -    <%= f.label :cid %><br />
       -    <%= f.text_field :cid %>
       -  </p>
       -  <p>
       -    <%= f.label :dial_job_id %><br />
       -    <%= f.text_field :dial_job_id %>
       -  </p>
       -  <p>
       -    <%= f.label :provider %><br />
       -    <%= f.text_field :provider %>
       -  </p>
       -  <p>
       -    <%= f.label :completed %><br />
       -    <%= f.check_box :completed %>
       -  </p>
       -  <p>
       -    <%= f.label :busy %><br />
       -    <%= f.check_box :busy %>
       -  </p>
       -  <p>
       -    <%= f.label :seconds %><br />
       -    <%= f.text_field :seconds %>
       -  </p>
       -  <p>
       -    <%= f.label :ringtime %><br />
       -    <%= f.text_field :ringtime %>
       -  </p>
       -  <p>
       -    <%= f.label :rawfile %><br />
       -    <%= f.text_field :rawfile %>
       -  </p>
       -  <p>
       -    <%= f.submit "Update" %>
       -  </p>
       -<% end %>
       -
       -<%= link_to 'Show', @dial_result %> |
       -<%= link_to 'Back', dial_results_path(@project) %>
 (DIR) diff --git a/app/views/dial_results/index.html.erb b/app/views/dial_results/index.html.erb
       @@ -1,57 +0,0 @@
       -<% if @jobs.length > 0 %>
       -<h1 class='title'>Completed Jobs</h1>
       -
       -<%= raw(will_paginate @jobs) %>
       -<table class='table table-striped table-bordered' width='90%'>
       -  <thead>
       -  <tr>
       -    <th>ID</th>
       -    <th>Range</th>
       -        <th>CallerID</th>
       -    <th>Connected</th>
       -    <th>Date</th>
       -    <th>Actions</th>
       -  </tr>
       -  </thead>
       -  <tbody>
       -
       -<% @jobs.sort{|a,b| b.id <=> a.id}.each do |dial_job|  %>
       -  <tr>
       -    <td><%=h dial_job.id %></td>
       -    <td><%=h dial_job.range %></td>
       -    <td><%=h dial_job.cid_mask %></td>
       -    <td><%=h (
       -                DialResult.count(:conditions => ['dial_job_id = ? and processed = ?', dial_job.id, true]).to_s +
       -                "/" +
       -                DialResult.count(:conditions => ['dial_job_id = ?', dial_job.id]).to_s
       -        )%></td>
       -    <td><%=h dial_job.started_at.localtime.strftime("%Y-%m-%d %H:%M:%S") %></td>
       -
       -    <td>
       -        <a class="btn btn-mini" href="<%= view_dial_result_path(@project,dial_job) %>" rel="tooltip" title="View Call Connections" ><i class="icon-bar-chart"></i></a>
       -
       -                <% if(dial_job.processed) %>
       -                        <a class="btn btn-mini" href="<%= analyze_dial_result_path(@project,dial_job) %>" rel="tooltip" title="View Call Analysis"><i class="icon-eye-open"></i></a>
       -                        <a class="btn btn-mini" href="<%= reanalyze_dial_result_path(@project,dial_job) %>" data-confirm="Reprocess this job?" rel="nofollow tooltip" title="Rerun Call Analysis"><i class="icon-refresh"></i></a>
       -                <% else %>
       -                        <a class="btn btn-mini" href="<%= analyze_dial_result_path(@project,dial_job) %>" data-confirm="Analyze this job?" rel="nofollow tooltip" title="Run Call Analysis"><i class="icon-bolt"></i></a>
       -                <% end %>
       -
       -            <a class="btn btn-mini" href="<%= dial_result_path(@project,dial_job) %>" data-confirm="Delete all data for this job?" data-method="delete" rel="nofollow tooltip" title="Delete Call Data"><i class="icon-trash"></i></a>
       -        </td>
       -  </tr>
       -
       -<% end %>
       -</tbody>
       -</table>
       -
       -<%= raw(will_paginate @jobs) %>
       -
       -<% else %>
       -
       -<h1 class='title'>No Completed Jobs</h1>
       -<br/>
       -
       -<% end %>
       -
       -<a class="btn" href="<%= new_dial_job_path(@project) %>"><i class="icon-plus"></i> Start Job </a>
 (DIR) diff --git a/app/views/dial_results/new.html.erb b/app/views/dial_results/new.html.erb
       @@ -1,43 +0,0 @@
       -<h1>New dial_result</h1>
       -
       -<%= form_for(@dial_result) do |f| %>
       -  <%= f.error_messages %>
       -
       -  <p>
       -    <%= f.label :number %><br />
       -    <%= f.text_field :number %>
       -  </p>
       -  <p>
       -    <%= f.label :dial_job_id %><br />
       -    <%= f.text_field :dial_job_id %>
       -  </p>
       -  <p>
       -    <%= f.label :provider %><br />
       -    <%= f.text_field :provider %>
       -  </p>
       -  <p>
       -    <%= f.label :completed %><br />
       -    <%= f.check_box :completed %>
       -  </p>
       -  <p>
       -    <%= f.label :busy %><br />
       -    <%= f.check_box :busy %>
       -  </p>
       -  <p>
       -    <%= f.label :seconds %><br />
       -    <%= f.text_field :seconds %>
       -  </p>
       -  <p>
       -    <%= f.label :ringtime %><br />
       -    <%= f.text_field :ringtime %>
       -  </p>
       -  <p>
       -    <%= f.label :rawfile %><br />
       -    <%= f.text_field :rawfile %>
       -  </p>
       -  <p>
       -    <%= f.submit "Create" %>
       -  </p>
       -<% end %>
       -
       -<%= link_to 'Back', dial_results_path(@project) %>
 (DIR) diff --git a/app/views/dial_results/show.html.erb b/app/views/dial_results/show.html.erb
       @@ -1,48 +0,0 @@
       -<p>
       -  <b>Number:</b>
       -  <%=h @dial_result.number %>
       -</p>
       -
       -<p>
       -  <b>CallerID:</b>
       -  <%=h @dial_result.cid %>
       -</p>
       -
       -<p>
       -  <b>Dial job:</b>
       -  <%=h @dial_result.dial_job_id %>
       -</p>
       -
       -<p>
       -  <b>Provider:</b>
       -  <%=h @dial_result.provider %>
       -</p>
       -
       -<p>
       -  <b>Completed:</b>
       -  <%=h @dial_result.completed %>
       -</p>
       -
       -<p>
       -  <b>Busy:</b>
       -  <%=h @dial_result.busy %>
       -</p>
       -
       -<p>
       -  <b>Seconds:</b>
       -  <%=h @dial_result.seconds %>
       -</p>
       -
       -<p>
       -  <b>Ringtime:</b>
       -  <%=h @dial_result.ringtime %>
       -</p>
       -
       -<p>
       -  <b>Rawfile:</b>
       -  <%=h @dial_result.rawfile %>
       -</p>
       -
       -
       -<%= link_to 'Edit', edit_dial_result_path(@project, @dial_result) %> |
       -<%= link_to 'Back', dial_results_path(@project) %>
 (DIR) diff --git a/app/views/dial_results/view.html.erb b/app/views/dial_results/view.html.erb
       @@ -1,58 +0,0 @@
       -<% if @dial_results %>
       -
       -
       -<h1 class='title'>Dial Results for Job <%=@dial_results[0].dial_job_id%></h1>
       -
       -<%= raw(will_paginate @dial_results) %>
       -<table width='100%' align='center' border=0 cellspacing=0 cellpadding=6>
       -<tr>
       -        <td align='center'>
       -                <%= render :partial => 'shared/graphs/call_results' %>
       -        </td>
       -</tr>
       -</table>
       -
       -<br/>
       -
       -<table class='table table-striped table-bordered' width='90%' id='results'>
       -  <thead>
       -  <tr>
       -    <th>Number</th>
       -    <th>CallerID</th>
       -    <th>Provider</th>
       -    <th>Completed</th>
       -    <th>Busy</th>
       -    <th>Seconds</th>
       -        <th>Ring Time</th>
       -  </tr>
       -  </thead>
       -  <tbody>
       -<% for dial_result in @dial_results.sort{|a,b| a.number <=> b.number } %>
       -  <tr>
       -    <td><%= dial_result.number %></td>
       -    <td><%= dial_result.cid %></td>
       -    <td><%= dial_result.provider.name %></td>
       -    <td><%= dial_result.completed %></td>
       -    <td><%= dial_result.busy %></td>
       -    <td><%= dial_result.seconds %></td>
       -    <td><%= dial_result.ringtime.to_i %></td>
       -  </tr>
       -<% end %>
       -  </tbody>
       -</table>
       -<%= raw(will_paginate @dial_results) %>
       -
       -<% else %>
       -
       -<h1 class='title'>No Dial Results</h1>
       -
       -<% end %>
       -<br />
       -
       -<%= javascript_tag do %>
       -// For fixed width containers
       -$('#results').dataTable({
       -  "sDom": "<'row'<'span6'l><'span6'f>r>t<'row'<'span6'i><'span6'p>>",
       -  "sPaginationType": "bootstrap"
       -});
       -<% end %>
 (DIR) diff --git a/app/views/home/about.html.erb b/app/views/home/about.html.erb
       @@ -43,21 +43,21 @@ and research purposes only. The latest version of WarVOX can be found in <i clas
                <td>
                        Active Jobs
                </td>
       -        <td><%= DialJob.count(:conditions => ['status = ?', 'active']) %></td>
       +        <td><%= Job.count(:conditions => { :completed_at => nil }) %></td>
        </tr>
        
        <tr>
                <td>
                        Total Jobs
                </td>
       -        <td><%= DialJob.count %></td>
       +        <td><%= Job.count %></td>
        </tr>
        
        <tr>
                <td>
       -                Results
       +                Calls
                </td>
       -        <td><%= DialResult.count %></td>
       +        <td><%= Call.count %></td>
        </tr>
        </table>
        </div>
 (DIR) diff --git a/app/views/home/check.html.erb b/app/views/home/check.html.erb
       @@ -4,9 +4,9 @@
        <!--
                        @has_project  = ( Project.count > 0 )
                        @has_provider = ( Provider.count > 0 )
       -                @has_job      = ( DialJob.count > 0 )
       -                @has_result   = ( DialResult.where(:completed => true ).count > 0 )
       -                @has_analysis = ( DialResult.where(:processed => true ).count > 0 )
       +                @has_job      = ( Job.count > 0 )
       +                @has_result   = ( Call.where(:completed => true ).count > 0 )
       +                @has_analysis = ( Call.where(:processed => true ).count > 0 )
        -->
        
        <br/>
       @@ -54,7 +54,7 @@
                        <div class="alert alert-error">
                                <strong>Ring Ring!</strong>
                                <% if @has_project %>
       -                                <p>You need to <a href="<%= new_dial_job_path(Project.last) %>">dial your target range</a> to gather audio data.</p>
       +                                <p>You need to <a href="<%= new_job_path(Project.last) %>">dial your target range</a> to gather audio data.</p>
                                <% else %>
                                        <p>You need to <a href="<%= new_project_path %>">create a project</a>.</p>
                                <% end %>
 (DIR) diff --git a/app/views/home/index.html.erb b/app/views/home/index.html.erb
       @@ -26,7 +26,7 @@ connected to modems, allowing for a comprehensive audit of a telephone system.
        In order to make phone calls, WarVOX needs to be configured with one or more service providers. For a list of compatible VoIP ISPs, please see the <a href="http://warvox.org/#Providers">Service Providers</a> section of the WarVOX web site. To add a new provider to WarVOX, access the web interface, click the <a href="/providers">Providers</a> link, and fill in the <b>New Provider</b> form. This form allows you to specify a nickname for the provider and indicate how many concurrent outbound calls can be made using this account. WarVOX can make use of multiple service providers and multiple outbuond calls per provider when processing jobs.
        </p>
        
       -<p>Once one or more service providers have been configured, click the <a href="/dial_jobs/">Jobs</a> link. This will present a form that asks for the phone number range to dial, the number of seconds of audio to capture, and the maximum number of outgoing lines to use for this job.
       +<p>Once one or more service providers have been configured, click the <a href="/jobs/">Jobs</a> link. This will present a form that asks for the phone number range to dial, the number of seconds of audio to capture, and the maximum number of outgoing lines to use for this job.
        </p>
        
        <p>The phone number range is specified by entering the phone number (including country code), with numbers replaced by X's where an entire range should be dialed. For example, the value 1-512-555-XXXX will make 10,000 calls, one to each number within the 512-555 exchange. In contrast, the value 1-512-555-555X will only make 10 calls, covering 5550 to 5559. Only 5 digits of the phone number range can be masked.
       @@ -44,11 +44,11 @@ The outgoing line count is limited by the number of providers available and the 
        </p>
        
        <p>
       -Once the job parameters have been specified, click the <b>Create</b> button to start dialing. If you return the main <a href="/dial_jobs/">Jobs</a> screen you can track the progress of the job via the percentage value in the active jobs table. The amount of time a given job takes depends on the number of phone numbers within the range, the number of seconds of audio to record, and the number of concurrent outbound lines. A 1,000 number range with 53 seconds of audio and 10 outbound lines would take approximately 100 minutes to complete.
       +Once the job parameters have been specified, click the <b>Create</b> button to start dialing. If you return the main <a href="/jobs/">Jobs</a> screen you can track the progress of the job via the percentage value in the active jobs table. The amount of time a given job takes depends on the number of phone numbers within the range, the number of seconds of audio to record, and the number of concurrent outbound lines. A 1,000 number range with 53 seconds of audio and 10 outbound lines would take approximately 100 minutes to complete.
        </p>
        
        <p>
       -After the job completes, access the <a href="/dial_results/">Results</a> link to view a listing of finished jobs. Click the <b>View</b> link to look at the raw call breakdown. To analyze the call data, click on the Results link, and then click the <b>Analyze Calls</b> link. Depending on the speed of your server, this can take up to 60 seconds per connected call to process all of the data. Once the data has been processed, the results will now be listed under the <a href="/analyze/">Analysis</a> link. Viewing the results will provide the signal graph, spectrum graph, and an audio player to hear the actual call.
       +After the job completes, access the <a href="/calls/">Results</a> link to view a listing of finished jobs. Click the <b>View</b> link to look at the raw call breakdown. To analyze the call data, click on the Results link, and then click the <b>Analyze Calls</b> link. Depending on the speed of your server, this can take up to 60 seconds per connected call to process all of the data. Once the data has been processed, the results will now be listed under the <a href="/analyze/">Analysis</a> link. Viewing the results will provide the signal graph, spectrum graph, and an audio player to hear the actual call.
        </p>
        
        
 (DIR) diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb
       @@ -32,20 +32,29 @@
        
                                <%= menu_group :pull => :right  do %>
                                <% if @project and @project.id %>
       -                                <li class="project-title"><%= truncate(@project.name, :length => 20) %></li>
       -                                <%= menu_item "Dial", new_dial_job_path(@project) %>
       -                                <%= menu_item "Jobs", dial_jobs_path(@project) %>
       -                                <%= menu_item "Results", dial_results_path(@project) %>
       +                                <li>
       +                                        <%= link_to(
       +                                                raw(
       +                                                        '<i class="icon-chevron-left icon-white"></i> ' +
       +                                                        truncate(@project.name, :length => 20).html_safe +
       +                                                        ' <i class="icon-chevron-right icon-white"></i>'), project_path(@project), :class => 'project-title') %>
       +                                </li>
       +                                <%= menu_item "Results", calls_path(@project) %>
                                        <%= menu_item "Analysis", analyze_path(@project)%>
                                <% end %>
        
       +                                <%= menu_item "Jobs", jobs_path %>
                                        <%= drop_down "Projects" do %>
       -                                        <%= menu_item raw('<i class="icon-list"></i> Browse Projects'),projects_path %>
       +                                        <% if Project.count > 0 %>
       +                                                <%= menu_item raw('<i class="icon-list"></i> Browse Projects'),projects_path %>
       +                                        <% end %>
                                                <%= menu_item raw('<i class="icon-plus"></i> Create Project'), new_project_path %>
       -                                        <%= drop_down_divider %>
       -                                        <%= drop_down_header "Recent Projects" %>
       -                                        <% Project.find(:all, :order => 'ID DESC', :limit => 5).each do |project| %>
       -                                                <%= menu_item raw('<i class="icon-chevron-right"></i> ' + truncate(project.name, :length => 15).html_safe),project_path(project) %>
       +                                        <% if Project.count > 0 %>
       +                                                <%= drop_down_divider %>
       +                                                <%= drop_down_header "Recent Projects" %>
       +                                                <% Project.find(:all, :order => 'ID DESC', :limit => 5).each do |project| %>
       +                                                        <%= menu_item raw('<i class="icon-chevron-right"></i> ' + truncate(project.name, :length => 15).html_safe),project_path(project) %>
       +                                                <% end %>
                                                <% end %>
                                        <% end %>
        
       @@ -65,13 +74,9 @@
        
              <div class="row">
                <div class="span12 content">
       -<% # render_breadcrumbs %>
                  <div class="content">
        
       -
       -
       -<p style="color: green"><%= flash[:notice] %></p>
       -
       +          <p style="color: green"><%= flash[:notice] %></p>
                  <%= yield %>
                  </div>
        
 (DIR) diff --git a/app/views/projects/edit.html.erb b/app/views/projects/edit.html.erb
       @@ -1,11 +1,10 @@
        <h1 class='title'>Update Project</h1>
        
        <%= semantic_form_for(@project) do |f| %>
       -        <%= f.input :name, :as => :string, :label => 'Project Name' %>
       +        <%= f.input :name, :as => :string, :label => 'Name' %>
                <%= f.input :description, :as => :text, :input_html => { :class => 'project_description' } %>
       -        <%= f.input :included,  :as => :text, :label => 'Default phone numbers to include', :input_html => { :class => 'project_includes' } %>
       -        <%= f.input :excluded,  :as => :text, :label => 'Default phone numbers to exclude', :input_html => { :class => 'project_includes' } %>
       -        <%= f.actions :submit, :label => 'Update' %>
       +        <%= f.action :submit, :label => 'Update', :button_html => { :class => 'btn btn-large fbtn' }  %>
       +        <a class="btn btn-link" href="<%= projects_path %>"rel="tooltip" title="Return to projects"><i class="icon-return"></i>Cancel</a>
        <% end %>
        
       -<%= link_to 'Back', projects_path %>
       +<%= set_focus('project_name') %>
 (DIR) diff --git a/app/views/projects/index.html.erb b/app/views/projects/index.html.erb
       @@ -7,17 +7,23 @@
          <tr>
            <th>Name</th>
            <th>Description</th>
       +    <th>Jobs</th>
       +    <th>Calls</th>
       +    <th>Analyzed</th>
            <th>Date</th>
            <th>Actions</th>
          </tr>
          </thead>
          <tbody>
        
       -<% @projects.sort{|a,b| b.id <=> a.id}.each do |project|  %>
       +<% @projects.each do |project|  %>
          <tr>
            <td><%= link_to( h(project.name), project_path(project)) %></td>
            <td><%=truncate(project.description, :length => 40, :separator => '') %></td>
       -    <td><%=h project.updated_at.localtime.strftime("%Y-%m-%d %H:%M:%S") %></td>
       +    <td><%= project.jobs.count %></td>
       +    <td><%= project.calls.count %></td>
       +    <td><%= project.calls.where('analysis_completed_at IS NOT NULL').count %></td>
       +    <td><%= project.updated_at.localtime.strftime("%Y-%m-%d %H:%M:%S") %></td>
            <td>
                      <a class="btn btn-mini" href="<%= edit_project_path(project) %>"rel="tooltip" title="Update Project Information"><i class="icon-pencil"></i></a>
                    <a class="btn btn-mini" href="<%= project_path(project) %>" data-confirm="Delete this project?" data-method="delete" rel="nofollow tooltip" title="Delete Project"><i class="icon-trash"></i></a>
 (DIR) diff --git a/app/views/projects/new.html.erb b/app/views/projects/new.html.erb
       @@ -1,18 +1,10 @@
        <h1 class='title'>New Project</h1>
        
       -<div class='fconstrained'>
       -
        <%= semantic_form_for(@new_project) do |f| %>
       -        <%= f.input :name, :as => :string, :label => 'Name' %>
       +        <%= f.input :name, :as => :string, :label => 'Name', :input_html => { :autofocus => true }  %>
                <%= f.input :description, :as => :text, :input_html => { :class => 'project_description' } %>
       -        <%= f.input :included,  :as => :text, :label => 'Default phone numbers to include', :input_html => { :class => 'project_includes' } %>
       -        <%= f.input :excluded,  :as => :text, :label => 'Default phone numbers to exclude', :input_html => { :class => 'project_includes' } %>
       -
                <%= f.action :submit, :label => 'Create', :button_html => { :class => 'btn btn-large fbtn' }  %>
       -
                <a class="btn btn-link" href="<%= projects_path %>"rel="tooltip" title="Return to projects"><i class="icon-return"></i>Cancel</a>
        <% end %>
       -</div>
       -
        
        <%= set_focus('project_name') %>
 (DIR) diff --git a/app/views/providers/edit.html.erb b/app/views/providers/edit.html.erb
       @@ -1,38 +1,13 @@
       -<h1 class='title'>Edit Provider</h1>
       +<h1 class='title'>Update Provider</h1>
        
       -<%= form_for(@provider) do |f| %>
       -  <%= f.error_messages %>
       -  <p>
       -    <%= f.label :enabled %><br />
       -    <%= f.check_box :enabled %>
       -  </p>
       -  <p>
       -    <%= f.label :name %><br />
       -    <%= f.text_field :name %>
       -  </p>
       -  <p>
       -    <%= f.label :host %><br />
       -    <%= f.text_field :host %>
       -  </p>
       -  <p>
       -    <%= f.label :port %><br />
       -    <%= f.text_field :port %>
       -  </p>
       -  <p>
       -    <%= f.label :user %><br />
       -    <%= f.text_field :user %>
       -  </p>
       -  <p>
       -    <%= f.label :pass %><br />
       -    <%= f.text_field :pass %>
       -  </p>
       -  <p>
       -    <%= f.label :lines %><br />
       -    <%= f.text_field :lines %>
       -  </p>
       -  <p>
       -    <%= f.submit "Update" %>
       -  </p>
       -<% end %>
       +<%= semantic_form_for(@provider) do |f| %>
       +        <%= f.input :name, :as => :string, :label => 'Name', :hint => 'A friendly name for this provider', :autofocus => true %>
       +        <%= f.input :host, :as => :string, :label => 'IAX2 Server', :hint => 'The IP address or hostname of the IAX2 service' %>
       +        <%= f.input :port, :as => :number, :label => 'IAX2 Port', :hint => 'The port of the IAX2 service' %>
       +        <%= f.input :user, :as => :string, :label => 'Username' %>
       +        <%= f.input :pass, :as => :string, :label => 'Password', :input_html => { :autocomplete => false }  %>
       +        <%= f.input :lines, :as => :number, :label => 'Maximum Lines', :hint => 'Maximum concurrent outbound lines' %>
        
       -<%= link_to 'Back', providers_path %>
       +        <%= f.action :submit, :label => 'Update', :button_html => { :class => 'btn btn-large fbtn' }  %>
       +        <a class="btn btn-link" href="<%= providers_path %>" rel="tooltip" title="Return to providers"><i class="icon-return"></i>Cancel</a>
       +<% end %>
 (DIR) diff --git a/app/views/providers/index.html.erb b/app/views/providers/index.html.erb
       @@ -1,27 +1,29 @@
        <% if @providers.length > 0 %>
        <h1 class='title'>VoIP Providers (IAX)</h1>
       +
       +<%= will_paginate @providers, :renderer => BootstrapPagination::Rails %>
        <table class='table table-striped table-bordered' width='90%'>
          <tr>
            <th>Active</th>
            <th>Provider Name</th>
       -    <th>IAX Server</th>
       -    <th>IAX Port</th>
       +    <th>IAX2 Server</th>
       +    <th>IAX2 Port</th>
            <th>Username</th>
            <th>Lines</th>
            <th>Actions</th>
        
          </tr>
        
       -<% for provider in @providers %>
       +<% @providers.each do |provider|  %>
          <tr>
            <td>
            <i class="icon-<%= provider.enabled  ? "ok" : "remove" %>"></i>
            </td>
       -    <td><%=h provider.name %></td>
       -    <td><%=h provider.host %></td>
       -    <td><%=h provider.port %></td>
       -    <td><%=h provider.user %></td>
       -    <td><%=h provider.lines %></td>
       +    <td><%= provider.name %></td>
       +    <td><%= provider.host %></td>
       +    <td><%= provider.port %></td>
       +    <td><%= provider.user %></td>
       +    <td><%= provider.lines %></td>
            <td>
                    <a class="btn btn-mini" href="<%= edit_provider_path(provider) %>"rel="tooltip" title="Update Provider Information"><i class="icon-pencil"></i></a>
                    <a class="btn btn-mini" href="<%= provider_path(provider) %>" data-confirm="Remove this provider?" data-method="delete" rel="nofollow tooltip" title="Remove Provider"><i class="icon-trash"></i></a>
       @@ -29,18 +31,10 @@
          </tr>
        <% end %>
        </table>
       -
       -<br />
       -
       -
       -<a class="btn" href="<%= new_provider_path %>"><i class="icon-plus"></i> Add Provider </a>
       -
       +<%= will_paginate @providers, :renderer => BootstrapPagination::Rails %>
        <% else %>
       -
        <h1 class='title'>No Configured Providers</h1>
       -<br/>
       +<% end %>
        
       +<br/>
        <a class="btn" href="<%= new_provider_path %>"><i class="icon-plus"></i> Add Provider </a>
       -
       -
       -<% end %>
 (DIR) diff --git a/app/views/providers/new.html.erb b/app/views/providers/new.html.erb
       @@ -1,34 +1,13 @@
        <h1 class='title'>New Provider</h1>
        
       -<%= form_for(@provider) do |f| %>
       -  <%= f.error_messages %>
       -  <p>
       -    <%= f.label :name, 'The nickname for this provider' %><br />
       -    <%= f.text_field :name %>
       -  </p>
       -  <p>
       -    <%= f.label :host, 'The IAX2 server name' %><br />
       -    <%= f.text_field :host %>
       -  </p>
       -  <p>
       -    <%= f.label :port, 'The IAX2 port (normally 4569)' %><br />
       -    <%= f.text_field :port, :value => 4569 %>
       -  </p>
       -  <p>
       -    <%= f.label :user, 'The username to access the provider' %><br />
       -    <%= f.text_field :user %>
       -  </p>
       -  <p>
       -    <%= f.label :pass, 'The password to access the provider' %><br />
       -    <%= f.text_field :pass %>
       -  </p>
       -  <p>
       -    <%= f.label :lines, 'The number of available outbound lines' %><br />
       -    <%= f.text_field :lines, :value => 1 %>
       -  </p>
       -  <p>
       -    <%= f.submit "Create" %>
       -  </p>
       -<% end %>
       +<%= semantic_form_for(@provider) do |f| %>
       +        <%= f.input :name, :as => :string, :label => 'Name', :hint => 'A friendly name for this provider', :autofocus => true %>
       +        <%= f.input :host, :as => :string, :label => 'IAX2 Server', :hint => 'The IP address or hostname of the IAX2 service' %>
       +        <%= f.input :port, :as => :number, :label => 'IAX2 Port', :hint => 'The port of the IAX2 service' %>
       +        <%= f.input :user, :as => :string, :label => 'Username' %>
       +        <%= f.input :pass, :as => :password, :label => 'Password'  %>
       +        <%= f.input :lines, :as => :number, :label => 'Maximum Lines', :hint => 'Maximum concurrent outbound lines' %>
        
       -<%= link_to 'Back', providers_path %>
       +        <%= f.action :submit, :label => 'Create', :button_html => { :class => 'btn btn-large fbtn' }  %>
       +        <a class="btn btn-link" href="<%= providers_path %>" rel="tooltip" title="Return to providers"><i class="icon-return"></i>Cancel</a>
       +<% end %>
 (DIR) diff --git a/app/views/providers/show.html.erb b/app/views/providers/show.html.erb
       @@ -1,38 +0,0 @@
       -<h1 class='title'>View Provider</h1>
       -<p>
       -  <b>Enabled:</b>
       -  <%=h @provider.enabled %>
       -</p>
       -<p>
       -  <b>Name:</b>
       -  <%=h @provider.name %>
       -</p>
       -
       -<p>
       -  <b>Host:</b>
       -  <%=h @provider.host %>
       -</p>
       -
       -<p>
       -  <b>Port:</b>
       -  <%=h @provider.port %>
       -</p>
       -
       -<p>
       -  <b>User:</b>
       -  <%=h @provider.user %>
       -</p>
       -
       -<p>
       -  <b>Pass:</b>
       -  ********
       -</p>
       -
       -<p>
       -  <b>Lines:</b>
       -  <%=h @provider.lines %>
       -</p>
       -
       -
       -<%= link_to 'Edit', edit_provider_path(@provider) %> |
       -<%= link_to 'Back', providers_path %>
 (DIR) diff --git a/app/views/user_sessions/new.html.erb b/app/views/user_sessions/new.html.erb
       @@ -6,7 +6,7 @@
                        <% if @user_session.errors.any? %>
                                <div class="alert"><%= @user_session.errors.full_messages.first %></div>
                        <% end %>
       -                <p><div class="login-label"><h3>Username</h3></div><%= f.text_field :login, :spellcheck => false %></p>
       +                <p><div class="login-label"><h3>Username</h3></div><%= f.text_field :login, :spellcheck => false, :autofocus => true %></p>
                        <p><div class="login-label"><h3>Password</h3></div><%= f.password_field :password, :autocomplete => 'off' %></p>
                        <%= f.submit "Sign in", :class => "btn-login btn btn-warning" %>
                <% end %>
 (DIR) diff --git a/bin/export_audio.rb b/bin/export_audio.rb
       @@ -12,16 +12,13 @@ $:.unshift(File.join(File.expand_path(File.dirname(base)), '..', 'lib'))
        
        require 'warvox'
        require 'fileutils'
       -
       +require 'yaml'
        
        ENV['RAILS_ENV'] ||= 'production'
       -
        $:.unshift(File.join(File.expand_path(File.dirname(base)), '..'))
       -require 'config/boot'
       -require 'config/environment'
        
        def usage
       -        $stderr.puts "Usage: #{$0} [Output Dir] [Job ID] <Type>"
       +        $stderr.puts "Usage: #{$0} [Output Dir] [Project ID] <Line Type>"
                exit
        end
        
       @@ -29,41 +26,66 @@ end
        # Script
        #
        
       -dir = ARGV.shift
       -job = ARGV.shift
       -typ = ARGV.shift
       +output     = ARGV.shift
       +project_id = ARGV.shift
       +line_type  = ARGV.shift
        
       -if(job and job == "-h")
       +if(output and output == "-h") or (! output)
                usage()
        end
        
       -if(not job)
       -        $stderr.puts "Listing all available jobs"
       -        $stderr.puts "=========================="
       -        DialJob.all.each do |j|
       -                puts "#{j.id}\t#{j.started_at} --> #{j.completed_at}"
       +require 'config/boot'
       +require 'config/environment'
       +
       +if(not project_id)
       +        $stderr.puts "Listing all projects"
       +        $stderr.puts "===================="
       +        Project.find(:all).each do |j|
       +                puts "#{j.id}\t#{j.name}\t#{j.created_at}"
                end
                exit
        end
        
       -
       -::FileUtils.mkdir_p(dir)
       +FileUtils.mkdir_p(output)
        
        begin
       -        cnt = 0
       -        DialResult.where(:dial_job_id => job.to_i).find_each do |r|
       -                next if not r.number
       +        cond = { :project_id => project_id.to_i, :answered => true, :busy => false }
       +        if line_type
       +                cond[:line_type] = line_type.downcase
       +        end
       +
       +        Call.where(cond).find(:order => :number) do |r|
                        m = r.media
       -                next if not m
       -                next if m.audio.to_s.length == 0
       -                out = ::File.join(dir, "#{r.number}.raw")
       -                ::File.open(out, "wb") do |fd|
       -                        fd.write( m.audio )
       +                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
       -                cnt += 1
                end
       -        $stderr.puts "Wrote #{cnt} audio files to #{dir}"
       -rescue ActiveRecord::RecordNotFound
       -        $stderr.puts "Job not found"
       -        exit
        end
 (DIR) diff --git a/bin/iaxrecord.rb b/bin/iaxrecord.rb
       @@ -2,23 +2,31 @@
        
        $:.unshift(::File.join(::File.dirname(__FILE__), "..", "lib"))
        
       +
       +def stop
       +        exit(0)
       +end
       +
       +trap("SIGINT")  { stop() }
       +trap("SIGTERM") { stop() }
       +
        require 'rubygems'
        require "rex/proto/iax2"
        require "optparse"
        
        parser = OptionParser.new
       -opts   = { 
       -        :recording_time => 52 
       +opts   = {
       +        :recording_time => 52
        }
        
        parser.banner = "Usage: #{$0} [options]"
        parser.on("-s server") do |v|
                opts[:server_host] = v
        end
       -        
       +
        parser.on("-u user") do |v|
                opts[:username] = v
       -end        
       +end
        
        parser.on("-p pass") do |v|
                opts[:password] = v
       @@ -26,34 +34,34 @@ end
        
        parser.on("-o output") do |v|
                opts[:output] = v
       -end        
       +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 
       -end        
       +        opts[:caller_number] = v
       +end
        
        parser.on("-l seconds") do |v|
                opts[:recording_time] = v.to_i
       -end        
       +end
        
        parser.on("-d") do |v|
                opts[:debugging] = true
       -end        
       +end
        
        parser.on("-h") do
                $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)        
       +        exit(1)
        end
        
        
       @@ -61,7 +69,7 @@ cli = Rex::Proto::IAX2::Client.new(opts)
        
        reg = cli.create_call
        r   = reg.register
       -if not r 
       +if not r
                $stderr.puts "ERROR: Unable to register with the IAX server"
                exit(0)
        end
       @@ -75,7 +83,7 @@ end
        
        begin
        
       -::Timeout.timeout( opts[:recording_time] ) do 
       +::Timeout.timeout( opts[:recording_time] ) do
                while (c.state != :hangup)
                        case c.state
                        when :ringing
       @@ -102,4 +110,3 @@ end
        fd.close
        
        $stdout.puts "COMPLETED: BYTES=#{cnt} RINGTIME=#{c.ring_time} FILE=#{ ::File.expand_path( opts[:output] ) } BUSY=#{c.busy ? 1 : 0} FAIL=#{cnt == 0 ? 1 : 0}"
       -
 (DIR) diff --git a/bin/import_audio.rb b/bin/import_audio.rb
       @@ -17,8 +17,6 @@ require 'fileutils'
        ENV['RAILS_ENV'] ||= 'production'
        
        $:.unshift(File.join(File.expand_path(File.dirname(base)), '..'))
       -require 'config/boot'
       -require 'config/environment'
        
        def usage
                $stderr.puts "Usage: #{$0} [Input Directory] <Project ID> <Provider ID>"
       @@ -34,6 +32,8 @@ if (dir and dir =="-h") or (! dir)
                usage()
        end
        
       +require 'config/boot'
       +require 'config/environment'
        
        project_id  = ARGV.shift
        provider_id = ARGV.shift
       @@ -66,7 +66,7 @@ end
        
        unless project
                project = Project.create(
       -                :name       => "Import from #{dir}",
       +                :name       => "Import from #{dir} at #{Time.now.utc.to_s}",
                        :created_by => "importer"
                )
        end
       @@ -87,7 +87,6 @@ end
        
        job = Job.new
        job.project_id   = project.id
       -job.name         = "Import Audio Job"
        job.locked_by    = "importer"
        job.locked_at    = Time.now.utc
        job.started_at   = Time.now.utc
       @@ -95,7 +94,7 @@ job.created_by   = "importer"
        job.task         = "import"
        job.args         = Marshal.dump({ :directory => dir, :project_id => project.id, :provider_id => provider.id })
        job.status       = "running"
       -job.save
       +job.save!
        
        pct  = 0
        cnt  = 0
 (DIR) diff --git a/bin/warvox.rb b/bin/warvox.rb
       @@ -12,10 +12,15 @@ while File.symlink?(base)
                base = File.expand_path(File.readlink(base), File.dirname(base))
        end
        
       +$:.unshift(File.join(File.expand_path(File.dirname(base)), '..', 'lib'))
       +
        voxroot = File.expand_path(File.join(File.dirname(base), '..'))
        voxserv = File.expand_path(File.join(File.expand_path(voxroot), 'script', 'rails'))
        manager = File.expand_path(File.join(File.dirname(base), 'worker_manager.rb'))
        
       +require 'warvox'
       +
       +
        Dir.chdir(voxroot)
        
        def stop
       @@ -77,15 +82,22 @@ $stderr.puts "[*] Starting WarVOX on #{$browser_url}"
        $stderr.puts ""
        
        
       +WarVOX::Log.info("WarVOX is starting up...")
       +
        @manager_pid = Process.fork()
        if not @manager_pid
       -        while (ARGV.shift) { }
       +        while ARGV.shift do
       +        end
                load(manager)
                exit(0)
        end
        
       +WarVOX::Log.info("Worker Manager has PID #{@manager_pid}")
       +
        @webserver_pid = $$
        
       +WarVOX::Log.info("Web Server has PID #{@manager_pid}")
       +
        while(ARGV.length > 0); ARGV.shift; end
        args.each {|arg| ARGV.push(arg) }
        
 (DIR) diff --git a/config/routes.rb b/config/routes.rb
       @@ -1,44 +1,46 @@
        Web::Application.routes.draw do
        
       -  resources :projects
       -  resources :settings
       -  resources :providers
        
       -  resources :users
       +
       +
       +
          match "login" => "user_sessions#new", :as => "login"
          match "logout" => "user_sessions#destroy", :as => "logout"
       +
          resources :user_sessions
        
          match  '/projects/:project_id/all'                    => 'projects#index', :as => :all_projects
        
       -  match  '/projects/:project_id/jobs'                   => 'dial_jobs#index', :as => :dial_jobs
       -  match  '/projects/:project_id/jobs/:id/run'           => 'dial_jobs#run', :as => :run_dial_job
       -  match  '/projects/:project_id/jobs/:id/stop'          => 'dial_jobs#stop', :as => :stop_dial_job
       -  match  '/projects/:project_id/jobs/new'               => 'dial_jobs#new', :as => :new_dial_job
       -  delete '/projects/:project_id/jobs/:id'               => 'dial_jobs#destroy'
        
       -  match  '/projects/:project_id/results/'               => 'dial_results#index', :as => :dial_results
       -  match  '/projects/:project_id/results/:id/view'       => 'dial_results#view', :as => :view_dial_result
       -  match  '/projects/:project_id/results/:id/analyze'    => 'dial_results#analyze', :as => :analyze_dial_result
       -  match  '/projects/:project_id/results/:id/reanalyze'  => 'dial_results#reanalyze', :as => :reanalyze_dial_result
       -  match  '/projects/:project_id/results/:id/purge'      => 'dial_results#purge', :as => :purge_dial_result
       -  delete '/projects/:project_id/results/:id'            => 'dial_results#destroy'
       +  match  '/jobs/dial'          => 'jobs#new_dialer', :as => :new_dialer_job
       +  match  '/jobs/dialer'          => 'jobs#dialer', :as => :dialer_job
       +  match  '/jobs/analyze'       => 'jobs#new_analyzer', :as => :new_analyzer_job
       +  match  '/jobs/analyzer'       => 'jobs#analyzer', :as => :analyzer_job
       +  match  '/jobs/:id/stop'          => 'jobs#stop', :as => :stop_job
       +
       +
       +  match  '/projects/:project_id/calls/'               => 'calls#index', :as => :calls
       +  match  '/projects/:project_id/calls/:id/view'       => 'calls#view', :as => :view_call
       +  match  '/projects/:project_id/calls/:id/analyze'    => 'calls#analyze', :as => :analyze_call
       +  match  '/projects/:project_id/calls/:id/reanalyze'  => 'calls#reanalyze', :as => :reanalyze_call
       +  match  '/projects/:project_id/calls/:id/purge'      => 'calls#purge', :as => :purge_call
       +  delete '/projects/:project_id/calls/:id'            => 'calls#destroy'
        
          match '/projects/:project_id/analyze'             => 'analyze#index', :as => :analyze
          match '/projects/:project_id/analyze/:id/resource/:result_id/:type' => 'analyze#resource', :as => :resource_analyze
          match '/projects/:project_id/analyze/:id/view'    => 'analyze#view', :as => :view_analyze
       -  match '/projects/:project_id/analyze/:dial_result_id/matches'    => 'analyze#view_matches', :as => :view_matches
       +  match '/projects/:project_id/analyze/:call_id/matches'    => 'analyze#view_matches', :as => :view_matches
          match '/projects/:project_id/analyze/:id/show'    => 'analyze#show', :as => :show_analyze
        
       -  match '/projects/:project_id/providers'           => 'providers#index', :as => :project_providers
        
       +  resources :settings
       +  resources :providers
       +  resources :users
       +  resources :projects
       +  resources :jobs
        
       -  match '/projects/:project_id/about'    => 'home#about', :as => :project_about
       -
       -  match '/projects/:project_id/settings'    => 'settings#index', :as => :project_settings
       -  match '/about'               => 'home#about'
       -  match '/home/about'          => 'home#about'
       -  match '/help'                => 'home#help'
       +  match '/about'               => 'home#about', :as => :about
       +  match '/help'                => 'home#help',  :as => :help
          match '/check'               => 'home#check', :as => :check
        
        
 (DIR) diff --git a/db/migrate/20121228171549_initial_schema.rb b/db/migrate/20121228171549_initial_schema.rb
       @@ -49,7 +49,6 @@ class InitialSchema < ActiveRecord::Migration
                        create_table "jobs" do |t|
                                t.timestamps
                                t.integer        "project_id", :null => false
       -                        t.string        "name", :null => false
                                t.string        "locked_by"
                                t.timestamp        "locked_at"
                                t.timestamp        "started_at"
       @@ -73,6 +72,7 @@ class InitialSchema < ActiveRecord::Migration
                        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"
       @@ -104,6 +104,7 @@ class InitialSchema < ActiveRecord::Migration
        
                        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"
 (DIR) diff --git a/db/schema.rb b/db/schema.rb
       @@ -17,6 +17,7 @@ ActiveRecord::Schema.define(:version => 20121228171549) do
        
          create_table "call_media", :force => true do |t|
            t.integer "call_id",      :null => false
       +    t.integer "project_id",   :null => false
            t.binary  "audio"
            t.binary  "mp3"
            t.binary  "png_big"
       @@ -35,6 +36,7 @@ ActiveRecord::Schema.define(:version => 20121228171549) do
            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"
       @@ -51,7 +53,6 @@ ActiveRecord::Schema.define(:version => 20121228171549) do
            t.datetime "created_at",                  :null => false
            t.datetime "updated_at",                  :null => false
            t.integer  "project_id",                  :null => false
       -    t.string   "name",                        :null => false
            t.string   "locked_by"
            t.datetime "locked_at"
            t.datetime "started_at"
       @@ -68,6 +69,7 @@ ActiveRecord::Schema.define(:version => 20121228171549) do
            t.datetime "created_at",                       :null => false
            t.datetime "updated_at",                       :null => false
            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"
       @@ -85,7 +87,7 @@ ActiveRecord::Schema.define(:version => 20121228171549) do
          create_table "projects", :force => true do |t|
            t.datetime "created_at",  :null => false
            t.datetime "updated_at",  :null => false
       -    t.text     "name"
       +    t.text     "name",        :null => false
            t.text     "description"
            t.text     "included"
            t.text     "excluded"
 (DIR) diff --git a/lib/warvox.rb b/lib/warvox.rb
       @@ -7,11 +7,14 @@ require 'warvox/config'
        require 'warvox/jobs'
        require 'warvox/phone'
        require 'warvox/audio'
       +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'))
       -        JobManager = WarVOX::JobQueue.new
       +        Log  = Logger.new( WarVOX::Config.log_file )
       +        Log.level = WarVOX::Config.log_level
       +
        end
 (DIR) diff --git a/lib/warvox/config.rb b/lib/warvox/config.rb
       @@ -83,6 +83,14 @@ module Config
                        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 = []
 (DIR) diff --git a/lib/warvox/jobs/dialer.rb b/lib/warvox/jobs/dialer.rb
       @@ -145,7 +145,7 @@ class Dialer < Base
                                                        end
                                                end
        
       -                                        :ActiveRecord::Base.connection_pool.with_connection do
       +                                        ::ActiveRecord::Base.connection_pool.with_connection do
                                                        job = Job.find(@job_id)
                                                        if not job
                                                                raise RuntimeError, "The parent job is not available"
       @@ -158,8 +158,8 @@ class Dialer < Base
                                                        res.provider_id   = prov[:id]
                                                        res.answered      = (fail == 0) ? true : false
                                                        res.busy          = (busy == 1) ? true : false
       -                                                res.audio_seconds = (byte / 16000)  # 8khz @ 16-bit
       -                                                res.ring_seconds  = ring
       +                                                res.audio_length = (byte / 16000)  # 8khz @ 16-bit
       +                                                res.ring_length  = ring
                                                        res.caller_id     = cid
        
                                                        res.save
       @@ -211,7 +211,7 @@ class Dialer < Base
        
                def update_progress(pct)
                        ::ActiveRecord::Base.connection_pool.with_connection {
       -                        Job.update({ :progress => pct }, { :id => @job_id })
       +                        Job.update_all({ :progress => pct }, { :id => @job_id })
                        }
                end