diff --git a/app/controllers/api/v2/dossiers_controller.rb b/app/controllers/api/v2/dossiers_controller.rb new file mode 100644 index 000000000..83c07802d --- /dev/null +++ b/app/controllers/api/v2/dossiers_controller.rb @@ -0,0 +1,26 @@ +class API::V2::DossiersController < API::V2::BaseController + before_action :ensure_dossier_present + + def pdf + @include_infos_administration = true + render(file: 'dossiers/show', formats: [:pdf]) + end + + def geojson + send_data dossier.to_feature_collection.to_json, + type: 'application/json', + filename: "dossier-#{dossier.id}-features.json" + end + + private + + def ensure_dossier_present + if dossier.blank? + head :unauthorized + end + end + + def dossier + @dossier ||= GlobalID::Locator.locate_signed(params[:id].to_s, for: 'api_v2') + end +end diff --git a/app/controllers/root_controller.rb b/app/controllers/root_controller.rb index a6d3b385b..9c4341420 100644 --- a/app/controllers/root_controller.rb +++ b/app/controllers/root_controller.rb @@ -12,6 +12,8 @@ class RootController < ApplicationController return redirect_to manager_root_path end + @stat = Stat.first + render 'landing' end diff --git a/app/controllers/stats_controller.rb b/app/controllers/stats_controller.rb index a39604a1e..67bef7dd7 100644 --- a/app/controllers/stats_controller.rb +++ b/app/controllers/stats_controller.rb @@ -4,23 +4,35 @@ class StatsController < ApplicationController MEAN_NUMBER_OF_CHAMPS_IN_A_FORM = 24.0 def index + stat = Stat.first + procedures = Procedure.publiees_ou_closes dossiers = Dossier.state_not_brouillon @procedures_numbers = procedures_numbers(procedures) - @dossiers_numbers = dossiers_numbers(dossiers) + + @dossiers_numbers = dossiers_numbers( + stat.dossiers_not_brouillon, + stat.dossiers_depose_avant_30_jours, + stat.dossiers_deposes_entre_60_et_30_jours + ) @satisfaction_usagers = satisfaction_usagers @contact_percentage = contact_percentage - @dossiers_states = dossiers_states + @dossiers_states_for_pie = { + "Brouillon" => stat.dossiers_brouillon, + "En construction" => stat.dossiers_en_construction, + "En instruction" => stat.dossiers_en_instruction, + "Terminé" => stat.dossiers_termines + } @procedures_cumulative = cumulative_hash(procedures, :published_at) @procedures_in_the_last_4_months = last_four_months_hash(procedures, :published_at) - @dossiers_cumulative = cumulative_hash(dossiers, :en_construction_at) - @dossiers_in_the_last_4_months = last_four_months_hash(dossiers, :en_construction_at) + @dossiers_cumulative = stat.dossiers_cumulative + @dossiers_in_the_last_4_months = stat.dossiers_in_the_last_4_months if administration_signed_in? @dossier_instruction_mean_time = Rails.cache.fetch("dossier_instruction_mean_time", expires_in: 1.day) do @@ -96,10 +108,7 @@ class StatsController < ApplicationController } end - def dossiers_numbers(dossiers) - total = dossiers.count - last_30_days_count = dossiers.where(en_construction_at: 1.month.ago..Time.zone.now).count - previous_count = dossiers.where(en_construction_at: 2.months.ago..1.month.ago).count + def dossiers_numbers(total, last_30_days_count, previous_count) if previous_count != 0 evolution = (((last_30_days_count.to_f / previous_count) - 1) * 100).round(0) else @@ -114,15 +123,6 @@ class StatsController < ApplicationController } end - def dossiers_states - { - 'Brouilllon' => Dossier.state_brouillon.count, - 'En construction' => Dossier.state_en_construction.count, - 'En instruction' => Dossier.state_en_instruction.count, - 'Terminé' => Dossier.state_termine.count - } - end - def satisfaction_usagers legend = { Feedback.ratings.fetch(:unhappy) => "Mécontents", diff --git a/app/graphql/schema.graphql b/app/graphql/schema.graphql index c84c2ee0c..698bb9ba7 100644 --- a/app/graphql/schema.graphql +++ b/app/graphql/schema.graphql @@ -55,7 +55,7 @@ interface Champ { type ChampDescriptor { """ - Description des champs d'un bloc répétable. + Description des champs d’un bloc répétable. """ champDescriptors: [ChampDescriptor!] @@ -71,7 +71,7 @@ type ChampDescriptor { label: String! """ - List des options d'un champ avec selection. + List des options d’un champ avec selection. """ options: [String!] @@ -246,7 +246,7 @@ type Demarche { datePublication: ISO8601DateTime! """ - L'état de dossier pour une démarche déclarative + L’état de dossier pour une démarche déclarative """ declarative: DossierDeclarativeState @@ -256,7 +256,7 @@ type Demarche { description: String! """ - Liste de tous les dossiers d'une démarche. + Liste de tous les dossiers d’une démarche. """ dossiers( """ @@ -290,7 +290,7 @@ type Demarche { last: Int """ - L'ordre des dossiers. + L’ordre des dossiers. """ order: Order = ASC @@ -317,7 +317,7 @@ type Demarche { service: Service! """ - L'état de la démarche. + L’état de la démarche. """ state: DemarcheState! @@ -380,6 +380,11 @@ Un dossier type Dossier { annotations: [Champ!]! archived: Boolean! + + """ + L’URL de l’attestation au format PDF. + """ + attestation: File avis: [Avis!]! champs: [Champ!]! @@ -403,6 +408,11 @@ type Dossier { """ dateTraitement: ISO8601DateTime demandeur: Demandeur! + + """ + L’URL du GeoJSON contenant les données cartographiques du dossier. + """ + geojson: File groupeInstructeur: GroupeInstructeur! id: ID! instructeurs: [Profile!]! @@ -414,10 +424,15 @@ type Dossier { Le numero du dossier. """ number: Int! + + """ + L’URL du dossier au format PDF. + """ + pdf: File revision: Revision! """ - L'état du dossier. + L’état du dossier. """ state: DossierState! usager: Profile! @@ -754,7 +769,7 @@ type Entreprise { dateCreation: ISO8601Date! """ - effectif moyen d'une année + effectif moyen d’une année """ effectifAnnuel: Effectif @@ -829,7 +844,7 @@ Un groupe instructeur avec ces dossiers """ type GroupeInstructeurWithDossiers { """ - Liste de tous les dossiers d'une démarche. + Liste de tous les dossiers d’une démarche. """ dossiers( """ @@ -858,7 +873,7 @@ type GroupeInstructeurWithDossiers { last: Int """ - L'ordre des dossiers. + L’ordre des dossiers. """ order: Order = ASC @@ -1107,7 +1122,7 @@ type Query { ): Demarche! """ - Informations sur un dossier d'une démarche. + Informations sur un dossier d’une démarche. """ dossier( """ diff --git a/app/graphql/types/champ_descriptor_type.rb b/app/graphql/types/champ_descriptor_type.rb index 08394a8a9..1c14c3806 100644 --- a/app/graphql/types/champ_descriptor_type.rb +++ b/app/graphql/types/champ_descriptor_type.rb @@ -14,8 +14,8 @@ module Types field :description, String, "Description du champ.", null: true field :required, Boolean, "Est-ce que le champ est obligatoire ?", null: false, method: :mandatory? - field :champ_descriptors, [Types::ChampDescriptorType], "Description des champs d'un bloc répétable.", null: true - field :options, [String], "List des options d'un champ avec selection.", null: true + field :champ_descriptors, [Types::ChampDescriptorType], "Description des champs d’un bloc répétable.", null: true + field :options, [String], "List des options d’un champ avec selection.", null: true def champ_descriptors if object.repetition? diff --git a/app/graphql/types/champs/piece_justificative_champ_type.rb b/app/graphql/types/champs/piece_justificative_champ_type.rb index bf0b70571..0f16e11a5 100644 --- a/app/graphql/types/champs/piece_justificative_champ_type.rb +++ b/app/graphql/types/champs/piece_justificative_champ_type.rb @@ -1,6 +1,5 @@ module Types::Champs class PieceJustificativeChampType < Types::BaseObject - include Rails.application.routes.url_helpers implements Types::ChampType field :file, Types::File, null: true, extensions: [ diff --git a/app/graphql/types/demarche_type.rb b/app/graphql/types/demarche_type.rb index 79dc5f9c9..71a0e042e 100644 --- a/app/graphql/types/demarche_type.rb +++ b/app/graphql/types/demarche_type.rb @@ -20,8 +20,8 @@ module Types field :number, Int, "Le numero de la démarche.", null: false, method: :id field :title, String, "Le titre de la démarche.", null: false, method: :libelle field :description, String, "Description de la démarche.", null: false - field :state, DemarcheState, "L'état de la démarche.", null: false - field :declarative, DossierDeclarativeState, "L'état de dossier pour une démarche déclarative", null: true, method: :declarative_with_state + field :state, DemarcheState, "L’état de la démarche.", null: false + field :declarative, DossierDeclarativeState, "L’état de dossier pour une démarche déclarative", null: true, method: :declarative_with_state field :date_creation, GraphQL::Types::ISO8601DateTime, "Date de la création.", null: false, method: :created_at field :date_publication, GraphQL::Types::ISO8601DateTime, "Date de la publication.", null: false, method: :published_at @@ -32,8 +32,8 @@ module Types field :groupe_instructeurs, [Types::GroupeInstructeurType], null: false field :service, Types::ServiceType, null: false - 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." + 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." argument :created_since, GraphQL::Types::ISO8601DateTime, required: false, description: "Dossiers déposés depuis la date." argument :updated_since, GraphQL::Types::ISO8601DateTime, required: false, description: "Dossiers mis à jour depuis la date." argument :state, Types::DossierType::DossierState, required: false, description: "Dossiers avec statut." diff --git a/app/graphql/types/dossier_type.rb b/app/graphql/types/dossier_type.rb index 43bb2106c..341ae242c 100644 --- a/app/graphql/types/dossier_type.rb +++ b/app/graphql/types/dossier_type.rb @@ -10,7 +10,7 @@ module Types global_id_field :id field :number, Int, "Le numero du dossier.", null: false, method: :id - field :state, DossierState, "L'état du dossier.", null: false + field :state, DossierState, "L’état du dossier.", null: false field :date_passage_en_construction, GraphQL::Types::ISO8601DateTime, "Date de dépôt.", null: false, method: :en_construction_at field :date_passage_en_instruction, GraphQL::Types::ISO8601DateTime, "Date de passage en instruction.", null: true, method: :en_instruction_at @@ -24,6 +24,10 @@ module Types { Extensions::Attachment => { attachment: :justificatif_motivation } } ] + field :pdf, Types::File, "L’URL du dossier au format PDF.", null: true + field :geojson, Types::File, "L’URL du GeoJSON contenant les données cartographiques du dossier.", null: true + field :attestation, Types::File, "L’URL de l’attestation au format PDF.", null: true + field :usager, Types::ProfileType, null: false field :groupe_instructeur, Types::GroupeInstructeurType, null: false field :revision, Types::RevisionType, null: false @@ -81,6 +85,30 @@ module Types Loaders::Association.for(object.class, :champs_private).load(object) end + def pdf + sgid = object.to_sgid(expires_in: 1.hour, for: 'api_v2') + { + filename: "dossier-#{object.id}.pdf", + content_type: 'application/pdf', + url: Rails.application.routes.url_helpers.api_v2_dossier_pdf_url(id: sgid) + } + end + + def geojson + sgid = object.to_sgid(expires_in: 1.hour, for: 'api_v2') + { + filename: "dossier-#{object.id}-features.json", + content_type: 'application/json', + url: Rails.application.routes.url_helpers.api_v2_dossier_geojson_url(id: sgid) + } + end + + def attestation + if object.termine? && object.procedure.attestation_template&.activated? + Loaders::Association.for(object.class, attestation: { pdf_attachment: :blob }).load(object).then(&:pdf) + end + end + def self.authorized?(object, context) authorized_demarche?(object.procedure, context) end diff --git a/app/graphql/types/file.rb b/app/graphql/types/file.rb index 6f30cd9a9..0fc4cf1bd 100644 --- a/app/graphql/types/file.rb +++ b/app/graphql/types/file.rb @@ -7,7 +7,11 @@ module Types field :content_type, String, null: false def url - object.service_url + if object.is_a?(Hash) + object[:url] + else + object.service_url + end end end end diff --git a/app/graphql/types/groupe_instructeur_with_dossiers_type.rb b/app/graphql/types/groupe_instructeur_with_dossiers_type.rb index 66f0a47a8..d8d69e124 100644 --- a/app/graphql/types/groupe_instructeur_with_dossiers_type.rb +++ b/app/graphql/types/groupe_instructeur_with_dossiers_type.rb @@ -2,8 +2,8 @@ module Types class GroupeInstructeurWithDossiersType < GroupeInstructeurType description "Un groupe instructeur avec ces dossiers" - 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." + 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." argument :created_since, GraphQL::Types::ISO8601DateTime, required: false, description: "Dossiers déposés depuis la date." argument :updated_since, GraphQL::Types::ISO8601DateTime, required: false, description: "Dossiers mis à jour depuis la date." argument :state, Types::DossierType::DossierState, required: false, description: "Dossiers avec statut." diff --git a/app/graphql/types/personne_morale_type.rb b/app/graphql/types/personne_morale_type.rb index bef7c6d02..a0216046b 100644 --- a/app/graphql/types/personne_morale_type.rb +++ b/app/graphql/types/personne_morale_type.rb @@ -16,7 +16,7 @@ module Types field :siret_siege_social, String, null: false field :code_effectif_entreprise, String, null: true field :effectif_mensuel, EffectifType, null: true, description: "effectif pour un mois donné" - field :effectif_annuel, EffectifType, null: true, description: "effectif moyen d'une année" + field :effectif_annuel, EffectifType, null: true, description: "effectif moyen d’une année" field :date_creation, GraphQL::Types::ISO8601Date, null: false field :nom, String, null: false field :prenom, String, null: false diff --git a/app/graphql/types/query_type.rb b/app/graphql/types/query_type.rb index fee775cb4..4ed32c7ab 100644 --- a/app/graphql/types/query_type.rb +++ b/app/graphql/types/query_type.rb @@ -4,7 +4,7 @@ module Types argument :number, Int, "Numéro de la démarche.", required: true end - field :dossier, DossierType, null: false, description: "Informations sur un dossier d'une démarche." do + field :dossier, DossierType, null: false, description: "Informations sur un dossier d’une démarche." do argument :number, Int, "Numéro du dossier.", required: true end diff --git a/app/jobs/update_stats_job.rb b/app/jobs/update_stats_job.rb new file mode 100644 index 000000000..b1202b67e --- /dev/null +++ b/app/jobs/update_stats_job.rb @@ -0,0 +1,7 @@ +class UpdateStatsJob < CronJob + self.schedule_expression = "every 1 hour" + + def perform(*args) + Stat.update_stats + end +end diff --git a/app/models/dossier.rb b/app/models/dossier.rb index 1c1b1b559..d4a99ce22 100644 --- a/app/models/dossier.rb +++ b/app/models/dossier.rb @@ -286,7 +286,7 @@ class Dossier < ApplicationRecord end scope :for_procedure, -> (procedure) { includes(:user, :groupe_instructeur).where(groupe_instructeurs: { procedure: procedure }) } - scope :for_api_v2, -> { includes(procedure: [:administrateurs], etablissement: [], individual: [], traitements: []) } + scope :for_api_v2, -> { includes(procedure: [:administrateurs, :attestation_template], etablissement: [], individual: [], traitements: []) } scope :with_notifications, -> do joins(:follows) diff --git a/app/models/stat.rb b/app/models/stat.rb new file mode 100644 index 000000000..50d68a105 --- /dev/null +++ b/app/models/stat.rb @@ -0,0 +1,93 @@ +# == Schema Information +# +# Table name: stats +# +# id :bigint not null, primary key +# administrations_partenaires :bigint default(0) +# dossiers_brouillon :bigint default(0) +# dossiers_cumulative :jsonb not null +# dossiers_depose_avant_30_jours :bigint default(0) +# dossiers_deposes_entre_60_et_30_jours :bigint default(0) +# dossiers_en_construction :bigint default(0) +# dossiers_en_instruction :bigint default(0) +# dossiers_in_the_last_4_months :jsonb not null +# dossiers_not_brouillon :bigint default(0) +# dossiers_termines :bigint default(0) +# created_at :datetime not null +# updated_at :datetime not null +# +class Stat < ApplicationRecord + class << self + def update_stats + states = dossiers_states + stat = Stat.first || Stat.new + + stat.update( + dossiers_en_construction: states['en_construction'], + dossiers_en_instruction: states['en_instruction'], + dossiers_brouillon: states['brouillon'], + dossiers_depose_avant_30_jours: states['dossiers_depose_avant_30_jours'], + dossiers_deposes_entre_60_et_30_jours: states['dossiers_deposes_entre_60_et_30_jours'], + dossiers_not_brouillon: states['not_brouillon'], + dossiers_termines: states['termines'], + dossiers_cumulative: cumulative_hash(Dossier.state_not_brouillon, :en_construction_at), + dossiers_in_the_last_4_months: last_four_months_hash(Dossier.state_not_brouillon, :en_construction_at), + administrations_partenaires: AdministrateursProcedure.joins(:procedure).merge(Procedure.publiees_ou_closes).select('distinct administrateur_id').count + ) + end + + private + + def dossiers_states + query = <<-EOF + SELECT + COUNT(*) FILTER ( WHERE state != 'brouillon' ) AS "not_brouillon", + COUNT(*) FILTER ( WHERE state != 'brouillon' and en_construction_at BETWEEN :one_month_ago AND :now ) AS "dossiers_depose_avant_30_jours", + COUNT(*) FILTER ( WHERE state != 'brouillon' and en_construction_at BETWEEN :two_months_ago AND :one_month_ago ) AS "dossiers_deposes_entre_60_et_30_jours", + COUNT(*) FILTER ( WHERE state = 'brouillon' ) AS "brouillon", + COUNT(*) FILTER ( WHERE state = 'en_construction' ) AS "en_construction", + COUNT(*) FILTER ( WHERE state = 'en_instruction' ) AS "en_instruction", + COUNT(*) FILTER ( WHERE state in ('accepte', 'refuse', 'sans_suite') ) AS "termines" + FROM dossiers + WHERE hidden_at IS NULL + EOF + + sanitized_query = ActiveRecord::Base.sanitize_sql([ + query, + now: Time.zone.now, + one_month_ago: 1.month.ago, + two_months_ago: 2.months.ago + ]) + + Dossier.connection.select_all(sanitized_query).first + end + + def last_four_months_hash(association, date_attribute) + min_date = 3.months.ago.beginning_of_month.to_date + + association + .where(date_attribute => min_date..max_date) + .group("DATE_TRUNC('month', #{date_attribute})") + .count + .to_a + .sort_by { |a| a[0] } + .map { |e| [I18n.l(e.first, format: "%B %Y"), e.last] } + end + + def cumulative_hash(association, date_attribute) + sum = 0 + association + .where("#{date_attribute} < ?", max_date) + .group("DATE_TRUNC('month', #{date_attribute})") + .count + .to_a + .sort_by { |a| a[0] } + .map { |x, y| { x => (sum += y) } } + .reduce({}, :merge) + end + + def max_date + Time.zone.now.beginning_of_month - 1.second + end + end +end diff --git a/app/views/new_administrateur/procedures/jeton.html.haml b/app/views/new_administrateur/procedures/jeton.html.haml index 55bb4a625..2e4341d38 100644 --- a/app/views/new_administrateur/procedures/jeton.html.haml +++ b/app/views/new_administrateur/procedures/jeton.html.haml @@ -19,6 +19,6 @@ propre à votre démarche. = f.label :api_entreprise_token, "Jeton" - = f.password_field :api_entreprise_token, class: 'form-control' + = f.password_field :api_entreprise_token, value: @procedure.read_attribute(:api_entreprise_token), class: 'form-control' .text-right = f.button 'Enregistrer', class: 'button primary send' diff --git a/app/views/new_administrateur/procedures/show.html.haml b/app/views/new_administrateur/procedures/show.html.haml index 6167b9bfe..21ae54a9d 100644 --- a/app/views/new_administrateur/procedures/show.html.haml +++ b/app/views/new_administrateur/procedures/show.html.haml @@ -77,7 +77,7 @@ Choix du service administratif .card-admin-action - if @procedure.service.present? - = link_to 'Modifier', edit_admin_service_path(@procedure.service), class: 'button' + = link_to 'Modifier', edit_admin_service_path(@procedure.service, procedure_id: @procedure.id), class: 'button' - elsif current_administrateur.services.present? = link_to 'Choisir', admin_services_path(procedure_id: @procedure.id), class: 'button' - else diff --git a/app/views/root/landing.html.haml b/app/views/root/landing.html.haml index d0636dc25..85568cee2 100644 --- a/app/views/root/landing.html.haml +++ b/app/views/root/landing.html.haml @@ -43,14 +43,14 @@ %ul.numbers %li.number .number-value - = number_with_delimiter(AdministrateursProcedure.joins(:procedure).merge(Procedure.publiees_ou_closes).select('distinct administrateur_id').count, :locale => :fr) + = number_with_delimiter(@stat&.administrations_partenaires, :locale => :fr) .number-label< administrations %br<> partenaires %li.number .number-value - = number_with_delimiter(Dossier.state_not_brouillon.count, :locale => :fr) + = number_with_delimiter(@stat&.dossiers_not_brouillon, :locale => :fr) .number-label< dossiers %br<> diff --git a/app/views/stats/index.html.haml b/app/views/stats/index.html.haml index 6f60f2628..07553baef 100644 --- a/app/views/stats/index.html.haml +++ b/app/views/stats/index.html.haml @@ -55,7 +55,7 @@ .chart-container .chart - = pie_chart @dossiers_states, + = pie_chart @dossiers_states_for_pie, colors: ["rgba(222, 238, 265, 1)", "rgba(191, 220, 249, 1)", "rgba(113, 176, 239, 1)", "rgba(61, 149, 236, 1)"] .stat-card.stat-card-half.pull-left diff --git a/config/routes.rb b/config/routes.rb index 2106289e3..b207161a6 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -222,6 +222,8 @@ Rails.application.routes.draw do namespace :v2 do post :graphql, to: "graphql#execute" + get 'dossiers/pdf/:id', format: :pdf, to: "dossiers#pdf", as: :dossier_pdf + get 'dossiers/geojson/:id', to: "dossiers#geojson", as: :dossier_geojson end end diff --git a/db/migrate/20201002124154_create_stats.rb b/db/migrate/20201002124154_create_stats.rb new file mode 100644 index 000000000..571de3423 --- /dev/null +++ b/db/migrate/20201002124154_create_stats.rb @@ -0,0 +1,19 @@ +class CreateStats < ActiveRecord::Migration[6.0] + def change + create_table :stats do |t| + t.bigint :dossiers_not_brouillon, default: 0 + t.bigint :dossiers_brouillon, default: 0 + t.bigint :dossiers_en_construction, default: 0 + t.bigint :dossiers_en_instruction, default: 0 + t.bigint :dossiers_termines, default: 0 + t.bigint :dossiers_depose_avant_30_jours, default: 0 + t.bigint :dossiers_deposes_entre_60_et_30_jours, default: 0 + t.bigint :administrations_partenaires, default: 0 + + t.jsonb :dossiers_cumulative, null: false, default: '{}' + t.jsonb :dossiers_in_the_last_4_months, null: false, default: '{}' + + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb index d623d85e6..b201463e5 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2020_09_30_143755) do +ActiveRecord::Schema.define(version: 2020_10_02_124154) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -586,6 +586,21 @@ ActiveRecord::Schema.define(version: 2020_09_30_143755) do t.index ["administrateur_id"], name: "index_services_on_administrateur_id" end + create_table "stats", force: :cascade do |t| + t.bigint "dossiers_not_brouillon", default: 0 + t.bigint "dossiers_brouillon", default: 0 + t.bigint "dossiers_en_construction", default: 0 + t.bigint "dossiers_en_instruction", default: 0 + t.bigint "dossiers_termines", default: 0 + t.bigint "dossiers_depose_avant_30_jours", default: 0 + t.bigint "dossiers_deposes_entre_60_et_30_jours", default: 0 + t.bigint "administrations_partenaires", default: 0 + t.jsonb "dossiers_cumulative", default: "{}", null: false + t.jsonb "dossiers_in_the_last_4_months", default: "{}", null: false + t.datetime "created_at", precision: 6, null: false + t.datetime "updated_at", precision: 6, null: false + end + create_table "task_records", id: false, force: :cascade do |t| t.string "version", null: false end diff --git a/lib/tasks/deployment/20201006123842_setup_first_stats.rake b/lib/tasks/deployment/20201006123842_setup_first_stats.rake new file mode 100644 index 000000000..cfe4c93af --- /dev/null +++ b/lib/tasks/deployment/20201006123842_setup_first_stats.rake @@ -0,0 +1,11 @@ +namespace :after_party do + desc 'Deployment task: setup_first_stats' + task setup_first_stats: :environment do + Stat.update_stats + + # Update task as completed. If you remove the line below, the task will + # run with every deploy (or every time you call after_party:run). + AfterParty::TaskRecord + .create version: AfterParty::TaskRecorder.new(__FILE__).timestamp + end +end diff --git a/spec/controllers/api/v2/dossiers_controller_spec.rb b/spec/controllers/api/v2/dossiers_controller_spec.rb new file mode 100644 index 000000000..a233c8daa --- /dev/null +++ b/spec/controllers/api/v2/dossiers_controller_spec.rb @@ -0,0 +1,38 @@ +describe API::V2::DossiersController do + let(:dossier) { create(:dossier, :accepte, :with_attestation) } + let(:sgid) { dossier.to_sgid(expires_in: 1.hour, for: 'api_v2') } + + describe 'fetch pdf' do + subject { get :pdf, params: { id: sgid } } + + it 'should get' do + expect(subject.status).to eq(200) + expect(subject.body).not_to be_nil + end + + context 'error' do + let(:sgid) { 'yolo' } + + it 'should error' do + expect(subject.status).to eq(401) + end + end + end + + describe 'fetch geojson' do + subject { get :geojson, params: { id: sgid } } + + it 'should get' do + expect(subject.status).to eq(200) + expect(subject.body).not_to be_nil + end + + context 'error' do + let(:sgid) { 'yolo' } + + it 'should error' do + expect(subject.status).to eq(401) + end + end + end +end diff --git a/spec/controllers/api/v2/graphql_controller_spec.rb b/spec/controllers/api/v2/graphql_controller_spec.rb index 02aa269b5..07adc00cb 100644 --- a/spec/controllers/api/v2/graphql_controller_spec.rb +++ b/spec/controllers/api/v2/graphql_controller_spec.rb @@ -436,6 +436,35 @@ describe API::V2::GraphqlController do end end + context "with links" do + let(:dossier) { create(:dossier, :accepte, :with_attestation, procedure: procedure) } + let(:query) do + "{ + dossier(number: #{dossier.id}) { + id + number + pdf { + url + } + geojson { + url + } + attestation { + url + } + } + }" + end + + it "urls should be returned" do + expect(gql_errors).to eq(nil) + + expect(gql_data[:dossier][:pdf][:url]).not_to be_nil + expect(gql_data[:dossier][:geojson][:url]).not_to be_nil + expect(gql_data[:dossier][:attestation][:url]).not_to be_nil + end + end + context "when there are missing data" do before do dossier.etablissement.update!(entreprise_code_effectif_entreprise: nil, entreprise_capital_social: nil,