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 <jgaskins@hey.com>

* Update spec/requests/admin/users/users_export_spec.rb

Co-authored-by: Jamie Gaskins <jgaskins@hey.com>

* Update spec/requests/admin/users/users_export_spec.rb

Co-authored-by: Jamie Gaskins <jgaskins@hey.com>

* fix: export should not error for unregistered users

Co-authored-by: Jamie Gaskins <jgaskins@hey.com>
This commit is contained in:
Ridhwana 2022-04-12 15:24:32 +02:00 committed by GitHub
parent 2ba80fcd13
commit aff29406ac
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 138 additions and 2 deletions

View file

@ -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

View file

@ -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

View file

@ -44,7 +44,7 @@
</div>
<div>
<dt class="color-base-60 fw-normal">Joined on</dt>
<dd><%= user.registered_at.strftime("%d %b, %Y") %></dd>
<dd><%= user.registered_at&.strftime("%d %b, %Y") %></dd>
</div>
</dl>
<div>
@ -83,7 +83,7 @@
<td>
<%= format_last_activity_timestamp(user.last_activity) %>
<p class="fs-xs color-base-60">
<%= user.registered_at.strftime("%d %b, %Y") %>
<%= user.registered_at&.strftime("%d %b, %Y") %>
</p>
</td>
<td>

View file

@ -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 -%>

View file

@ -24,6 +24,9 @@
<div id="filter-users" class="hidden crayons-field flex-row items-center gap-2">
<%= render "admin/users/index/filter_role_field", f: f %>
</div>
<div>
<%= render "admin/users/index/controls/export", f: f %>
</div>
</div>
<% end %>
@ -36,6 +39,9 @@
<div class="crayons-field flex-row items-center gap-2">
<%= render "admin/users/index/filter_role_field", f: f %>
</div>
<div>
<%= render "admin/users/index/controls/export", f: f %>
</div>
<% end %>
<%= paginate @users, theme: "admin", scope: @users, label: "Paginate users" %>
</div>

View file

@ -0,0 +1,5 @@
<a href="<%= export_admin_users_path %>.csv">
<svg width="18" height="20" viewBox="0 0 18 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M13 0L18 5V19.008C17.9997 19.2712 17.895 19.5235 17.7088 19.7095C17.5226 19.8955 17.2702 20 17.007 20H0.993C0.730378 19.9982 0.479017 19.8931 0.293218 19.7075C0.107418 19.5219 0.00209465 19.2706 0 19.008V0.992C0 0.444 0.445 0 0.993 0H13ZM10 10V6H8V10H5L9 14L13 10H10Z" fill="#404040" />
</svg>
</a>

View file

@ -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"

View file

@ -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

View file

@ -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

View file

@ -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