From a10f3cffe4a45ba7bbe9809b4ca54fdf0a490b0b Mon Sep 17 00:00:00 2001 From: Du Peng Date: Fri, 1 Sep 2023 12:43:36 +0200 Subject: [PATCH] (feat) statistics improvements --- CHANGELOG.md | 8 +++ .../templates/admin/statistics/index.html | 10 +--- app/models/concerns/stat_concern.rb | 1 + app/models/stats/project.rb | 2 + .../builders/members_builder_service.rb | 6 +- .../builders/reservations_builder_service.rb | 3 +- .../builders/store_orders_builder_service.rb | 1 + .../statistics/concerns/projects_concern.rb | 14 ++++- app/services/statistics/fetcher_service.rb | 5 +- .../exports/statistics_current.xlsx.axlsx | 3 +- .../exports/statistics_global.xlsx.axlsx | 3 +- config/locales/app.admin.en.yml | 1 + config/locales/app.admin.fr.yml | 1 + config/locales/en.yml | 4 ++ config/locales/fr.yml | 4 ++ db/seeds/statistics.rb | 55 ++++++++++++++----- 16 files changed, 90 insertions(+), 31 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f47d048b..add0e6c5c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,14 @@ - Fix a bug: unable to update status to paid for latest payment schedule item - Fix a bug: unable to generate statistic - Feature: add a filter in members list (admin) to show only "not validated" members +- Concerning statistics: +- removes age and type column from all statistics tabs (only in web, not in xlsx export file) +- index: + - renames user column header for projects tab and projects xlsx export + - adds group name of user for every tab except projects tab + - adds status and project users names for projects tab +- [TODO DEPLOY] `rails db:seed` +- [TODO DEPLOY] `rails fablab:es:build_stats` - [TODO DEPLOY] `rails fablab:maintenance:regenerate_statistics[2014,1]` ## v6.0.13 2023 August 28 diff --git a/app/frontend/templates/admin/statistics/index.html b/app/frontend/templates/admin/statistics/index.html index 98f3d7c17..2a19a017b 100644 --- a/app/frontend/templates/admin/statistics/index.html +++ b/app/frontend/templates/admin/statistics/index.html @@ -256,12 +256,11 @@ {{ 'app.admin.statistics.reservation_date' }} {{ 'app.admin.statistics.date' }} - {{ 'app.admin.statistics.user' }} + {{ 'app.admin.statistics.project_author' }} + {{ 'app.admin.statistics.user' }} {{ 'app.admin.statistics.reservation_context' | translate }} - {{ 'app.admin.statistics.age' }} - {{ 'app.admin.statistics.type' }} {{type.active.label}} {{field.label}} {{ 'app.admin.statistics.revenue' | translate }} @@ -285,11 +284,6 @@ {{ formatReservationContext(datum._source.reservationContextId) }} - - {{datum._source.age}} {{ 'app.admin.statistics.years_old' | translate }} - {{ 'app.admin.statistics.unknown' }} - - {{formatSubtype(datum._source.subType)}} {{datum._source.stat}} diff --git a/app/models/concerns/stat_concern.rb b/app/models/concerns/stat_concern.rb index 2b0549ba4..c5bb52f5e 100644 --- a/app/models/concerns/stat_concern.rb +++ b/app/models/concerns/stat_concern.rb @@ -13,6 +13,7 @@ module StatConcern attribute :gender, String attribute :age, Integer attribute :group, String + attribute :groupName, String # has include Elasticsearch::Persistence::Model index_name 'stats' diff --git a/app/models/stats/project.rb b/app/models/stats/project.rb index d97668a96..0bb779727 100644 --- a/app/models/stats/project.rb +++ b/app/models/stats/project.rb @@ -12,4 +12,6 @@ class Stats::Project attribute :components, Array attribute :machines, Array attribute :users, Integer + attribute :status, String + attribute :projectUserNames, Array end diff --git a/app/services/statistics/builders/members_builder_service.rb b/app/services/statistics/builders/members_builder_service.rb index a7be86cf4..5aa57e672 100644 --- a/app/services/statistics/builders/members_builder_service.rb +++ b/app/services/statistics/builders/members_builder_service.rb @@ -11,7 +11,8 @@ class Statistics::Builders::MembersBuilderService Stats::Account.create({ date: format_date(m[:date]), type: 'member', subType: 'created', - stat: 1 }.merge(user_info_stat(m))) + stat: 1, + groupName: m[:groupName] }.merge(user_info_stat(m))) end # member ca list @@ -19,7 +20,8 @@ class Statistics::Builders::MembersBuilderService Stats::User.create({ date: format_date(m[:date]), type: 'revenue', subType: m[:group], - stat: m[:ca] }.merge(user_info_stat(m))) + stat: m[:ca], + groupName: m[:groupName] }.merge(user_info_stat(m))) end end end diff --git a/app/services/statistics/builders/reservations_builder_service.rb b/app/services/statistics/builders/reservations_builder_service.rb index ec6dba22f..36d09d6d1 100644 --- a/app/services/statistics/builders/reservations_builder_service.rb +++ b/app/services/statistics/builders/reservations_builder_service.rb @@ -19,7 +19,8 @@ class Statistics::Builders::ReservationsBuilderService name: r["#{category}_name".to_sym], reservationId: r[:reservation_id], reservationContextId: r[:reservation_context_id], - coupon: r[:coupon] + coupon: r[:coupon], + groupName: r[:groupName], }.merge(user_info_stat(r))) stat[:stat] = (type == 'booking' ? 1 : r[:nb_hours]) stat["#{category}Id".to_sym] = r["#{category}_id".to_sym] diff --git a/app/services/statistics/builders/store_orders_builder_service.rb b/app/services/statistics/builders/store_orders_builder_service.rb index 81a842d81..3310d0413 100644 --- a/app/services/statistics/builders/store_orders_builder_service.rb +++ b/app/services/statistics/builders/store_orders_builder_service.rb @@ -23,6 +23,7 @@ class Statistics::Builders::StoreOrdersBuilderService orderId: o[:order_id], state: o[:order_state], coupon: o[:coupon], + groupName: o[:groupName], stat: 1 }.merge(user_info_stat(o))) end end diff --git a/app/services/statistics/concerns/projects_concern.rb b/app/services/statistics/concerns/projects_concern.rb index a96f2380c..fd93ecd63 100644 --- a/app/services/statistics/concerns/projects_concern.rb +++ b/app/services/statistics/concerns/projects_concern.rb @@ -31,6 +31,12 @@ module Statistics::Concerns::ProjectsConcern sum end + def get_project_user_names(project) + project.project_users.map do |project_user| + { id: project_user.user.id, name: project_user.user.profile.full_name } + end + end + def project_info(project) { project_id: project.id, @@ -41,7 +47,9 @@ module Statistics::Concerns::ProjectsConcern project_themes: get_project_themes(project), project_components: get_projects_components(project), project_machines: get_projects_machines(project), - project_users: get_project_users(project) + project_users: get_project_users(project), + project_status: project.status&.name, + project_user_names: get_project_user_names(project), } end @@ -53,7 +61,9 @@ module Statistics::Concerns::ProjectsConcern themes: project[:project_themes], components: project[:project_components], machines: project[:project_machines], - users: project[:project_users] + users: project[:project_users], + status: project[:project_status], + projectUserNames: project[:project_user_names], } end end diff --git a/app/services/statistics/fetcher_service.rb b/app/services/statistics/fetcher_service.rb index 58717e9c0..d6b3867ed 100644 --- a/app/services/statistics/fetcher_service.rb +++ b/app/services/statistics/fetcher_service.rb @@ -207,7 +207,7 @@ class Statistics::FetcherService # @yieldparam [Hash] def each_project(options = default_options) Project.where('projects.published_at >= :start_date AND projects.published_at <= :end_date', options) - .eager_load(:licence, :themes, :components, :machines, :project_users, author: [:group]) + .eager_load(:licence, :themes, :components, :machines, :status, project_users: [{ user: :profile }], author: [:group]) .find_each do |p| result = { date: p.created_at.to_date }.merge(user_info(p.author)).merge(project_info(p)) yield result @@ -261,7 +261,8 @@ class Statistics::FetcherService user_id: statistic_profile.user_id, gender: statistic_profile.str_gender, age: statistic_profile.age, - group: statistic_profile.group ? statistic_profile.group.slug : nil + group: statistic_profile.group ? statistic_profile.group.slug : nil, + groupName: statistic_profile.group ? statistic_profile.group.name : nil, } end end diff --git a/app/views/exports/statistics_current.xlsx.axlsx b/app/views/exports/statistics_current.xlsx.axlsx index 97299c7e2..6134d2ad3 100644 --- a/app/views/exports/statistics_current.xlsx.axlsx +++ b/app/views/exports/statistics_current.xlsx.axlsx @@ -20,7 +20,8 @@ wb.add_worksheet(name: ExcelService.name_safe(index.label)) do |sheet| ## data table # heading labels - columns = [t('export.date'), t('export.user'), t('export.email'), t('export.phone'), t('export.gender'), t('export.age'), + user_heading_text = index.es_type_key == "project" ? t('export.project_author') : t('export.user') + columns = [t('export.date'), user_heading_text, t('export.email'), t('export.phone'), t('export.gender'), t('export.age'), t('export.type')] columns.push type.label unless type.simple fields.each do |f| diff --git a/app/views/exports/statistics_global.xlsx.axlsx b/app/views/exports/statistics_global.xlsx.axlsx index a9b47ad10..63790dfdf 100644 --- a/app/views/exports/statistics_global.xlsx.axlsx +++ b/app/views/exports/statistics_global.xlsx.axlsx @@ -12,7 +12,8 @@ indices.each do |index| wb.add_worksheet(name: ExcelService.statistic_type_sheet_name(type, wb)) do |sheet| ## data table # heading labels - columns = [t('export.date'), t('export.user'), t('export.email'), t('export.phone'), t('export.gender'), t('export.age'), + user_heading_text = index.es_type_key == "project" ? t('export.project_author') : t('export.user') + columns = [t('export.date'), user_heading_text, t('export.email'), t('export.phone'), t('export.gender'), t('export.age'), t('export.type')] columns.push type.label unless type.simple index.statistic_fields.each do |f| diff --git a/config/locales/app.admin.en.yml b/config/locales/app.admin.en.yml index 88d645082..f9b683532 100644 --- a/config/locales/app.admin.en.yml +++ b/config/locales/app.admin.en.yml @@ -1538,6 +1538,7 @@ en: click_here: "Click here to create your first one." average_cart: "Average cart:" reservation_context: Reservation context + project_author: Author #statistics graphs stats_graphs: statistics: "Statistics" diff --git a/config/locales/app.admin.fr.yml b/config/locales/app.admin.fr.yml index bf0063c4d..021fed191 100644 --- a/config/locales/app.admin.fr.yml +++ b/config/locales/app.admin.fr.yml @@ -1538,6 +1538,7 @@ fr: click_here: "Cliquez ici pour créer votre première formule." average_cart: "Panier moyen :" reservation_context: Nature de la réservation + project_author: Auteur #statistics graphs stats_graphs: statistics: "Statistiques" diff --git a/config/locales/en.yml b/config/locales/en.yml index f78c2203c..ccb8f1224 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -489,6 +489,9 @@ en: store: "Store" paid-processed: "Paid and/or processed" aborted: "Aborted" + project_status: Status + project_name: Name + project_user_names: Collaborators #statistics exports to the Excel file format export: entries: "Entries" @@ -507,6 +510,7 @@ en: deleted_user: "Deleted user" reservation_context: "Reservation context" coupon: "Coupon" + project_author: Author #initial price's category for events, created to replace the old "reduced amount" property price_category: reduced_fare: "Reduced fare" diff --git a/config/locales/fr.yml b/config/locales/fr.yml index 6722847d0..c6750e205 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -489,6 +489,9 @@ fr: store: "Boutique" paid-processed: "Payée et/ou traitée" aborted: "Interrompue" + project_status: Statut + project_name: Nom + project_user_names: Collaborateurs #statistics exports to the Excel file format export: entries: "Entrées" @@ -507,6 +510,7 @@ fr: deleted_user: "Utilisateur supprimé" reservation_context: "Nature de la réservation" coupon: "Code promo" + project_author: Auteur #initial price's category for events, created to replace the old "reduced amount" property price_category: reduced_fare: "Tarif réduit" diff --git a/db/seeds/statistics.rb b/db/seeds/statistics.rb index c09c2b60b..5b7dd92af 100644 --- a/db/seeds/statistics.rb +++ b/db/seeds/statistics.rb @@ -30,49 +30,76 @@ statistic_index_space = StatisticIndex.find_by(es_type_key: 'space') statistic_index_order = StatisticIndex.find_by(es_type_key: 'order') # statistic_fields -unless StatisticField.find_by(key: 'spaceDates') +unless StatisticField.find_by(key: 'spaceDates', statistic_index_id: statistic_index_space.id) StatisticField.create!({ key: 'spaceDates', label: I18n.t('statistics.space_dates'), statistic_index_id: statistic_index_space.id, data_type: 'list' }) end -unless StatisticField.find_by(key: 'machineDates') +unless StatisticField.find_by(key: 'groupName', statistic_index_id: statistic_index_space.id) + StatisticField.create!({ key: 'groupName', label: I18n.t('statistics.group'), statistic_index_id: statistic_index_space.id, data_type: 'text' }) +end +unless StatisticField.find_by(key: 'machineDates', statistic_index_id: 2) StatisticField.create!({ key: 'machineDates', label: I18n.t('statistics.machine_dates'), statistic_index_id: 2, data_type: 'list' }) end -unless StatisticField.find_by(key: 'trainingId') +unless StatisticField.find_by(key: 'groupName', statistic_index_id: 2) + StatisticField.create!({ key: 'groupName', label: I18n.t('statistics.group'), statistic_index_id: 2, data_type: 'text' }) +end +unless StatisticField.find_by(key: 'trainingId', statistic_index_id: 3) StatisticField.create!({ key: 'trainingId', label: I18n.t('statistics.training_id'), statistic_index_id: 3, data_type: 'index' }) end -unless StatisticField.find_by(key: 'trainingDate') +unless StatisticField.find_by(key: 'trainingDate', statistic_index_id: 3) StatisticField.create!({ key: 'trainingDate', label: I18n.t('statistics.training_date'), statistic_index_id: 3, data_type: 'date' }) end -unless StatisticField.find_by(key: 'eventId') +unless StatisticField.find_by(key: 'groupName', statistic_index_id: 3) + StatisticField.create!({ key: 'groupName', label: I18n.t('statistics.group'), statistic_index_id: 3, data_type: 'text' }) +end +unless StatisticField.find_by(key: 'eventId', statistic_index_id: 4) StatisticField.create!({ key: 'eventId', label: I18n.t('statistics.event_id'), statistic_index_id: 4, data_type: 'index' }) end -unless StatisticField.find_by(key: 'eventDate') +unless StatisticField.find_by(key: 'eventDate', statistic_index_id: 4) StatisticField.create!({ key: 'eventDate', label: I18n.t('statistics.event_date'), statistic_index_id: 4, data_type: 'date' }) end -unless StatisticField.find_by(key: 'themes') +unless StatisticField.find_by(key: 'groupName', statistic_index_id: 4) + StatisticField.create!({ key: 'groupName', label: I18n.t('statistics.group'), statistic_index_id: 4, data_type: 'text' }) +end +unless StatisticField.find_by(key: 'groupName', statistic_index_id: 5) + StatisticField.create!({ key: 'groupName', label: I18n.t('statistics.group'), statistic_index_id: 5, data_type: 'text' }) +end +unless StatisticField.find_by(key: 'themes', statistic_index_id: 6) StatisticField.create!({ key: 'themes', label: I18n.t('statistics.themes'), statistic_index_id: 6, data_type: 'list' }) end -unless StatisticField.find_by(key: 'components') +unless StatisticField.find_by(key: 'components', statistic_index_id: 6) StatisticField.create!({ key: 'components', label: I18n.t('statistics.components'), statistic_index_id: 6, data_type: 'list' }) end -unless StatisticField.find_by(key: 'machines') +unless StatisticField.find_by(key: 'machines', statistic_index_id: 6) StatisticField.create!({ key: 'machines', label: I18n.t('statistics.machines'), statistic_index_id: 6, data_type: 'list' }) end -unless StatisticField.find_by(key: 'name') +unless StatisticField.find_by(key: 'status', statistic_index_id: 6) + StatisticField.create!({ key: 'status', label: I18n.t('statistics.project_status'), statistic_index_id: 6, data_type: 'text' }) +end +unless StatisticField.find_by(key: 'name', statistic_index_id: 6) + StatisticField.create!({ key: 'name', label: I18n.t('statistics.project_name'), statistic_index_id: 6, data_type: 'text' }) +end +unless StatisticField.find_by(key: 'projectUserNames', statistic_index_id: 6) + StatisticField.create!({ key: 'projectUserNames', label: I18n.t('statistics.project_user_names'), statistic_index_id: 6, data_type: 'list' }) +end +unless StatisticField.find_by(key: 'name', statistic_index_id: 4) StatisticField.create!({ key: 'name', label: I18n.t('statistics.event_name'), statistic_index_id: 4, data_type: 'text' }) end -unless StatisticField.find_by(key: 'userId') +unless StatisticField.find_by(key: 'userId', statistic_index_id: 7) StatisticField.create!({ key: 'userId', label: I18n.t('statistics.user_id'), statistic_index_id: 7, data_type: 'index' }) end -unless StatisticField.find_by(key: 'eventTheme') +unless StatisticField.find_by(key: 'eventTheme', statistic_index_id: 4) StatisticField.create!({ key: 'eventTheme', label: I18n.t('statistics.event_theme'), statistic_index_id: 4, data_type: 'text' }) end -unless StatisticField.find_by(key: 'ageRange') +unless StatisticField.find_by(key: 'ageRange', statistic_index_id: 4) StatisticField.create!({ key: 'ageRange', label: I18n.t('statistics.age_range'), statistic_index_id: 4, data_type: 'text' }) end -unless StatisticField.find_by(key: 'groupName') +unless StatisticField.find_by(key: 'groupName', statistic_index_id: 1) StatisticField.create!({ key: 'groupName', label: I18n.t('statistics.group'), statistic_index_id: 1, data_type: 'text' }) end +unless StatisticField.find_by(key: 'groupName', statistic_index_id: statistic_index_order.id) + StatisticField.create!({ key: 'groupName', label: I18n.t('statistics.group'), statistic_index_id: statistic_index_order.id, data_type: 'text' }) +end # statistic_types unless StatisticType.find_by(key: 'booking', statistic_index_id: 2)