Skip to content

Commit

Permalink
Merge branch 'release-0.9.2'
Browse files Browse the repository at this point in the history
  • Loading branch information
agnessa committed Jan 15, 2015
2 parents fab50b6 + 372d510 commit 37a384e
Show file tree
Hide file tree
Showing 19 changed files with 307 additions and 4 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
### 0.9.2 (2015-01-15)
**Species+ Admin:**
* API tracking

**Species+:**
* fix to order of EU Decisions

### 0.9.1 (2015-01-14)
**Species+ Admin:**
* nomenclature management (temporarily disabled)
Expand Down
2 changes: 2 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ gem 'cancan'
gem 'ahoy_matey'
gem 'gon'
gem 'wicked'
gem 'groupdate'
gem "chartkick"

gem 'sidekiq', '~> 3.3.0'
gem 'sidekiq-status', '~> 0.5'
Expand Down
5 changes: 5 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ GEM
mime-types (>= 1.16)
celluloid (0.16.0)
timers (~> 4.0.0)
chartkick (1.3.2)
chronic (0.10.2)
chunky_png (1.2.8)
clerk (0.2.2)
Expand Down Expand Up @@ -193,6 +194,8 @@ GEM
json
multi_json
request_store (>= 1.0.5)
groupdate (2.4.0)
activesupport (>= 3)
guard (1.6.2)
listen (>= 0.6.0)
lumberjack (>= 1.0.2)
Expand Down Expand Up @@ -442,6 +445,7 @@ DEPENDENCIES
capistrano-slack!
capybara
carrierwave
chartkick
clerk
codeclimate-test-reporter
coffee-rails (~> 3.2.1)
Expand All @@ -458,6 +462,7 @@ DEPENDENCIES
geoip
git_pretty_accept
gon
groupdate
guard-bundler
guard-livereload
immigrant
Expand Down
15 changes: 15 additions & 0 deletions app/controllers/admin/api_usage_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
class Admin::ApiUsageController < Admin::AdminController
def index
@last_30_days_requests = ApiRequest.group(:response_status).order(:response_status)
.group_by_day(:created_at, range: 30.days.ago.midnight..Time.now).count
@all_requests = ApiRequest.all
@users_by_activity = ApiRequest.where('created_at > ?', 30.days.ago).group_by(&:user).sort_by { |k,v| -v.count }[0..4]
end

def show
@user = User.find(params[:id])
@last_30_days_requests = @user.api_requests.group(:response_status).order(:response_status)
.group_by_day(:created_at, range: 30.days.ago.midnight..Time.now).count
@all_requests = @user.api_requests
end
end
12 changes: 12 additions & 0 deletions app/helpers/admin/api_usage_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
module Admin::ApiUsageHelper
# Take the hash necessary for the line graph and replace 200 with 'Successful'
# and anything else with 'Failed' and return newly constructed hash
def sanitise_hash_for_line_graph(hash)
new_hash = {}
hash.map { |k,v|
n = k[0] == 200 ? 'Successful' : 'Failed'
new_hash[[n, k[1]]] = v
}
new_hash
end
end
5 changes: 5 additions & 0 deletions app/models/api_request.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class ApiRequest < ActiveRecord::Base
serialize :params, JSON

belongs_to :user
end
2 changes: 1 addition & 1 deletion app/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class User < ActiveRecord::Base

has_many :ahoy_visits, dependent: :nullify, class_name: 'Ahoy::Visit'
has_many :ahoy_events, dependent: :nullify, class_name: 'Ahoy::Event'

has_many :api_requests
belongs_to :geo_entity

validates :email, :uniqueness => true, :presence => true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ def eu_decisions
WHEN eu_decisions.type = 'EuOpinion'
THEN eu_decisions.start_date
WHEN eu_decisions.type = 'EuSuspension'
THEN (start_event->>'effective_at')::DATE
THEN (start_event->>'date')::DATE
END DESC,
subspecies_info DESC
SQL
Expand Down
71 changes: 71 additions & 0 deletions app/views/admin/api_usage/index.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<div class="row">
<div class="span12">
<div class="page-header">
<h1>API Usage</h1>
</div>

<div class="page-header">
<h4>Last 30 Days Requests by Response Status</h4>
</div>

<%= line_chart sanitise_hash_for_line_graph(@last_30_days_requests),
library: {
title: 'Requests by HTTP Status',
titlePosition: 'out',
colors: ['#4AC948', '#EE3B3B']
}
%>
</div>
</div>

<div class="row">
<div class="span6">
<div class="page-header">
<h4>All Requests by Response Status</h4>
</div>
<%= pie_chart ApiRequest.order(:response_status).group(:response_status).count, library: { legend: 'left', colors: ['#4AC948', '#EE3B3B'] } %>
</div>

<div class="span6">
<div class="page-header">
<h4>All Requests by Endpoint</h4>
</div>
<%= pie_chart ApiRequest.group(:controller).count, library: { legend: 'left' } %>
</div>
</div>

<div class="row">
<div class="span12">
<div class="page-header">
<h4>Top 5 Most Active Users in the last 30 days</h4>
</div>
<table class="table">
<thead>
<tr>
<th>Name</th>
<th>Email</th>
<th>Organisation</th>
<th>Country</th>
<th>Successful Requests</th>
<th>Failed Requests</th>
<th>View User Data</th>
</tr>
</thead>
<tbody>
<% unless @users_by_activity.empty? %>
<% @users_by_activity.each do |user, requests| %>
<tr>
<th><%= user.try(:name) %></th>
<th><%= user.try(:email) %></th>
<th><%= user.try(:geo_entity).try(:name_en) %></th>
<th><%= user.try(:organisation) %></th>
<th><%= requests.select { |req| req.response_status == 200 }.count %></th>
<th><%= requests.select { |req| req.response_status == 500 }.count %></th>
<th><%= link_to 'View', admin_api_usage_path(user) %></th>
</tr>
<% end %>
<% end %>
</tbody>
</table>
</div>
</div>
61 changes: 61 additions & 0 deletions app/views/admin/api_usage/show.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<div class="row">
<div class="span12">
<div class="page-header">
<h1>API Usage for <%= @user.name %></h1>
<%= link_to '&#8592; Back to Overview'.html_safe, admin_api_usage_index_path %>
</div>

<div class="page-header">
<h4>About this User</h4>
</div>

<dl class="dl-horizontal">
<dt>Email:<dt>
<dd><%= @user.email %></dd>
<dt>Organisation:<dt>
<dd><%= @user.try(:organisation) %></dd>
<dt>Country:<dt>
<dd><%= @user.try(:geo_entity).try(:name_en) %></dd>
<dt>Role:<dt>
<dd><%= @user.role %></dd>
<dt>Total Successful Requests:<dt>
<dd><%= number_with_delimiter @user.api_requests.where(response_status: 200).count, delimiter: ',' %></dd>
<dt>Total Failed Requests (to date):<dt>
<dd><%= number_with_delimiter @user.api_requests.where(response_status: 500).count, delimiter: ',' %></dd>
<dt>Date Joined:<dt>
<dd><%= @user.created_at.to_s(:long) %></dd>
</dl>
</div>
</div>

<div class="row">
<div class="span12">
<div class="page-header">
<h4>Last 30 Days Requests by Response Status</h4>
</div>

<%= line_chart sanitise_hash_for_line_graph(@last_30_days_requests),
library: {
title: 'Requests by HTTP Status',
titlePosition: 'out',
colors: ['#4AC948', '#EE3B3B']
}
%>
</div>
</div>

<div class="row">
<div class="span6">
<div class="page-header">
<h4>All Requests by Response Status</h4>
</div>
<%= pie_chart @user.api_requests.order(:response_status).group(:response_status).count, library: { legend: 'none', colors: ['#4AC948', '#EE3B3B'] } %>
</div>

<div class="span6">
<div class="page-header">
<h4>All Requests by Endpoint</h4>
</div>
<%= pie_chart @user.api_requests.group(:controller).count, library: { legend: 'none' } %>
</div>
</div>
3 changes: 3 additions & 0 deletions app/views/layouts/admin.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
<%= stylesheet_link_tag "admin" %>
<%= javascript_include_tag "admin" %>
<%= csrf_meta_tags %>
<% if controller_name == 'api_usage' %>
<%= javascript_include_tag "//www.google.com/jsapi", "chartkick" %>
<% end %>
<script type="text/javascript">
window.editorClass = '<%= controller.controller_name %>'
</script>
Expand Down
4 changes: 2 additions & 2 deletions app/views/shared/_topbar.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -180,8 +180,8 @@
<li><%= link_to 'Species+ Visits Tracking', admin_ahoy_visits_path %></li>
<li><%= link_to 'Species+ Activity Page', activities_path %></li>

<% if current_user && current_user.is_api? %>
<li><a href="#">API Usage</a></li>
<% if current_user && current_user.is_admin? %>
<li><%= link_to 'API Usage', admin_api_usage_index_path %></li>
<% end %>
</ul>
</li>
Expand Down
1 change: 1 addition & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
resources :geo_relationship_types, :only => [:index]
end
namespace :admin do
resources :api_usage, :only => [:index, :show]
resources :taxonomies, :only => [:index, :create, :update, :destroy]
resources :terms, :only => [:index, :create, :update, :destroy]
resources :sources, :only => [:index, :create, :update, :destroy]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
class AddAuthenticationTokenToUsers < ActiveRecord::Migration
def change
add_column :users, :authentication_token, :string
add_index :users, :authentication_token
end
end
16 changes: 16 additions & 0 deletions db/migrate/20150107173809_create_api_requests.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
class CreateApiRequests < ActiveRecord::Migration
def change
create_table :api_requests do |t|
t.references :user, index: true
t.string :controller
t.string :action
t.string :format
t.text :params
t.string :ip
t.integer :response_status
t.text :error_message

t.timestamps
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
class AddHigherTaxaFieldsToApiTaxonConceptsView < ActiveRecord::Migration
def change
execute "DROP VIEW IF EXISTS api_taxon_concepts_view"
execute "CREATE VIEW api_taxon_concepts_view AS #{view_sql('20150114104555', 'api_taxon_concepts_view')}"
end
end
93 changes: 93 additions & 0 deletions db/views/api_taxon_concepts_view/20150114104555.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
SELECT
tc.id,
tc.parent_id,
taxonomies.name,
CASE WHEN taxonomies.name = 'CITES_EU' THEN TRUE ELSE FALSE END AS taxonomy_is_cites_eu,
tc.full_name,
tc.author_year,
'A' AS name_status,
ranks.name AS rank,
tc.taxonomic_position,
tc.data->'kingdom_name' AS kingdom_name,
tc.data->'phylum_name' AS phylum_name,
tc.data->'class_name' AS class_name,
tc.data->'order_name' AS order_name,
tc.data->'family_name' AS family_name,
tc.data->'genus_name' AS genus_name,
tc.data->'species_name' AS species_name,
ROW_TO_JSON(
ROW(
tc.data->'kingdom_name',
tc.data->'phylum_name',
tc.data->'class_name',
tc.data->'order_name',
tc.data->'family_name'
)::api_higher_taxa
) AS higher_taxa,
ARRAY_TO_JSON(
ARRAY_AGG_NOTNULL(
ROW(
synonyms.id, synonyms.full_name, synonyms.author_year, synonyms.data->'rank_name'
)::api_taxon_concept
)
) AS synonyms,
NULL AS accepted_names,
tc.created_at,
tc.updated_at
FROM taxon_concepts tc
JOIN taxonomies ON taxonomies.id = tc.taxonomy_id
JOIN ranks ON ranks.id = tc.rank_id
LEFT JOIN taxon_relationships tr
ON tr.taxon_concept_id = tc.id
LEFT JOIN taxon_relationship_types trt
ON trt.id = tr.taxon_relationship_type_id AND trt.name = 'HAS_SYNONYM'
LEFT JOIN taxon_concepts synonyms
ON synonyms.id = tr.other_taxon_concept_id
WHERE tc.name_status = 'A'
GROUP BY tc.id, tc.parent_id, taxonomies.name, tc.full_name, tc.author_year, ranks.name,
tc.taxonomic_position,
tc.created_at, tc.updated_at

UNION ALL

SELECT
tc.id,
NULL AS parent_id,
taxonomies.name,
CASE WHEN taxonomies.name = 'CITES' THEN TRUE ELSE FALSE END AS taxonomy_is_cites_eu,
tc.full_name,
tc.author_year,
'S' AS name_status,
ranks.name AS rank,
NULL AS taxonomic_position,
NULL AS kingdom_name,
NULL AS phylum_name,
NULL AS class_name,
NULL AS order_name,
NULL AS family_name,
NULL AS genus_name,
NULL AS species_name,
NULL::JSON AS higher_taxa,
NULL AS synonyms,
ARRAY_TO_JSON(
ARRAY_AGG_NOTNULL(
ROW(
accepted_names.id, accepted_names.full_name, accepted_names.author_year, accepted_names.data->'rank_name'
)::api_taxon_concept
)
) AS accepted_names,
tc.created_at,
tc.updated_at
FROM taxon_concepts tc
JOIN taxonomies ON taxonomies.id = tc.taxonomy_id
JOIN ranks ON ranks.id = tc.rank_id
JOIN taxon_relationships tr
ON tr.other_taxon_concept_id = tc.id
JOIN taxon_relationship_types trt
ON trt.id = tr.taxon_relationship_type_id AND trt.name = 'HAS_SYNONYM'
JOIN taxon_concepts accepted_names
ON accepted_names.id = tr.taxon_concept_id
WHERE tc.name_status = 'S'
GROUP BY tc.id, tc.parent_id, taxonomies.name, tc.full_name, tc.author_year, ranks.name,
tc.taxonomic_position,
tc.created_at, tc.updated_at;
Binary file added vendor/cache/chartkick-1.3.2.gem
Binary file not shown.
Binary file added vendor/cache/groupdate-2.4.0.gem
Binary file not shown.

0 comments on commit 37a384e

Please sign in to comment.