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) {
|
if (content.id) {
|
||||||
$state.go('app.admin.members_import_result', { id: content.id });
|
$state.go('app.admin.members_import_result', { id: content.id });
|
||||||
} else {
|
} 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)
|
* Controller used in the member's import results page (admin view)
|
||||||
*/
|
*/
|
||||||
Application.Controllers.controller('ImportMembersResultController', ['$scope', '$state', 'importItem',
|
Application.Controllers.controller('ImportMembersResultController', ['$scope', '$state', 'Import', 'importItem',
|
||||||
function ($scope, $state, importItem) {
|
function ($scope, $state, Import, importItem) {
|
||||||
/* PUBLIC SCOPE */
|
/* PUBLIC SCOPE */
|
||||||
|
|
||||||
// Current import as saved in database
|
// Current import as saved in database
|
||||||
$scope.import = importItem;
|
$scope.import = importItem;
|
||||||
|
|
||||||
|
// Current import results
|
||||||
|
$scope.results = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Changes the admin's view to the members import page
|
* Changes the admin's view to the members import page
|
||||||
*/
|
*/
|
||||||
$scope.cancel = function () { $state.go('app.admin.members_import'); };
|
$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="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>
|
||||||
</div>
|
</div>
|
||||||
|
@ -10,10 +10,14 @@ class Import < ActiveRecord::Base
|
|||||||
belongs_to :user
|
belongs_to :user
|
||||||
|
|
||||||
validates :attachment, file_size: { maximum: Rails.application.secrets.max_import_size&.to_i || 5.megabytes.to_i }
|
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]
|
after_commit :proceed_import, on: [:create]
|
||||||
|
|
||||||
|
def results_hash
|
||||||
|
YAML.safe_load(results, [Symbol]) if results
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def proceed_import
|
def proceed_import
|
||||||
|
@ -5,40 +5,75 @@ class Members::ImportService
|
|||||||
class << self
|
class << self
|
||||||
def import(import)
|
def import(import)
|
||||||
require 'csv'
|
require 'csv'
|
||||||
|
log = []
|
||||||
CSV.foreach(import.attachment.url, headers: true, col_sep: ';') do |row|
|
CSV.foreach(import.attachment.url, headers: true, col_sep: ';') do |row|
|
||||||
# try to find member based on import.update_field
|
begin
|
||||||
user = User.find_by(import.update_field.to_sym => import.update_field)
|
log << { row: row.to_hash }
|
||||||
if user
|
|
||||||
service = Members::MembersService.new(user)
|
# try to find member based on import.update_field
|
||||||
service.update(row_to_params(row))
|
user = User.find_by(import.update_field.to_sym => row[import.update_field])
|
||||||
else
|
params = row_to_params(row, user)
|
||||||
user = User.new(row)
|
if user
|
||||||
service = Members::MembersService.new(user)
|
service = Members::MembersService.new(user)
|
||||||
service.create(import.user, row_to_params(row))
|
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
|
||||||
end
|
end
|
||||||
|
log
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def row_to_params(row)
|
def row_to_params(row, user)
|
||||||
{
|
res = {
|
||||||
|
id: row['id'],
|
||||||
username: row['username'],
|
username: row['username'],
|
||||||
email: row['email'],
|
email: row['email'],
|
||||||
password: row['password'],
|
password: row['password'],
|
||||||
password_confirmation: row['password'],
|
password_confirmation: row['password'],
|
||||||
is_allow_contact: row['allow_contact'],
|
is_allow_contact: row['allow_contact'] == 'yes',
|
||||||
is_allow_newsletter: row['allow_newsletter'],
|
is_allow_newsletter: row['allow_newsletter'] == 'yes',
|
||||||
group_id: Group.friendly.find(row['group'])&.id,
|
group_id: group_id(row),
|
||||||
tag_ids: Tag.where(id: row['tags'].split(',')).map(&:id),
|
tag_ids: tag_ids(row)
|
||||||
profile_attributes: profile_attributes(row),
|
|
||||||
invoicing_profile_attributes: invoicing_profile_attributes(row),
|
|
||||||
statistic_profile_attributes: statistic_profile_attributes(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
|
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'],
|
first_name: row['first_name'],
|
||||||
last_name: row['last_name'],
|
last_name: row['last_name'],
|
||||||
phone: row['phone'],
|
phone: row['phone'],
|
||||||
@ -61,28 +96,79 @@ class Members::ImportService
|
|||||||
lastfm: row['lastfm'],
|
lastfm: row['lastfm'],
|
||||||
flickr: row['flickr']
|
flickr: row['flickr']
|
||||||
}
|
}
|
||||||
|
|
||||||
|
res[:id] = user.profile.id if user&.profile
|
||||||
|
|
||||||
|
res
|
||||||
end
|
end
|
||||||
|
|
||||||
def invoicing_profile_attributes(row)
|
def invoicing_profile(row, user)
|
||||||
{
|
res = {}
|
||||||
address_attributes: {
|
|
||||||
address: row['address']
|
res[:id] = user.invoicing_profile.id if user&.invoicing_profile
|
||||||
},
|
|
||||||
organization_attributes: {
|
address_attributes = address(row, user)
|
||||||
name: row['organization_name'],
|
res[:address_attributes] = address_attributes if address_attributes
|
||||||
address_attributes: {
|
|
||||||
address: row['organization_address']
|
organization_attributes = organization(row, user)
|
||||||
}
|
res[:organization_attributes] = organization_attributes if organization_attributes
|
||||||
}
|
|
||||||
}
|
res
|
||||||
end
|
end
|
||||||
|
|
||||||
def statistic_profile_attributes(row)
|
def statistic_profile(row, user)
|
||||||
{
|
res = {
|
||||||
gender: row['gender'] == 'male',
|
gender: row['gender'] == 'male',
|
||||||
birthday: row['birthdate'],
|
birthday: row['birthdate']
|
||||||
training_ids: Training.where(id: row['trainings'].split(',')).map(&:id)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
end
|
||||||
end
|
end
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# frozen_string_literal: true
|
# 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.user do
|
||||||
json.full_name @import.user&.profile&.full_name
|
json.full_name @import.user&.profile&.full_name
|
||||||
end
|
end
|
||||||
|
@ -11,7 +11,10 @@ class MembersImportWorker
|
|||||||
raise SecurityError, 'Not allowed to import' unless import.user.admin?
|
raise SecurityError, 'Not allowed to import' unless import.user.admin?
|
||||||
raise KeyError, 'Wrong worker called' unless import.category == 'members'
|
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,
|
NotificationCenter.call type: :notify_admin_import_complete,
|
||||||
receiver: import.user,
|
receiver: import.user,
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
# Be sure to restart your server when you modify this file.
|
# Be sure to restart your server when you modify this file.
|
||||||
|
|
||||||
# Add new mime types for use in respond_to blocks:
|
# Add new mime types for use in respond_to blocks:
|
||||||
# Mime::Type.register "text/richtext", :rtf
|
# Mime::Type.register "text/richtext", :rtf
|
||||||
# Mime::Type.register_alias "text/html", :iphone
|
# 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
|
# import results
|
||||||
members_import_result:
|
members_import_result:
|
||||||
import_results: "Import results"
|
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:
|
members_edit:
|
||||||
# edit a member
|
# edit a member
|
||||||
|
@ -600,6 +600,12 @@ es:
|
|||||||
# import results
|
# import results
|
||||||
members_import_result:
|
members_import_result:
|
||||||
import_results: "Import results" # translation_missing
|
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:
|
members_edit:
|
||||||
# edit a member
|
# edit a member
|
||||||
|
@ -600,6 +600,12 @@ fr:
|
|||||||
# résultats de l'import
|
# résultats de l'import
|
||||||
members_import_result:
|
members_import_result:
|
||||||
import_results: "Résultats de l'import"
|
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:
|
members_edit:
|
||||||
# modifier un membre
|
# modifier un membre
|
||||||
|
@ -600,6 +600,12 @@ pt:
|
|||||||
# import results
|
# import results
|
||||||
members_import_result:
|
members_import_result:
|
||||||
import_results: "Import results" # translation_missing
|
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:
|
members_edit:
|
||||||
# edit a member
|
# edit a member
|
||||||
|
Loading…
x
Reference in New Issue
Block a user