commit
3f15cb99fd
11 changed files with 300 additions and 49 deletions
20
app/assets/javascripts/toggle_chart.js
Normal file
20
app/assets/javascripts/toggle_chart.js
Normal 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();
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
@import "card";
|
||||
|
||||
.stats {
|
||||
.stat-card {
|
||||
@extend .card;
|
||||
margin: 15px auto;
|
||||
max-width: 1200px;
|
||||
}
|
||||
}
|
124
app/assets/stylesheets/stats.scss
Normal file
124
app/assets/stylesheets/stats.scss
Normal 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;
|
||||
}
|
|
@ -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
|
56
app/controllers/stats_controller.rb
Normal file
56
app/controllers/stats_controller.rb
Normal 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
|
|
@ -7,9 +7,6 @@
|
|||
|
||||
= 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
|
||||
|
||||
%br
|
||||
|
|
|
@ -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
|
|
@ -5,6 +5,8 @@
|
|||
\-
|
||||
= 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 'Contact', "mailto:"+t('dynamics.contact_email')
|
||||
|
|
53
app/views/stats/index.html.haml
Normal file
53
app/views/stats/index.html.haml
Normal 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
|
|
@ -45,14 +45,14 @@ Rails.application.routes.draw do
|
|||
authenticate :administration do
|
||||
resources :administrations, only: [:index, :create]
|
||||
namespace :administrations do
|
||||
resources :stats, only: [:index]
|
||||
|
||||
require 'sidekiq/web'
|
||||
require 'sidekiq/cron/web'
|
||||
mount Sidekiq::Web => '/sidekiq'
|
||||
end
|
||||
end
|
||||
|
||||
resources :stats, only: [:index]
|
||||
|
||||
namespace :france_connect do
|
||||
get 'particulier' => 'particulier#login'
|
||||
get 'particulier/callback' => 'particulier#callback'
|
||||
|
|
43
spec/controllers/stats_controller_spec.rb
Normal file
43
spec/controllers/stats_controller_spec.rb
Normal 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
|
Loading…
Reference in a new issue