Merge pull request #87 from sgmap/better-stats

Better stats
This commit is contained in:
gregoirenovel 2017-04-11 17:05:53 +02:00 committed by GitHub
commit 3f15cb99fd
11 changed files with 300 additions and 49 deletions

View file

@ -0,0 +1,20 @@
var TPS = TPS || {};
TPS.toggleChart = function(event, chartClass) {
var nextSelectorItem = $(event.target),
nextChart = $(chartClass),
nextChartId = nextChart.children().first().attr("id"),
currentSelectorItem = nextSelectorItem.parent().find(".segmented-control-item-active"),
currentChart = nextSelectorItem.parent().parent().find(".chart:not(.hidden)");
// Change the current selector and the next selector states
currentSelectorItem.toggleClass("segmented-control-item-active");
nextSelectorItem.toggleClass("segmented-control-item-active");
// Hide the currently shown chart and show the new one
currentChart.toggleClass("hidden");
nextChart.toggleClass("hidden");
// Reflow needed, see https://github.com/highcharts/highcharts/issues/1979
Chartkick.charts[nextChartId].getChartObject().reflow();
}

View file

@ -1,9 +0,0 @@
@import "card";
.stats {
.stat-card {
@extend .card;
margin: 15px auto;
max-width: 1200px;
}
}

View file

@ -0,0 +1,124 @@
@import "card";
$dark-grey: #333333;
$light-grey: #999999;
$blue: rgba(61, 149, 236, 1);
$blue-hover: rgba(61, 149, 236, 0.8);
$default-space: 15px;
$new-h1-margin-bottom: 4 * $default-space;
.new-h1 {
color: $dark-grey;
text-align: center;
margin-top: 0;
margin-bottom: $new-h1-margin-bottom;
}
$statistiques-padding-top: $default-space * 2;
.statistiques {
width: 1040px;
margin: 0 auto;
padding-top: $statistiques-padding-top;
}
.stat-cards {
.stat-card:nth-of-type(even) {
margin-right: 0px;
}
}
$stat-card-margin-bottom: 3 * $default-space;
.stat-card {
@extend .card;
margin-bottom: $stat-card-margin-bottom;
border-radius: 5px;
box-shadow: none;
border: 1px solid rgba(0, 0, 0, 0.15);
}
$stat-card-half-horizontal-spacing: 4 * $default-space;
.stat-card-half {
width: calc((100% - #{$stat-card-half-horizontal-spacing}) / 2);
margin-right: 3 * $default-space;
}
.stat-card-title {
color: $dark-grey;
font-size: 26px;
font-weight: 500;
width: 200px;
}
$segmented-control-margin-top: $default-space;
.segmented-control {
border-radius: 36px;
height: 36px;
line-height: 36px;
font-size: 0;
padding: 0;
display: inline-block;
margin-top: $segmented-control-margin-top;
}
$segmented-control-item-horizontal-padding: $default-space;
$segmented-control-item-border-radius: 2 * $default-space;
.segmented-control-item {
color: $blue;
display: inline-block;
font-size: 15px;
border: 2px solid $blue;
margin-right: -2px;
padding-left: $segmented-control-item-horizontal-padding;
padding-right: $segmented-control-item-horizontal-padding;
color: $blue;
&:first-of-type {
border-radius: $segmented-control-item-border-radius 0px 0px $segmented-control-item-border-radius;
}
&:last-of-type {
border-radius: 0px $segmented-control-item-border-radius $segmented-control-item-border-radius 0px;
margin-right: 0;
}
&:hover {
background-color: $blue-hover;
color: white;
cursor: pointer;
}
}
.segmented-control-item-active {
background-color: $blue;
color: white;
}
.chart-container {
margin-top: 36px;
}
.chart {
width: 100%;
}
$big-number-card-padding: 2 * $segmented-control-item-border-radius;
.big-number-card {
padding: $big-number-card-padding;
}
.big-number-card-title {
display: block;
text-align: center;
margin: 0 auto;
color: $light-grey;
}
.big-number-card-number {
display: block;
text-align: center;
font-size: 90px;
line-height: 90px;
font-weight: bold;
color: $blue;
}

View file

@ -1,24 +0,0 @@
module Administrations
class StatsController < ApplicationController
before_action :authenticate_administration!
def index
procedures = Procedure.where(created_at: Time.current.all_quarter).group("date_trunc('day', created_at)").count
dossiers = Dossier.where(created_at: Time.current.all_quarter).group("date_trunc('day', created_at)").count
@procedures = clean_hash(procedures)
@dossiers = clean_hash(dossiers)
end
private
def clean_hash h
h.keys.each{ |key| h[key.to_date] = h[key]; h.delete(key) }
min_date = h.keys.min
max_date = h.keys.max
(min_date..max_date).each do |date|
h[date] = 0 if h[date].nil?
end
h
end
end
end

View file

@ -0,0 +1,56 @@
class StatsController < ApplicationController
def index
procedures = Procedure.where(:published => true)
dossiers = Dossier.where.not(:state => :draft)
@procedures_30_days_flow = thirty_days_flow_hash(procedures)
@dossiers_30_days_flow = thirty_days_flow_hash(dossiers)
@procedures_cumulative = cumulative_hash(procedures)
@dossiers_cumulative = cumulative_hash(dossiers)
@procedures_count = procedures.count
@dossiers_count = dossiers.count
end
private
def thirty_days_flow_hash(association)
min_date = 30.days.ago.to_date
max_date = Time.now.to_date
thirty_days_flow_hash = association
.where(:created_at => min_date..max_date)
.group("date_trunc('day', created_at)")
.count
clean_hash(thirty_days_flow_hash, min_date, max_date)
end
def clean_hash(h, min_date, max_date)
# Convert keys to date
h = Hash[h.map { |(k, v)| [k.to_date, v] }]
# Add missing vales where count is 0
(min_date..max_date).each do |date|
if h[date].nil?
h[date] = 0
end
end
h
end
def cumulative_hash(association)
sum = 0
association
.group("DATE_TRUNC('month', created_at)")
.count
.to_a
.sort{ |x, y| x[0] <=> y[0] }
.map { |x, y| { x => (sum += y)} }
.reduce({}, :merge)
end
end

View file

@ -7,9 +7,6 @@
= f.submit 'Créer un administrateur', class: 'btn btn-success', id: 'submit_new_administrateur' = f.submit 'Créer un administrateur', class: 'btn btn-success', id: 'submit_new_administrateur'
.text-center
= link_to 'Stats', administrations_stats_path, style: 'margin-bottom: 50px; display: block;', 'data-no-turbolink': true
= smart_listing_render :admins = smart_listing_render :admins
%br %br

View file

@ -1,11 +0,0 @@
= javascript_include_tag 'https://code.highcharts.com/highcharts.js', 'chartkick'
.container
.stats
.stat-card
%h1 Procédures créées
= line_chart @procedures
.stat-card
%h1 Dossiers créés
= line_chart @dossiers

View file

@ -5,6 +5,8 @@
\- \-
= link_to 'Nouveautés', 'https://github.com/sgmap/tps/releases', target: '_blank' = link_to 'Nouveautés', 'https://github.com/sgmap/tps/releases', target: '_blank'
\- \-
= link_to 'Statistiques', stats_path
\-
= link_to 'CGU / Mentions légales', cgu_path = link_to 'CGU / Mentions légales', cgu_path
\- \-
= link_to 'Contact', "mailto:"+t('dynamics.contact_email') = link_to 'Contact', "mailto:"+t('dynamics.contact_email')

View file

@ -0,0 +1,53 @@
= javascript_include_tag 'https://code.highcharts.com/highcharts.js', 'chartkick'
.statistiques
%h1.new-h1 Statistiques
.stat-cards
.stat-card.stat-card-half.pull-left
%ul.segmented-control.pull-right
%li.segmented-control-item.segmented-control-item-active{ :onclick => "TPS.toggleChart(event, '.cumulative-procedures-chart');" }
Cumul
%li.segmented-control-item{ :onclick => "TPS.toggleChart(event, '.flux-procedures-chart');" }
Flux (30 jours)
%span.stat-card-title.pull-left Démarches dématérialisées
.clearfix
.chart-container
.chart.cumulative-procedures-chart
= area_chart @procedures_cumulative,
:colors => ["rgba(61, 149, 236, 1)"]
.chart.flux-procedures-chart.hidden
= line_chart @procedures_30_days_flow,
:colors => ["rgba(61, 149, 236, 1)"]
.stat-card.stat-card-half.pull-left
%ul.segmented-control.pull-right
%li.segmented-control-item.segmented-control-item-active{ :onclick => "TPS.toggleChart(event, '.cumulative-dossiers-chart');" }
Cumul
%li.segmented-control-item{ :onclick => "TPS.toggleChart(event, '.flux-dossiers-chart');" }
Flux (30 jours)
%span.stat-card-title.pull-left Dossiers déposés
.clearfix
.chart-container
.chart.cumulative-dossiers-chart
= area_chart @dossiers_cumulative,
:colors => ["rgba(61, 149, 236, 1)"]
.chart.flux-dossiers-chart.hidden
= line_chart @dossiers_30_days_flow,
:colors => ["rgba(61, 149, 236, 1)"]
.stat-card.stat-card-half.big-number-card.pull-left
%span.big-number-card-title TOTAL DÉMARCHES DÉMATÉRIALISÉES
%span.big-number-card-number
= @procedures_count
.stat-card.stat-card-half.big-number-card.pull-left
%span.big-number-card-title TOTAL DOSSIERS DÉPOSÉS
%span.big-number-card-number
= @dossiers_count
.clearfix

View file

@ -45,14 +45,14 @@ Rails.application.routes.draw do
authenticate :administration do authenticate :administration do
resources :administrations, only: [:index, :create] resources :administrations, only: [:index, :create]
namespace :administrations do namespace :administrations do
resources :stats, only: [:index]
require 'sidekiq/web' require 'sidekiq/web'
require 'sidekiq/cron/web' require 'sidekiq/cron/web'
mount Sidekiq::Web => '/sidekiq' mount Sidekiq::Web => '/sidekiq'
end end
end end
resources :stats, only: [:index]
namespace :france_connect do namespace :france_connect do
get 'particulier' => 'particulier#login' get 'particulier' => 'particulier#login'
get 'particulier/callback' => 'particulier#callback' get 'particulier/callback' => 'particulier#callback'

View file

@ -0,0 +1,43 @@
require 'spec_helper'
describe StatsController, type: :controller do
describe '#thirty_days_flow_hash' do
before do
FactoryGirl.create(:procedure, :created_at => 45.days.ago)
FactoryGirl.create(:procedure, :created_at => 15.days.ago)
FactoryGirl.create(:procedure, :created_at => 1.day.ago)
@expected_hash = {}
(30.days.ago.to_date..Time.now.to_date).each do |day|
if [15.days.ago.to_date, 1.day.ago.to_date].include?(day)
@expected_hash[day] = 1
else
@expected_hash[day] = 0
end
end
end
let (:association) { Procedure.all }
subject { StatsController.new.send(:thirty_days_flow_hash, association) }
it { expect(subject).to eq(@expected_hash) }
end
describe '#cumulative_hash' do
before do
FactoryGirl.create(:procedure, :created_at => 45.days.ago)
FactoryGirl.create(:procedure, :created_at => 15.days.ago)
FactoryGirl.create(:procedure, :created_at => 15.days.ago)
end
let (:association) { Procedure.all }
subject { StatsController.new.send(:cumulative_hash, association) }
it { expect(subject).to eq({
45.days.ago.beginning_of_month => 1,
15.days.ago.beginning_of_month => 3
}) }
end
end