From aff29406ac4e9fbde8209bc778934a3ee7b68a6c Mon Sep 17 00:00:00 2001 From: Ridhwana Date: Tue, 12 Apr 2022 15:24:32 +0200 Subject: [PATCH] First version of exporting the Users to a CSV (#17217) * feat: add the export route * feat: base export csv * refactor: move the svg into its own file * feat: add the export partial * feat: export teh correct fields etc. for the csv * user status helper * feat: ensure that we format the time * feat: add a spec for the CSV * remove space * remove puts * chore: remove blank space * feat: update traits * Update app/views/admin/users/export.csv.erb Co-authored-by: Jamie Gaskins * Update spec/requests/admin/users/users_export_spec.rb Co-authored-by: Jamie Gaskins * Update spec/requests/admin/users/users_export_spec.rb Co-authored-by: Jamie Gaskins * fix: export should not error for unregistered users Co-authored-by: Jamie Gaskins --- app/controllers/admin/users_controller.rb | 21 ++++++++++ app/helpers/admin/users_helper.rb | 14 +++++++ app/views/admin/users/_member_index.html.erb | 4 +- app/views/admin/users/export.csv.erb | 6 +++ .../admin/users/index/_controls.html.erb | 6 +++ .../users/index/controls/_export.html.erb | 5 +++ config/routes/admin.rb | 3 ++ spec/factories/users.rb | 8 ++++ spec/helpers/admin/users_helper_spec.rb | 32 +++++++++++++++ .../requests/admin/users/users_export_spec.rb | 41 +++++++++++++++++++ 10 files changed, 138 insertions(+), 2 deletions(-) create mode 100644 app/views/admin/users/export.csv.erb create mode 100644 app/views/admin/users/index/controls/_export.html.erb create mode 100644 spec/requests/admin/users/users_export_spec.rb diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb index fb26e71e7..55ad74172 100644 --- a/app/controllers/admin/users_controller.rb +++ b/app/controllers/admin/users_controller.rb @@ -16,6 +16,15 @@ module Admin email_body ].freeze + ATTIBUTES_FOR_CSV = %i[ + id name username email registered_at + ].freeze + + ATTRIBUTES_FOR_LAST_ACTIVITY = %i[ + registered last_comment_at last_article_at latest_article_updated_at last_reacted_at profile_updated_at + last_moderation_notification last_notification_activity + ].freeze + after_action only: %i[update user_status banish full_delete unpublish_all_articles merge] do Audit::Logger.log(:moderator, current_user, params.dup) end @@ -69,6 +78,18 @@ module Admin redirect_to admin_user_path(params[:id]) end + def export + @users = User.select(ATTIBUTES_FOR_CSV + ATTRIBUTES_FOR_LAST_ACTIVITY).includes(:organizations) + + respond_to do |format| + format.csv do + response.headers["Content-Type"] = "text/csv" + response.headers["Content-Disposition"] = "attachment; filename=users.csv" + render template: "admin/users/export" + end + end + end + def user_status @user = User.find(params[:id]) begin diff --git a/app/helpers/admin/users_helper.rb b/app/helpers/admin/users_helper.rb index b8a58d1f6..46580ac0e 100644 --- a/app/helpers/admin/users_helper.rb +++ b/app/helpers/admin/users_helper.rb @@ -31,5 +31,19 @@ module Admin "Resource Admin: #{user.roles.pluck(:resource_type).compact.join(', ')}" end end + + def user_status(user) + if user.suspended? + "Suspended" + elsif user.warned? + "Warned" + elsif user.comment_suspended? + "Comment Suspended" + elsif user.trusted? + "Trusted" + else + "Good Standing" + end + end end end diff --git a/app/views/admin/users/_member_index.html.erb b/app/views/admin/users/_member_index.html.erb index d5ecf53ab..ded819095 100644 --- a/app/views/admin/users/_member_index.html.erb +++ b/app/views/admin/users/_member_index.html.erb @@ -44,7 +44,7 @@
Joined on
-
<%= user.registered_at.strftime("%d %b, %Y") %>
+
<%= user.registered_at&.strftime("%d %b, %Y") %>
@@ -83,7 +83,7 @@ <%= format_last_activity_timestamp(user.last_activity) %>

- <%= user.registered_at.strftime("%d %b, %Y") %> + <%= user.registered_at&.strftime("%d %b, %Y") %>

diff --git a/app/views/admin/users/export.csv.erb b/app/views/admin/users/export.csv.erb new file mode 100644 index 000000000..fa3eab4f5 --- /dev/null +++ b/app/views/admin/users/export.csv.erb @@ -0,0 +1,6 @@ +<%- headers = ["Name", "Username", "Email address", "Status", "Joining date", "Last activity", "Organizations"] -%> +<%= CSV.generate_line headers -%> +<%- @users.each do |user| -%> + <%- row = [user.name, user.username, user.email, user_status(user), user.registered_at&.strftime("%d %b, %Y"), user.last_activity&.strftime("%d %b, %Y"), user.organizations.pluck(:name)] -%> + <%= CSV.generate_line(row).html_safe -%> +<%- end -%> \ No newline at end of file diff --git a/app/views/admin/users/index/_controls.html.erb b/app/views/admin/users/index/_controls.html.erb index 3c4f96b4e..4347d30fa 100644 --- a/app/views/admin/users/index/_controls.html.erb +++ b/app/views/admin/users/index/_controls.html.erb @@ -24,6 +24,9 @@ +
+ <%= render "admin/users/index/controls/export", f: f %> +
<% end %> @@ -36,6 +39,9 @@
<%= render "admin/users/index/filter_role_field", f: f %>
+
+ <%= render "admin/users/index/controls/export", f: f %> +
<% end %> <%= paginate @users, theme: "admin", scope: @users, label: "Paginate users" %> diff --git a/app/views/admin/users/index/controls/_export.html.erb b/app/views/admin/users/index/controls/_export.html.erb new file mode 100644 index 000000000..bea5d0636 --- /dev/null +++ b/app/views/admin/users/index/controls/_export.html.erb @@ -0,0 +1,5 @@ + + + + + diff --git a/config/routes/admin.rb b/config/routes/admin.rb index 5cfc56fdc..f646a7595 100644 --- a/config/routes/admin.rb +++ b/config/routes/admin.rb @@ -45,6 +45,9 @@ namespace :admin do resources :users, only: %i[index show update destroy] do resources :email_messages, only: :show + collection do + get "export" + end member do post "banish" diff --git a/spec/factories/users.rb b/spec/factories/users.rb index f8489868b..7880fe09d 100644 --- a/spec/factories/users.rb +++ b/spec/factories/users.rb @@ -110,6 +110,14 @@ FactoryBot.define do after(:build) { |user| user.add_role(:suspended) } end + trait :warned do + after(:build) { |user| user.add_role(:warned) } + end + + trait :comment_suspended do + after(:build) { |user| user.add_role(:comment_suspended) } + end + trait :invited do after(:build) do |user| user.registered = false diff --git a/spec/helpers/admin/users_helper_spec.rb b/spec/helpers/admin/users_helper_spec.rb index 77452d0b4..bcc30b52b 100644 --- a/spec/helpers/admin/users_helper_spec.rb +++ b/spec/helpers/admin/users_helper_spec.rb @@ -86,4 +86,36 @@ describe Admin::UsersHelper do expect(role).to be_nil end end + + describe "#user_status" do + it "renders the proper status for a user that is suspended" do + suspended_user = create(:user, :suspended) + status = helper.user_status(suspended_user) + expect(status).to eq "Suspended" + end + + it "renders the proper status for a user that is warned" do + warned_user = create(:user, :warned) + status = helper.user_status(warned_user) + expect(status).to eq "Warned" + end + + it "renders the proper status for a user that is comment suspended" do + comment_suspended_user = create(:user, :comment_suspended) + status = helper.user_status(comment_suspended_user) + expect(status).to eq "Comment Suspended" + end + + it "renders the proper status for a user that is trusted" do + trusted_user = create(:user, :trusted) + status = helper.user_status(trusted_user) + expect(status).to eq "Trusted" + end + + it "renders the proper status for a user that is good standing" do + good_standing_user = create(:user) + status = helper.user_status(good_standing_user) + expect(status).to eq "Good Standing" + end + end end diff --git a/spec/requests/admin/users/users_export_spec.rb b/spec/requests/admin/users/users_export_spec.rb new file mode 100644 index 000000000..7972e1fe3 --- /dev/null +++ b/spec/requests/admin/users/users_export_spec.rb @@ -0,0 +1,41 @@ +require "rails_helper" + +RSpec.describe "/admin/users/export", type: :request do + let(:admin) do + create(:user, :super_admin, + name: "Admin1", username: "admin1", email: "admin1@gmail.com", + registered_at: "2020-05-06T13:09:47+0000") + end + let!(:user) do + create(:user, :org_member, + name: "John Doe", username: "john_doe", email: "john_doe@gmail.com", + registered_at: "2020-06-08T13:09:47+0000") + end + + before do + sign_in(admin) + get "#{export_admin_users_path}.csv" + end + + it "renders successfully" do + expect(response).to have_http_status :ok + end + + it "renders the headers" do + expect(response.body).to include("Name,Username,Email address,Status,Joining date,Last activity,Organizations") + end + + it "shows the correct number of total rows" do + # This takes into account empty lines after each row + expect(response.body.lines.count).to eq(3) + end + + it "shows the correct fields", :aggregate_failures do + expect(response.body).to include('Admin1,admin1,admin1@gmail.com,Good Standing,"06 May, 2020","06 May, 2020",[]') + # rubocop:disable Style/PercentLiteralDelimiters, Layout/LineLength + expect(response.body).to include( + %{John Doe,john_doe,john_doe@gmail.com,Good Standing,"08 Jun, 2020","08 Jun, 2020","[""#{user.organizations.first.name}""]"}, + ) + # rubocop:enable Style/PercentLiteralDelimiters, Layout/LineLength + end +end