Merge pull request #7327 from betagouv/manager-dubious-procedures
Liste les démarches douteuses depuis le manager
This commit is contained in:
commit
299e29a6e6
14 changed files with 202 additions and 132 deletions
15
app/controllers/manager/dubious_procedures_controller.rb
Normal file
15
app/controllers/manager/dubious_procedures_controller.rb
Normal file
|
@ -0,0 +1,15 @@
|
|||
module Manager
|
||||
class DubiousProceduresController < Manager::ApplicationController
|
||||
def index
|
||||
raw_resources = DubiousProcedure.all
|
||||
resources = Kaminari.paginate_array(raw_resources).page(params[:_page]).per(records_per_page)
|
||||
page = Administrate::Page::Collection.new(dashboard)
|
||||
|
||||
render locals: {
|
||||
resources: resources,
|
||||
page: page,
|
||||
show_search_bar: false
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
12
app/dashboards/dubious_procedure_dashboard.rb
Normal file
12
app/dashboards/dubious_procedure_dashboard.rb
Normal file
|
@ -0,0 +1,12 @@
|
|||
require "administrate/base_dashboard"
|
||||
|
||||
class DubiousProcedureDashboard < Administrate::BaseDashboard
|
||||
ATTRIBUTE_TYPES = {
|
||||
id: Field::Number,
|
||||
libelle: Field::String,
|
||||
dubious_champs: Field::String,
|
||||
aasm_state: Field::String
|
||||
}.freeze
|
||||
COLLECTION_ATTRIBUTES = [:id, :libelle, :dubious_champs, :aasm_state].freeze
|
||||
COLLECTION_FILTERS = {}.freeze
|
||||
end
|
|
@ -1,33 +0,0 @@
|
|||
class Cron::FindDubiousProceduresJob < Cron::CronJob
|
||||
self.schedule_expression = "every day at midnight"
|
||||
|
||||
FORBIDDEN_KEYWORDS = [
|
||||
'NIR', 'NIRPP', 'race', 'religion',
|
||||
'carte bancaire', 'carte bleue', 'sécurité sociale',
|
||||
'agdref', 'syndicat', 'syndical',
|
||||
'parti politique', 'opinion politique', 'bord politique', 'courant politique',
|
||||
'médical', 'handicap', 'maladie', 'allergie', 'hospitalisé', 'RQTH', 'vaccin'
|
||||
]
|
||||
|
||||
# \\y is a word boundary
|
||||
def self.forbidden_regexp
|
||||
FORBIDDEN_KEYWORDS.map { |keyword| "\\y#{keyword}\\y" }
|
||||
.join('|')
|
||||
end
|
||||
|
||||
def perform(*args)
|
||||
# ~* -> case insensitive regexp match
|
||||
# https://www.postgresql.org/docs/current/static/functions-matching.html#FUNCTIONS-POSIX-REGEXP
|
||||
forbidden_tdcs = TypeDeChamp
|
||||
.joins(:procedure)
|
||||
.where("unaccent(types_de_champ.libelle) ~* unaccent(?)", Cron::FindDubiousProceduresJob.forbidden_regexp)
|
||||
.where(type_champ: [TypeDeChamp.type_champs.fetch(:text), TypeDeChamp.type_champs.fetch(:textarea)])
|
||||
.where(procedures: { closed_at: nil, whitelisted_at: nil })
|
||||
|
||||
dubious_procedures_and_tdcs = forbidden_tdcs
|
||||
.group_by { |type_de_champ| type_de_champ.procedure.id }
|
||||
.map { |_procedure_id, tdcs| [tdcs[0].procedure, tdcs] }
|
||||
|
||||
AdministrationMailer.dubious_procedures(dubious_procedures_and_tdcs).deliver_later
|
||||
end
|
||||
end
|
|
@ -20,12 +20,4 @@ class AdministrationMailer < ApplicationMailer
|
|||
subject: subject,
|
||||
reply_to: CONTACT_EMAIL)
|
||||
end
|
||||
|
||||
def dubious_procedures(procedures_and_type_de_champs)
|
||||
@procedures_and_type_de_champs = procedures_and_type_de_champs
|
||||
subject = "[RGS] De nouvelles démarches comportent des champs interdits"
|
||||
|
||||
mail(to: EQUIPE_EMAIL,
|
||||
subject: subject)
|
||||
end
|
||||
end
|
||||
|
|
45
app/models/dubious_procedure.rb
Normal file
45
app/models/dubious_procedure.rb
Normal file
|
@ -0,0 +1,45 @@
|
|||
class DubiousProcedure
|
||||
extend ActiveModel::Naming
|
||||
extend ActiveModel::Translation
|
||||
|
||||
attr_accessor :id, :libelle, :dubious_champs, :aasm_state
|
||||
|
||||
FORBIDDEN_KEYWORDS = [
|
||||
'NIR', 'NIRPP', 'race', 'religion',
|
||||
'carte bancaire', 'carte bleue', 'sécurité sociale',
|
||||
'agdref', 'syndicat', 'syndical',
|
||||
'parti politique', 'opinion politique', 'bord politique', 'courant politique',
|
||||
'médical', 'handicap', 'maladie', 'allergie', 'hospitalisé', 'RQTH', 'vaccin'
|
||||
]
|
||||
|
||||
def persisted?
|
||||
false
|
||||
end
|
||||
|
||||
def self.all
|
||||
procedures_with_forbidden_tdcs_sql = TypeDeChamp
|
||||
.joins(:procedure)
|
||||
.select("string_agg(types_de_champ.libelle, ' - ') as dubious_champs, procedures.id as procedure_id, procedures.libelle as procedure_libelle, procedures.aasm_state as procedure_aasm_state")
|
||||
.where("unaccent(types_de_champ.libelle) ~* unaccent(?)", forbidden_regexp)
|
||||
.where(type_champ: [TypeDeChamp.type_champs.fetch(:text), TypeDeChamp.type_champs.fetch(:textarea)])
|
||||
.where(procedures: { closed_at: nil, whitelisted_at: nil })
|
||||
.group("procedures.id")
|
||||
.order("procedures.id asc")
|
||||
.to_sql
|
||||
|
||||
ActiveRecord::Base.connection.execute(procedures_with_forbidden_tdcs_sql).map do |procedure|
|
||||
p = DubiousProcedure.new
|
||||
p.id = procedure["procedure_id"]
|
||||
p.dubious_champs = procedure["dubious_champs"]
|
||||
p.libelle = procedure["procedure_libelle"]
|
||||
p.aasm_state = procedure["procedure_aasm_state"]
|
||||
p
|
||||
end
|
||||
end
|
||||
|
||||
# \\y is a word boundary
|
||||
def self.forbidden_regexp
|
||||
FORBIDDEN_KEYWORDS.map { |keyword| "\\y#{keyword}\\y" }
|
||||
.join('|')
|
||||
end
|
||||
end
|
|
@ -94,7 +94,7 @@ class TypeDeChamp < ApplicationRecord
|
|||
scope :fillable, -> { where.not(type_champ: [type_champs.fetch(:header_section), type_champs.fetch(:explication)]) }
|
||||
|
||||
scope :dubious, -> {
|
||||
where("unaccent(types_de_champ.libelle) ~* unaccent(?)", Cron::FindDubiousProceduresJob.forbidden_regexp)
|
||||
where("unaccent(types_de_champ.libelle) ~* unaccent(?)", DubiousProcedure.forbidden_regexp)
|
||||
.where(type_champ: [TypeDeChamp.type_champs.fetch(:text), TypeDeChamp.type_champs.fetch(:textarea)])
|
||||
}
|
||||
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
- content_for(:title, 'Liste de démarches douteuses')
|
||||
|
||||
- if @procedures_and_type_de_champs.any?
|
||||
%ul
|
||||
- @procedures_and_type_de_champs.each do |procedure, type_de_champs|
|
||||
%li{ style: 'margin-bottom: 8px;' }
|
||||
= link_to "Nº #{procedure.id},", manager_procedure_url(procedure)
|
||||
#{procedure.libelle} :
|
||||
%b= type_de_champs.map(&:libelle).join(', ')
|
||||
%br
|
||||
État : #{procedure.aasm_state}
|
||||
%br
|
||||
Nombre de dossier : #{procedure.dossiers.count}
|
||||
%br
|
||||
Admin :
|
||||
%ul
|
||||
- procedure.administrateurs.each do |administrateur|
|
||||
%li
|
||||
= link_to "#{administrateur.email}", "mailto:#{administrateur.email}"
|
||||
- else
|
||||
Il n’y a aucune démarche douteuse aujourd'hui
|
56
app/views/manager/dubious_procedures/_collection.html.erb
Normal file
56
app/views/manager/dubious_procedures/_collection.html.erb
Normal file
|
@ -0,0 +1,56 @@
|
|||
<%#
|
||||
# Collection
|
||||
|
||||
This partial is used on the `index` and `show` pages
|
||||
to display a collection of resources in an HTML table.
|
||||
|
||||
## Local variables:
|
||||
|
||||
- `collection_presenter`:
|
||||
An instance of [Administrate::Page::Collection][1].
|
||||
The table presenter uses `ResourceDashboard::COLLECTION_ATTRIBUTES` to determine
|
||||
the columns displayed in the table
|
||||
- `resources`:
|
||||
An ActiveModel::Relation collection of resources to be displayed in the table.
|
||||
By default, the number of resources is limited by pagination
|
||||
or by a hard limit to prevent excessive page load times
|
||||
|
||||
[1]: http://www.rubydoc.info/gems/administrate/Administrate/Page/Collection
|
||||
%>
|
||||
|
||||
<table aria-labelledby="<%= table_title %>">
|
||||
<thead>
|
||||
<tr>
|
||||
<% collection_presenter.attribute_types.each do |attr_name, attr_type| %>
|
||||
<th class="cell-label
|
||||
cell-label--<%= attr_type.html_class %>
|
||||
cell-label--<%= collection_presenter.ordered_html_class(attr_name) %>"
|
||||
scope="col"
|
||||
role="columnheader"
|
||||
aria-sort="<%= sort_order(collection_presenter.ordered_html_class(attr_name)) %>">
|
||||
<%= t(
|
||||
"helpers.label.#{collection_presenter.resource_name}.#{attr_name}",
|
||||
default: resource_class.human_attribute_name(attr_name),
|
||||
).titleize %>
|
||||
</th>
|
||||
<% end %>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<% resources.each do |resource| %>
|
||||
<tr class="js-table-row" >
|
||||
<% collection_presenter.attributes_for(resource).each do |attribute| %>
|
||||
<td class="cell-data cell-data--<%= attribute.html_class %>">
|
||||
<a href="<%= manager_procedure_url(resource.id) -%>"
|
||||
tabindex="-1"
|
||||
class="action-show"
|
||||
>
|
||||
<%= render_field attribute %>
|
||||
</a>
|
||||
</td>
|
||||
<% end %>
|
||||
</tr>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
9
config/locales/models/dubious_procedure/fr.yml
Normal file
9
config/locales/models/dubious_procedure/fr.yml
Normal file
|
@ -0,0 +1,9 @@
|
|||
fr:
|
||||
activemodel:
|
||||
models:
|
||||
dubious_procedure:
|
||||
other: Démarches douteuses
|
||||
attributes:
|
||||
dubious_procedure:
|
||||
dubious_champs: Champs douteux
|
||||
aasm_state: Etat
|
|
@ -58,6 +58,8 @@ Rails.application.routes.draw do
|
|||
|
||||
resources :zones, only: [:index, :show]
|
||||
|
||||
resources :dubious_procedures, only: [:index]
|
||||
|
||||
post 'demandes/create_administrateur'
|
||||
post 'demandes/refuse_administrateur'
|
||||
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
namespace :after_party do
|
||||
desc 'Deployment task: remove_old_find_dubious_procedures_job_from_delayed_job_table'
|
||||
task remove_old_dubious_proc_job_from_delayed_job_table: :environment do
|
||||
puts "Running deploy task 'remove_old_dubious_proc_job_from_delayed_job_table'"
|
||||
|
||||
cron = Delayed::Job.where.not(cron: nil)
|
||||
.where("handler LIKE ?", "%FindDubiousProceduresJob%")
|
||||
.first
|
||||
cron.destroy if cron
|
||||
|
||||
AfterParty::TaskRecord
|
||||
.create version: AfterParty::TaskRecorder.new(__FILE__).timestamp
|
||||
end
|
||||
end
|
|
@ -1,61 +0,0 @@
|
|||
RSpec.describe Cron::FindDubiousProceduresJob, type: :job do
|
||||
describe 'perform' do
|
||||
let(:mailer_double) { double('mailer', deliver_later: true) }
|
||||
let(:procedure) { create(:procedure, types_de_champ: tdcs) }
|
||||
let(:allowed_tdc) { build(:type_de_champ, libelle: 'fournir') }
|
||||
|
||||
before do
|
||||
procedure
|
||||
|
||||
allow(AdministrationMailer).to receive(:dubious_procedures) do |arg|
|
||||
@dubious_procedures_args = arg
|
||||
end.and_return(mailer_double)
|
||||
|
||||
Cron::FindDubiousProceduresJob.new.perform
|
||||
end
|
||||
|
||||
context 'with suspicious champs' do
|
||||
let(:forbidden_tdcs) do
|
||||
[
|
||||
build(:type_de_champ, libelle: 'num de securite sociale, stp'),
|
||||
build(:type_de_champ, libelle: "t'aurais une carte bancaire ?")
|
||||
]
|
||||
end
|
||||
|
||||
let(:tdcs) { forbidden_tdcs + [allowed_tdc] }
|
||||
|
||||
it 'mails tech about the dubious procedure' do
|
||||
receive_procedure, receive_forbidden_tdcs = @dubious_procedures_args[0]
|
||||
|
||||
expect(receive_procedure).to eq(procedure)
|
||||
expect(receive_forbidden_tdcs).to match_array(forbidden_tdcs)
|
||||
|
||||
expect(AdministrationMailer).to have_received(:dubious_procedures).with(@dubious_procedures_args)
|
||||
end
|
||||
|
||||
context 'and a whitelisted procedure' do
|
||||
let(:procedure) { create(:procedure, :whitelisted) }
|
||||
|
||||
it { expect(AdministrationMailer).to have_received(:dubious_procedures).with([]) }
|
||||
end
|
||||
|
||||
context 'and a closed procedure' do
|
||||
let(:procedure) { create(:procedure, :closed) }
|
||||
|
||||
it { expect(AdministrationMailer).to have_received(:dubious_procedures).with([]) }
|
||||
end
|
||||
|
||||
context 'and a discarded procedure' do
|
||||
let(:procedure) { create(:procedure, :discarded) }
|
||||
|
||||
it { expect(AdministrationMailer).to have_received(:dubious_procedures).with([]) }
|
||||
end
|
||||
end
|
||||
|
||||
context 'with no suspicious champs' do
|
||||
let(:tdcs) { [allowed_tdc] }
|
||||
|
||||
it { expect(AdministrationMailer).to have_received(:dubious_procedures).with([]) }
|
||||
end
|
||||
end
|
||||
end
|
|
@ -28,12 +28,4 @@ RSpec.describe AdministrationMailer, type: :mailer do
|
|||
|
||||
it { expect(subject.subject).not_to be_empty }
|
||||
end
|
||||
|
||||
describe '#dubious_procedures' do
|
||||
let(:procedures_and_type_de_champs) { [] }
|
||||
|
||||
subject { described_class.dubious_procedures(procedures_and_type_de_champs) }
|
||||
|
||||
it { expect(subject.subject).not_to be_empty }
|
||||
end
|
||||
end
|
||||
|
|
48
spec/models/dubious_procedure_spec.rb
Normal file
48
spec/models/dubious_procedure_spec.rb
Normal file
|
@ -0,0 +1,48 @@
|
|||
describe DubiousProcedure, type: :model do
|
||||
describe '#all' do
|
||||
let!(:procedure) { create(:procedure, types_de_champ: tdcs) }
|
||||
let(:allowed_tdc) { build(:type_de_champ, libelle: 'fournir') }
|
||||
subject { DubiousProcedure.all }
|
||||
|
||||
context 'with suspicious champs' do
|
||||
let(:forbidden_tdcs) do
|
||||
[
|
||||
build(:type_de_champ, libelle: 'num de securite sociale, stp'),
|
||||
build(:type_de_champ, libelle: "t'aurais une carte bancaire ?")
|
||||
]
|
||||
end
|
||||
|
||||
let(:tdcs) { forbidden_tdcs + [allowed_tdc] }
|
||||
|
||||
it 'returns dubious procedures' do
|
||||
expect(subject.first.id).to eq(procedure.id)
|
||||
expect(subject.first.libelle).to eq(procedure.libelle)
|
||||
expect(subject.first.dubious_champs).to eq("num de securite sociale, stp - t'aurais une carte bancaire ?")
|
||||
end
|
||||
|
||||
context 'and a whitelisted procedure' do
|
||||
let(:procedure) { create(:procedure, :whitelisted) }
|
||||
|
||||
it { expect(subject).to eq([]) }
|
||||
end
|
||||
|
||||
context 'and a closed procedure' do
|
||||
let(:procedure) { create(:procedure, :closed) }
|
||||
|
||||
it { expect(subject).to eq([]) }
|
||||
end
|
||||
|
||||
context 'and a discarded procedure' do
|
||||
let(:procedure) { create(:procedure, :discarded) }
|
||||
|
||||
it { expect(subject).to eq([]) }
|
||||
end
|
||||
end
|
||||
|
||||
context 'with no suspicious champs' do
|
||||
let(:tdcs) { [allowed_tdc] }
|
||||
|
||||
it { expect(subject).to eq([]) }
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Add table
Reference in a new issue