Merge pull request #8045 from tchak/feat-export-geojson

feat(export): add GeoJSON export
This commit is contained in:
Paul Chavard 2022-11-18 10:11:58 +01:00 committed by GitHub
commit c200e03771
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 94 additions and 36 deletions

View file

@ -1,7 +1,8 @@
@import "placeholders";
turbo-events {
display: none;
html,
body {
min-height: 100vh;
}
.page-wrapper {

View file

@ -8,7 +8,27 @@ class Dossiers::ExportComponent < ApplicationComponent
end
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
def download_export_path(export_format:, force_export: false, no_progress_notification: nil)

View file

@ -1,12 +1,13 @@
---
en:
everything_csv_html: Ask an export in format .csv<br>(only folders, without repeatable fields)
everything_xlsx_html: Ask an export in format .xlsx
everything_ods_html: Ask an export in format .ods
everything_zip_html: Ask an export in format .zip<br>(does not contains timestamp nor operation logs )
everything_short: Ask an export in format%{export_format}
everything_pending_html: Ask an export in format %{export_format} is being generated<br>(ask %{export_time} ago)
everything_ready_html: Download the export in format %{export_format}<br>(generated %{export_time} ago)
everything_csv_html: Request an export in .csv format<br>(only files, without repeatable fields)
everything_xlsx_html: Request an export in .xlsx format
everything_ods_html: Request an export in .ods format
everything_zip_html: Request an export in .zip format<br>(does not contains timestamp nor operation logs)
everything_json_html: Request an export in .json format (GeoJSON)
everything_short: Request an export in %{export_format} format
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:
one: Download a file

View file

@ -4,6 +4,7 @@ fr:
everything_xlsx_html: Demander un export au format .xlsx
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_json_html: Demander un export au format .json (GeoJSON)
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_ready_html: Télécharger lexport au format %{export_format}<br>(généré il y a %{export_time})

View file

@ -96,26 +96,6 @@ module DossierHelper
"#{base_url}/rechercher?terme=#{siren_or_siret}"
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)
if user_information.full_name.empty?
t("shared.dossiers.france_connect_informations.details_no_name")

View file

@ -61,6 +61,7 @@ class Champ < ApplicationRecord
:mesri?,
:rna?,
:siret?,
:carte?,
:stable_id,
:mandatory?,
to: :type_de_champ

View file

@ -1144,6 +1144,13 @@ class Dossier < ApplicationRecord
}
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)
exceptions = self.api_entreprise_job_exceptions ||= []
exceptions << exception.inspect

View file

@ -23,7 +23,8 @@ class Export < ApplicationRecord
csv: 'csv',
ods: 'ods',
xlsx: 'xlsx',
zip: 'zip'
zip: 'zip',
json: 'json'
}, _prefix: true
enum time_span_type: {
@ -53,7 +54,7 @@ class Export < ApplicationRecord
FORMATS_WITH_TIME_SPAN = [:xlsx, :ods, :csv].flat_map do |format|
[{ format: format, time_span_type: 'everything' }]
end
FORMATS = [:xlsx, :ods, :csv, :zip].map do |format|
FORMATS = [:xlsx, :ods, :csv, :zip, :json].map do |format|
{ format: format }
end
@ -127,6 +128,10 @@ class Export < ApplicationRecord
zip: {
time_span_type: {},
statut: filtered.filter(&:format_zip?).index_by(&:statut)
},
json: {
time_span_type: {},
statut: filtered.filter(&:format_json?).index_by(&:statut)
}
}
end
@ -195,6 +200,8 @@ class Export < ApplicationRecord
service.to_ods
when :zip
service.to_zip
when :json
service.to_geo_json
end
end

View file

@ -64,7 +64,10 @@ class GeoArea < ApplicationRecord
description: description,
filename: filename,
id: id,
champ_label: champ.libelle,
champ_id: champ.stable_id,
champ_row: champ.row,
champ_private: champ.private?,
dossier_id: champ.dossier_id
).compact
}

View file

@ -200,6 +200,10 @@ class ProcedureRevision < ApplicationRecord
revision_types_de_champ.find_by!(type_de_champ: tdc)
end
def carte?
types_de_champ_public.any?(&:carte?)
end
private
def compute_estimated_fill_duration

View file

@ -42,6 +42,11 @@ class ProcedureExportService
end
end
def to_geo_json
io = StringIO.new(dossiers.to_feature_collection.to_json)
create_blob(io, :json)
end
private
def create_blob(io, format)
@ -77,6 +82,8 @@ class ProcedureExportService
'application/vnd.oasis.opendocument.spreadsheet'
when :zip
'application/zip'
when :json
'application/json'
end
end

View file

@ -1444,7 +1444,9 @@ describe Dossier do
},
properties: {
area: 103.6,
champ_label: champ_carte.libelle,
champ_id: champ_carte.stable_id,
champ_private: false,
dossier_id: dossier.id,
id: geo_area.id,
source: 'selection_utilisateur'

View file

@ -61,9 +61,9 @@ RSpec.describe Export, type: :model do
context 'when an export is made for one groupe instructeur' do
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_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_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], 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: {} }, 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: {} }, json: { statut: {}, time_span_type: {} } }) }
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 } },
xlsx: { 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

View file

@ -467,4 +467,27 @@ describe ProcedureExportService do
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