Merge pull request #5667 from betagouv/dev

2020-10-06-01
This commit is contained in:
Kara Diaby 2020-10-06 10:47:31 +02:00 committed by GitHub
commit e103c27cd0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 219 additions and 21 deletions

View file

@ -82,6 +82,10 @@ class ApplicationController < ActionController::Base
Flipper.enabled?(feature_name, current_user)
end
def feature_enabled_for?(feature_name, item)
Flipper.enabled?(feature_name, item)
end
def authenticate_logged_user!
if instructeur_signed_in?
authenticate_instructeur!

View file

@ -71,13 +71,19 @@ module Instructeurs
end
def create_avis
@new_avis = create_avis_from_params(avis.dossier, avis.confidentiel)
@procedure = Procedure.find(params[:procedure_id])
if !feature_enabled_for?(:expert_not_allowed_to_invite, @procedure)
@new_avis = create_avis_from_params(avis.dossier, avis.confidentiel)
if @new_avis.nil?
redirect_to instruction_instructeur_avis_path(avis.procedure, avis)
if @new_avis.nil?
redirect_to instruction_instructeur_avis_path(avis.procedure, avis)
else
set_avis_and_dossier
render :instruction
end
else
set_avis_and_dossier
render :instruction
flash.alert = "Cette démarche ne vous permet pas de demander un avis externe"
redirect_to instruction_instructeur_avis_path(avis.procedure, avis)
end
end

View file

@ -1,6 +1,6 @@
module NewAdministrateur
class ProceduresController < AdministrateurController
before_action :retrieve_procedure, only: [:champs, :annotations, :edit, :monavis, :update_monavis, :jeton, :update_jeton, :publication, :publish, :transfert]
before_action :retrieve_procedure, only: [:champs, :annotations, :edit, :monavis, :update_monavis, :jeton, :update_jeton, :publication, :publish, :transfert, :allow_expert_review]
before_action :procedure_locked?, only: [:champs, :annotations]
ITEMS_PER_PAGE = 25
@ -158,6 +158,12 @@ module NewAdministrateur
def transfert
end
def allow_expert_review
@procedure.update!(allow_expert_review: !@procedure.allow_expert_review)
flash.notice = @procedure.allow_expert_review? ? "Avis externes activés" : "Avis externes désactivés"
redirect_to admin_procedure_path(@procedure)
end
def transfer
admin = Administrateur.by_email(params[:email_admin].downcase)
if admin.nil?

View file

@ -78,14 +78,14 @@ class GeoArea < ApplicationRecord
end
def area
if polygon? && RGeo::Geos.supported?
rgeo_geometry&.area&.round(1)
if polygon?
GeojsonService.area(geometry.deep_symbolize_keys).round(1)
end
end
def length
if line? && RGeo::Geos.supported?
rgeo_geometry.length.round(1)
if line?
GeojsonService.length(geometry.deep_symbolize_keys).round(1)
end
end

View file

@ -4,6 +4,7 @@
#
# id :integer not null, primary key
# aasm_state :string default("brouillon")
# allow_expert_review :boolean default(TRUE), not null
# api_entreprise_token :string
# ask_birthday :boolean default(FALSE), not null
# auto_archive_on :date

View file

@ -14,4 +14,117 @@ class GeojsonService
polygon.to_json
end
# The following code is ported from turfjs
# https://github.com/Turfjs/turf/blob/master/packages/turf-area/index.ts
EQUATORIAL_RADIUS = 6378137
def self.area(geojson)
calculate_area(geojson)
end
def self.length(geojson)
segment_reduce(geojson, 0) do |previous_value, segment|
coordinates = segment[:geometry][:coordinates]
previous_value + distance(coordinates[0], coordinates[1])
end
end
def self.distance(from, to)
coordinates1 = from
coordinates2 = to
d_lat = degrees_to_radians(coordinates2[1] - coordinates1[1])
d_lon = degrees_to_radians(coordinates2[0] - coordinates1[0])
lat1 = degrees_to_radians(coordinates1[1])
lat2 = degrees_to_radians(coordinates2[1])
a = (Math.sin(d_lat / 2)**2) + (Math.sin(d_lon / 2)**2) * Math.cos(lat1) * Math.cos(lat2)
radians = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a))
radians * EQUATORIAL_RADIUS
end
def self.calculate_area(geom)
total = 0
case geom[:type]
when 'Polygon'
polygon_area(geom[:coordinates]);
when 'MultiPolygon'
geom[:coordinates].each do |coordinates|
total += polygon_area(coordinates)
end
total
else
total
end
end
def self.polygon_area(coordinates)
total = 0
if coordinates.present?
coordinates = coordinates.dup
total += ring_area(coordinates.shift).abs
coordinates.each do |coordinates|
total -= ring_area(coordinates).abs
end
end
total
end
def self.ring_area(coordinates)
total = 0
coords_length = coordinates.size
if coords_length > 2
coords_length.times do |i|
if i == coords_length - 2 # i = N-2
lower_index = coords_length - 2
middle_index = coords_length - 1
upper_index = 0
elsif i == coords_length - 1 # i = N-1
lower_index = coords_length - 1
middle_index = 0
upper_index = 1
else # i = 0 to N-3
lower_index = i
middle_index = i + 1
upper_index = i + 2
end
p1 = coordinates[lower_index]
p2 = coordinates[middle_index]
p3 = coordinates[upper_index]
total += (rad(p3[0]) - rad(p1[0])) * Math.sin(rad(p2[1]))
end
total = total * EQUATORIAL_RADIUS * EQUATORIAL_RADIUS / 2
end
total
end
def self.segment_reduce(geojson, initial_value)
previous_value = initial_value
started = false
coordinates = geojson[:coordinates].dup
from = coordinates.shift
coordinates.each do |to|
current_segment = { type: 'Feature', geometry: { type: 'LineString', coordinates: [from, to] } }
from = to
if started == false && initial_value.blank?
previous_value = current_segment
else
previous_value = yield previous_value, current_segment
end
started = true
end
previous_value
end
def self.rad(num)
num * Math::PI / 180
end
def self.degrees_to_radians(degrees)
rad(degrees % 360)
end
end

View file

@ -31,7 +31,7 @@
.send-wrapper
= f.submit 'Envoyer votre avis', class: 'button send'
- if !@dossier.termine?
- if !@dossier.termine? && !feature_enabled_for?(:expert_not_allowed_to_invite, @avis.procedure)
= render partial: "instructeurs/shared/avis/form", locals: { url: avis_instructeur_avis_path(@avis.procedure, @avis), linked_dossiers: @dossier.linked_dossiers_for(current_instructeur), must_be_confidentiel: @avis.confidentiel?, avis: @new_avis }
- if @dossier.avis_for(current_instructeur).present?

View file

@ -4,7 +4,10 @@
.container
- if !@dossier.termine?
= render partial: "instructeurs/shared/avis/form", locals: { url: avis_instructeur_dossier_path(@dossier.procedure, @dossier), linked_dossiers: @dossier.linked_dossiers_for(current_instructeur), must_be_confidentiel: false, avis: @avis }
- if @dossier.procedure.allow_expert_review
= render partial: "instructeurs/shared/avis/form", locals: { url: avis_instructeur_dossier_path(@dossier.procedure, @dossier), linked_dossiers: @dossier.linked_dossiers_for(current_instructeur), must_be_confidentiel: false, avis: @avis }
- else
%p Cette démarche n'autorise pas la demande d'avis à un expert. Veuillez contacter votre administrateur
- if @dossier.avis.present?
= render partial: 'instructeurs/shared/avis/list', locals: { avis: @dossier.avis, avis_seen_at: @avis_seen_at }

View file

@ -11,8 +11,8 @@
placeholder: 'chemin-de-la-démarche',
required: true,
class: 'form',
pattern: '^[a-z0-9_-]{3,50}$',
title: "De 3 à 50 caractères; minuscules, chiffres et tiret seulement",
pattern: '^[a-z0-9_-]{3,200}$',
title: "De 3 à 200 caractères; minuscules, chiffres et tiret seulement",
data: { debounce: true, url: admin_procedure_publish_validate_path(procedure)},
autocomplete: 'off',
style: 'width: 300px; display: inline;')

View file

@ -139,6 +139,21 @@
.card-admin-action
= link_to 'Modifier', edit_admin_procedure_attestation_template_path(@procedure), class: 'button'
.card-admin
- if @procedure.allow_expert_review?
%div
%span.icon.accept
%p.card-admin-status-accept Activé
- else
%div
%span.icon.clock
%p.card-admin-status-todo Désactivée
%div
%p.card-admin-title Avis externes
%p.card-admin-subtitle Demander des avis aux experts invités
.card-admin-action
= link_to "#{@procedure.allow_expert_review? ? 'Désactiver' : 'Activer'}", allow_expert_review_admin_procedure_path(@procedure), method: :put, class: 'button'
.card-admin
%div
%span.icon.clock

View file

@ -365,6 +365,7 @@ Rails.application.routes.draw do
patch 'update_monavis'
get 'jeton'
patch 'update_jeton'
put :allow_expert_review
end
get 'publication' => 'procedures#publication', as: :publication

View file

@ -0,0 +1,5 @@
class AddAllowInstructorInviteExpert < ActiveRecord::Migration[6.0]
def change
add_column :procedures, :allow_expert_review, :boolean, default: true, null: false
end
end

View file

@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2020_09_02_103047) do
ActiveRecord::Schema.define(version: 2020_09_30_143755) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@ -543,6 +543,7 @@ ActiveRecord::Schema.define(version: 2020_09_02_103047) do
t.string "api_entreprise_token"
t.bigint "draft_revision_id"
t.bigint "published_revision_id"
t.boolean "allow_expert_review", default: true, null: false
t.index ["declarative_with_state"], name: "index_procedures_on_declarative_with_state"
t.index ["draft_revision_id"], name: "index_procedures_on_draft_revision_id"
t.index ["hidden_at"], name: "index_procedures_on_hidden_at"

View file

@ -152,6 +152,26 @@ describe Instructeurs::AvisController, type: :controller do
end
end
describe '#expert_cannot_invite_another_expert' do
let!(:previous_avis) { Avis.create(dossier: dossier, claimant: claimant, instructeur: instructeur, confidentiel: previous_avis_confidentiel) }
let(:previous_avis_confidentiel) { false }
let(:asked_confidentiel) { false }
let(:intro) { 'introduction' }
let(:emails) { ["toto@totomail.com"] }
let(:invite_linked_dossiers) { nil }
before do
Flipper.enable_actor(:expert_not_allowed_to_invite, procedure)
post :create_avis, params: { id: previous_avis.id, procedure_id: procedure.id, avis: { emails: emails, introduction: intro, confidentiel: asked_confidentiel, invite_linked_dossiers: invite_linked_dossiers, introduction_file: @introduction_file } }
end
context 'when the expert cannot invite another expert' do
let(:asked_confidentiel) { false }
it { expect(flash.alert).to eq("Cette démarche ne vous permet pas de demander un avis externe") }
it { expect(response).to redirect_to(instruction_instructeur_avis_path(procedure, previous_avis)) }
end
end
describe '#create_avis' do
let!(:previous_avis) { Avis.create(dossier: dossier, claimant: claimant, instructeur: instructeur, confidentiel: previous_avis_confidentiel) }
let(:emails) { ['a@b.com'] }

View file

@ -488,4 +488,21 @@ describe NewAdministrateur::ProceduresController, type: :controller do
end
end
end
describe 'PUT #allow_expert_review' do
let!(:procedure) { create :procedure, :with_service, administrateur: admin }
context 'when admin refuse to invite experts on this procedure' do
before do
procedure.update!(allow_expert_review: false)
procedure.reload
end
it { expect(procedure.allow_expert_review).to be_falsy }
end
context 'when admin accept to invite experts on this procedure (true by default)' do
it { expect(procedure.allow_expert_review).to be_truthy }
end
end
end

View file

@ -1305,7 +1305,7 @@ describe Dossier do
'type' => 'Polygon'
},
properties: {
area: 219.0,
area: 103.6,
champ_id: champ_carte.stable_id,
dossier_id: dossier.id,
id: geo_area.id,

View file

@ -2,21 +2,19 @@ RSpec.describe GeoArea, type: :model do
describe '#area' do
let(:geo_area) { build(:geo_area, :polygon) }
it { expect(geo_area.area).to eq(219.0) }
it { expect(geo_area.area).to eq(103.6) }
end
describe '#area (hourglass polygon)' do
let(:geo_area) { build(:geo_area, :hourglass_polygon) }
# This test fails in my local environement end the problem exists in production.
# Must be some mismatch between CI/production. I still want this fix in production.
it.pending { expect(geo_area.area).to be_nil }
it { expect(geo_area.area).to eq(32.4) }
end
describe '#length' do
let(:geo_area) { build(:geo_area, :line_string) }
it { expect(geo_area.length).to eq(30.8) }
it { expect(geo_area.length).to eq(21.2) }
end
describe '#location' do

View file

@ -20,4 +20,12 @@ describe 'instructeurs/avis/instruction.html.haml', type: :view do
let(:confidentiel) { false }
it { is_expected.to have_text("Cet avis est partagé avec les autres experts") }
end
context 'when an expert is not allowed to invite another expert' do
let(:confidentiel) { false }
before do
Flipper.enable_actor(:expert_not_allowed_to_invite, avis.procedure)
end
it { is_expected.to have_no_text("Inviter des personnes à donner leur avis") }
end
end