really faster stats by precomputing some of them
This commit is contained in:
parent
f5f363ae99
commit
ad01d4dee5
5 changed files with 143 additions and 27 deletions
|
@ -4,7 +4,7 @@ class StatsController < ApplicationController
|
|||
MEAN_NUMBER_OF_CHAMPS_IN_A_FORM = 24.0
|
||||
|
||||
def index
|
||||
@dossiers_states = dossiers_states
|
||||
stat = Stat.first
|
||||
|
||||
procedures = Procedure.publiees_ou_closes
|
||||
dossiers = Dossier.state_not_brouillon
|
||||
|
@ -12,22 +12,27 @@ class StatsController < ApplicationController
|
|||
@procedures_numbers = procedures_numbers(procedures)
|
||||
|
||||
@dossiers_numbers = dossiers_numbers(
|
||||
@dossiers_states['not_brouillon'],
|
||||
@dossiers_states['last_30_days_count'],
|
||||
@dossiers_states['previous_count']
|
||||
stat.dossiers_not_brouillon,
|
||||
stat.dossiers_depose_avant_30_jours,
|
||||
stat.dossiers_deposes_entre_60_et_30_jours
|
||||
)
|
||||
|
||||
@satisfaction_usagers = satisfaction_usagers
|
||||
|
||||
@contact_percentage = contact_percentage
|
||||
|
||||
@dossiers_states_for_pie = @dossiers_states.slice("Brouillon", "En construction", "En instruction", "Terminé")
|
||||
@dossiers_states_for_pie = {
|
||||
"Brouillon" => stat.dossiers_brouillon,
|
||||
"En construction" => stat.dossiers_en_construction,
|
||||
"En instruction" => stat.dossiers_en_instruction,
|
||||
"Terminé" => stat.dossiers_termines
|
||||
}
|
||||
|
||||
@procedures_cumulative = cumulative_hash(procedures, :published_at)
|
||||
@procedures_in_the_last_4_months = last_four_months_hash(procedures, :published_at)
|
||||
|
||||
@dossiers_cumulative = cumulative_hash(dossiers, :en_construction_at)
|
||||
@dossiers_in_the_last_4_months = last_four_months_hash(dossiers, :en_construction_at)
|
||||
@dossiers_cumulative = stat.dossiers_cumulative
|
||||
@dossiers_in_the_last_4_months = stat.dossiers_in_the_last_4_months
|
||||
|
||||
if administration_signed_in?
|
||||
@dossier_instruction_mean_time = Rails.cache.fetch("dossier_instruction_mean_time", expires_in: 1.day) do
|
||||
|
@ -118,25 +123,6 @@ class StatsController < ApplicationController
|
|||
}
|
||||
end
|
||||
|
||||
def dossiers_states
|
||||
query = <<-EOF
|
||||
SELECT
|
||||
COUNT(*) FILTER ( WHERE state != 'brouillon' ) AS "not_brouillon",
|
||||
COUNT(*) FILTER ( WHERE state != 'brouillon' and en_construction_at BETWEEN :one_month_ago AND :now ) AS "last_30_days_count",
|
||||
COUNT(*) FILTER ( WHERE state != 'brouillon' and en_construction_at BETWEEN :two_months_ago AND :one_month_ago ) AS "previous_count",
|
||||
COUNT(*) FILTER ( WHERE state = 'brouillon' ) AS "Brouillon",
|
||||
COUNT(*) FILTER ( WHERE state = 'en_construction' ) AS "En construction",
|
||||
COUNT(*) FILTER ( WHERE state = 'en_instruction' ) AS "En instruction",
|
||||
COUNT(*) FILTER ( WHERE state in ('accepte', 'refuse', 'sans_suite') ) AS "Terminé"
|
||||
FROM dossiers
|
||||
WHERE hidden_at IS NULL
|
||||
EOF
|
||||
|
||||
sanitized_query = ActiveRecord::Base.sanitize_sql([query, now: Time.zone.now, one_month_ago: 1.month.ago, two_months_ago: 2.months.ago])
|
||||
|
||||
Dossier.connection.select_all(sanitized_query).first
|
||||
end
|
||||
|
||||
def satisfaction_usagers
|
||||
legend = {
|
||||
Feedback.ratings.fetch(:unhappy) => "Mécontents",
|
||||
|
|
7
app/jobs/update_stats_job.rb
Normal file
7
app/jobs/update_stats_job.rb
Normal file
|
@ -0,0 +1,7 @@
|
|||
class UpdateStatsJob < CronJob
|
||||
self.schedule_expression = "every 1 hour"
|
||||
|
||||
def perform(*args)
|
||||
Stat.update_stats
|
||||
end
|
||||
end
|
91
app/models/stat.rb
Normal file
91
app/models/stat.rb
Normal file
|
@ -0,0 +1,91 @@
|
|||
# == Schema Information
|
||||
#
|
||||
# Table name: stats
|
||||
#
|
||||
# id :bigint not null, primary key
|
||||
# dossiers_brouillon :bigint default(0)
|
||||
# dossiers_cumulative :jsonb not null
|
||||
# dossiers_depose_avant_30_jours :bigint default(0)
|
||||
# dossiers_deposes_entre_60_et_30_jours :bigint default(0)
|
||||
# dossiers_en_construction :bigint default(0)
|
||||
# dossiers_en_instruction :bigint default(0)
|
||||
# dossiers_in_the_last_4_months :jsonb not null
|
||||
# dossiers_not_brouillon :bigint default(0)
|
||||
# dossiers_termines :bigint default(0)
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
#
|
||||
class Stat < ApplicationRecord
|
||||
class << self
|
||||
def update_stats
|
||||
states = dossiers_states
|
||||
stat = Stat.first || Stat.new
|
||||
|
||||
stat.update(
|
||||
dossiers_en_construction: states['en_construction'],
|
||||
dossiers_en_instruction: states['en_instruction'],
|
||||
dossiers_brouillon: states['brouillon'],
|
||||
dossiers_depose_avant_30_jours: states['dossiers_depose_avant_30_jours'],
|
||||
dossiers_deposes_entre_60_et_30_jours: states['dossiers_deposes_entre_60_et_30_jours'],
|
||||
dossiers_not_brouillon: states['not_brouillon'],
|
||||
dossiers_termines: states['termines'],
|
||||
dossiers_cumulative: cumulative_hash(Dossier.state_not_brouillon, :en_construction_at),
|
||||
dossiers_in_the_last_4_months: last_four_months_hash(Dossier.state_not_brouillon, :en_construction_at)
|
||||
)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def dossiers_states
|
||||
query = <<-EOF
|
||||
SELECT
|
||||
COUNT(*) FILTER ( WHERE state != 'brouillon' ) AS "not_brouillon",
|
||||
COUNT(*) FILTER ( WHERE state != 'brouillon' and en_construction_at BETWEEN :one_month_ago AND :now ) AS "dossiers_depose_avant_30_jours",
|
||||
COUNT(*) FILTER ( WHERE state != 'brouillon' and en_construction_at BETWEEN :two_months_ago AND :one_month_ago ) AS "dossiers_deposes_entre_60_et_30_jours",
|
||||
COUNT(*) FILTER ( WHERE state = 'brouillon' ) AS "brouillon",
|
||||
COUNT(*) FILTER ( WHERE state = 'en_construction' ) AS "en_construction",
|
||||
COUNT(*) FILTER ( WHERE state = 'en_instruction' ) AS "en_instruction",
|
||||
COUNT(*) FILTER ( WHERE state in ('accepte', 'refuse', 'sans_suite') ) AS "termines"
|
||||
FROM dossiers
|
||||
WHERE hidden_at IS NULL
|
||||
EOF
|
||||
|
||||
sanitized_query = ActiveRecord::Base.sanitize_sql([
|
||||
query,
|
||||
now: Time.zone.now,
|
||||
one_month_ago: 1.month.ago,
|
||||
two_months_ago: 2.months.ago
|
||||
])
|
||||
|
||||
Dossier.connection.select_all(sanitized_query).first
|
||||
end
|
||||
|
||||
def last_four_months_hash(association, date_attribute)
|
||||
min_date = 3.months.ago.beginning_of_month.to_date
|
||||
|
||||
association
|
||||
.where(date_attribute => min_date..max_date)
|
||||
.group("DATE_TRUNC('month', #{date_attribute})")
|
||||
.count
|
||||
.to_a
|
||||
.sort_by { |a| a[0] }
|
||||
.map { |e| [I18n.l(e.first, format: "%B %Y"), e.last] }
|
||||
end
|
||||
|
||||
def cumulative_hash(association, date_attribute)
|
||||
sum = 0
|
||||
association
|
||||
.where("#{date_attribute} < ?", max_date)
|
||||
.group("DATE_TRUNC('month', #{date_attribute})")
|
||||
.count
|
||||
.to_a
|
||||
.sort_by { |a| a[0] }
|
||||
.map { |x, y| { x => (sum += y) } }
|
||||
.reduce({}, :merge)
|
||||
end
|
||||
|
||||
def max_date
|
||||
Time.zone.now.beginning_of_month - 1.second
|
||||
end
|
||||
end
|
||||
end
|
18
db/migrate/20201002124154_create_stats.rb
Normal file
18
db/migrate/20201002124154_create_stats.rb
Normal file
|
@ -0,0 +1,18 @@
|
|||
class CreateStats < ActiveRecord::Migration[6.0]
|
||||
def change
|
||||
create_table :stats do |t|
|
||||
t.bigint :dossiers_not_brouillon, default: 0
|
||||
t.bigint :dossiers_brouillon, default: 0
|
||||
t.bigint :dossiers_en_construction, default: 0
|
||||
t.bigint :dossiers_en_instruction, default: 0
|
||||
t.bigint :dossiers_termines, default: 0
|
||||
t.bigint :dossiers_depose_avant_30_jours, default: 0
|
||||
t.bigint :dossiers_deposes_entre_60_et_30_jours, default: 0
|
||||
|
||||
t.jsonb :dossiers_cumulative, null: false, default: '{}'
|
||||
t.jsonb :dossiers_in_the_last_4_months, null: false, default: '{}'
|
||||
|
||||
t.timestamps
|
||||
end
|
||||
end
|
||||
end
|
16
db/schema.rb
16
db/schema.rb
|
@ -10,7 +10,7 @@
|
|||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(version: 2020_09_30_143755) do
|
||||
ActiveRecord::Schema.define(version: 2020_10_02_124154) do
|
||||
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "plpgsql"
|
||||
|
@ -586,6 +586,20 @@ ActiveRecord::Schema.define(version: 2020_09_30_143755) do
|
|||
t.index ["administrateur_id"], name: "index_services_on_administrateur_id"
|
||||
end
|
||||
|
||||
create_table "stats", force: :cascade do |t|
|
||||
t.bigint "dossiers_not_brouillon", default: 0
|
||||
t.bigint "dossiers_brouillon", default: 0
|
||||
t.bigint "dossiers_en_construction", default: 0
|
||||
t.bigint "dossiers_en_instruction", default: 0
|
||||
t.bigint "dossiers_termines", default: 0
|
||||
t.bigint "dossiers_depose_avant_30_jours", default: 0
|
||||
t.bigint "dossiers_deposes_entre_60_et_30_jours", default: 0
|
||||
t.jsonb "dossiers_cumulative", default: "{}", null: false
|
||||
t.jsonb "dossiers_in_the_last_4_months", default: "{}", null: false
|
||||
t.datetime "created_at", precision: 6, null: false
|
||||
t.datetime "updated_at", precision: 6, null: false
|
||||
end
|
||||
|
||||
create_table "task_records", id: false, force: :cascade do |t|
|
||||
t.string "version", null: false
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue