gaku_imex 0.2.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +24 -0
- data/.rspec +2 -0
- data/.travis.yml +31 -0
- data/Gemfile +21 -0
- data/LICENSE +674 -0
- data/README.md +29 -0
- data/Rakefile +27 -0
- data/app/controllers/gaku/admin/import_files_controller.rb +74 -0
- data/app/controllers/gaku/students/reports_controller.rb +19 -0
- data/app/models/gaku/import_file.rb +23 -0
- data/app/models/gaku/student_injector.rb +32 -0
- data/app/overrides/import_menu.rb +6 -0
- data/app/overrides/reports.rb +6 -0
- data/app/reports/student_record_personal_info.en.tlf +1 -0
- data/app/reports/student_record_personal_info.ja.tlf +1 -0
- data/app/views/gaku/admin/import_files/_check_modal.html.slim +10 -0
- data/app/views/gaku/admin/import_files/_created_students.html.slim +8 -0
- data/app/views/gaku/admin/import_files/_duplicated_students.html.slim +8 -0
- data/app/views/gaku/admin/import_files/_form.html.slim +3 -0
- data/app/views/gaku/admin/import_files/_form_fields.html.slim +4 -0
- data/app/views/gaku/admin/import_files/_import_file.html.slim +2 -0
- data/app/views/gaku/admin/import_files/_import_files.html.slim +24 -0
- data/app/views/gaku/admin/import_files/_not_saved_student.html.slim +3 -0
- data/app/views/gaku/admin/import_files/_not_saved_students.html.slim +8 -0
- data/app/views/gaku/admin/import_files/_student.html.slim +4 -0
- data/app/views/gaku/admin/import_files/_table_fields.html.slim +8 -0
- data/app/views/gaku/admin/import_files/_tabs.html.slim +20 -0
- data/app/views/gaku/admin/import_files/check.js.erb +2 -0
- data/app/views/gaku/admin/import_files/create.js.erb +7 -0
- data/app/views/gaku/admin/import_files/destroy.js.erb +2 -0
- data/app/views/gaku/admin/import_files/import.js.erb +1 -0
- data/app/views/gaku/admin/import_files/index.js.erb +1 -0
- data/app/views/gaku/admin/import_files/new.js.erb +3 -0
- data/app/views/gaku/shared/menu/_imex_menu.html.erb +9 -0
- data/app/views/gaku/students/reports/_report_link.html.slim +7 -0
- data/app/views/gaku/students/reports/index.en.pdf.thinreports +15 -0
- data/app/views/gaku/students/reports/index.ja.pdf.thinreports +15 -0
- data/app/workers/gaku/importers/students_worker.rb +14 -0
- data/config/initializers/sidekiq.rb +6 -0
- data/config/routes.rb +17 -0
- data/db/migrate/20131011100726_create_gaku_import_files.rb +14 -0
- data/gaku_imex.gemspec +32 -0
- data/lib/gaku/imex.rb +13 -0
- data/lib/gaku/imex/engine.rb +21 -0
- data/lib/gaku/importers/students/csv.rb +58 -0
- data/lib/gaku/importers/students/student_identity.rb +15 -0
- data/lib/gaku_imex.rb +2 -0
- data/lib/generators/gaku_imex/install/install_generator.rb +77 -0
- data/lib/generators/gaku_imex/install/templates/Procfile +2 -0
- data/lib/generators/gaku_imex/install/templates/config/sidekiq.yml +8 -0
- data/lib/generators/gaku_imex/install/templates/log/sidekiq.log +0 -0
- data/spec/features/import_file_spec.rb +115 -0
- data/spec/lib/gaku/importers/students/csv_spec.rb +84 -0
- data/spec/models/import_file_spec.rb +32 -0
- data/spec/models/student_spec.rb +17 -0
- data/spec/spec_helper.rb +14 -0
- data/spec/support/factories.rb +12 -0
- data/spec/support/foreign_system_students.csv +3 -0
- data/spec/support/format_error_students.csv +3 -0
- data/spec/support/new_students.csv +2 -0
- data/spec/support/students.csv +1006 -0
- data/spec/workers/students_worker_spec.rb +9 -0
- metadata +216 -0
@@ -0,0 +1,6 @@
|
|
1
|
+
require 'sidekiq'
|
2
|
+
#Sidekiq.configure_server do |config|
|
3
|
+
# config.redis = { url: 'redis://redistogo:23997367276d9d8e473756154c3da248@spadefish.redistogo.com:9679/'}
|
4
|
+
#end
|
5
|
+
|
6
|
+
ENV['REDISTOGO_URL'] = 'redis://redistogo:23997367276d9d8e473756154c3da248@spadefish.redistogo.com:9679/'
|
data/config/routes.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
#Gaku::Core::Engine.routes.prepend do
|
2
|
+
Gaku::Core::Engine.routes.draw do
|
3
|
+
|
4
|
+
namespace :admin do
|
5
|
+
resources :import_files do
|
6
|
+
member do
|
7
|
+
get :import
|
8
|
+
get :check
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
resources :students do
|
14
|
+
resources :reports, only: :index, controller: 'students/reports'
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class CreateGakuImportFiles < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
|
4
|
+
create_table :gaku_import_files do |t|
|
5
|
+
t.string 'context'
|
6
|
+
t.string 'importer_type'
|
7
|
+
t.string 'data_file_file_name'
|
8
|
+
t.string 'data_file_content_type'
|
9
|
+
t.integer 'data_file_file_size'
|
10
|
+
t.datetime 'data_file_updated_at'
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
data/gaku_imex.gemspec
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.platform = Gem::Platform::RUBY
|
5
|
+
s.name = 'gaku_imex'
|
6
|
+
s.version = '0.2.2'
|
7
|
+
s.summary = 'Importers and Exporters for GAKU Engine'
|
8
|
+
s.description = "Importers and Exporters for GAKU Engine"
|
9
|
+
s.required_ruby_version = '>= 2.1.3'
|
10
|
+
|
11
|
+
s.authors = ['Rei Kagetsuki', 'Vassil Kalkov']
|
12
|
+
s.email = 'info@genshin.org'
|
13
|
+
s.homepage = 'http://github.com/GAKUEngine/gaku_imex'
|
14
|
+
|
15
|
+
s.files = `git ls-files`.split($RS)
|
16
|
+
s.test_files = s.files.grep(/^spec\//)
|
17
|
+
s.require_path = 'lib'
|
18
|
+
|
19
|
+
s.requirements << 'postgres'
|
20
|
+
s.requirements << 'redis'
|
21
|
+
|
22
|
+
s.add_dependency 'gaku_core', '~> 0.2.2'
|
23
|
+
s.add_dependency 'gaku_testing', '~> 0.2.2'
|
24
|
+
s.add_dependency 'gaku_admin', '~> 0.2.2'
|
25
|
+
|
26
|
+
s.add_dependency 'sidekiq'
|
27
|
+
s.add_dependency 'sinatra'
|
28
|
+
|
29
|
+
s.add_dependency 'smarter_csv'
|
30
|
+
s.add_dependency 'thinreports-rails'
|
31
|
+
|
32
|
+
end
|
data/lib/gaku/imex.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
module Gaku
|
2
|
+
module Imex
|
3
|
+
class Engine < Rails::Engine
|
4
|
+
engine_name 'gaku_imex'
|
5
|
+
|
6
|
+
config.autoload_paths += %W(#{config.root}/lib)
|
7
|
+
|
8
|
+
def self.activate
|
9
|
+
Dir.glob(File.join(File.dirname(__FILE__), '../../../app/**/*_injector*.rb')) do |c|
|
10
|
+
Rails.configuration.cache_classes ? require(c) : load(c)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
config.to_prepare &method(:activate).to_proc
|
15
|
+
|
16
|
+
config.after_initialize do
|
17
|
+
Rails.application.routes_reloader.reload!
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Gaku::Importers::Students
|
2
|
+
class Csv
|
3
|
+
include Gaku::Importers::Students::StudentIdentity
|
4
|
+
|
5
|
+
attr_reader :csv, :import_file_id
|
6
|
+
|
7
|
+
def initialize(import_file_id, path)
|
8
|
+
@import_file_id = import_file_id
|
9
|
+
@csv = SmarterCSV.process(path)
|
10
|
+
end
|
11
|
+
|
12
|
+
def import
|
13
|
+
cleanup_student_states
|
14
|
+
|
15
|
+
enrollment_status_code = Gaku::EnrollmentStatus.where(code: 'enrolled', active: true, immutable: true)
|
16
|
+
.first_or_create!.try(:code)
|
17
|
+
|
18
|
+
ActiveRecord::Base.transaction do
|
19
|
+
@csv.each do |row|
|
20
|
+
|
21
|
+
next if student_record(row)
|
22
|
+
|
23
|
+
student = Gaku::Student.new(filter_row(row))
|
24
|
+
student.enrollment_status_code = enrollment_status_code
|
25
|
+
|
26
|
+
if student.save
|
27
|
+
$redis.rpush "import_file:#{@import_file_id}:created", student.id
|
28
|
+
else
|
29
|
+
$redis.rpush "import_file:#{@import_file_id}:not_saved", not_saved_students(student)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def student_record(row)
|
38
|
+
Gaku::Student.where(foreign_id_code: row[:foreign_id_code].to_s).first.try(:tap) do |student|
|
39
|
+
$redis.rpush "import_file:#{@import_file_id}:duplicated", student.id
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def filter_row(row)
|
44
|
+
row.select { |k, v| Gaku::Student.csv_column_fields.include?(k.to_s) }
|
45
|
+
end
|
46
|
+
|
47
|
+
def not_saved_students(student)
|
48
|
+
"#{student.foreign_id_code}, #{student.name}, #{student.surname}"
|
49
|
+
end
|
50
|
+
|
51
|
+
def cleanup_student_states
|
52
|
+
%w( created duplicated not_saved ).each do |state|
|
53
|
+
$redis.del "import_file:#{@import_file_id}:#{state}"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Gaku::Importers::Students::StudentIdentity
|
2
|
+
def normalize_id_num(id_number)
|
3
|
+
if id_number.to_i == 0 # ID is alphanumeric
|
4
|
+
return id_number.to_s
|
5
|
+
else # ID number is a number, defaulted to float from sheet data
|
6
|
+
return id_number.to_i.to_s
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def find_student_by_student_ids(serial_id, foreign_id_code = nil)
|
11
|
+
student = Gaku::Student.where(serial_id: normalize_id_num(student_id_number)).first
|
12
|
+
return student unless student.nil?
|
13
|
+
Gaku::Student.where(foreign_id_code: normalize_id_num(foreign_id_code)).first
|
14
|
+
end
|
15
|
+
end
|
data/lib/gaku_imex.rb
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
require 'highline/import'
|
3
|
+
require 'bundler'
|
4
|
+
require 'bundler/cli'
|
5
|
+
|
6
|
+
module GakuImex
|
7
|
+
class InstallGenerator < Rails::Generators::Base
|
8
|
+
|
9
|
+
class_option :auto_accept, type: :boolean
|
10
|
+
class_option :lib_name, type: :string, default: 'gaku_imex'
|
11
|
+
class_option :env, type: :string, default: 'development'
|
12
|
+
|
13
|
+
def self.source_paths
|
14
|
+
paths = superclass.source_paths
|
15
|
+
paths << File.expand_path('../templates', "../../#{__FILE__}")
|
16
|
+
paths << File.expand_path('../templates', "../#{__FILE__}")
|
17
|
+
paths << File.expand_path('../templates', __FILE__)
|
18
|
+
paths.flatten
|
19
|
+
end
|
20
|
+
|
21
|
+
def prepare_options
|
22
|
+
@env = options[:env]
|
23
|
+
end
|
24
|
+
|
25
|
+
def clear_logs
|
26
|
+
remove_file 'log/sidekiq.log'
|
27
|
+
add_file 'log/sidekiq.log'
|
28
|
+
end
|
29
|
+
|
30
|
+
def add_files
|
31
|
+
template 'config/sidekiq.yml', 'config/sidekiq.yml'
|
32
|
+
template 'Procfile', 'Procfile'
|
33
|
+
end
|
34
|
+
|
35
|
+
def add_migrations
|
36
|
+
run 'rake railties:install:migrations FROM=gaku_imex'
|
37
|
+
end
|
38
|
+
|
39
|
+
def run_migrations
|
40
|
+
run_migrations = options[:auto_run_migrations] || ['', 'y', 'Y'].include?(ask 'Would you like to run the migrations now? [Y/n]')
|
41
|
+
if run_migrations
|
42
|
+
run 'rake db:migrate'
|
43
|
+
else
|
44
|
+
puts "Skiping rake db:migrate, don't forget to run it!"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def notify_about_routes
|
49
|
+
insert_into_file File.join('config', 'routes.rb'), after: "Application.routes.draw do\n" do
|
50
|
+
%Q{
|
51
|
+
# This line mounts GakuImex routes
|
52
|
+
|
53
|
+
require 'sidekiq/web'
|
54
|
+
mount Sidekiq::Web => '/sidekiq'
|
55
|
+
|
56
|
+
}
|
57
|
+
end
|
58
|
+
|
59
|
+
unless options[:quiet]
|
60
|
+
puts '*' * 50
|
61
|
+
puts "We added the following line to your application's config/routes.rb file:"
|
62
|
+
puts ' '
|
63
|
+
puts " mount Sidekiq::Web => '/sidekiq'"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def complete
|
68
|
+
unless options[:quiet]
|
69
|
+
puts '*' * 50
|
70
|
+
puts "GakuImex has been installed successfully. You're all ready to go!"
|
71
|
+
puts ' '
|
72
|
+
puts 'Enjoy!'
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
end
|
File without changes
|
@@ -0,0 +1,115 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'Admin ImportFile' do
|
4
|
+
|
5
|
+
before { as :admin }
|
6
|
+
before(:all) { set_resource 'admin-import-file' }
|
7
|
+
|
8
|
+
let(:import_file) { create(:import_file) }
|
9
|
+
|
10
|
+
let(:student) { create(:student) }
|
11
|
+
|
12
|
+
context 'new', js: true do
|
13
|
+
before do
|
14
|
+
visit gaku.admin_root_path
|
15
|
+
click_link 'Importer'
|
16
|
+
click new_link
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'creates and shows' do
|
20
|
+
expect do
|
21
|
+
absolute_path = Rails.root + '../support/new_students.csv'
|
22
|
+
attach_file 'import_file_data_file', absolute_path
|
23
|
+
select 'students', from: 'import_file_importer_type'
|
24
|
+
click submit
|
25
|
+
flash_created?
|
26
|
+
end.to change(Gaku::ImportFile, :count).by 1
|
27
|
+
|
28
|
+
has_content? 'students'
|
29
|
+
has_content? 'Import files list(1)'
|
30
|
+
end
|
31
|
+
|
32
|
+
it { has_validations? }
|
33
|
+
end
|
34
|
+
|
35
|
+
context 'importing', js: true do
|
36
|
+
before do
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'create student' do
|
40
|
+
student
|
41
|
+
import_file
|
42
|
+
$redis.rpush "import_file:#{import_file.id}:created", student.id
|
43
|
+
visit gaku.admin_root_path
|
44
|
+
click_link 'Importer'
|
45
|
+
click new_link
|
46
|
+
|
47
|
+
click '.check-link'
|
48
|
+
click '#created-students-tab-link'
|
49
|
+
has_content? student.name
|
50
|
+
has_content? student.surname
|
51
|
+
expect(page.all('#created-students-index tbody tr').count).to eq 1
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'duplicate student' do
|
55
|
+
student
|
56
|
+
import_file
|
57
|
+
$redis.rpush "import_file:#{import_file.id}:duplicated", student.id
|
58
|
+
visit gaku.admin_root_path
|
59
|
+
click_link 'Importer'
|
60
|
+
click new_link
|
61
|
+
|
62
|
+
click '.check-link'
|
63
|
+
click '#duplicated-students-tab-link'
|
64
|
+
has_content? student.name
|
65
|
+
has_content? student.surname
|
66
|
+
expect(page.all('#duplicated-students-index tbody tr').count).to eq 1
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'not saved student' do
|
70
|
+
import_file
|
71
|
+
$redis.rpush "import_file:#{import_file.id}:not_saved", "10052014, John, Doe"
|
72
|
+
visit gaku.admin_root_path
|
73
|
+
click_link 'Importer'
|
74
|
+
click new_link
|
75
|
+
|
76
|
+
click '.check-link'
|
77
|
+
click '#not-saved-students-tab-link'
|
78
|
+
has_content? '10052014'
|
79
|
+
has_content? 'John'
|
80
|
+
has_content? 'Doe'
|
81
|
+
expect(page.all('#not-saved-students-index tbody tr').count).to eq 1
|
82
|
+
end
|
83
|
+
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
|
88
|
+
context 'existing', js: true do
|
89
|
+
|
90
|
+
before do
|
91
|
+
import_file
|
92
|
+
visit gaku.admin_root_path
|
93
|
+
click_link 'Importer'
|
94
|
+
end
|
95
|
+
|
96
|
+
it 'import' do
|
97
|
+
click '.import-link'
|
98
|
+
expect(page).to have_content('Importing for students started!')
|
99
|
+
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'deletes' do
|
103
|
+
has_content? import_file.data_file_file_name
|
104
|
+
count? 'Import files list(1)'
|
105
|
+
|
106
|
+
expect do
|
107
|
+
ensure_delete_is_working
|
108
|
+
flash_destroyed?
|
109
|
+
end.to change(Gaku::ImportFile, :count).by(-1)
|
110
|
+
|
111
|
+
has_no_content? import_file.data_file_file_name
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Gaku::Importers::Students::Csv do
|
4
|
+
|
5
|
+
let!(:import_file) { create(:import_file) }
|
6
|
+
|
7
|
+
describe 'initialize' do
|
8
|
+
it 'initializes' do
|
9
|
+
expect(described_class.new(import_file.id, 'spec/support/new_students.csv').csv)
|
10
|
+
.to eq [{:national_registration_code=>999,
|
11
|
+
:name=>"零紀",
|
12
|
+
:middle_name=>"Flux",
|
13
|
+
:surname=>"影月",
|
14
|
+
:name_reading=>"レイキ",
|
15
|
+
:middle_name_reading=>"フラックス",
|
16
|
+
:surname_reading=>"カゲツキ",
|
17
|
+
:gender=>1,
|
18
|
+
:birth_date=>"2006-04-19",
|
19
|
+
:enrollment_status_code=>"enrolled"
|
20
|
+
}]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe '#import' do
|
25
|
+
subject { described_class.new(import_file.id, 'spec/support/new_students.csv').import }
|
26
|
+
|
27
|
+
it 'creates new student' do
|
28
|
+
expect do
|
29
|
+
subject
|
30
|
+
end.to change(Gaku::Student, :count).by(1)
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'saves all attributes' do
|
34
|
+
subject
|
35
|
+
|
36
|
+
created_student = Gaku::Student.last
|
37
|
+
expect(created_student.name).to eq '零紀'
|
38
|
+
expect(created_student.surname).to eq '影月'
|
39
|
+
expect(created_student.name_reading).to eq 'レイキ'
|
40
|
+
expect(created_student.surname_reading).to eq 'カゲツキ'
|
41
|
+
expect(created_student.gender).to eq true
|
42
|
+
expect(created_student.birth_date.to_s).to eq '2006-04-19'
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'sets enrollment status' do
|
46
|
+
subject
|
47
|
+
|
48
|
+
created_student = Gaku::Student.last
|
49
|
+
expect(created_student.enrollment_status.code).to eq 'enrolled'
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'returns an array of created students' do
|
53
|
+
students = subject
|
54
|
+
created_student = Gaku::Student.last
|
55
|
+
expect($redis.lrange "import_file:#{import_file.id}:created", 0, -1).to eq [created_student.id.to_s]
|
56
|
+
expect($redis.llen "import_file:#{import_file.id}:created").to eq 1
|
57
|
+
expect($redis.llen "import_file:#{import_file.id}:not_saved").to eq 0
|
58
|
+
end
|
59
|
+
|
60
|
+
xit 'ignores records with ID set to prevent tainting the DB' do
|
61
|
+
students = described_class.new('spec/support/students.csv').import
|
62
|
+
expect(students[:created].count).to eq 0
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'imports students from a foreign system' do
|
66
|
+
described_class.new(import_file.id, 'spec/support/foreign_system_students.csv').import
|
67
|
+
expect($redis.llen "import_file:#{import_file.id}:created").to eq 2
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'checks foreign_id_code and does not overwrite or re-create existing records' do
|
71
|
+
create(:student, foreign_id_code: 55567)
|
72
|
+
create(:student, foreign_id_code: 55568)
|
73
|
+
|
74
|
+
described_class.new(import_file.id, 'spec/support/foreign_system_students.csv').import
|
75
|
+
expect($redis.llen "import_file:#{import_file.id}:created").to eq 0
|
76
|
+
expect($redis.llen "import_file:#{import_file.id}:duplicated").to eq 2
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'returns an array of students with errors' do
|
80
|
+
described_class.new(import_file.id, 'spec/support/format_error_students.csv').import
|
81
|
+
expect($redis.llen "import_file:#{import_file.id}:not_saved").to eq 2
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|