gaku_imex 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|