Dashboard

This commit is contained in:
manson 2014-05-15 19:32:51 +08:00
parent a13182d73e
commit b057a4ce74
22 changed files with 501 additions and 14 deletions

View File

@ -34,6 +34,8 @@ gem 'mongoid-grid_fs', github: 'ahoward/mongoid-grid_fs'
gem 'kaminari'
gem "impressionist"
gem "chartkick"
gem 'usagewatch_ext'
gem 'ckeditor'
gem 'unicorn'

View File

@ -1,7 +1,89 @@
class Admin::DashboardsController < ApplicationController
before_action :authenticate_user
layout "back_end"
class Admin::DashboardsController < OrbitAdminController
layout "basic_back_end"
def index
# check_backend_openness
apps = ['bulletin', 'page', 'page_context', 'web_link']
@module_app_contents, @module_app_contents_total = get_module_app_count(apps)
@recent_updated = get_recently_updated(apps)
@most_visited = get_most_visited(apps)
end
def reload_all_content
@module_app_contents, @module_app_contents_total = get_module_app_count('bulletin', 'page_context', 'web_link')
respond_to do |format|
format.js { render 'reload', locals: {div_id: 'all_content'} }
end
end
def reload_most_visited
@most_visited = get_most_visited('bulletin', 'page_context','page')
respond_to do |format|
format.js { render 'reload', locals: {div_id: 'most_visited'} }
end
end
def reload_recent_update
@recent_updated = get_recently_updated('bulletin', 'page_context', 'web_link')
respond_to do |format|
format.js { render 'reload', locals: {div_id: 'recent_update'} }
end
end
def get_cpu_usage
@usw = Usagewatch
render :js => @usw.uw_cpuused.to_s
end
def get_mem_usage
@usw = Usagewatch
render :js => @usw.uw_memused.to_s
end
def get_disk_usage
@usw = Usagewatch
render :json => @usw.uw_diskused_perc.to_s
end
protected
def get_module_app_count(args)
a = {}
total = 0
args.each do |module_app|
module_app_class = module_app.classify.constantize
count = module_app_class.count
a.merge!(module_app => count)
total += count
end
[Kaminari.paginate_array(a.sort {|a,b| b[1]<=>a[1]}).page(params[:page]).per(5), total]
end
def get_recently_updated(args)
a = {}
args.each do |module_app|
module_app_class = module_app.classify.constantize
objects = module_app_class.order_by([:updated_at, :desc]).limit(20)
objects.each do |object|
a.merge!(object => object.updated_at) unless (object.archived rescue nil)
end
end
sorted_objects = a.sort {|a,b| b[1]<=>a[1]}
sorted_objects[0..19]
Kaminari.paginate_array(sorted_objects).page(params[:page]).per(5)
end
def get_most_visited(args)
a = {}
args.each do |module_app|
module_app_class = module_app.classify.constantize
objects = module_app_class.order_by([:view_count, :desc]).limit(20)
objects.each do |object|
a.merge!(object => object.view_count) if object.view_count > 0 && (!object.archived rescue true)
end
end
sorted_objects = a.sort {|a,b| b[1]<=>a[1]}
sorted_objects[0..19]
Kaminari.paginate_array(sorted_objects).page(params[:page]).per(5)
end
end

View File

@ -23,6 +23,7 @@ class PagesController < ApplicationController
@manifest = @key
@dataApi = nil
page = Page.first
impressionist(page)
OrbitHelper.set_params params
OrbitHelper.set_site_locale locale
render :html => render_final_page("home",page,true).html_safe
@ -66,6 +67,7 @@ class PagesController < ApplicationController
layout = true
end
impressionist(page)
# render render_final_page("#{module_app}/#{params[:target_action]}",page)
render :html => render_final_page("#{module_app}/#{params[:target_action]}",page,layout).html_safe
else

View File

@ -154,12 +154,26 @@ module ApplicationHelper
file.close
doc.to_html.html_safe
else
unless data['impressionist'].blank?
impression = data['impressionist'].impressions.create
impression.user_id = request.session['user_id']
impression.controller_name = params[:target_controller]
impression.action_name = params[:target_action]
impression.ip_address = request.remote_ip
impression.session_hash = request.session.id
impression.request_hash = @impressionist_hash
impression.referrer = request.referrer
impression.save
data['impressionist'].view_count = data['impressionist'].impressions.count
data['impressionist'].save
end
wrap_elements = doc.css("*[data-list][data-level='0']")
if wrap_elements.count == 0
wrap_element_html = doc.to_s
el = wrap_element_html
data.each do |key,value|
next if key.eql? 'impressionist'
value = value.nil? ? "" : value
el = el.gsub("{{#{key}}}",value)
el = el.gsub("%7B%7B#{key}%7D%7D",value)
@ -178,6 +192,7 @@ module ApplicationHelper
end
if data["#{not_array_key}"]
data["#{not_array_key}"].each do |key,value|
next if key.eql? 'impressionist'
value = value.nil? ? "" : value
html = html.gsub("{{#{key}}}",value)
html = html.gsub("%7B%7B#{key}%7D%7D",value)

View File

@ -91,6 +91,39 @@ module OrbitBackendHelper
def render_filter(fields)
render :partial => "shared/filter", :locals =>{:fields => fields}
end
def display_visitors(options={})
Impression.where(options).distinct(:request_hash).count
end
def display_visitors_today
display_visitors(created_at: {'$gte' => Time.now.beginning_of_day, '$lte' => Time.now})
end
def display_visitors_this_week
display_visitors(created_at: {'$gte' => Time.now-7.days, '$lte' => Time.now})
end
def display_visitors_this_month
display_visitors(created_at: {'$gte' => Time.now-30.days, '$lte' => Time.now})
end
def display_visitors_this_year
display_visitors(created_at: {'$gte' => Time.now-365.days, '$lte' => Time.now})
end
def get_month_traffic
result = []
(0..30).each do |i|
visits = Impression.where( created_at: {
'$gte' => Time.now.beginning_of_day-i.days,
'$lte' => Time.now.end_of_day-i.days}
).count
result.push([ Time.now.beginning_of_day-i.days, visits])
end
[:name=> t(:visitors_count),:data=>result]
end
end

View File

@ -68,6 +68,10 @@ module OrbitHelper
end
end
def self.orbit_impressionist(object)
impressionist(object)
end
# ===============================================================
# Breadcrumbs
# ===============================================================

View File

@ -1,6 +1,7 @@
class Page
include Mongoid::Document
include Mongoid::Timestamps
include OrbitModel::Impression
field :name, localize: true
field :number, type: Integer
@ -10,8 +11,8 @@ class Page
field :data_count, type: Integer, default: 10
field :enabled_for, type: Array, default: []
field :menu_enabled_for, type: Array, default: []
field :categories, type: Array, :default => []
field :enabled_for_sitemap, type: Array, :default => []
field :categories, type: Array, :default => []
field :enabled_for_sitemap, type: Array, :default => []
has_many :page_parts, :autosave => true, :dependent => :destroy

View File

@ -0,0 +1,29 @@
<div class="box-header">
<h2>
<i class="icons-book-2"></i>
<span class="break"></span>
<%= t(:all_content) %>
</h2>
</div>
<div class="box-content">
<table class="table table-bordered table-striped table-hover">
<thead>
<tr>
<th><%= t(:module) %></th>
<th class="span1"><%= t(:quantity) %></th>
</tr>
</thead>
<tbody>
<% @module_app_contents.each do |module_app_content| %>
<tr>
<td><%= t("dashboard.#{module_app_content[0]}") %></td>
<td><%= module_app_content[1] %></td>
</tr>
<% end %>
</tbody>
</table>
<ul class="pager">
<li><%= link_to_previous_page @module_app_contents, t(:previous), params: {controller: 'admin/dashboards', action: 'reload_all_content'}, remote: true %></li>
<li><%= link_to_next_page @module_app_contents, t(:next), params: {controller: 'admin/dashboards', action: 'reload_all_content'}, remote: true %></li>
</ul>
</div>

View File

@ -0,0 +1,31 @@
<div class="box-header">
<h2>
<i class="icons-trophy"></i>
<span class="break"></span>
<%= t(:most_visited_page) %>
</h2>
</div>
<div class="box-content">
<table class="table table-bordered table-striped table-hover">
<thead>
<tr>
<th class="span7"><%= t(:title) %></th>
<th class="span2"><%= t(:module) %></th>
<th class="span3"><%= t(:hits) %></th>
</tr>
</thead>
<tbody>
<% @most_visited.each do |object| %>
<tr>
<td><%= (object[0].title rescue nil) || (object[0].page.title rescue nil) || (object[0].name rescue nil) %></td>
<td><%= t("dashboard.#{object[0].class.to_s.underscore}") %></td>
<td><%= object[1] %></td>
</tr>
<% end %>
</tbody>
</table>
<ul class="pager">
<li><%= link_to_previous_page @most_visited, t(:previous), params: {controller: 'admin/dashboards', action: 'reload_most_visited'}, remote: true %></li>
<li><%= link_to_next_page @most_visited, t(:next), params: {controller: 'admin/dashboards', action: 'reload_most_visited'}, remote: true %></li>
</ul>
</div>

View File

@ -0,0 +1,29 @@
<div class="box-header">
<h2>
<i class="icons-cycle"></i>
<span class="break"></span>
<%= t(:recent_update) %>
</h2>
</div>
<div class="box-content">
<table class="table table-bordered table-striped table-hover">
<thead>
<tr>
<th><%= t(:title) %></th>
<th class="span2"><%= t(:module) %></th>
</tr>
</thead>
<tbody>
<% @recent_updated.each do |object| %>
<tr>
<td><%= (object[0].title rescue nil) || (object[0].page.title rescue nil) || (object[0].name rescue nil) %></td>
<td><%= t("dashboard.#{object[0].class.to_s.underscore}") %></td>
</tr>
<% end %>
</tbody>
</table>
<ul class="pager">
<li><%= link_to_previous_page @recent_updated, t(:previous), params: {controller: 'admin/dashboards', action: 'reload_recent_update'}, remote: true %></li>
<li><%= link_to_next_page @recent_updated, t(:next), params: {controller: 'admin/dashboards', action: 'reload_recent_update'}, remote: true %></li>
</ul>
</div>

View File

@ -0,0 +1,74 @@
<div class="box-header">
<h2>
<i class="icons-gauge"></i>
<span class="break"></span>
<%= t(:server_usage) %>
</h2>
</div>
<div class="box-content" style='overflow: hidden; text-align:center;'>
<div style="min-width: 543px;">
<div id="cpu_usage" class="usages pull-left " style=""></div>
<div id="mem_usage" class="usages" style=""></div>
<div id="disk_usage" class="usages pull-right" style=""></div>
</div>
</div>
<style type="text/css">
.usages{
width:180px;
height:144px;
display: inline-block;
}
</style>
<script type="text/javascript">
var Gages = { 'CPU':{
'JustGage': null,
'container': 'cpu_usage',
'title': 'CPU Usage',
'fn': 'get_cpu_usage',
'update_interval': 2000
},
'Mem':{
'JustGage': null,
'container': 'mem_usage',
'title': 'Memory Usage',
'fn': 'get_mem_usage',
'update_interval': 30000
},
'Disk':{
'JustGage': null,
'container': 'disk_usage',
'title': 'Disk Usage',
'fn': 'get_disk_usage',
'update_interval': 180000
}
};
$(function () {
$.each(Gages,function(id,Gage){
Gage['JustGage'] = new JustGage({
id: Gage['container'],
value: 0,
min: 0,
max: 100,
title: Gage['title'],
shadowVerticalOffset: 10,
levelColors: ['#39D824','#FFC33F','#F51F1F'],
titleFontColor: '#666',
valueFontColor: '#666',
labelFontColor: '#666',
label: '%',
refreshAnimationTime: 800
});
update_usage(Gage);
});
});
function update_usage(Gage){
$.get('/admin/dashboards/'+Gage['fn'],function(usage){
Gage['JustGage'].refresh(parseInt(usage));
setTimeout(function(){update_usage(Gage);},Gage['update_interval']);
});
}
</script>

View File

@ -0,0 +1,35 @@
<div class="box-header">
<h2>
<i class="icon-th-list"></i>
<span class="break"></span>
<%= t(:traffic) %>
</h2>
</div>
<div class="box-content">
<table class="table table-bordered table-striped table-hover">
<thead>
<tr>
<th><%= t(:total_visitors) %></th>
<th class="span1"><%= display_visitors %></th>
</tr>
</thead>
<tbody>
<tr>
<td><%= t(:visitors_today) %></td>
<td><%= display_visitors_today %></td>
</tr>
<tr>
<td><%= t(:visitors_this_week) %></td>
<td><%= display_visitors_this_week %></td>
</tr>
<tr>
<td><%= t(:visitors_this_month) %></td>
<td><%= display_visitors_this_month %></td>
</tr>
<tr>
<td><%= t(:visitors_this_year) %></td>
<td><%= display_visitors_this_year %></td>
</tr>
</tbody>
</table>
</div>

View File

@ -0,0 +1,18 @@
<div class="box-header">
<h2>
<i class="icon-road"></i>
<span class="break"></span>
<%= t(:monthly_traffic)%>
</h2>
</div>
<div class="box-content">
<div id="month_traffic" style="min-width: 310px; margin: 0 auto">
<%= line_chart get_month_traffic, :height => "200px", :name => 'Visit',
:library => {
legend: 'none',
chartArea:{ width: '95%', left: 50, right: 0},
hAxis:{format:'MMM-d',gridlines:{color: '#CCC', count: 31}},
vAxis:{minValue:-100,viewWindowMode: 'maximized'}
} %>
</div>
</div>

View File

@ -0,0 +1,47 @@
<%= javascript_include_tag "//www.google.com/jsapi", "chartkick"%>
<%= javascript_include_tag "/static/justgage.1.0.1.min.js" %>
<%= javascript_include_tag "/static/raphael.2.1.0.min.js" %>
<section id="main-wrap">
<div class="wrap-inner initial">
<div class="row-fluid">
<% if is_admin? %>
<div class="box span7">
<div id='server_loading'>
<%= render 'server_loading' %>
</div>
</div>
<% end %>
<div class="box span5">
<div id='traffic'>
<%= render 'traffic' %>
</div>
</div>
</div>
<div class="row-fluid">
<div class="box span">
<div id='server_loading'>
<%= render 'traffic_loading' %>
</div>
</div>
</div>
<hr>
<div class="row-fluid">
<div class="box span4">
<div id='recent_update'>
<%= render 'recent_update' %>
</div>
</div>
<div class="box span4">
<div id='all_content'>
<%= render 'all_content' %>
</div>
</div>
<div class="box span4">
<div id='most_visited'>
<%= render 'most_visited' %>
</div>
</div>
</div>
</div>
</section>

View File

@ -0,0 +1 @@
$("#<%= div_id %>").html("<%= j render div_id %>")

View File

@ -18,14 +18,14 @@
<%= render 'layouts/side_bar' %>
<section id="main-wrap">
<div class="wrap-inner">
<div id="filter" class="topnav clearfix">
<ul class="breadcrumb text-info pull-left">
<% if @module_app.present?%>
<%= back_end_breadcrumb %>
<%end%>
</ul>
<%= yield :right_nav %>
</div>
<% if @module_app.present?%>
<div id="filter" class="topnav clearfix">
<ul class="breadcrumb text-info pull-left">
<%= back_end_breadcrumb %>
</ul>
<%= yield :right_nav %>
</div>
<%end%>
<%= yield %>
</div>
</section>

View File

@ -0,0 +1,21 @@
<!DOCTYPE HTML>
<html>
<head>
<title><%= current_site.title %> | <%= t('dashboard_') %></title>
<%= render 'shared/meta' %>
<%= render 'shared/google_font' %>
<%= stylesheet_link_tag "basic" %>
<%= stylesheet_link_tag params[:controller] if Rails.application.assets.find_asset "#{params[:controller]}.css" %>
<%= render 'shared/ie_html5_fix' %>
<%= javascript_include_tag "basic" %>
<%= javascript_include_tag params[:controller] if Rails.application.assets.find_asset "#{params[:controller]}.js" %>
<%= yield :page_specific_css %>
<%= yield :page_specific_javascript %>
<%= csrf_meta_tag %>
</head>
<body id="dashboards">
<%= render 'layouts/orbit_bar_backend' %>
<%= render 'layouts/side_bar' %>
<%= yield %>
</body>
</html>

View File

@ -22,7 +22,14 @@ zh_tw:
login: 登入
logout: 登出
cancel: 取消
dashboard:
bulletin: 公告
page_context: 頁面內容
web_link: 連結
page: 頁面
dashboard_: 儀表版
server_usage: 主機負載
traffic: 流量
desktop: 桌面
disable: 停用
password: 密碼
@ -64,6 +71,18 @@ zh_tw:
frequency: 頻率
edit: 編輯
delete_: 刪除
total_visitors: 總計造訪人次
monthly_traffic: 本月流量
most_visited_page: 最多瀏覽頁面
visitors_count: 造訪人次
visitors_this_month: 本月造訪人次
visitors_this_week: 本星期造訪人次
visitors_this_year: 今年造訪人次
visitors_today: 今日造訪人次
recent_update: 近期更新
all_content: 所有內容
quantity: 數量
hits: 點擊次數
site:
system_preference: 系統狀態

View File

@ -44,7 +44,17 @@ Orbit::Application.routes.draw do
resources :passwords
namespace :admin do
resources :dashboards
resources :dashboards do
collection do
get 'reload_all_content'
get 'reload_most_visited'
get 'reload_recent_update'
get 'get_cpu_usage'
get 'get_mem_usage'
get 'get_disk_usage'
get 'get_month_traffic'
end
end
resources :members
resources :roles do
get 'role_field'

View File

@ -0,0 +1,12 @@
module OrbitModel
module Impression
def self.included(base)
include Impressionist::Impressionable
base.extend ClassMethods
base.is_impressionable :counter_cache => true, :column_name => :view_count, :unique => :request_hash
base.field :view_count, :type => Integer, :default => 0
end
end
end

12
public/static/justgage.1.0.1.min.js vendored Normal file

File diff suppressed because one or more lines are too long

10
public/static/raphael.2.1.0.min.js vendored Normal file

File diff suppressed because one or more lines are too long