Sync research changes - warvox - VoIP based wardialing tool, forked from rapid7/warvox.
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) README
---
(DIR) commit 3e3fbfb5bd9f1295c66868ae3a94b3d41040a98d
(DIR) parent 93b2de6b613e819c4123fa15eccf878a5adb4949
(HTM) Author: HD Moore <hd_moore@rapid7.com>
Date: Sun, 13 Jan 2013 11:12:14 -0600
Sync research changes
Diffstat:
M app/assets/stylesheets/bootstrap_a… | 93 ++++++++++++++++++++++++++++---
M app/controllers/application_contro… | 2 +-
M app/controllers/jobs_controller.rb | 2 +-
M app/controllers/projects_controlle… | 196 +++++++++++++++++++------------
M app/helpers/application_helper.rb | 4 ++++
M app/models/call.rb | 31 +++++++++++++++++++++++--------
M app/models/job.rb | 20 +++++++++++++++++---
M app/views/jobs/index.html.erb | 14 +++++++++-----
M app/views/jobs/results.html.erb | 6 +++---
M app/views/layouts/application.html… | 2 +-
M app/views/projects/index.html.erb | 4 ++--
M app/views/projects/show.html.erb | 240 +++++++++++++++++++++++++------
M app/views/shared/graphs/_sparkline… | 4 ++--
M db/migrate/20121228171549_initial_… | 2 +-
M db/schema.rb | 2 +-
15 files changed, 467 insertions(+), 155 deletions(-)
---
(DIR) diff --git a/app/assets/stylesheets/bootstrap_and_overrides.css.less b/app/assets/stylesheets/bootstrap_and_overrides.css.less
@@ -86,8 +86,13 @@
.sparkline {
- width: 100px;
- height: 20px;
+ width: 100%;
+ height: 30px;
+}
+
+.sparkline-title {
+ text-align: center;
+ font-size: 11px;
}
.call-detail {
@@ -99,17 +104,89 @@
border-bottom: 1px solid #eeeeee;
}
+.zoom {
+ background-color: @orange;
+ height: 40px;
+}
+
+
.stat-box {
padding: 10px;
-
- background-color: #eeeeee;
- border: 1px solid #bbbbbb;
- font-size: 18px;
+ background-color: white;
+ border: 2px solid @darkGray;
+ font-size: 16px;
font-weight: bold;
color: @darkGray;
- width: 65px;
+ width: 55px;
margin: auto auto;
text-align: center;
+
+ border-top-left-radius: 20px;
+ border-top-right-radius: 20px;
+ border-bottom-right-radius: 20px;
+ border-bottom-left-radius: 20px;
+}
+
+.stat-modem {
+ background-color: white;
+ border: 2px solid @red;
+ color: @red;
+}
+
+
+.stat-nodata {
+ background-color: #f4f4f4;
+ border: 2px solid @darkGray;
+ color: @darkGray;
+ font-size: 24px;
+}
+
+.stat-completed {
+ background-color: white;
+ border: 2px solid @orange;
+ color: @darkGray;
+}
+
+.stat-voice {
+ background-color: white;
+ border: 2px solid @green;
+ color: @darkGray;
+}
+
+.stat-voicemail {
+ background-color: white;
+ border: 2px solid @blue;
+ color: @darkGray;
+}
+
+.stat-fax {
+ background-color: white;
+ border: 2px solid @darkGray;
+ color: @darkGray;
+}
+
+.stat-modem {
+ background-color: white;
+ border: 2px solid @red;
+ color: @darkGray;
+}
+
+
+.arrow-down {
+ font-size: 18px;
+ text-align: center;
+ margin: auto auto;
+ padding-top: 10px;
+ padding-bottom: 10px;
+ color: @darkOrange;
+}
+
+.arrow-right {
+ font-size: 22px;
+ text-align: center;
+ margin: auto auto;
+ padding-top: 10px;
+ padding-bottom: 10px;
}
.stat-subtitle {
@@ -170,7 +247,7 @@
}
.progress_pct {
- color: @red;
+ color: @darkGray;
margin-left: 10px;
font-weight: bold;
}
(DIR) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
@@ -6,7 +6,7 @@ class ApplicationController < ActionController::Base
before_filter :require_user, :load_project
add_breadcrumb :projects, :root_path
-
+ include ActionView::Helpers::NumberHelper
private
(DIR) diff --git a/app/controllers/jobs_controller.rb b/app/controllers/jobs_controller.rb
@@ -7,7 +7,7 @@ class JobsController < ApplicationController
@submitted_jobs = Job.where(:status => ['submitted', 'scheduled'], :completed_at => nil)
@active_jobs = Job.where(:status => 'running', :completed_at => nil)
- @inactive_jobs = Job.where('status NOT IN (?) OR completed_at IS NULL', ['submitted', 'scheduled', 'running']).paginate(
+ @inactive_jobs = Job.where('status NOT IN (?)', ['submitted', 'scheduled', 'running']).paginate(
:page => params[:page],
:order => 'id DESC',
:per_page => 30
(DIR) diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
@@ -1,7 +1,7 @@
class ProjectsController < ApplicationController
def index
- @projects = Project.paginate(
+ @projects = Project.paginate(
:page => params[:page],
:order => 'id DESC',
:per_page => 10
@@ -10,83 +10,129 @@ class ProjectsController < ApplicationController
@new_project = Project.new
respond_to do |format|
- format.html # index.html.erb
- format.xml { render :xml => @projects }
+ format.html
+ format.xml { render :xml => @projects }
end
end
- # GET /projects/1
- # GET /projects/1.xml
- def show
- @project = Project.find(params[:id])
- respond_to do |format|
- format.html # show.html.erb
- format.xml { render :xml => @project }
- end
-
- end
-
- # GET /projects/new
- # 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 }
- end
- end
-
- # GET /projects/1/edit
- def edit
- @project = Project.find(params[:id])
- end
-
- # POST /projects
- # POST /projects.xml
- def create
- @new_project = Project.new(params[:project])
- @new_project.created_by = current_user.login
-
- respond_to do |format|
- if @new_project.save
- flash[:notice] = 'Project was successfully created.'
- format.html { redirect_to(project_path(@new_project)) }
- format.xml { render :xml => @project, :status => :created, :location => @new_project }
- else
- format.html { render :action => "new" }
- format.xml { render :xml => @new_project.errors, :status => :unprocessable_entity }
- end
- end
- end
-
- # PUT /projects/1
- # PUT /projects/1.xml
- def update
- @project = Project.find(params[:id])
-
- respond_to do |format|
- if @project.update_attributes(params[:project])
- flash[:notice] = 'Project was successfully updated.'
- format.html { redirect_to projects_path }
- format.xml { head :ok }
- else
- format.html { render :action => "edit" }
- format.xml { render :xml => @project.errors, :status => :unprocessable_entity }
- end
- end
- end
-
- # DELETE /projects/1
- # DELETE /projects/1.xml
- def destroy
- @project = Project.find(params[:id])
- @project.destroy
-
- respond_to do |format|
- format.html { redirect_to(projects_url) }
- format.xml { head :ok }
- end
- end
+ def show
+ @project = Project.find(params[:id])
+ @active_jobs = @project.jobs.where(:status => 'running', :completed_at => nil)
+ @inactive_jobs = @project.jobs.where('status NOT IN (?)', ['submitted', 'scheduled', 'running']).paginate(
+ :page => params[:page],
+ :order => 'id DESC',
+ :per_page => 30
+ )
+
+ @boxes = {
+ :called => { :cnt => @project.calls.count },
+ :answered => { :cnt => @project.calls.where(:answered => true).count },
+ :analyzed => { :cnt => @project.calls.where('analysis_completed_at IS NOT NULL').count },
+ :voice => { :cnt => @project.lines.where(:line_type => 'voice').count },
+ :voicemail => { :cnt => @project.lines.where(:line_type => 'voicemail').count },
+ :fax => { :cnt => @project.lines.where(:line_type => 'fax').count },
+ :modem => { :cnt => @project.lines.where(:line_type => 'modem').count }
+ }
+
+ if @boxes[:called][:cnt] == 0
+ @boxes[:called][:txt] = '0'
+ @boxes[:called][:cls] = 'nodata'
+
+ # No calls, so everything else is unknown
+ [ :answered, :analyzed, :voice, :voicemail, :fax, :modem ].each do |t|
+ @boxes[t][:txt] = '?'
+ @boxes[t][:cls] = 'nodata'
+ end
+
+ else
+
+ [ :called, :answered, :analyzed].each do |t|
+ @boxes[t][:txt] = number_with_delimiter(@boxes[t][:cnt])
+ @boxes[t][:cls] = 'completed'
+ end
+
+ if @boxes[:answered][:cnt] == 0
+ @boxes[:answered][:txt] = '0'
+ @boxes[:answered][:cls] = 'nodata'
+ end
+
+ if @boxes[:analyzed][:cnt] == 0
+ [ :voice, :voicemail, :fax, :modem ].each do |t|
+ @boxes[t][:txt] = '?'
+ @boxes[t][:cls] = 'nodata'
+ end
+ @boxes[:analyzed][:cls] = 'nodata'
+ else
+
+ @boxes[:voice][:txt] = number_with_delimiter(@boxes[:voice][:cnt])
+ @boxes[:voice][:cls] = 'voice'
+
+ @boxes[:voicemail][:txt] = number_with_delimiter(@boxes[:voicemail][:cnt])
+ @boxes[:voicemail][:cls] = 'voicemail'
+
+ @boxes[:fax][:txt] = number_with_delimiter(@boxes[:fax][:cnt])
+ @boxes[:fax][:cls] = 'fax'
+
+ @boxes[:modem][:txt] = number_with_delimiter(@boxes[:modem][:cnt])
+ @boxes[:modem][:cls] = 'modem'
+ end
+ end
+
+ respond_to do |format|
+ format.html
+ format.xml { render :xml => @project }
+ end
+ end
+
+ def new
+ @new_project = Project.new
+ respond_to do |format|
+ format.html
+ format.xml { render :xml => @new_project }
+ end
+ end
+
+
+ def edit
+ @project = Project.find(params[:id])
+ end
+
+ def create
+ @new_project = Project.new(params[:project])
+ @new_project.created_by = current_user.login
+
+ respond_to do |format|
+ if @new_project.save
+ format.html { redirect_to(project_path(@new_project)) }
+ format.xml { render :xml => @project, :status => :created, :location => @new_project }
+ else
+ format.html { render :action => "new" }
+ format.xml { render :xml => @new_project.errors, :status => :unprocessable_entity }
+ end
+ end
+ end
+
+ def update
+ @project = Project.find(params[:id])
+
+ respond_to do |format|
+ if @project.update_attributes(params[:project])
+ format.html { redirect_to projects_path }
+ format.xml { head :ok }
+ else
+ format.html { render :action => "edit" }
+ format.xml { render :xml => @project.errors, :status => :unprocessable_entity }
+ end
+ end
+ end
+ def destroy
+ @project = Project.find(params[:id])
+ @project.destroy
+ respond_to do |format|
+ format.html { redirect_to(projects_url) }
+ format.xml { head :ok }
+ end
+ end
end
(DIR) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
@@ -67,6 +67,10 @@ module ApplicationHelper
end
end
+ def format_job_rate(job)
+ pluralize(job.rate.to_i, "call") + "/s"
+ end
+
#
# Includes any javascripts specific to this view. The hosts/show view
# will automatically include any javascripts at public/javascripts/hosts/show.js.
(DIR) diff --git a/app/models/call.rb b/app/models/call.rb
@@ -1,14 +1,14 @@
class Call < ActiveRecord::Base
- reportable :hourly, :aggregation => :count, :grouping => :hour, :live_data => true, :cacheable => false
- reportable :daily, :aggregation => :count, :grouping => :day, :live_data => true, :cacheable => false
- reportable :weekly, :aggregation => :count, :grouping => :week, :live_data => true, :cacheable => false
- reportable :monthly, :aggregation => :count, :grouping => :month, :live_data => true, :cacheable => false
+ reportable :hourly, :aggregation => :count, :grouping => :hour, :live_data => true, :cacheable => false, :limit => 24
+ reportable :daily, :aggregation => :count, :grouping => :day, :live_data => true, :cacheable => false, :limit => 7
+ reportable :weekly, :aggregation => :count, :grouping => :week, :live_data => true, :cacheable => false, :limit => 52
+ reportable :monthly, :aggregation => :count, :grouping => :month, :live_data => true, :cacheable => false, :limit => 12
- reportable :analyzed_hourly, :aggregation => :count, :grouping => :hour, :date_column => :analysis_completed_at, :live_data => true, :cacheable => false
- reportable :analyzed_daily, :aggregation => :count, :grouping => :day, :date_column => :analysis_completed_at, :live_data => true, :cacheable => false
- reportable :analyzed_weekly, :aggregation => :count, :grouping => :week, :date_column => :analysis_completed_at, :live_data => true, :cacheable => false
- reportable :analyzed_monthly, :aggregation => :count, :grouping => :month, :date_column => :analysis_completed_at, :live_data => true, :cacheable => false
+ reportable :analyzed_hourly, :aggregation => :count, :grouping => :hour, :date_column => :analysis_completed_at, :live_data => true, :cacheable => false, :limit => 24
+ reportable :analyzed_daily, :aggregation => :count, :grouping => :day, :date_column => :analysis_completed_at, :live_data => true, :cacheable => false, :limit => 7
+ reportable :analyzed_weekly, :aggregation => :count, :grouping => :week, :date_column => :analysis_completed_at, :live_data => true, :cacheable => false, :limit => 52
+ reportable :analyzed_monthly, :aggregation => :count, :grouping => :month, :date_column => :analysis_completed_at, :live_data => true, :cacheable => false, :limit => 12
belongs_to :project
belongs_to :provider
@@ -37,6 +37,8 @@ class Call < ActiveRecord::Base
}
+ after_save :update_linked_line
+
def paginate_matches(scope, min_match, page, per_page)
scope_limit = ""
@@ -68,4 +70,17 @@ class Call < ActiveRecord::Base
CallMedium.columns_hash.keys.reject{|x| x =~ /^id|_id$/}
end
+ def linked_line
+ Line.find_or_create_by_number_and_project_id(self[:number], self[:project_id])
+ end
+
+ def update_linked_line
+ line = linked_line
+
+ if self[:line_type]
+ line.line_type = self[:line_type]
+ line.save
+ end
+ end
+
end
(DIR) diff --git a/app/models/job.rb b/app/models/job.rb
@@ -47,7 +47,6 @@ class Job < ActiveRecord::Base
end
end
-
# XXX: Purging a single job will be slow, but deleting the project is fast
has_many :calls, :dependent => :destroy
@@ -105,9 +104,8 @@ class Job < ActiveRecord::Base
:seconds => self.seconds.to_i,
:cid_mask => self.cid_mask
})
- $stderr.puts self.inspect
-
return self.save
+
when 'analysis'
self.status = 'submitted'
self.args = Marshal.dump({
@@ -122,4 +120,20 @@ class Job < ActiveRecord::Base
end
end
+ def rate
+ tend = (self.completed_at || Time.now)
+ tlen = tend.to_f - self.started_at.to_f
+
+ case self.task
+ when 'dialer'
+ Call.where('job_id = ?', self.id).count() / tlen
+ when 'analysis'
+ Call.where('job_id = ? AND analysis_completed_at > ? AND analysis_completed_at < ?', self.details[:target_id], self.created_at, tend).count() / tlen
+ when 'import'
+ Call.where('job_id = ?', self.id).count() / tlen
+ else
+ 0
+ end
+ end
+
end
(DIR) diff --git a/app/views/jobs/index.html.erb b/app/views/jobs/index.html.erb
@@ -17,7 +17,7 @@
<td><%= job.id %></td>
<td><%= format_job_details(job) %></td>
<td><%= format_job_status(job) %></td>
- <td><%= job.created_at.localtime.strftime("%Y-%m-%d %H:%M:%S %Z") %></td>
+ <td><%= time_ago_in_words(job.created_at) %> ago</td>
<td>
<a class="btn" href="<%= job_path(job) %>" data-confirm="Remove this job?" data-method="delete" rel="nofollow tooltip" title="Remove Job"><i class="icon-trash"></i></a>
@@ -38,6 +38,7 @@
<th>ID</th>
<th>Task</th>
<th>Progress</th>
+ <th>Rate</th>
<th>Launched</th>
<th>Actions</th>
<th>Project</th>
@@ -48,13 +49,14 @@
<td><%= job.id %></td>
<td><%= format_job_details(job) %></td>
<td>
- <div class="progress progress-success progress-striped progress-bar">
+ <div class="progress progress-warning progress-striped progress-bar">
<div class="bar" style="width: <%= job.progress %>%">
<span class='progress_pct'><%= job.progress %>%</span>
</div>
</div>
</td>
- <td><%= job.created_at.localtime.strftime("%Y-%m-%d %H:%M:%S %Z") %></td>
+ <td><%= format_job_rate(job) %></td>
+ <td><%= time_ago_in_words(job.created_at) %> ago</td>
<td>
<% if job.task == "dialer" %>
<a class="btn" href="<%= view_results_path(job.project,job) %>" rel="tooltip" title="View Current Stats" ><i class="icon-zoom-in"></i></a>
@@ -88,6 +90,7 @@
<th>ID</th>
<th>Task</th>
<th>Status</th>
+ <th>Rate</th>
<th>Created</th>
<th>Completed</th>
<th>Project</th>
@@ -106,8 +109,9 @@
<td><%= job.id %></td>
<td><%= format_job_details(job) %></td>
<td><%= format_job_status(job) %></td>
- <td><%= job.created_at.localtime.strftime("%Y-%m-%d %H:%M:%S %Z") %></td>
- <td><%= job.completed_at ? job.completed_at.localtime.strftime("%Y-%m-%d %H:%M:%S %Z") : "incomplete" %></td>
+ <td><%= format_job_rate(job) %></td>
+ <td><%= time_ago_in_words(job.created_at) %> ago</td>
+ <td><%= job.completed_at ? "after " + time_ago_in_words(Time.at(Time.now.to_i - (job.completed_at.to_f - job.created_at.to_f))) : "incomplete" %></td>
<td><%= link_to( h(truncate(job.project.name, :length => 25)), project_path(job.project)) %></td>
</tr>
<% end %>
(DIR) diff --git a/app/views/jobs/results.html.erb b/app/views/jobs/results.html.erb
@@ -49,7 +49,7 @@
<td><span rel="tooltip" class="xtooltip" title="<%= pct_analyzed %>% analyzed"><%= number_with_delimiter(cnt_analyzed) %></span></td>
- <td><%= job.created_at.strftime("%Y-%m-%d %H:%M:%S") %></td>
+ <td><%= time_ago_in_words(job.created_at) %> ago</td>
<td><%= job.created_by %></td>
<td>
<a class="btn" href="<%= view_results_path(@project,job) %>" rel="tooltip" title="View Call Connections" ><i class="icon-zoom-in"></i></a>
@@ -59,11 +59,11 @@
<% if pct_analyzed == 100 %>
<a class="btn" href="<%= reanalyze_job_path(@project,job) %>" data-confirm="Reprocess this job?" rel="nofollow tooltip" title="Rerun Call Analysis"><i class="icon-refresh"></i></a>
<% else %>
- <a class="btn" href="<%= analyze_job_path(@project,job) %>" data-confirm="Continue to process this job?" rel="nofollow tooltip" title="Finish Call Analysis"><i class="icon-cog"></i></a>
+ <a class="btn" href="<%= analyze_job_path(@project,job) %>" data-confirm="Continue to process this job?" rel="nofollow tooltip" title="Finish Call Analysis"><i class="icon-cogs"></i></a>
<% end %>
<% else %>
<% if cnt_answered > 0 %>
- <a class="btn" href="<%= analyze_job_path(@project,job) %>" data-confirm="Analyze this job?" rel="nofollow tooltip" title="Run Call Analysis"><i class="icon-cog"></i></a>
+ <a class="btn" href="<%= analyze_job_path(@project,job) %>" data-confirm="Analyze this job?" rel="nofollow tooltip" title="Run Call Analysis"><i class="icon-cogs"></i></a>
<% end %>
<% end %>
(DIR) diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb
@@ -77,7 +77,7 @@
<%= menu_item raw('<i class="icon-info-sign"></i> About'), about_path %>
<% end %>
- <%= menu_item raw('<div class="help-icon"><i class="icon-off icon-white" rel="tooltip" title="Logout"></i></div>'), logout_path %>
+ <%= menu_item raw('<div class="help-icon"><i class="icon-signout icon-white" rel="tooltip" title="Logout"></i></div>'), logout_path %>
<%= menu_item raw('<div class="help-icon"><i class="icon-question-sign icon-white" rel="tooltip" title="Help!"></i></div>'), check_path %>
<% end %>
(DIR) diff --git a/app/views/projects/index.html.erb b/app/views/projects/index.html.erb
@@ -10,7 +10,7 @@
<th>Jobs</th>
<th>Calls</th>
<th>Analyzed</th>
- <th>Date</th>
+ <th>Created</th>
<th>Actions</th>
</tr>
</thead>
@@ -23,7 +23,7 @@
<td><%= number_with_delimiter(project.jobs.count) %></td>
<td><%= number_with_delimiter(project.calls.count) %></td>
<td><%= number_with_delimiter(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><%= time_ago_in_words(project.created_at) %> ago</td>
<td>
<a class="btn" href="<%= edit_project_path(project) %>"rel="tooltip" title="Update Project Information"><i class="icon-pencil"></i></a>
<a class="btn" 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/show.html.erb b/app/views/projects/show.html.erb
@@ -1,6 +1,3 @@
-
-
-
<div class="row-fluid">
<div class="span12">
<a class="btn btn-small pull-right" href="<%= edit_project_path(@project) %>" rel="tooltip" title="Update project settings"><i class="icon-wrench"></i> Settings</a>
@@ -9,72 +6,227 @@
</div>
</div>
-
<div class="row-fluid">
- <div class="span2">
- <div class="stat-box">
- <div><%= number_with_delimiter( @project.calls.count ) %></div>
+ <div class="span3">
+ <div class="stat-box stat-<%= @boxes[:called][:cls]%>">
+ <div><%= @boxes[:called][:txt] %></div>
<span class="stat-subtitle">Calls</span>
</div>
</div>
- <div class="span2">
- <div class="stat-box">
- <div><%= number_with_delimiter( @project.calls.where(:answered => true).count ) %></div>
- <span class="stat-subtitle">Answered</span>
- </div>
- </div>
- <div class="span2">
- <div class="stat-box">
- <div><%= number_with_delimiter( @project.calls.where('analysis_completed_at IS NOT NULL').count ) %></div>
- <span class="stat-subtitle">Analyzed</span>
+ <div class="span9">
+ <div class="row-fluid">
+ <div class="span2"> </div>
+ <div class="span2"><a href="<%= new_dialer_job_path %>" class="btn" rel="tooltip" title="Gather data by dialing a range of numbers"><i class="icon-phone"></i> <strong>Wardial</strong></a></div>
+ <% if @boxes[:answered][:cnt] > 0 %>
+ <div class="span2"><a href="#" class="btn" rel="tooltip" title="Analyze call data to determine line types and frequencies"><i class="icon-cogs"></i> <strong>Analyze</strong></a></div>
+ <div class="span2"><a href="#" class="btn" rel="tooltip" title="Look up meta-information about specific lines"><i class="icon-user"></i> <strong>Identify</strong></a></div>
+ <div class="span2"><a href="#" class="btn" rel="tooltip" title="Signal analysis and other research tools"><i class="icon-star"></i> <strong>Research</strong></a></div>
+ <% else %>
+ <div class="span2"><a href="#" class="btn disabled" rel="tooltip" title="No call data is available to analyze"><i class="icon-cogs"></i> <strong>Analyze</strong></a></div>
+ <div class="span2"><a href="#" class="btn" rel="tooltip" title="Look up meta-information about specific lines"><i class="icon-user"></i> <strong>Identify</strong></a></div>
+ <div class="span2"><a href="#" class="btn disabled" rel="tooltip" title="No call data is available to research"><i class="icon-star"></i> <strong>Research</strong></a></div>
+ <% end %>
+ <div class="span2"> </div>
</div>
</div>
+</div>
- <div class="span2">
- <div class="stat-box">
- <div><%= number_with_delimiter( @project.calls.where(:line_type => 'voice').count ) %></div>
- <span class="stat-subtitle">Voice</span>
- </div>
- </div>
- <div class="span2">
- <div class="stat-box">
- <div><%= number_with_delimiter( @project.calls.where(:line_type => 'fax').count ) %></div>
- <span class="stat-subtitle">Fax</span>
+<div class="row-fluid">
+ <div class="span3 arrow-down"><i class="icon-arrow-down"></i></div>
+ <div class="span9"> </div>
+</div>
+
+<div class="row-fluid">
+ <div class="span3">
+ <div class="stat-box stat-<%= @boxes[:answered][:cls]%>">
+ <div><%= @boxes[:answered][:txt] %></div>
+ <span class="stat-subtitle">Answered</span>
</div>
</div>
- <div class="span2">
- <div class="stat-box">
- <div><%= number_with_delimiter( @project.calls.where(:line_type => 'modem').count ) %></div>
- <span class="stat-subtitle">Modem</span>
+ <div class="span9"></div>
+</div>
+
+<div class="row-fluid">
+ <div class="span3 arrow-down"><i class="icon-arrow-down"></i></div>
+ <div class="span9"> </div>
+</div>
+
+<div class="row-fluid">
+ <div class="span3">
+ <div class="stat-box stat-<%= @boxes[:analyzed][:cls]%>">
+ <div><%= @boxes[:analyzed][:txt] %></div>
+ <span class="stat-subtitle">Analyzed</span>
</div>
</div>
+ <div class="span9"> </div>
</div>
-
+<div class="row-fluid">
+ <div class="span3 arrow-down"><i class="icon-arrow-down"></i></div>
+ <div class="span9"> </div>
+</div>
<div class="row-fluid">
- <div class="span2">
+ <div class="span3">
+ <div class="row-fluid">
+ <div class="span6">
+ <div class="stat-box stat-<%= @boxes[:voice][:cls]%>">
+ <div><%= @boxes[:voice][:txt] %></div>
+ <span class="stat-subtitle">Voice</span>
+ </div>
+ </div>
+ <div class="span6">
+ <div class="stat-box stat-<%= @boxes[:voicemail][:cls]%>">
+ <div><%= @boxes[:voicemail][:txt] %></div>
+ <span class="stat-subtitle">VoiceMail</span>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="span3 sparkline-cell">
<%= render :partial => 'shared/graphs/sparkline', :locals => { :points => @project.calls.hourly_report.to_a.map{|x| x[1] } } %>
- Calls/Hour
+ <span class='sparkline-title'>Phone Calls / Hour (Last Day)</a>
</div>
- <div class="span2">
+ <div class="span3 sparkline-cell">
<%= render :partial => 'shared/graphs/sparkline', :locals => { :points => @project.calls.daily_report.to_a.map{|x| x[1] } } %>
- Calls/Day
+ <span class='sparkline-title'>Phone Calls / Day (Last Week)</a>
</div>
- <div class="span2">
+ <div class="span3 sparkline-cell">
<%= render :partial => 'shared/graphs/sparkline', :locals => { :points => @project.calls.weekly_report.to_a.map{|x| x[1] } } %>
- Calls/Week
+ <span class='sparkline-title'>Phone Calls / Week (Last Month)</a>
+ </div>
+</div>
+
+<div class="row-fluid">
+ <div class="span12"> </div>
+</div>
+
+<div class="row-fluid">
+ <div class="span3">
+ <div class="row-fluid">
+ <div class="span6">
+ <div class="stat-box stat-<%= @boxes[:fax][:cls]%>">
+ <div><%= @boxes[:fax][:txt] %></div>
+ <span class="stat-subtitle">Fax</span>
+ </div>
+ </div>
+ <div class="span6">
+ <div class="stat-box stat-<%= @boxes[:modem][:cls]%>">
+ <div><%= @boxes[:modem][:txt] %></div>
+ <span class="stat-subtitle">Modems</span>
+ </div>
+ </div>
+ </div>
</div>
- <div class="span2">
+ <div class="span3 sparkline-cell">
<%= render :partial => 'shared/graphs/sparkline', :locals => { :points => @project.calls.analyzed_hourly_report.to_a.map{|x| x[1] } } %>
- Analysis/Hour
+ <span class='sparkline-title'>Analyzed Calls / Hour (Last Day)</a>
</div>
- <div class="span2">
+ <div class="span3 sparkline-cell">
<%= render :partial => 'shared/graphs/sparkline', :locals => { :points => @project.calls.analyzed_daily_report.to_a.map{|x| x[1] } } %>
- Analysis/Day
+ <span class='sparkline-title'>Analyzed Calls / Day (Last Week)</a>
</div>
- <div class="span2">
+ <div class="span3 sparkline-cell">
<%= render :partial => 'shared/graphs/sparkline', :locals => { :points => @project.calls.analyzed_weekly_report.to_a.map{|x| x[1] } } %>
- Analysis/Week
+ <span class='sparkline-title'>Analyzed Calls / Week (Last Month)</a>
+ </div>
+ <div class="span6"> </div>
+</div>
+
+<div class="row-fluid">
+ <div class="span12"> </div>
+</div>
+
+<div class="row-fluid">
+ <div class="span12">
+ <p class='project-header'><%= @project.description %></p>
+ </div>
+</div>
+
+<% if @active_jobs.count > 0 %>
+
+<div class="row-fluid">
+ <div class="span12">
+
+ <h2 class='title'>Active Jobs</h1>
+
+ <table class='table table-striped table-condensed' width='90%'>
+ <tr>
+ <th>ID</th>
+ <th>Task</th>
+ <th>Progress</th>
+ <th>Rate</th>
+ <th>Launched</th>
+ <th>Actions</th>
+ </tr>
+
+<% @active_jobs.each do |job| %>
+ <tr class='active_job_row'>
+ <td><%= job.id %></td>
+ <td><%= format_job_details(job) %></td>
+ <td>
+ <div class="progress progress-warning progress-striped progress-bar">
+ <div class="bar" style="width: <%= job.progress %>%">
+ <span class='progress_pct'><%= job.progress %>%</span>
+ </div>
+ </div>
+ </td>
+ <td><%= format_job_rate(job) %></td>
+ <td><%= time_ago_in_words(job.created_at) %> ago</td>
+ <td>
+ <% if job.task == "dialer" %>
+ <a class="btn" href="<%= view_results_path(job.project,job) %>" rel="tooltip" title="View Current Stats" ><i class="icon-zoom-in"></i></a>
+ <% end %>
+ <% if job.task == "analysis" and job.details[:scope].to_s != "calls" %>
+ <a class="btn" href="<%= view_analyze_path(job.project,job.details[:target_id]||job.id) %>" rel="tooltip" title="View Call Analysis"><i class="icon-eye-open"></i></a>
+ <% end %>
+ <a class="btn" href="<%= stop_job_path(job) %>" data-confirm="Terminate this job?" rel="nofollow tooltip" title="Terminate Job"><i class="icon-stop"></i></a>
+ </td>
+ </tr>
+<% end %>
+ </table>
+ </div>
+</div>
+<% end %>
+
+
+<% if(@inactive_jobs.length > 0) %>
+
+<div class="row-fluid">
+ <div class="span12">
+ <h2 class='title'>Completed Jobs</h2>
+
+ <%= will_paginate @inactive_jobs, :renderer => BootstrapPagination::Rails %>
+ <table class='table table-striped table-condensed' width='90%'>
+ <tr>
+ <th>ID</th>
+ <th>Task</th>
+ <th>Status</th>
+ <th>Rate</th>
+ <th>Started</th>
+ <th>Completed</th>
+ </tr>
+
+ <% @inactive_jobs.each do |job|
+ special = ""
+ case job.status
+ when "error"
+ special = "error"
+ when "stopped"
+ special = "warning"
+ end
+ %>
+ <tr class='<%= special %>'>
+ <td><%= job.id %></td>
+ <td><%= format_job_details(job) %></td>
+ <td><%= format_job_status(job) %></td>
+ <td><%= format_job_rate(job) %></td>
+ <td><%= time_ago_in_words(job.created_at) %> ago</td>
+ <td><%= job.completed_at ? "after " + time_ago_in_words(Time.at(Time.now.to_i - (job.completed_at.to_f - job.created_at.to_f))) : "incomplete" %></td>
+ </tr>
+ <% end %>
+ </table>
+ <%= will_paginate @inactive_jobs, :renderer => BootstrapPagination::Rails %>
</div>
</div>
+<% end %>
(DIR) diff --git a/app/views/shared/graphs/_sparkline.html.erb b/app/views/shared/graphs/_sparkline.html.erb
@@ -57,8 +57,8 @@
}
},
series: [{
- color:'#666',
- fillColor:'rgba(204,204,204,.25)',
+ color:'#BB4607',
+ fillColor:'#fcfcfc',
data: [ <%= raw(points.join(", ")) %> ]
}]
});
(DIR) diff --git a/db/migrate/20121228171549_initial_schema.rb b/db/migrate/20121228171549_initial_schema.rb
@@ -65,7 +65,7 @@ class InitialSchema < ActiveRecord::Migration
t.timestamps
t.text "number", :null => false
t.integer "project_id", :null => false
- t.text "type"
+ t.text "line_type"
t.text "notes"
end
(DIR) diff --git a/db/schema.rb b/db/schema.rb
@@ -92,7 +92,7 @@ ActiveRecord::Schema.define(:version => 20130113004653) do
t.datetime "updated_at", :null => false
t.text "number", :null => false
t.integer "project_id", :null => false
- t.text "type"
+ t.text "line_type"
t.text "notes"
end