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.
Files changed (64) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +24 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +31 -0
  5. data/Gemfile +21 -0
  6. data/LICENSE +674 -0
  7. data/README.md +29 -0
  8. data/Rakefile +27 -0
  9. data/app/controllers/gaku/admin/import_files_controller.rb +74 -0
  10. data/app/controllers/gaku/students/reports_controller.rb +19 -0
  11. data/app/models/gaku/import_file.rb +23 -0
  12. data/app/models/gaku/student_injector.rb +32 -0
  13. data/app/overrides/import_menu.rb +6 -0
  14. data/app/overrides/reports.rb +6 -0
  15. data/app/reports/student_record_personal_info.en.tlf +1 -0
  16. data/app/reports/student_record_personal_info.ja.tlf +1 -0
  17. data/app/views/gaku/admin/import_files/_check_modal.html.slim +10 -0
  18. data/app/views/gaku/admin/import_files/_created_students.html.slim +8 -0
  19. data/app/views/gaku/admin/import_files/_duplicated_students.html.slim +8 -0
  20. data/app/views/gaku/admin/import_files/_form.html.slim +3 -0
  21. data/app/views/gaku/admin/import_files/_form_fields.html.slim +4 -0
  22. data/app/views/gaku/admin/import_files/_import_file.html.slim +2 -0
  23. data/app/views/gaku/admin/import_files/_import_files.html.slim +24 -0
  24. data/app/views/gaku/admin/import_files/_not_saved_student.html.slim +3 -0
  25. data/app/views/gaku/admin/import_files/_not_saved_students.html.slim +8 -0
  26. data/app/views/gaku/admin/import_files/_student.html.slim +4 -0
  27. data/app/views/gaku/admin/import_files/_table_fields.html.slim +8 -0
  28. data/app/views/gaku/admin/import_files/_tabs.html.slim +20 -0
  29. data/app/views/gaku/admin/import_files/check.js.erb +2 -0
  30. data/app/views/gaku/admin/import_files/create.js.erb +7 -0
  31. data/app/views/gaku/admin/import_files/destroy.js.erb +2 -0
  32. data/app/views/gaku/admin/import_files/import.js.erb +1 -0
  33. data/app/views/gaku/admin/import_files/index.js.erb +1 -0
  34. data/app/views/gaku/admin/import_files/new.js.erb +3 -0
  35. data/app/views/gaku/shared/menu/_imex_menu.html.erb +9 -0
  36. data/app/views/gaku/students/reports/_report_link.html.slim +7 -0
  37. data/app/views/gaku/students/reports/index.en.pdf.thinreports +15 -0
  38. data/app/views/gaku/students/reports/index.ja.pdf.thinreports +15 -0
  39. data/app/workers/gaku/importers/students_worker.rb +14 -0
  40. data/config/initializers/sidekiq.rb +6 -0
  41. data/config/routes.rb +17 -0
  42. data/db/migrate/20131011100726_create_gaku_import_files.rb +14 -0
  43. data/gaku_imex.gemspec +32 -0
  44. data/lib/gaku/imex.rb +13 -0
  45. data/lib/gaku/imex/engine.rb +21 -0
  46. data/lib/gaku/importers/students/csv.rb +58 -0
  47. data/lib/gaku/importers/students/student_identity.rb +15 -0
  48. data/lib/gaku_imex.rb +2 -0
  49. data/lib/generators/gaku_imex/install/install_generator.rb +77 -0
  50. data/lib/generators/gaku_imex/install/templates/Procfile +2 -0
  51. data/lib/generators/gaku_imex/install/templates/config/sidekiq.yml +8 -0
  52. data/lib/generators/gaku_imex/install/templates/log/sidekiq.log +0 -0
  53. data/spec/features/import_file_spec.rb +115 -0
  54. data/spec/lib/gaku/importers/students/csv_spec.rb +84 -0
  55. data/spec/models/import_file_spec.rb +32 -0
  56. data/spec/models/student_spec.rb +17 -0
  57. data/spec/spec_helper.rb +14 -0
  58. data/spec/support/factories.rb +12 -0
  59. data/spec/support/foreign_system_students.csv +3 -0
  60. data/spec/support/format_error_students.csv +3 -0
  61. data/spec/support/new_students.csv +2 -0
  62. data/spec/support/students.csv +1006 -0
  63. data/spec/workers/students_worker_spec.rb +9 -0
  64. 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/'
@@ -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
@@ -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
@@ -0,0 +1,13 @@
1
+ require 'gaku/core'
2
+ require 'gaku/admin'
3
+ require 'sidekiq/web'
4
+ require 'smarter_csv'
5
+
6
+ module Gaku
7
+
8
+ module Imex
9
+ end
10
+
11
+ end
12
+
13
+ require 'gaku/imex/engine'
@@ -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
@@ -0,0 +1,2 @@
1
+ require 'gaku/imex'
2
+ require 'thinreports-rails'
@@ -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
@@ -0,0 +1,2 @@
1
+ web: bundle exec rails s -p $PORT
2
+ worker: bundle exec sidekiq -C config/sidekiq.yml
@@ -0,0 +1,8 @@
1
+ # Sample configuration file for Sidekiq.
2
+ # Options here can still be overridden by cmd line args.
3
+ # sidekiq -C config.yml
4
+ ---
5
+ :verbose: false
6
+ :pidfile: ./tmp/pids/sidekiq.pid
7
+ :logfile: ./log/sidekiq.log
8
+ :concurrency: 25
@@ -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