Skip to content

Commit

Permalink
Merge pull request #68 from leifg/add_statistics
Browse files Browse the repository at this point in the history
Add Statistics
  • Loading branch information
leifg committed Oct 4, 2013
2 parents 6122257 + e36be59 commit 8b57765
Show file tree
Hide file tree
Showing 15 changed files with 16,988 additions and 1 deletion.
3 changes: 3 additions & 0 deletions app/assets/javascripts/application.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
//= require global
//= require bootstrap
//= require turbolinks
//= require angular
//= require chart
//= require angles
//= require_tree .

$.fn.editable.defaults.mode = 'inline';
Expand Down
31 changes: 31 additions & 0 deletions app/assets/javascripts/statistics.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
var app = angular.module('Marozi', ['angles']);

app.config(function($httpProvider) {
var authToken = $('meta[name="csrf-token"]').attr('content');
$httpProvider.defaults.headers.common['X-CSRF-TOKEN'] = authToken;
});

app.controller('MaleFemaleChartCtrl', function($scope, $http, $location) {
var id = parseId($location.absUrl());
$http.get('/api/statistics/clubs/'+id+'/gender_ratio').success(function(data){
$scope.chart = [
{
value: data.female,
color:"#F7464A",
},
{
value: data.male,
color:"#949FB1",
},
];
});
});

function parseId(location){
var id = /(\d+)\/statistics/.exec(location)[1];
return id;
}

$(document).on('ready page:load', function() {
angular.bootstrap(document, ['Marozi']);
});
4 changes: 4 additions & 0 deletions app/controllers/clubs_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,8 @@ def my_club
@club = Club.find_by_member_id session[:current_user]
render action: 'show'
end

def my_club_statistics
redirect_to club_statistics_path(session[:current_user_club])
end
end
12 changes: 12 additions & 0 deletions app/controllers/statistics_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
class StatisticsController < ApplicationController
def gender_ratio
type = params[:type]
unless %w{clubs districts multiple_districts}.include?(type)
render :status => :forbidden, :text => "Forbidden type: #{type.inspect}" \
and return
end
constant = Kernel.const_get(type.singularize.camelize)
instance = constant.find_by!(oid: params[:id])
render json: instance.gender_ratio(:active)
end
end
1 change: 1 addition & 0 deletions app/models/club.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
class Club < ActiveRecord::Base
include Versioning
include MemberStatistics

has_many :members, autosave: true
has_many :offices, as: :provides_offices
Expand Down
13 changes: 13 additions & 0 deletions app/models/concerns/member_statistics.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
module MemberStatistics
extend ActiveSupport::Concern

def gender_ratio status
f,m = members
.select{|member| member.status == status}
.partition{|member| member.gender == :female}
{
female: f.size,
male: m.size,
}
end
end
7 changes: 7 additions & 0 deletions app/views/clubs/statistics.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.row{'ng-app' => 'Marozi'}
.span4
%p.lead
.info-box{'ng-controller' => 'MaleFemaleChartCtrl'}
.info-box-caption{'ng-controller' => 'MaleFemaleChartCtrl'}
=t(:'clubs.statistics.female_male')
%canvas{piechart: '', options: 'options', data: 'chart', id: 'male_female_ratio', width: '300', height: '300'}
4 changes: 3 additions & 1 deletion app/views/shared/_menu.haml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@
%a{href: '#'}= t(:'navbar.club.accept_to_club')
%li
%a{href: '#'}= t(:'navbar.club.add_lions_office')
%li
%a{href: my_club_statistics_path}= t(:'navbar.club.statistics')
%li.dropdown
%a{href: '#', class: 'dropdown-toggle', :'data-toggle' => 'dropdown'}
= t(:'navbar.district.title')
Expand Down Expand Up @@ -82,4 +84,4 @@
%input.search-query{type: 'text', placeholder: t(:'navbar.search.title')}
.icon-search
:javascript
MyApp.initFunction(MyApp.disable_menu_item);
MyApp.initFunction(MyApp.disable_menu_item);
3 changes: 3 additions & 0 deletions config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ en:
accept_to_club: Accept Guest into Club
add_guest: Add Guest
add_lions_office: Add Lions Office
statistics: Statistics
committees:
title: 'Committees'
district:
Expand Down Expand Up @@ -87,6 +88,8 @@ en:
officers_secretary: 'Secretary'
officers_treasurer: 'Treasurer'
officers_it_appointee: 'IT Apointee'
statistics:
female_male: Female/Male Ratio
committees:
district_title: 'Committees of Multiple District %{multiple_district}'
title_activity: 'Activity'
Expand Down
9 changes: 9 additions & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,15 @@
post '/sessions/new' => 'sessions#create', as: :login
get '/logout' => 'sessions#destroy', as: :logout

# statistics
get '/clubs/my_club/statistics' => 'clubs#my_club_statistics', as: :my_club_statistics
get '/clubs/:id/statistics/' => 'clubs#statistics', as: :club_statistics

# statistics api
scope :api do
get '/statistics/:type/:id/gender_ratio' => 'statistics#gender_ratio'
end

resources :members
resources :clubs
resources :districts
Expand Down
40 changes: 40 additions & 0 deletions spec/controllers/statistics_controller_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
require 'spec_helper'

describe StatisticsController do
context 'protected methods' do
include_context 'protected controller'

let(:existing_id) {300066}

describe '#gender_ratio' do
it 'dispatches correctly to club' do
club_mock = double('Club')
Club.stub(:find_by!).and_return(club_mock)
expect(club_mock).to receive(:gender_ratio).and_return('correctly stubbed')
get :gender_ratio, id: existing_id, type: 'clubs'
expect(response.body).to eq 'correctly stubbed'
end

it 'dispatches correctly to district' do
district_mock = double('District')
District.stub(:find_by!).and_return(district_mock)
expect(district_mock).to receive(:gender_ratio).and_return('correctly stubbed')
get :gender_ratio, id: existing_id, type: 'districts'
expect(response.body).to eq 'correctly stubbed'
end

it 'dispatches correctly to multiple district' do
multiple_district_mock = double('MultipleDistrict')
MultipleDistrict.stub(:find_by!).and_return(multiple_district_mock)
expect(multiple_district_mock).to receive(:gender_ratio).and_return('correctly stubbed')
get :gender_ratio, id: existing_id, type: 'multiple_districts'
expect(response.body).to eq 'correctly stubbed'
end

it 'returns 403 for wrong type' do
get :gender_ratio, id: existing_id, type: 'fancy_injection'
expect(response.status).to eq 403
end
end
end
end
85 changes: 85 additions & 0 deletions spec/models/concerns/member_statistics_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
require 'spec_helper'

describe MemberStatistics do
describe '#gender_ratio' do
let(:active_members_only) do
members = []
{
female: 11,
male: 12,
}.each do |gender, num|
num.times do
member = double('Member')
member.stub(:gender){ gender }
member.stub(:status){ :active }
members << member
end
end

o = double()
o.stub(:members) { members }
o.extend(described_class)
end

let(:active_inactive_members) do
members = []
[
{
status: :active,
gender: :female,
num: 5,
},
{
status: :passive,
gender: :female,
num: 9,
},
{
status: :active,
gender: :male,
num: 3,
},
{
status: :passive,
gender: :male,
num: 16,
}
].each do |hash|
hash[:num].times do
member = double('Member')
member.stub(:gender){ hash[:gender] }
member.stub(:status){ hash[:status] }
members << member
end
end

o = double()
o.stub(:members) { members }
o.extend(described_class)
end

it 'returns correct ratio for active only members' do
expected_result = {
female: 11,
male: 12,
}
expect(active_members_only.gender_ratio(:active)).to eq(expected_result)
end

it 'returns correct ratio for mixed members active' do
expected_result = {
female: 5,
male: 3,
}
expect(active_inactive_members.gender_ratio(:active)).to eq(expected_result)
end

it 'returns correct ratio for mixed members active' do
expected_result = {
female: 9,
male: 16,
}
expect(active_inactive_members.gender_ratio(:passive)).to eq(expected_result)
end
end
end
55 changes: 55 additions & 0 deletions vendor/assets/javascripts/angles.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
var angles = angular.module("angles", []);


angles.chart = function (type) {
return {
restrict: "A",
scope: {
data: "=",
options: "=",
id: "@"
},
link: function ($scope, $elem) {
var ctx = $elem[0].getContext("2d");
var chart = new Chart(ctx);

$scope.$watch("data", function (newVal, oldVal) {
// if data not defined, exit
if (!newVal) return;

chart[type]($scope.data, $scope.options);
}, true);
}
}
}


/* General Chart Wrapper */
angles.directive("chart", function () {
return {
restrict: "A",
scope: {
data: "=",
type: "@",
options: "=",
id: "@"
},
link: function ($scope, $elem) {
var ctx = $elem[0].getContext("2d");
var chart = new Chart(ctx);

$scope.$watch("data", function (newVal, oldVal) {
chart[$scope.type]($scope.data, $scope.options);
}, true);
}
}
});


/* Aliases for various chart types */
angles.directive("linechart", function () { return angles.chart("Line"); });
angles.directive("barchart", function () { return angles.chart("Bar"); });
angles.directive("radarchart", function () { return angles.chart("Radar"); });
angles.directive("polarchart", function () { return angles.chart("PolarArea"); });
angles.directive("piechart", function () { return angles.chart("Pie"); });
angles.directive("donutchart", function () { return angles.chart("Doughnut"); });

0 comments on commit 8b57765

Please sign in to comment.