-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Adding XSV gem version 1.2.0 * Importing xlsx files * Better UI * Fixing a counting issue * Adding a task to clean the Members table * Adding comments to views * Better display inside settings * Better breadcrumb --------- Co-authored-by: Stephane Paquet <spaquet@up4b.com>
- Loading branch information
Showing
26 changed files
with
484 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
class ImportsController < ApplicationController | ||
before_action :authenticate_user! | ||
before_action :redirect_if_unauthenticated | ||
|
||
def new | ||
end | ||
|
||
def create | ||
current_user.import_file.attach(params[:file]) | ||
ProcessExcelImportJob.perform_async(current_user.id) | ||
redirect_to imports_path, notice: 'File is being processed. Check back later for the results.' | ||
end | ||
|
||
def index | ||
@import_results = current_user.import_results.order(created_at: :desc).limit(5) | ||
end | ||
|
||
def show | ||
@import_result = current_user.import_results.find(params[:id]) | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
module ImportsHelper | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
# == Schema Information | ||
# | ||
# Table name: import_results | ||
# | ||
# id :uuid not null, primary key | ||
# filename :string not null | ||
# message :text | ||
# status :integer default("uploading"), not null | ||
# created_at :datetime not null | ||
# updated_at :datetime not null | ||
# user_id :uuid not null | ||
# | ||
# Indexes | ||
# | ||
# index_import_results_on_status (status) | ||
# index_import_results_on_user_id (user_id) | ||
# | ||
# Foreign Keys | ||
# | ||
# fk_rails_... (user_id => users.id) | ||
# | ||
class ImportResult < ApplicationRecord | ||
belongs_to :user | ||
|
||
validates :filename, presence: true | ||
|
||
enum status: { uploading: 0, processing: 10, success: 20, error: 30 } | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
require "xsv" | ||
|
||
class ExcelImportService | ||
def initialize(user) | ||
@user = user | ||
end | ||
|
||
def process_excel_file(file_path) | ||
# Determine the file extension | ||
file_extension = File.extname(file_path) | ||
|
||
# Process the Excel file using the appropriate method | ||
case file_extension | ||
when ".xlsx" | ||
success, errors, message = process_xlsx_file(file_path) | ||
else | ||
return [false, ["Unsupported file format"]] | ||
end | ||
|
||
[success, errors, message] | ||
end | ||
|
||
private | ||
|
||
def process_xlsx_file(file_path) | ||
errors = [] | ||
processed_records = 0 | ||
error_records = 0 | ||
|
||
sheet = Xsv.open(file_path).sheets[0] # Assuming the data is in the first sheet | ||
|
||
# Iterate through each row in the sheet, starting from the second row (index 1) | ||
sheet.each_with_index do |row, index| | ||
next if index.zero? # Skip header row | ||
|
||
email = row[0] | ||
# role = row[1] | ||
|
||
# Stop processing when an empty row is encountered | ||
break if email.blank? | ||
|
||
# Validate the email and role | ||
if email.blank? || !email.match?(URI::MailTo::EMAIL_REGEXP) | ||
errors << "Row #{index + 1}: Invalid email | #{email}" | ||
error_records += 1 | ||
processed_records += 1 | ||
next | ||
end | ||
|
||
# if role.blank? || !["admin", ""].include?(role.downcase) | ||
# errors << "Row #{index + 1}: Invalid role" | ||
# error_records += 1 | ||
# next | ||
# end | ||
|
||
# Create the user | ||
user = User.new( email: email, | ||
invited_at: Time.current, | ||
invited: true ) | ||
|
||
# TODO: Assign role using rollify. | ||
|
||
# Save the user, and add an error message if saving fails | ||
unless user.save | ||
errors << "Row #{index + 1}: Failed to save user - #{user.errors.full_messages.join(", ")}" | ||
error_records += 1 | ||
processed_records += 1 | ||
next | ||
end | ||
|
||
# Assign the user to the organization | ||
member = Member.new | ||
member.organization_id = @user.organization.id | ||
member.user_id = user.id | ||
|
||
unless member.save | ||
errors << "Row #{index + 1}: Failed to save user to organization - #{member.errors.full_messages.join(", ")}" | ||
error_records += 1 | ||
processed_records += 1 | ||
next | ||
end | ||
|
||
# Send email to the user | ||
user.send_invite! | ||
|
||
# Incrementing the number of processed records | ||
processed_records += 1 | ||
|
||
end | ||
|
||
success = errors.empty? | ||
message = "Processed records: #{processed_records}, records with errors: #{error_records}" | ||
[success, errors, message] | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
class ProcessExcelImportJob | ||
include Sidekiq::Job | ||
|
||
# Prevent retry as the file is always removed at the end of this process. | ||
sidekiq_options retry: false | ||
|
||
def perform(user_id) | ||
user = User.find(user_id) | ||
import_file = user.import_file | ||
|
||
# Get the original filename | ||
original_file_name = import_file.filename.to_s | ||
|
||
# Create a new ImportResult record with the "processing" status | ||
import_result = user.import_results.create( | ||
status: :processing, | ||
message: "", | ||
filename: original_file_name | ||
) | ||
|
||
# Open the file using the Active Storage blob | ||
import_file.open do |temp_file| | ||
# Process the Excel file using the ExcelImportService | ||
excel_import_service = ExcelImportService.new(user) | ||
success, errors, message = excel_import_service.process_excel_file(temp_file.path) | ||
|
||
# Add error messages if any | ||
if !errors.nil? | ||
message << "\n" | ||
message << errors.join("\n") | ||
end | ||
|
||
# Update the import result with the final status and message | ||
import_result.update( | ||
status: success ? :success : :error, | ||
message: message | ||
) | ||
end | ||
|
||
# Cleanup | ||
user.import_file.purge | ||
end | ||
|
||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
<!-- app/views/imports/_import_result.html.erb --> | ||
<tr class="bg-white border-b dark:bg-gray-800 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-600"> | ||
<th scope="row" class="flex items-center px-6 py-4 text-gray-900 whitespace-nowrap dark:text-white"> | ||
<div class="pl-3"> | ||
<div class="text-base font-semibold"><%= import_result.filename %></div> | ||
<div class="font-normal text-gray-500 text-sm">Uploaded: <%= import_result.created_at.strftime("%Y %b %d at %l%P : %M") %></div> | ||
<% if import_result.created_at != import_result.updated_at %> | ||
<div class="font-normal text-gray-500 text-sm">Processed: <%= import_result.updated_at.strftime("%Y %b %d at %l%P : %M") %></div> | ||
<% end %> | ||
</div> | ||
</th> | ||
<td class="px-6 py-4"> | ||
<div class="flex items-center"> | ||
<% if import_result.success? %> | ||
<div class="h-2.5 w-2.5 rounded-full bg-green-500 mr-2"></div> | ||
Success | ||
<% end %> | ||
<% if import_result.error? %> | ||
<div class="h-2.5 w-2.5 rounded-full bg-red-500 mr-2"></div> | ||
Failed | ||
<% end %> | ||
<% if import_result.processing? %> | ||
<div class="h-2.5 w-2.5 rounded-full bg-blue-500 mr-2"></div> | ||
Processing | ||
<% end %> | ||
</div> | ||
</td> | ||
<td class="px-6 py-4"> | ||
<%= import_result.message.split("\n")[0] %> | ||
</td> | ||
<td class="px-6 py-4"> | ||
<%= link_to import_path(import_result.id) do %> | ||
<svg class="w-6 h-6" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"> | ||
<path stroke-linecap="round" stroke-linejoin="round" d="M6.75 12a.75.75 0 11-1.5 0 .75.75 0 011.5 0zM12.75 12a.75.75 0 11-1.5 0 .75.75 0 011.5 0zM18.75 12a.75.75 0 11-1.5 0 .75.75 0 011.5 0z"></path> | ||
</svg> | ||
<% end %> | ||
</td> | ||
</tr> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
<!-- app/views/imports/index.html.erb --> | ||
<%= turbo_frame_tag "settings_main" do %> | ||
<%= render partial: "settings/breadcrumb", locals: { sub1: "Users" } %> | ||
<div class="mb-6"> | ||
<%= link_to new_import_path, class: "text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center inline-flex items-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800" do %> | ||
Upload a file | ||
<% end %> | ||
</div> | ||
<div class="relative overflow-x-auto shadow-md sm:rounded-lg"> | ||
<table class="w-full text-sm text-left text-gray-500 dark:text-gray-400"> | ||
<thead class="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400"> | ||
<tr> | ||
<th scope="col" class="px-6 py-3"> | ||
Filename | ||
</th> | ||
<th scope="col" class="px-6 py-3"> | ||
Status | ||
</th> | ||
<th scope="col" class="px-6 py-3"> | ||
Message | ||
</th> | ||
<th scope="col" class="px-6 py-3"> | ||
</th> | ||
</tr> | ||
</thead> | ||
<tbody> | ||
<%= render partial: "import_result", collection: @import_results %> | ||
</tbody> | ||
</table> | ||
</div> | ||
<% end %> |
Oops, something went wrong.