Merge pull request #7489 from betagouv/opendata-export-demarches-desc
Permet la génération json du descriptif des démarches publiques Une prochaine PR permettra la publication de ce fichier json dans data.gouv.fr
This commit is contained in:
commit
8788ee70b9
9 changed files with 194 additions and 5 deletions
|
@ -72,7 +72,7 @@ class API::V2::Schema < GraphQL::Schema
|
|||
|
||||
use GraphQL::Execution::Interpreter
|
||||
use GraphQL::Analysis::AST
|
||||
use GraphQL::Schema::Timeout, max_seconds: 10
|
||||
use GraphQL::Schema::Timeout, max_seconds: 30
|
||||
use GraphQL::Batch
|
||||
use GraphQL::Backtrace
|
||||
|
||||
|
|
|
@ -581,7 +581,7 @@ type Demarche {
|
|||
number: Int!
|
||||
publishedRevision: Revision
|
||||
revisions: [Revision!]!
|
||||
service: Service!
|
||||
service: Service
|
||||
|
||||
"""
|
||||
État de la démarche.
|
||||
|
@ -600,6 +600,8 @@ Ceci est une version abrégée du type `Demarche`, qui n’expose que les métad
|
|||
Cela évite l’accès récursif aux dossiers.
|
||||
"""
|
||||
type DemarcheDescriptor {
|
||||
cadreJuridique: String
|
||||
|
||||
"""
|
||||
Date de la création.
|
||||
"""
|
||||
|
@ -629,6 +631,7 @@ type DemarcheDescriptor {
|
|||
Pour une démarche déclarative, état cible des dossiers à valider automatiquement
|
||||
"""
|
||||
declarative: DossierDeclarativeState
|
||||
deliberation: String
|
||||
|
||||
"""
|
||||
Description de la démarche.
|
||||
|
@ -641,7 +644,7 @@ type DemarcheDescriptor {
|
|||
"""
|
||||
number: Int!
|
||||
revision: Revision!
|
||||
service: Service!
|
||||
service: Service
|
||||
|
||||
"""
|
||||
État de la démarche.
|
||||
|
|
12
app/graphql/types/base_field.rb
Normal file
12
app/graphql/types/base_field.rb
Normal file
|
@ -0,0 +1,12 @@
|
|||
module Types
|
||||
class BaseField < GraphQL::Schema::Field
|
||||
def initialize(*args, internal: false, **kwargs, &block)
|
||||
@internal = internal
|
||||
super(*args, **kwargs, &block)
|
||||
end
|
||||
|
||||
def visible?(ctx)
|
||||
super && (@internal ? ctx[:internal_use] : true)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,5 +1,6 @@
|
|||
module Types
|
||||
class DemarcheDescriptorType < Types::BaseObject
|
||||
field_class BaseField
|
||||
description "Une démarche (métadonnées)
|
||||
Ceci est une version abrégée du type `Demarche`, qui n’expose que les métadonnées.
|
||||
Cela évite l’accès récursif aux dossiers."
|
||||
|
@ -18,7 +19,12 @@ Cela évite l’accès récursif aux dossiers."
|
|||
field :date_fermeture, GraphQL::Types::ISO8601DateTime, "Date de la fermeture.", null: true
|
||||
|
||||
field :revision, Types::RevisionType, null: false
|
||||
field :service, Types::ServiceType, null: false
|
||||
field :service, Types::ServiceType, null: true
|
||||
|
||||
field :cadre_juridique, String, null: true
|
||||
field :deliberation, String, null: true
|
||||
|
||||
field :dossiers_count, Int, null: false, internal: true
|
||||
|
||||
def service
|
||||
Loaders::Record.for(Service).load(procedure.service_id)
|
||||
|
@ -28,10 +34,22 @@ Cela évite l’accès récursif aux dossiers."
|
|||
object.is_a?(ProcedureRevision) ? object : object.active_revision
|
||||
end
|
||||
|
||||
def dossiers_count
|
||||
object.dossiers.count
|
||||
end
|
||||
|
||||
def deliberation
|
||||
Rails.application.routes.url_helpers.url_for(procedure.deliberation) if procedure.deliberation.attached?
|
||||
end
|
||||
|
||||
def state
|
||||
procedure.aasm.current_state
|
||||
end
|
||||
|
||||
def cadre_juridique
|
||||
procedure.cadre_juridique
|
||||
end
|
||||
|
||||
def number
|
||||
procedure.id
|
||||
end
|
||||
|
|
|
@ -30,7 +30,7 @@ module Types
|
|||
field :date_fermeture, GraphQL::Types::ISO8601DateTime, "Date de la fermeture.", null: true, method: :closed_at
|
||||
|
||||
field :groupe_instructeurs, [Types::GroupeInstructeurType], null: false
|
||||
field :service, Types::ServiceType, null: false
|
||||
field :service, Types::ServiceType, null: true
|
||||
|
||||
field :dossiers, Types::DossierType.connection_type, "Liste de tous les dossiers d’une démarche.", null: false do
|
||||
argument :order, Types::Order, default_value: :asc, required: false, description: "L’ordre des dossiers."
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
module Types
|
||||
class QueryType < Types::BaseObject
|
||||
field_class BaseField
|
||||
|
||||
field :demarche, DemarcheType, null: false, description: "Informations concernant une démarche." do
|
||||
argument :number, Int, "Numéro de la démarche.", required: true
|
||||
end
|
||||
|
@ -12,6 +14,12 @@ module Types
|
|||
argument :number, Int, "Numéro du groupe instructeur.", required: true
|
||||
end
|
||||
|
||||
field :demarches_publiques, DemarcheDescriptorType.connection_type, null: false, internal: true, max_page_size: 30
|
||||
|
||||
def demarches_publiques
|
||||
Procedure.opendata
|
||||
end
|
||||
|
||||
def demarche(number:)
|
||||
Procedure.for_api_v2.find(number)
|
||||
rescue => e
|
||||
|
|
|
@ -219,6 +219,7 @@ class Procedure < ApplicationRecord
|
|||
scope :brouillons, -> { where(aasm_state: :brouillon) }
|
||||
scope :publiees, -> { where(aasm_state: :publiee) }
|
||||
scope :closes, -> { where(aasm_state: [:close, :depubliee]) }
|
||||
scope :opendata, -> { where(opendata: true) }
|
||||
scope :publiees_ou_closes, -> { where(aasm_state: [:publiee, :close, :depubliee]) }
|
||||
scope :by_libelle, -> { order(libelle: :asc) }
|
||||
scope :created_during, -> (range) { where(created_at: range) }
|
||||
|
|
100
app/services/demarches_publiques_export_service.rb
Normal file
100
app/services/demarches_publiques_export_service.rb
Normal file
|
@ -0,0 +1,100 @@
|
|||
class DemarchesPubliquesExportService
|
||||
attr_reader :io
|
||||
def initialize(io)
|
||||
@io = io
|
||||
end
|
||||
|
||||
def call
|
||||
end_cursor = nil
|
||||
first = true
|
||||
write_array_opening
|
||||
loop do
|
||||
write_demarches_separator if !first
|
||||
execute_query(cursor: end_cursor)
|
||||
end_cursor = last_cursor
|
||||
io.write(jsonify(demarches))
|
||||
first = false
|
||||
break if !has_next_page?
|
||||
end
|
||||
write_array_closing
|
||||
io.close
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def execute_query(cursor: nil)
|
||||
result = API::V2::Schema.execute(query, variables: { cursor: cursor }, context: { internal_use: true })
|
||||
raise DemarchesPubliquesExportService::Error.new(result["errors"]) if result["errors"]
|
||||
@graphql_data = result["data"]
|
||||
end
|
||||
|
||||
def query
|
||||
"query($cursor: String) {
|
||||
demarchesPubliques(after: $cursor) {
|
||||
pageInfo {
|
||||
endCursor
|
||||
hasNextPage
|
||||
}
|
||||
edges {
|
||||
node {
|
||||
number
|
||||
title
|
||||
description
|
||||
datePublication
|
||||
service { nom organisme typeOrganisme }
|
||||
cadreJuridique
|
||||
deliberation
|
||||
dossiersCount
|
||||
revision {
|
||||
champDescriptors {
|
||||
type
|
||||
label
|
||||
description
|
||||
required
|
||||
options
|
||||
champDescriptors {
|
||||
type
|
||||
label
|
||||
description
|
||||
required
|
||||
options
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}"
|
||||
end
|
||||
|
||||
def last_cursor
|
||||
@graphql_data["demarchesPubliques"]["pageInfo"]["endCursor"]
|
||||
end
|
||||
|
||||
def has_next_page?
|
||||
@graphql_data["demarchesPubliques"]["pageInfo"]["hasNextPage"]
|
||||
end
|
||||
|
||||
def demarches
|
||||
@graphql_data["demarchesPubliques"]["edges"].map { |edge| edge["node"] }
|
||||
end
|
||||
|
||||
def jsonify(demarches)
|
||||
demarches.map(&:to_json).join(',')
|
||||
end
|
||||
|
||||
def write_array_opening
|
||||
io.write('[')
|
||||
end
|
||||
|
||||
def write_array_closing
|
||||
io.write(']')
|
||||
end
|
||||
|
||||
def write_demarches_separator
|
||||
io.write(',')
|
||||
end
|
||||
end
|
||||
|
||||
class DemarchesPubliquesExportService::Error < StandardError
|
||||
end
|
47
spec/services/demarches_publiques_export_service_spec.rb
Normal file
47
spec/services/demarches_publiques_export_service_spec.rb
Normal file
|
@ -0,0 +1,47 @@
|
|||
describe DemarchesPubliquesExportService do
|
||||
let(:procedure) { create(:procedure, :published, :with_service, :with_type_de_champ) }
|
||||
let!(:dossier) { create(:dossier, procedure: procedure) }
|
||||
let(:io) { StringIO.new }
|
||||
|
||||
describe 'call' do
|
||||
it 'generate json for all closed procedures' do
|
||||
expected_result = {
|
||||
number: procedure.id,
|
||||
title: procedure.libelle,
|
||||
description: "Demande de subvention à l'intention des associations",
|
||||
service: {
|
||||
nom: procedure.service.nom,
|
||||
organisme: "organisme",
|
||||
typeOrganisme: "association"
|
||||
},
|
||||
cadreJuridique: "un cadre juridique important",
|
||||
deliberation: nil,
|
||||
datePublication: procedure.published_at.iso8601,
|
||||
dossiersCount: 1,
|
||||
revision: {
|
||||
champDescriptors: [
|
||||
{
|
||||
description: procedure.types_de_champ.first.description,
|
||||
label: procedure.types_de_champ.first.libelle,
|
||||
options: nil,
|
||||
required: false,
|
||||
type: "text",
|
||||
champDescriptors: nil
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
DemarchesPubliquesExportService.new(io).call
|
||||
expect(JSON.parse(io.string)[0]
|
||||
.deep_symbolize_keys)
|
||||
.to eq(expected_result)
|
||||
end
|
||||
it 'raises exception when procedure with bad data' do
|
||||
procedure.libelle = nil
|
||||
procedure.save(validate: false)
|
||||
|
||||
expect { DemarchesPubliquesExportService.new(io).call }.to raise_error(DemarchesPubliquesExportService::Error)
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue