1
0
mirror of https://github.com/LaCasemate/fab-manager.git synced 2025-01-29 18:52:22 +01:00

import new users from CSV and view results in app

TODO:
 - update users though CSV
This commit is contained in:
Sylvain 2019-09-26 17:05:51 +02:00
parent 4deaf1f75a
commit ff5de97c92
11 changed files with 218 additions and 45 deletions

View File

@ -635,7 +635,7 @@ Application.Controllers.controller('ImportMembersController', ['$scope', '$state
if (content.id) {
$state.go('app.admin.members_import_result', { id: content.id });
} else {
growl.error(content);
growl.error(JSON.stringify(content));
}
}
@ -647,17 +647,40 @@ Application.Controllers.controller('ImportMembersController', ['$scope', '$state
/**
* Controller used in the member's import results page (admin view)
*/
Application.Controllers.controller('ImportMembersResultController', ['$scope', '$state', 'importItem',
function ($scope, $state, importItem) {
Application.Controllers.controller('ImportMembersResultController', ['$scope', '$state', 'Import', 'importItem',
function ($scope, $state, Import, importItem) {
/* PUBLIC SCOPE */
// Current import as saved in database
$scope.import = importItem;
// Current import results
$scope.results = null;
/**
* Changes the admin's view to the members import page
*/
$scope.cancel = function () { $state.go('app.admin.members_import'); };
/* PRIVATE SCOPE */
/**
* Kind of constructor: these actions will be realized first when the controller is loaded
*/
const initialize = function () {
$scope.results = JSON.parse($scope.import.results);
if (!$scope.results) {
setTimeout(function() {
Import.get({ id: $scope.import.id }, function(data) {
$scope.import = data;
initialize();
});
}, 5000);
}
};
// !!! MUST BE CALLED AT THE END of the controller
initialize();
}
]);

View File

@ -20,9 +20,39 @@
<div class="row no-gutter">
<div class="col-sm-12 col-md-12 b-r nopadding">
<div class="col-sm-12 col-md-12">
<span>{{import}}</span>
<h2 class="m-l-lg">{{ 'members_import_result.import_details' | translate:{DATE:(import.created_at | amDateFormat:'L'), USER:import.user.full_name, ID:import.id} }}</h2>
<h3 class="m-l-lg" ng-hide="results"><i class="fa fa-spinner fa-pulse"></i> <span translate>{{ 'members_import_result.pending' }}</span></h3>
<div ng-show="results">
<h3 class="m-l-lg" translate>{{ 'members_import_result.results' }}</h3>
<div class="row p-h-lg" ng-repeat="resultRow in results track by $index">
<div class="scroll-x">
<table class="table table-bordered font-thin text-xs m-t-lg" ng-if="resultRow.row">
<tr>
<th ng-repeat="(key, value) in resultRow.row">{{key}}</th>
</tr>
<tr class="text-nowrap">
<td ng-repeat="(key, value) in resultRow.row">{{value}}</td>
</tr>
</table>
</div>
<div ng-if="resultRow.status">
<i class="fa fa-arrow-right m-l-lg m-r"></i>
<span class="m-r-md">{{ 'members_import_result.status_' + resultRow.status | translate:{ID:resultRow.user} }}</span>
<span ng-show="resultRow.result"><i class="fa fa-check-square-o green v-middle text-lg"></i></span>
<span ng-hide="resultRow.result" class="fa-stack red v-middle">
<i class="fa fa-square-o fa-stack-2x"></i>
<i class="fa fa-times fa-stack-1x"></i>
</span>
</div>
<div class="m-l-lg red" ng-if="!resultRow.row && !resultRow.status">
<span class="m-r" translate>{{ 'members_import_result.error_details' }}</span>{{resultRow}}
</div>
</div>
</div>
</div>
</div>

View File

@ -10,10 +10,14 @@ class Import < ActiveRecord::Base
belongs_to :user
validates :attachment, file_size: { maximum: Rails.application.secrets.max_import_size&.to_i || 5.megabytes.to_i }
validates :attachment, file_mime_type: { content_type: ['text/csv'] }
validates :attachment, file_mime_type: { content_type: %w[text/csv text/comma-separated-values application/vnd.ms-excel] }
after_commit :proceed_import, on: [:create]
def results_hash
YAML.safe_load(results, [Symbol]) if results
end
private
def proceed_import

View File

@ -5,40 +5,75 @@ class Members::ImportService
class << self
def import(import)
require 'csv'
log = []
CSV.foreach(import.attachment.url, headers: true, col_sep: ';') do |row|
# try to find member based on import.update_field
user = User.find_by(import.update_field.to_sym => import.update_field)
if user
service = Members::MembersService.new(user)
service.update(row_to_params(row))
else
user = User.new(row)
service = Members::MembersService.new(user)
service.create(import.user, row_to_params(row))
begin
log << { row: row.to_hash }
# try to find member based on import.update_field
user = User.find_by(import.update_field.to_sym => row[import.update_field])
params = row_to_params(row, user)
if user
service = Members::MembersService.new(user)
res = service.update(params)
log << { user: user.id, status: 'update', result: res }
else
user = User.new(params)
service = Members::MembersService.new(user)
res = service.create(import.user, params)
log << { user: nil, status: 'create', result: res }
end
log << user.errors.to_hash unless user.errors.to_hash.empty?
rescue StandardError => e
log << e.to_s
puts e
puts e.backtrace
end
end
log
end
private
def row_to_params(row)
{
def row_to_params(row, user)
res = {
id: row['id'],
username: row['username'],
email: row['email'],
password: row['password'],
password_confirmation: row['password'],
is_allow_contact: row['allow_contact'],
is_allow_newsletter: row['allow_newsletter'],
group_id: Group.friendly.find(row['group'])&.id,
tag_ids: Tag.where(id: row['tags'].split(',')).map(&:id),
profile_attributes: profile_attributes(row),
invoicing_profile_attributes: invoicing_profile_attributes(row),
statistic_profile_attributes: statistic_profile_attributes(row)
is_allow_contact: row['allow_contact'] == 'yes',
is_allow_newsletter: row['allow_newsletter'] == 'yes',
group_id: group_id(row),
tag_ids: tag_ids(row)
}
profile_attributes = profile(row, user)
res[:profile_attributes] = profile_attributes if profile_attributes
invoicing_profile_attributes = invoicing_profile(row, user)
res[:invoicing_profile_attributes] = invoicing_profile_attributes if invoicing_profile_attributes
statistic_profile_attributes = statistic_profile(row, user)
res[:statistic_profile_attributes] = statistic_profile_attributes if statistic_profile_attributes
res
end
def profile_attributes(row)
{
def group_id(row)
return unless row['group']
Group.friendly.find(row['group'])&.id
end
def tag_ids(row)
return unless row['tags']
Tag.where(id: row['tags'].split(',')).map(&:id)
end
def profile(row, user)
res = {
first_name: row['first_name'],
last_name: row['last_name'],
phone: row['phone'],
@ -61,28 +96,79 @@ class Members::ImportService
lastfm: row['lastfm'],
flickr: row['flickr']
}
res[:id] = user.profile.id if user&.profile
res
end
def invoicing_profile_attributes(row)
{
address_attributes: {
address: row['address']
},
organization_attributes: {
name: row['organization_name'],
address_attributes: {
address: row['organization_address']
}
}
}
def invoicing_profile(row, user)
res = {}
res[:id] = user.invoicing_profile.id if user&.invoicing_profile
address_attributes = address(row, user)
res[:address_attributes] = address_attributes if address_attributes
organization_attributes = organization(row, user)
res[:organization_attributes] = organization_attributes if organization_attributes
res
end
def statistic_profile_attributes(row)
{
def statistic_profile(row, user)
res = {
gender: row['gender'] == 'male',
birthday: row['birthdate'],
training_ids: Training.where(id: row['trainings'].split(',')).map(&:id)
birthday: row['birthdate']
}
res[:id] = user.statistic_profile.id if user&.statistic_profile
training_ids = training_ids(row)
res[:training_ids] = training_ids if training_ids
res
end
def address(row, user)
return unless row['address']
res = { address: row['address'] }
res[:id] = user.invoicing_profile.address.id if user&.invoicing_profile&.address
res
end
def organization(row, user)
return unless row['organization_name']
res = {
name: row['organization_name']
}
res[:id] = user.invoicing_profile.organization.id if user&.invoicing_profile&.organization
address_attributes = organization_address(row, user)
res[:address_attributes] = address_attributes if address_attributes
res
end
def organization_address(row, user)
return unless row['organization_address']
res = { address: row['organization_address'] }
res[:id] = user.invoicing_profile.organization.address.id if user&.invoicing_profile&.organization&.address
res
end
def training_ids(row)
return unless row['trainings']
Training.where(id: row['trainings'].split(',')).map(&:id)
end
end
end

View File

@ -1,6 +1,7 @@
# frozen_string_literal: true
json.extract! @import, :id, :category, :user_id, :update_field, :created_at, :updated_at, :results
json.extract! @import, :id, :category, :user_id, :update_field, :created_at, :updated_at
json.results @import.results_hash.to_json
json.user do
json.full_name @import.user&.profile&.full_name
end

View File

@ -11,7 +11,10 @@ class MembersImportWorker
raise SecurityError, 'Not allowed to import' unless import.user.admin?
raise KeyError, 'Wrong worker called' unless import.category == 'members'
Members::ImportService.import(import)
res = Members::ImportService.import(import)
import.results = res.to_yaml
import.save!
NotificationCenter.call type: :notify_admin_import_complete,
receiver: import.user,

View File

@ -1,7 +1,9 @@
# frozen_string_literal: true
# Be sure to restart your server when you modify this file.
# Add new mime types for use in respond_to blocks:
# Mime::Type.register "text/richtext", :rtf
# Mime::Type.register_alias "text/html", :iphone
Mime::Type.register "application/vnd.ms-excel", :xls
Mime::Type.register 'application/vnd.ms-excel', :xls

View File

@ -600,6 +600,12 @@ en:
# import results
members_import_result:
import_results: "Import results"
import_details: "Import #{{ID}}, of {{DATE}}, initiated by {{USER}}" # angular interpolation
results: "Results"
pending: "Pending..."
status_create: "Creating a new user"
status_update: "Updating user {{ID}}" # angular interpolation
error_details: "Error's details:"
members_edit:
# edit a member

View File

@ -600,6 +600,12 @@ es:
# import results
members_import_result:
import_results: "Import results" # translation_missing
import_details: "Import #{{ID}}, of {{DATE}}, initiated by {{USER}}" # angular interpolation # translation_missing
results: "Results" # translation_missing
pending: "Pending..." # translation_missing
status_create: "Creating a new user" # translation_missing
status_update: "Updating user {{ID}}" # angular interpolation # translation_missing
error_details: "Error's details:" # translation_missing
members_edit:
# edit a member

View File

@ -600,6 +600,12 @@ fr:
# résultats de l'import
members_import_result:
import_results: "Résultats de l'import"
import_details: "Import n°{{ID}}, du {{DATE}}, initié par {{USER}}" # angular interpolation
results: "Résultats"
pending: "En cours..."
status_create: "Création d'un nouvel utilisateur"
status_update: "Mise à jour de l'utilisateur {{ID}}" # angular interpolation
error_details: "Détails de l'erreur :"
members_edit:
# modifier un membre

View File

@ -600,6 +600,12 @@ pt:
# import results
members_import_result:
import_results: "Import results" # translation_missing
import_details: "Import #{{ID}}, of {{DATE}}, initiated by {{USER}}" # angular interpolation # translation_missing
results: "Results" # translation_missing
pending: "Pending..." # translation_missing
status_create: "Creating a new user" # translation_missing
status_update: "Updating user {{ID}}" # angular interpolation # translation_missing
error_details: "Error's details:" # translation_missing
members_edit:
# edit a member