From 84e0cbb28d5f421f0744224d8a2342a044075c38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A0xim=20Colls?= Date: Mon, 11 Nov 2024 08:53:32 +0100 Subject: [PATCH] Add verification data export page in stats module (#552) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add verification data export page in stats module * Add filtering by current organization * Remove byebug * Fix export fields * Translate into ca and es * Add icon to export data menu item * Add user hash column in authorization serializer * Fix scope name serialization key * Add documentation to Readme --------- Co-authored-by: Francisco Bolívar --- config/routes.rb | 1 + decidim-stats/README.md | 7 +++ .../stats/authorization_exports_controller.rb | 39 +++++++++++++++ .../stats/authorization_exports_form.rb | 17 +++++++ .../stats/authorization_exports_job.rb | 46 ++++++++++++++++++ .../decidim/stats/authorization_serializer.rb | 47 +++++++++++++++++++ .../authorization_exports/index.html.erb | 39 +++++++++++++++ decidim-stats/config/locales/ca.yml | 10 ++++ decidim-stats/config/locales/en.yml | 10 ++++ decidim-stats/config/locales/es.yml | 10 ++++ decidim-stats/lib/decidim/stats/engine.rb | 14 ++++++ 11 files changed, 240 insertions(+) create mode 100644 decidim-stats/app/controllers/decidim/stats/authorization_exports_controller.rb create mode 100644 decidim-stats/app/form/decidim/stats/authorization_exports_form.rb create mode 100644 decidim-stats/app/jobs/decidim/stats/authorization_exports_job.rb create mode 100644 decidim-stats/app/serializers/decidim/stats/authorization_serializer.rb create mode 100644 decidim-stats/app/views/decidim/stats/authorization_exports/index.html.erb create mode 100644 decidim-stats/config/locales/ca.yml create mode 100644 decidim-stats/config/locales/en.yml create mode 100644 decidim-stats/config/locales/es.yml diff --git a/config/routes.rb b/config/routes.rb index 7427c3e8c1..ba864d5d27 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -51,6 +51,7 @@ end mount Decidim::Core::Engine => "/" + mount Decidim::Stats::Engine, at: "/stats", as: "decidim_stats" mount Decidim::EphemeralParticipation::Engine, at: "/", as: "decidim_ephemeral_participation" mount LetterOpenerWeb::Engine, at: "/letter_opener" if Rails.env.development? authenticate :user, ->(u) { u.admin? } do diff --git a/decidim-stats/README.md b/decidim-stats/README.md index 76bb7af3fc..225edaeef1 100644 --- a/decidim-stats/README.md +++ b/decidim-stats/README.md @@ -38,6 +38,13 @@ participatory_space_type,participatory_space_id,component_id,action,metric_type, participatory_processes,2,4,comment,age_group,20-24,6 ``` +## Authorization data export page + +This module adds a new admin page, “Export verification data” accessible under the Admin > Participants menu as “Export Data.” + +On this page, admins can fill out a form to specify a Start Date, End Date, and Verification Method. Using these parameters, the form allows the export of census data gathered during the user verification process. Internally, it exports selected metadata fields from the Authorization model. + + ## Development guide The module has different pieces: diff --git a/decidim-stats/app/controllers/decidim/stats/authorization_exports_controller.rb b/decidim-stats/app/controllers/decidim/stats/authorization_exports_controller.rb new file mode 100644 index 0000000000..49d05ebda4 --- /dev/null +++ b/decidim-stats/app/controllers/decidim/stats/authorization_exports_controller.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +module Decidim + module Stats + class AuthorizationExportsController < Decidim::Admin::ApplicationController + layout "decidim/admin/users" + + def index + enforce_permission_to :index, :authorization_workflow + + @workflows = Decidim::Verifications.workflows.select do |manifest| + current_organization.available_authorizations.include?(manifest.name.to_s) + end + + @form = form(AuthorizationExportsForm).instance + end + + def create + AuthorizationExportsJob.perform_later( + current_user, + current_organization, + name: authorization_params[:authorization_handler_name], + start_date: authorization_params[:start_date], + end_date: authorization_params[:end_date] + ) + + flash[:notice] = t("decidim.admin.exports.notice") + + redirect_to authorization_exports_path + end + + private + + def authorization_params + params.require(:authorization_exports).permit(:authorization_handler_name, :start_date, :end_date) + end + end + end +end diff --git a/decidim-stats/app/form/decidim/stats/authorization_exports_form.rb b/decidim-stats/app/form/decidim/stats/authorization_exports_form.rb new file mode 100644 index 0000000000..22aa5ead5e --- /dev/null +++ b/decidim-stats/app/form/decidim/stats/authorization_exports_form.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +module Decidim + module Stats + # A form to validate the date range for authorization exports + class AuthorizationExportsForm < Form + include TranslatableAttributes + + attribute :start_date, Decidim::Attributes::LocalizedDate + attribute :end_date, Decidim::Attributes::LocalizedDate + attribute :authorization_handler_name, String + + validates :start_date, presence: true + validates :end_date, presence: true + end + end +end diff --git a/decidim-stats/app/jobs/decidim/stats/authorization_exports_job.rb b/decidim-stats/app/jobs/decidim/stats/authorization_exports_job.rb new file mode 100644 index 0000000000..996e1dab23 --- /dev/null +++ b/decidim-stats/app/jobs/decidim/stats/authorization_exports_job.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +module Decidim + module Stats + class AuthorizationExportsJob < ApplicationJob + queue_as :default + + def perform(user, organization, filters) + ExportMailer.export( + user, + export_file_name, + export_data(organization, filters) + ).deliver_now + end + + def export_data(organization, filters) + Decidim::Exporters::CSV.new( + collection(organization, filters), + serializer + ).export + end + + def export_file_name + "authorizations_export" + end + + def collection(organization, filters) + Decidim::Authorization.joins(:user) + .where( + granted_at: filters[:start_date]..filters[:end_date], + name: filters[:name] + ) + Decidim::Authorization.joins(:user) + .where(decidim_users: { decidim_organization_id: organization.id }) + .where( + granted_at: filters[:start_date]..filters[:end_date], + name: filters[:name] + ) + end + + def serializer + Decidim::Stats::AuthorizationSerializer + end + end + end +end diff --git a/decidim-stats/app/serializers/decidim/stats/authorization_serializer.rb b/decidim-stats/app/serializers/decidim/stats/authorization_serializer.rb new file mode 100644 index 0000000000..62121834ab --- /dev/null +++ b/decidim-stats/app/serializers/decidim/stats/authorization_serializer.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +module Decidim + module Stats + # This class serializes a Authorization so can be exported to CSV + class AuthorizationSerializer < Decidim::Exporters::Serializer + attr_reader :authorization + + def initialize(authorization) + @authorization = authorization + end + + def serialize + { + user_hash:, + date_of_birth: metadata["date_of_birth"], + postal_code: metadata["postal_code"], + scope_name: metadata["scope"], + scope_id: metadata["scope_id"], + scope_code: metadata["scope_code"], + gender: metadata_extras["gender"], + granted_at: authorization.granted_at + } + end + + private + + def metadata + authorization.metadata || {} + end + + def metadata_extras + metadata["extras"] || {} + end + + def user_hash + return "" unless user + + Digest::SHA256.hexdigest(user.id.to_s).last(8) + end + + def user + authorization.user + end + end + end +end diff --git a/decidim-stats/app/views/decidim/stats/authorization_exports/index.html.erb b/decidim-stats/app/views/decidim/stats/authorization_exports/index.html.erb new file mode 100644 index 0000000000..17ed76d9a0 --- /dev/null +++ b/decidim-stats/app/views/decidim/stats/authorization_exports/index.html.erb @@ -0,0 +1,39 @@ +<% add_decidim_page_title(t("title", scope: "decidim.stats.authorization_exports")) %> + +
+

+ <%= t("title", scope: "decidim.stats.authorization_exports") %> +

+
+ +
+
+
+
+
+
+ <%= decidim_form_for @form, url: authorization_exports_path, multipart: true, html: { class: "form form-defaults" } do |form| %> +
+ <%= form.date_field :start_date, label: t("start_date", scope: "decidim.stats.authorization_exports") %> +
+ +
+ <%= form.date_field :end_date, label: t("end_date", scope: "decidim.stats.authorization_exports") %> +
+ +
+ <%= form.select :authorization_handler_name, @workflows.map { |workflow| [workflow.fullname, workflow.name] }, label: t("authorization_handler_name", scope: "decidim.stats.authorization_exports") %> +
+ +
+
+ <%= submit_tag t("submit", scope: "decidim.stats.authorization_exports"), class: "button button__sm button__secondary" %> +
+
+ <% end %> +
+
+
+
+
+
diff --git a/decidim-stats/config/locales/ca.yml b/decidim-stats/config/locales/ca.yml new file mode 100644 index 0000000000..193d64cd8d --- /dev/null +++ b/decidim-stats/config/locales/ca.yml @@ -0,0 +1,10 @@ +ca: + decidim: + stats: + authorization_exports: + title: Exportar dades de verificació + authorization_handler_name: Mètode de verificació + end_date: Data fi + start_date: Data inici + submit: Exportar dades + menu: Exportar dades diff --git a/decidim-stats/config/locales/en.yml b/decidim-stats/config/locales/en.yml new file mode 100644 index 0000000000..904335942c --- /dev/null +++ b/decidim-stats/config/locales/en.yml @@ -0,0 +1,10 @@ +en: + decidim: + stats: + authorization_exports: + title: Export verification data + authorization_handler_name: Verification method + end_date: End date + start_date: Start date + submit: Export verification data + menu: Export data diff --git a/decidim-stats/config/locales/es.yml b/decidim-stats/config/locales/es.yml new file mode 100644 index 0000000000..6381d8ffde --- /dev/null +++ b/decidim-stats/config/locales/es.yml @@ -0,0 +1,10 @@ +es: + decidim: + stats: + authorization_exports: + title: Exportar datos de verificación + authorization_handler_name: Método de verificación + end_date: Fecha fin + start_date: Fecha inicio + submit: Exportar datos + menu: Exportar datos diff --git a/decidim-stats/lib/decidim/stats/engine.rb b/decidim-stats/lib/decidim/stats/engine.rb index ddd3554c74..282cd70c25 100644 --- a/decidim-stats/lib/decidim/stats/engine.rb +++ b/decidim-stats/lib/decidim/stats/engine.rb @@ -4,6 +4,20 @@ module Decidim module Stats class Engine < ::Rails::Engine isolate_namespace Decidim::Stats + + routes do + resources :authorization_exports, only: [:index, :create] + end + + initializer "decidim_stats.admin_menus" do + Decidim.menu :admin_user_menu do |menu| + menu.add_item :authorization_exports, + I18n.t("authorization_exports.menu", scope: "decidim.stats"), + decidim_stats.authorization_exports_path, + active: is_active_link?(decidim_stats.authorization_exports_path), + icon_name: "download-line" + end + end end end end