commit
e103c27cd0
18 changed files with 219 additions and 21 deletions
|
@ -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!
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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?
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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?
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -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;')
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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"
|
||||
|
|
|
@ -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'] }
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue