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:
parent
4deaf1f75a
commit
ff5de97c92
@ -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();
|
||||
}
|
||||
]);
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user