diff --git a/app/assets/javascripts/controllers/main_nav.js b/app/assets/javascripts/controllers/main_nav.js index 73f2e3806..7b3c40c54 100644 --- a/app/assets/javascripts/controllers/main_nav.js +++ b/app/assets/javascripts/controllers/main_nav.js @@ -121,12 +121,6 @@ Application.Controllers.controller('MainNavController', ['$scope', function ($sc linkIcon: 'file-pdf-o', authorizedRoles: ['admin', 'manager'] }, - { - state: 'app.admin.statistics', - linkText: 'app.public.common.statistics', - linkIcon: 'bar-chart-o', - authorizedRoles: ['admin'] - }, { class: 'menu-spacer', authorizedRoles: ['admin'] @@ -154,11 +148,20 @@ Application.Controllers.controller('MainNavController', ['$scope', function ($sc $scope.adminNavLinks = adminNavLinks; if ($scope.modules.spaces) { - return $scope.adminNavLinks.splice(3, 0, { + $scope.adminNavLinks.splice(3, 0, { state: 'app.public.spaces_list', linkText: 'app.public.common.manage_the_spaces', linkIcon: 'rocket' }); } + + if ($scope.modules.statistics) { + $scope.adminNavLinks.splice($scope.modules.spaces ? 9 : 8, 0, { + state: 'app.admin.statistics', + linkText: 'app.public.common.statistics', + linkIcon: 'bar-chart-o', + authorizedRoles: ['admin'] + }); + } } ]); diff --git a/app/assets/javascripts/router.js.erb b/app/assets/javascripts/router.js.erb index 57b93692a..563920f9f 100644 --- a/app/assets/javascripts/router.js.erb +++ b/app/assets/javascripts/router.js.erb @@ -37,7 +37,7 @@ angular.module('application.router', ['ui.router']) logoFile: ['CustomAsset', function (CustomAsset) { return CustomAsset.get({ name: 'logo-file' }).$promise; }], logoBlackFile: ['CustomAsset', function (CustomAsset) { return CustomAsset.get({ name: 'logo-black-file' }).$promise; }], sharedTranslations: ['Translations', function (Translations) { return Translations.query(['app.shared', 'app.public.common']).$promise; }], - modulesPromise: ['Setting', function (Setting) { return Setting.query({ names: "['spaces_module', 'plans_module', 'invoicing_module', 'wallet_module']" }).$promise; }] + modulesPromise: ['Setting', function (Setting) { return Setting.query({ names: "['spaces_module', 'plans_module', 'invoicing_module', 'wallet_module', 'statistics_module']" }).$promise; }] }, onEnter: ['$rootScope', 'logoFile', 'logoBlackFile', 'modulesPromise', 'CSRF', function ($rootScope, logoFile, logoBlackFile, modulesPromise, CSRF) { // Retrieve Anti-CSRF tokens from cookies @@ -50,6 +50,7 @@ angular.module('application.router', ['ui.router']) plans: (modulesPromise.plans_module === 'true'), invoicing: (modulesPromise.invoicing_module === 'true'), wallet: (modulesPromise.wallet_module === 'true'), + statistics: (modulesPromise.statistics_module === 'true') }; }] }) @@ -993,6 +994,7 @@ angular.module('application.router', ['ui.router']) // statistics .state('app.admin.statistics', { url: '/admin/statistics', + abstract: !Fablab.statisticsModule, views: { 'main@': { templateUrl: '<%= asset_path "admin/statistics/index.html.erb" %>', @@ -1007,6 +1009,7 @@ angular.module('application.router', ['ui.router']) }) .state('app.admin.stats_graphs', { url: '/admin/statistics/evolution', + abstract: !Fablab.statisticsModule, views: { 'main@': { templateUrl: '<%= asset_path "admin/statistics/graphs.html" %>', @@ -1036,7 +1039,7 @@ angular.module('application.router', ['ui.router']) 'booking_cancel_delay', 'main_color', 'secondary_color', 'spaces_module', 'twitter_analytics', \ 'fablab_name', 'name_genre', 'reminder_enable', 'plans_module', 'confirmation_required', \ 'reminder_delay', 'visibility_yearly', 'visibility_others', 'wallet_module', \ - 'display_name_enable', 'machines_sort_by', 'fab_analytics', \ + 'display_name_enable', 'machines_sort_by', 'fab_analytics', 'statistics_module', \ 'link_name', 'home_content', 'home_css', 'phone_required']` }).$promise; }], privacyDraftsPromise: ['Setting', function (Setting) { return Setting.get({ name: 'privacy_draft', history: true }).$promise; }], diff --git a/app/assets/templates/admin/settings/general.html b/app/assets/templates/admin/settings/general.html index 2b9482555..9d5b4980a 100644 --- a/app/assets/templates/admin/settings/general.html +++ b/app/assets/templates/admin/settings/general.html @@ -475,15 +475,25 @@ yes-label="app.shared.buttons.yes" no-label="app.shared.buttons.no"> -
-

{{ 'app.admin.settings.general.wallet' }}

-

- -
+
+

{{ 'app.admin.settings.general.wallet' }}

+

+ +
+
+

{{ 'app.admin.settings.general.statistics' }}

+

+ +
diff --git a/app/models/setting.rb b/app/models/setting.rb index 4496fc4ff..d89391f65 100644 --- a/app/models/setting.rb +++ b/app/models/setting.rb @@ -104,7 +104,8 @@ class Setting < ApplicationRecord stripe_currency invoice_prefix confirmation_required - wallet_module] } + wallet_module + statistics_module] } # WARNING: when adding a new key, you may also want to add it in app/policies/setting_policy.rb#public_whitelist def value @@ -117,6 +118,11 @@ class Setting < ApplicationRecord last_value&.created_at end + def previous_update + previous_value = history_values.order(HistoryValue.arel_table['created_at'].desc).limit(2).last + previous_value&.created_at + end + def value=(val) admin = User.admins.first save && history_values.create(invoicing_profile: admin.invoicing_profile, value: val) diff --git a/app/services/setting_service.rb b/app/services/setting_service.rb index 6a8e1c3d0..fed19d24a 100644 --- a/app/services/setting_service.rb +++ b/app/services/setting_service.rb @@ -16,9 +16,12 @@ class SettingService Stylesheet.home_page&.rebuild! if setting.name == 'home_css' # notify about a change in privacy policy - NotifyPrivacyUpdateWorker.perform_async(id) if setting.name == 'privacy_body' + NotifyPrivacyUpdateWorker.perform_async(setting.id) if setting.name == 'privacy_body' # sync all users on stripe SyncMembersOnStripeWorker.perform_async(setting.history_values.last&.invoicing_profile&.user&.id) if setting.name == 'stripe_secret_key' + + # generate statistics + PeriodStatisticsWorker.perform_async(setting.previous_update) if setting.name == 'statistics_module' && setting.value == 'true' end end diff --git a/app/views/application/index.html.erb b/app/views/application/index.html.erb index b69219f14..0a61cac51 100644 --- a/app/views/application/index.html.erb +++ b/app/views/application/index.html.erb @@ -26,6 +26,7 @@ Fablab.plansModule = ('<%= Setting.get('plans_module') %>' === 'true'); Fablab.spacesModule = ('<%= Setting.get('spaces_module') %>' === 'true'); Fablab.walletModule = ('<%= Setting.get('wallet_module') %>' === 'true'); + Fablab.statisticsModule = ('<%= Setting.get('statistics_module') %>' === 'true'); Fablab.defaultHost = "<%= Rails.application.secrets.default_host %>"; Fablab.trackingId = "<%= Setting.get('tracking_id') %>"; Fablab.superadminId = parseInt("<%= User.superadmin&.id %>", 10); diff --git a/app/workers/period_statistics_worker.rb b/app/workers/period_statistics_worker.rb new file mode 100644 index 000000000..b9cf062d6 --- /dev/null +++ b/app/workers/period_statistics_worker.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +# Asynchronously generate the statistics for the given period +# This worker is triggered when enabling the statistics and with `rails fablab:es:generate_stats` +class PeriodStatisticsWorker + include Sidekiq::Worker + + # @param period {String|Integer} date string or number of days until current date + def perform(period) + days = date_to_days(period) + puts "\n==> generating statistics for the last #{days} days <==\n" + if days.zero? + StatisticService.new.generate_statistic(start_date: DateTime.current.beginning_of_day, end_date: DateTime.current.end_of_day) + else + days.times.each do |i| + StatisticService.new.generate_statistic(start_date: i.day.ago.beginning_of_day, end_date: i.day.ago.end_of_day) + end + end + end + + def date_to_days(value) + date = Date.parse(value.to_s) + (DateTime.current.to_date - date).to_i + rescue ArgumentError + value.to_i + end +end diff --git a/app/workers/statistic_worker.rb b/app/workers/statistic_worker.rb index f075aa6c1..dd9da8264 100644 --- a/app/workers/statistic_worker.rb +++ b/app/workers/statistic_worker.rb @@ -1,7 +1,13 @@ +# frozen_string_literal: true + +# Asynchronously generate the statistics for the last passed day. +# This worker is triggered every nights, see schedule.yml class StatisticWorker include Sidekiq::Worker def perform + return unless Setting.get('statistics_module') + StatisticService.new.generate_statistic end end diff --git a/config/locales/app.admin.en.yml b/config/locales/app.admin.en.yml index 92682881a..2e406b972 100644 --- a/config/locales/app.admin.en.yml +++ b/config/locales/app.admin.en.yml @@ -1111,6 +1111,7 @@ en: confirmation_required_info: "Optionally, you can force the users to confirm their email address before being able to access Fab-manager." confirmation_is_required: "Confirmation required" wallet_module: "wallet module" + statistics_module: "statistics module" general: general: "General" title: "Title" @@ -1147,6 +1148,9 @@ en: wallet: "Wallet" wallet_info_html: "

The virtual wallet allows you to allocate a sum of money to users. Then, can spend this money as they wish, in Fab-manager.

Members cannot credit their wallet themselves, it's a privilege of managers and administrators.

" enable_wallet: "Enable wallet" + statistics: "Statistics" + statistics_info_html: "

Enable or disable the statistics module.

If enabled, every nights, the data of the day just passed will be consolidated in the database of a powerful analysis engine. Then, every administrators will be able to browse statistical charts and tables in the corresponding section.

" + enable_statistics: "Enable statistics" privacy: title: "Privacy" privacy_policy: "Privacy policy" diff --git a/config/locales/app.admin.fr.yml b/config/locales/app.admin.fr.yml index 22718c3b0..41b24b191 100644 --- a/config/locales/app.admin.fr.yml +++ b/config/locales/app.admin.fr.yml @@ -1111,6 +1111,7 @@ fr: confirmation_required_info: "De manière optionnelle, vous pouvez forcer les utilisateurs à confirmer leur adresse électronique avant de pouvoir accéder à Fab-manager." confirmation_is_required: "Confirmation requise" wallet_module: "module porte-monnaie" + statistics_module: "module de statistiques" general: general: "Général" title: "Titre" @@ -1147,6 +1148,9 @@ fr: wallet: "Porte-monnaie" wallet_info_html: "

Le porte-monnaie virtuel vous permet d'allouer une certaine somme d'argent aux utilisateurs. Ils peuvent ensuite dépenser cet argent comment bon leur semble, dans Fab-manager.

Les membres ne peuvent pas créditer leur porte-monnaie eux-même, c'est un privilège des gestionnaires et des administrateurs.

" enable_wallet: "Activer le porte-monnaie" + statistics: "Statistiques" + statistics_info_html: "

Activer ou désactiver le module de statistiques.

Si activé, chaque nuit, les données de la journée qui vient de s'écouler seront consolidées dans la base de données d'un puissant moteur d'analyse. Ensuite, chaque administrateur pourra parcourir les tableaux et graphiques statistiques dans la section correspondante.

" + enable_statistics: "Activer les statistiques" privacy: title: "Confidentialité" privacy_policy: "Politique de confidentialité" diff --git a/db/seeds.rb b/db/seeds.rb index 1327978f3..32a798a6c 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -888,6 +888,8 @@ Setting.set('confirmation_required', false) unless Setting.find_by(name: 'confir Setting.set('wallet_module', true) unless Setting.find_by(name: 'wallet_module').try(:value) +Setting.set('statistics_module', true) unless Setting.find_by(name: 'statistics_module').try(:value) + if StatisticCustomAggregation.count.zero? # available reservations hours for machines machine_hours = StatisticType.find_by(key: 'hour', statistic_index_id: 2) diff --git a/lib/tasks/fablab/es.rake b/lib/tasks/fablab/es.rake index 3b8285d25..0b27fdf0a 100644 --- a/lib/tasks/fablab/es.rake +++ b/lib/tasks/fablab/es.rake @@ -168,22 +168,13 @@ namespace :fablab do task :generate_stats, [:period] => :environment do |_task, args| raise 'FATAL ERROR: You must pass a number of days (=> past period) OR a date to generate statistics' unless args.period - days = date_to_days(args.period) - puts "\n==> generating statistics for the last #{days} days <==\n" - if days.zero? - StatisticService.new.generate_statistic(start_date: DateTime.current.beginning_of_day, end_date: DateTime.current.end_of_day) - else - days.times.each do |i| - StatisticService.new.generate_statistic(start_date: i.day.ago.beginning_of_day, end_date: i.day.ago.end_of_day) - end + unless Setting.get('statistics_module') + print 'Statistics are disabled. Do you still want to generate? (y/N) ' + confirm = STDIN.gets.chomp + raise 'Interrupted by user' unless confirm == 'y' end - end - def date_to_days(value) - date = Date.parse(value.to_s) - (DateTime.current.to_date - date).to_i - rescue ArgumentError - value.to_i + PeriodStatisticsWorker.perform(args.period) end end end diff --git a/lib/tasks/fablab/setup.rake b/lib/tasks/fablab/setup.rake index d41f5cd64..1f2b8c8c6 100644 --- a/lib/tasks/fablab/setup.rake +++ b/lib/tasks/fablab/setup.rake @@ -132,7 +132,8 @@ namespace :fablab do %w[_ STRIPE_CURRENCY stripe_currency], %w[_ INVOICE_PREFIX invoice_prefix FabManager_invoice], %w[_ USER_CONFIRMATION_NEEDED_TO_SIGN_IN confirmation_required false], - %w[! FABLAB_WITHOUT_WALLET wallet_module false] + %w[! FABLAB_WITHOUT_WALLET wallet_module false], + %w[! FABLAB_WITHOUT_STATISTICS statistics_module false] ] mapping.each do |m| diff --git a/test/fixtures/history_values.yml b/test/fixtures/history_values.yml index 3714b238c..83125dc88 100644 --- a/test/fixtures/history_values.yml +++ b/test/fixtures/history_values.yml @@ -685,3 +685,11 @@ history_value_72: value: true created_at: 2020-06-15 10:04:06.216541000 Z updated_at: 2020-06-15 10:04:06.216541000 Z + +history_value_73: + id: 73 + setting_id: 73 + invoicing_profile_id: 1 + value: true + created_at: 2020-06-17 10:48:19.002417000 Z + updated_at: 2020-06-17 10:48:19.002417000 Z diff --git a/test/fixtures/settings.yml b/test/fixtures/settings.yml index d7d04bfe8..e27f6fc9f 100644 --- a/test/fixtures/settings.yml +++ b/test/fixtures/settings.yml @@ -425,3 +425,9 @@ setting_72: created_at: 2020-06-15 10:04:06.216541000 Z updated_at: 2020-06-15 10:04:06.216541000 Z +setting_73: + id: 73 + name: statistics_module + created_at: 2020-06-17 10:48:19.002417000 Z + updated_at: 2020-06-17 10:48:19.002417000 Z +