Merge pull request #8045 from tchak/feat-export-geojson
feat(export): add GeoJSON export
This commit is contained in:
commit
c200e03771
14 changed files with 94 additions and 36 deletions
|
@ -1,7 +1,8 @@
|
||||||
@import "placeholders";
|
@import "placeholders";
|
||||||
|
|
||||||
turbo-events {
|
html,
|
||||||
display: none;
|
body {
|
||||||
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-wrapper {
|
.page-wrapper {
|
||||||
|
|
|
@ -8,7 +8,27 @@ class Dossiers::ExportComponent < ApplicationComponent
|
||||||
end
|
end
|
||||||
|
|
||||||
def exports
|
def exports
|
||||||
helpers.exports_list(@exports, @statut)
|
if @statut
|
||||||
|
Export::FORMATS.filter(&method(:allowed_format?)).map do |item|
|
||||||
|
export = @exports
|
||||||
|
.fetch(item.fetch(:format))
|
||||||
|
.fetch(:statut)
|
||||||
|
.fetch(@statut, nil)
|
||||||
|
item.merge(export: export)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
Export::FORMATS_WITH_TIME_SPAN.map do |item|
|
||||||
|
export = @exports
|
||||||
|
.fetch(item.fetch(:format))
|
||||||
|
.fetch(:time_span_type)
|
||||||
|
.fetch(item.fetch(:time_span_type), nil)
|
||||||
|
item.merge(export: export)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def allowed_format?(item)
|
||||||
|
item.fetch(:format) != :json || @procedure.active_revision.carte?
|
||||||
end
|
end
|
||||||
|
|
||||||
def download_export_path(export_format:, force_export: false, no_progress_notification: nil)
|
def download_export_path(export_format:, force_export: false, no_progress_notification: nil)
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
---
|
---
|
||||||
en:
|
en:
|
||||||
everything_csv_html: Ask an export in format .csv<br>(only folders, without repeatable fields)
|
everything_csv_html: Request an export in .csv format<br>(only files, without repeatable fields)
|
||||||
everything_xlsx_html: Ask an export in format .xlsx
|
everything_xlsx_html: Request an export in .xlsx format
|
||||||
everything_ods_html: Ask an export in format .ods
|
everything_ods_html: Request an export in .ods format
|
||||||
everything_zip_html: Ask an export in format .zip<br>(does not contains timestamp nor operation logs )
|
everything_zip_html: Request an export in .zip format<br>(does not contains timestamp nor operation logs)
|
||||||
everything_short: Ask an export in format%{export_format}
|
everything_json_html: Request an export in .json format (GeoJSON)
|
||||||
everything_pending_html: Ask an export in format %{export_format} is being generated<br>(ask %{export_time} ago)
|
everything_short: Request an export in %{export_format} format
|
||||||
everything_ready_html: Download the export in format %{export_format}<br>(generated %{export_time} ago)
|
everything_pending_html: An export in %{export_format} format is being generated<br>(ask %{export_time} ago)
|
||||||
|
everything_ready_html: Download the export in %{export_format} format<br>(generated %{export_time} ago)
|
||||||
download_all: Download all files
|
download_all: Download all files
|
||||||
download:
|
download:
|
||||||
one: Download a file
|
one: Download a file
|
||||||
|
|
|
@ -4,6 +4,7 @@ fr:
|
||||||
everything_xlsx_html: Demander un export au format .xlsx
|
everything_xlsx_html: Demander un export au format .xlsx
|
||||||
everything_ods_html: Demander un export au format .ods
|
everything_ods_html: Demander un export au format .ods
|
||||||
everything_zip_html: Demander un export au format .zip<br>(ne contient pas l'horodatage ni le journal de log)
|
everything_zip_html: Demander un export au format .zip<br>(ne contient pas l'horodatage ni le journal de log)
|
||||||
|
everything_json_html: Demander un export au format .json (GeoJSON)
|
||||||
everything_short: Demander un export au format %{export_format}
|
everything_short: Demander un export au format %{export_format}
|
||||||
everything_pending_html: Un export au format %{export_format} est en train d’être généré<br>(demandé il y a %{export_time})
|
everything_pending_html: Un export au format %{export_format} est en train d’être généré<br>(demandé il y a %{export_time})
|
||||||
everything_ready_html: Télécharger l’export au format %{export_format}<br>(généré il y a %{export_time})
|
everything_ready_html: Télécharger l’export au format %{export_format}<br>(généré il y a %{export_time})
|
||||||
|
|
|
@ -96,26 +96,6 @@ module DossierHelper
|
||||||
"#{base_url}/rechercher?terme=#{siren_or_siret}"
|
"#{base_url}/rechercher?terme=#{siren_or_siret}"
|
||||||
end
|
end
|
||||||
|
|
||||||
def exports_list(exports, statut = nil)
|
|
||||||
if statut
|
|
||||||
Export::FORMATS.map do |item|
|
|
||||||
export = exports
|
|
||||||
.fetch(item.fetch(:format))
|
|
||||||
.fetch(:statut)
|
|
||||||
.fetch(statut, nil)
|
|
||||||
item.merge(export: export)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
Export::FORMATS_WITH_TIME_SPAN.map do |item|
|
|
||||||
export = exports
|
|
||||||
.fetch(item.fetch(:format))
|
|
||||||
.fetch(:time_span_type)
|
|
||||||
.fetch(item.fetch(:time_span_type), nil)
|
|
||||||
item.merge(export: export)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def france_connect_informations(user_information)
|
def france_connect_informations(user_information)
|
||||||
if user_information.full_name.empty?
|
if user_information.full_name.empty?
|
||||||
t("shared.dossiers.france_connect_informations.details_no_name")
|
t("shared.dossiers.france_connect_informations.details_no_name")
|
||||||
|
|
|
@ -61,6 +61,7 @@ class Champ < ApplicationRecord
|
||||||
:mesri?,
|
:mesri?,
|
||||||
:rna?,
|
:rna?,
|
||||||
:siret?,
|
:siret?,
|
||||||
|
:carte?,
|
||||||
:stable_id,
|
:stable_id,
|
||||||
:mandatory?,
|
:mandatory?,
|
||||||
to: :type_de_champ
|
to: :type_de_champ
|
||||||
|
|
|
@ -1144,6 +1144,13 @@ class Dossier < ApplicationRecord
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.to_feature_collection
|
||||||
|
{
|
||||||
|
type: 'FeatureCollection',
|
||||||
|
features: GeoArea.joins(:champ).where(champ: { dossier: ids }).map(&:to_feature)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
def log_api_entreprise_job_exception(exception)
|
def log_api_entreprise_job_exception(exception)
|
||||||
exceptions = self.api_entreprise_job_exceptions ||= []
|
exceptions = self.api_entreprise_job_exceptions ||= []
|
||||||
exceptions << exception.inspect
|
exceptions << exception.inspect
|
||||||
|
|
|
@ -23,7 +23,8 @@ class Export < ApplicationRecord
|
||||||
csv: 'csv',
|
csv: 'csv',
|
||||||
ods: 'ods',
|
ods: 'ods',
|
||||||
xlsx: 'xlsx',
|
xlsx: 'xlsx',
|
||||||
zip: 'zip'
|
zip: 'zip',
|
||||||
|
json: 'json'
|
||||||
}, _prefix: true
|
}, _prefix: true
|
||||||
|
|
||||||
enum time_span_type: {
|
enum time_span_type: {
|
||||||
|
@ -53,7 +54,7 @@ class Export < ApplicationRecord
|
||||||
FORMATS_WITH_TIME_SPAN = [:xlsx, :ods, :csv].flat_map do |format|
|
FORMATS_WITH_TIME_SPAN = [:xlsx, :ods, :csv].flat_map do |format|
|
||||||
[{ format: format, time_span_type: 'everything' }]
|
[{ format: format, time_span_type: 'everything' }]
|
||||||
end
|
end
|
||||||
FORMATS = [:xlsx, :ods, :csv, :zip].map do |format|
|
FORMATS = [:xlsx, :ods, :csv, :zip, :json].map do |format|
|
||||||
{ format: format }
|
{ format: format }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -127,6 +128,10 @@ class Export < ApplicationRecord
|
||||||
zip: {
|
zip: {
|
||||||
time_span_type: {},
|
time_span_type: {},
|
||||||
statut: filtered.filter(&:format_zip?).index_by(&:statut)
|
statut: filtered.filter(&:format_zip?).index_by(&:statut)
|
||||||
|
},
|
||||||
|
json: {
|
||||||
|
time_span_type: {},
|
||||||
|
statut: filtered.filter(&:format_json?).index_by(&:statut)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
@ -195,6 +200,8 @@ class Export < ApplicationRecord
|
||||||
service.to_ods
|
service.to_ods
|
||||||
when :zip
|
when :zip
|
||||||
service.to_zip
|
service.to_zip
|
||||||
|
when :json
|
||||||
|
service.to_geo_json
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -64,7 +64,10 @@ class GeoArea < ApplicationRecord
|
||||||
description: description,
|
description: description,
|
||||||
filename: filename,
|
filename: filename,
|
||||||
id: id,
|
id: id,
|
||||||
|
champ_label: champ.libelle,
|
||||||
champ_id: champ.stable_id,
|
champ_id: champ.stable_id,
|
||||||
|
champ_row: champ.row,
|
||||||
|
champ_private: champ.private?,
|
||||||
dossier_id: champ.dossier_id
|
dossier_id: champ.dossier_id
|
||||||
).compact
|
).compact
|
||||||
}
|
}
|
||||||
|
|
|
@ -200,6 +200,10 @@ class ProcedureRevision < ApplicationRecord
|
||||||
revision_types_de_champ.find_by!(type_de_champ: tdc)
|
revision_types_de_champ.find_by!(type_de_champ: tdc)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def carte?
|
||||||
|
types_de_champ_public.any?(&:carte?)
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def compute_estimated_fill_duration
|
def compute_estimated_fill_duration
|
||||||
|
|
|
@ -42,6 +42,11 @@ class ProcedureExportService
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def to_geo_json
|
||||||
|
io = StringIO.new(dossiers.to_feature_collection.to_json)
|
||||||
|
create_blob(io, :json)
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def create_blob(io, format)
|
def create_blob(io, format)
|
||||||
|
@ -77,6 +82,8 @@ class ProcedureExportService
|
||||||
'application/vnd.oasis.opendocument.spreadsheet'
|
'application/vnd.oasis.opendocument.spreadsheet'
|
||||||
when :zip
|
when :zip
|
||||||
'application/zip'
|
'application/zip'
|
||||||
|
when :json
|
||||||
|
'application/json'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1444,7 +1444,9 @@ describe Dossier do
|
||||||
},
|
},
|
||||||
properties: {
|
properties: {
|
||||||
area: 103.6,
|
area: 103.6,
|
||||||
|
champ_label: champ_carte.libelle,
|
||||||
champ_id: champ_carte.stable_id,
|
champ_id: champ_carte.stable_id,
|
||||||
|
champ_private: false,
|
||||||
dossier_id: dossier.id,
|
dossier_id: dossier.id,
|
||||||
id: geo_area.id,
|
id: geo_area.id,
|
||||||
source: 'selection_utilisateur'
|
source: 'selection_utilisateur'
|
||||||
|
|
|
@ -61,9 +61,9 @@ RSpec.describe Export, type: :model do
|
||||||
context 'when an export is made for one groupe instructeur' do
|
context 'when an export is made for one groupe instructeur' do
|
||||||
let!(:export) { create(:export, groupe_instructeurs: [gi_1, gi_2]) }
|
let!(:export) { create(:export, groupe_instructeurs: [gi_1, gi_2]) }
|
||||||
|
|
||||||
it { expect(Export.find_for_groupe_instructeurs([gi_1.id], nil)).to eq({ csv: { statut: {}, time_span_type: {} }, xlsx: { statut: {}, time_span_type: {} }, ods: { statut: {}, time_span_type: {} }, zip: { statut: {}, time_span_type: {} } }) }
|
it { expect(Export.find_for_groupe_instructeurs([gi_1.id], nil)).to eq({ csv: { statut: {}, time_span_type: {} }, xlsx: { statut: {}, time_span_type: {} }, ods: { statut: {}, time_span_type: {} }, zip: { statut: {}, time_span_type: {} }, json: { statut: {}, time_span_type: {} } }) }
|
||||||
it { expect(Export.find_for_groupe_instructeurs([gi_2.id, gi_1.id], nil)).to eq({ csv: { statut: {}, time_span_type: { 'everything' => export } }, xlsx: { statut: {}, time_span_type: {} }, ods: { statut: {}, time_span_type: {} }, zip: { statut: {}, time_span_type: {} } }) }
|
it { expect(Export.find_for_groupe_instructeurs([gi_2.id, gi_1.id], nil)).to eq({ csv: { statut: {}, time_span_type: { 'everything' => export } }, xlsx: { statut: {}, time_span_type: {} }, ods: { statut: {}, time_span_type: {} }, zip: { statut: {}, time_span_type: {} }, json: { statut: {}, time_span_type: {} } }) }
|
||||||
it { expect(Export.find_for_groupe_instructeurs([gi_1.id, gi_2.id, gi_3.id], nil)).to eq({ csv: { statut: {}, time_span_type: {} }, xlsx: { statut: {}, time_span_type: {} }, ods: { statut: {}, time_span_type: {} }, zip: { statut: {}, time_span_type: {} } }) }
|
it { expect(Export.find_for_groupe_instructeurs([gi_1.id, gi_2.id, gi_3.id], nil)).to eq({ csv: { statut: {}, time_span_type: {} }, xlsx: { statut: {}, time_span_type: {} }, ods: { statut: {}, time_span_type: {} }, zip: { statut: {}, time_span_type: {} }, json: { statut: {}, time_span_type: {} } }) }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -78,7 +78,8 @@ RSpec.describe Export, type: :model do
|
||||||
csv: { statut: { Export.statuts.fetch(:suivis) => export_with_filter }, time_span_type: { 'everything' => export_global } },
|
csv: { statut: { Export.statuts.fetch(:suivis) => export_with_filter }, time_span_type: { 'everything' => export_global } },
|
||||||
xlsx: { statut: {}, time_span_type: {} },
|
xlsx: { statut: {}, time_span_type: {} },
|
||||||
ods: { statut: {}, time_span_type: {} },
|
ods: { statut: {}, time_span_type: {} },
|
||||||
zip: { statut: {}, time_span_type: {} }
|
zip: { statut: {}, time_span_type: {} },
|
||||||
|
json: { statut: {}, time_span_type: {} }
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -467,4 +467,27 @@ describe ProcedureExportService do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'to_geo_json' do
|
||||||
|
subject do
|
||||||
|
service
|
||||||
|
.to_geo_json
|
||||||
|
.open { |f| JSON.parse(f.read) }
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:dossier) { create(:dossier, :en_instruction, :with_populated_champs, :with_individual, procedure: procedure) }
|
||||||
|
let(:champ_carte) { dossier.champs_public.find(&:carte?) }
|
||||||
|
let(:properties) { subject['features'].first['properties'] }
|
||||||
|
|
||||||
|
before do
|
||||||
|
create(:geo_area, :polygon, champ: champ_carte)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should have features' do
|
||||||
|
expect(subject['features'].size).to eq(1)
|
||||||
|
expect(properties['dossier_id']).to eq(dossier.id)
|
||||||
|
expect(properties['champ_id']).to eq(champ_carte.stable_id)
|
||||||
|
expect(properties['champ_label']).to eq(champ_carte.libelle)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue