coupler 0.0.1-java

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 (258) hide show
  1. data/.document +5 -0
  2. data/.gitmodules +3 -0
  3. data/.rvmrc +1 -0
  4. data/.vimrc +40 -0
  5. data/Gemfile +27 -0
  6. data/Gemfile.lock +71 -0
  7. data/LICENSE +20 -0
  8. data/NOTES +6 -0
  9. data/README.rdoc +18 -0
  10. data/Rakefile +42 -0
  11. data/TODO +11 -0
  12. data/VERSION +1 -0
  13. data/bin/coupler +7 -0
  14. data/db/.gitignore +6 -0
  15. data/db/migrate/001_initial_schema.rb +166 -0
  16. data/db/migrate/002_stub.rb +4 -0
  17. data/db/migrate/003_stub.rb +4 -0
  18. data/db/migrate/004_create_comparisons.rb +28 -0
  19. data/db/migrate/005_move_database_name.rb +19 -0
  20. data/db/migrate/006_upgrade_comparisons.rb +34 -0
  21. data/db/migrate/007_add_which_to_comparisons.rb +23 -0
  22. data/db/migrate/008_add_result_field_to_transformations.rb +33 -0
  23. data/db/migrate/009_add_generated_flag_to_fields.rb +13 -0
  24. data/db/migrate/010_create_imports.rb +24 -0
  25. data/db/migrate/011_add_primary_key_type.rb +13 -0
  26. data/db/migrate/012_add_transformed_with_to_resources.rb +13 -0
  27. data/db/migrate/013_add_run_count_to_scenarios.rb +13 -0
  28. data/db/migrate/014_add_last_accessed_at_to_some_tables.rb +13 -0
  29. data/db/migrate/015_add_run_number_to_results.rb +15 -0
  30. data/db/migrate/016_fix_scenario_run_count.rb +27 -0
  31. data/db/migrate/017_rename_comparison_columns.rb +14 -0
  32. data/db/migrate/018_fix_scenario_linkage_type.rb +8 -0
  33. data/db/migrate/019_add_columns_to_imports.rb +24 -0
  34. data/db/migrate/020_rename_import_columns.rb +12 -0
  35. data/db/migrate/021_add_fields_to_connections.rb +15 -0
  36. data/db/migrate/022_remove_database_name_from_resources.rb +11 -0
  37. data/features/connections.feature +28 -0
  38. data/features/matchers.feature +35 -0
  39. data/features/projects.feature +11 -0
  40. data/features/resources.feature +62 -0
  41. data/features/scenarios.feature +45 -0
  42. data/features/step_definitions/coupler_steps.rb +145 -0
  43. data/features/step_definitions/matchers_steps.rb +26 -0
  44. data/features/step_definitions/resources_steps.rb +12 -0
  45. data/features/step_definitions/scenarios_steps.rb +7 -0
  46. data/features/step_definitions/transformations_steps.rb +3 -0
  47. data/features/support/env.rb +128 -0
  48. data/features/transformations.feature +22 -0
  49. data/features/wizard.feature +10 -0
  50. data/gfx/coupler-header.svg +213 -0
  51. data/gfx/coupler-sidebar.svg +656 -0
  52. data/gfx/coupler.svg +184 -0
  53. data/gfx/icon.svg +75 -0
  54. data/lib/coupler/base.rb +63 -0
  55. data/lib/coupler/config.rb +128 -0
  56. data/lib/coupler/data_uploader.rb +20 -0
  57. data/lib/coupler/database.rb +31 -0
  58. data/lib/coupler/extensions/connections.rb +57 -0
  59. data/lib/coupler/extensions/exceptions.rb +58 -0
  60. data/lib/coupler/extensions/imports.rb +43 -0
  61. data/lib/coupler/extensions/jobs.rb +21 -0
  62. data/lib/coupler/extensions/matchers.rb +64 -0
  63. data/lib/coupler/extensions/projects.rb +62 -0
  64. data/lib/coupler/extensions/resources.rb +89 -0
  65. data/lib/coupler/extensions/results.rb +100 -0
  66. data/lib/coupler/extensions/scenarios.rb +50 -0
  67. data/lib/coupler/extensions/transformations.rb +70 -0
  68. data/lib/coupler/extensions/transformers.rb +58 -0
  69. data/lib/coupler/extensions.rb +16 -0
  70. data/lib/coupler/helpers.rb +121 -0
  71. data/lib/coupler/import_buffer.rb +48 -0
  72. data/lib/coupler/logger.rb +16 -0
  73. data/lib/coupler/models/common_model.rb +104 -0
  74. data/lib/coupler/models/comparison.rb +166 -0
  75. data/lib/coupler/models/connection.rb +59 -0
  76. data/lib/coupler/models/field.rb +55 -0
  77. data/lib/coupler/models/import.rb +238 -0
  78. data/lib/coupler/models/job.rb +42 -0
  79. data/lib/coupler/models/jobify.rb +17 -0
  80. data/lib/coupler/models/matcher.rb +36 -0
  81. data/lib/coupler/models/project.rb +40 -0
  82. data/lib/coupler/models/resource.rb +287 -0
  83. data/lib/coupler/models/result.rb +92 -0
  84. data/lib/coupler/models/scenario/runner.rb +357 -0
  85. data/lib/coupler/models/scenario.rb +115 -0
  86. data/lib/coupler/models/transformation.rb +117 -0
  87. data/lib/coupler/models/transformer/runner.rb +28 -0
  88. data/lib/coupler/models/transformer.rb +110 -0
  89. data/lib/coupler/models.rb +30 -0
  90. data/lib/coupler/runner.rb +76 -0
  91. data/lib/coupler/scheduler.rb +56 -0
  92. data/lib/coupler.rb +34 -0
  93. data/log/.gitignore +1 -0
  94. data/misc/README +5 -0
  95. data/misc/jruby-json.license +57 -0
  96. data/misc/rack-flash.license +22 -0
  97. data/script/dbconsole.rb +5 -0
  98. data/src/edu/vanderbilt/coupler/Main.java +116 -0
  99. data/src/edu/vanderbilt/coupler/jruby.properties +1 -0
  100. data/tasks/annotations.rake +84 -0
  101. data/tasks/db.rake +120 -0
  102. data/tasks/environment.rake +12 -0
  103. data/tasks/jeweler.rake +43 -0
  104. data/tasks/package.rake +58 -0
  105. data/tasks/rdoc.rake +13 -0
  106. data/tasks/test.rake +63 -0
  107. data/tasks/vendor.rake +43 -0
  108. data/test/README.txt +6 -0
  109. data/test/config.yml +9 -0
  110. data/test/coupler/models/test_import.rb +221 -0
  111. data/test/factories.rb +91 -0
  112. data/test/fixtures/duplicate-keys.csv +5 -0
  113. data/test/fixtures/no-headers.csv +50 -0
  114. data/test/fixtures/people.csv +51 -0
  115. data/test/fixtures/varying-row-size.csv +4 -0
  116. data/test/helper.rb +156 -0
  117. data/test/integration/extensions/test_connections.rb +80 -0
  118. data/test/integration/extensions/test_imports.rb +94 -0
  119. data/test/integration/extensions/test_jobs.rb +52 -0
  120. data/test/integration/extensions/test_matchers.rb +134 -0
  121. data/test/integration/extensions/test_projects.rb +82 -0
  122. data/test/integration/extensions/test_resources.rb +150 -0
  123. data/test/integration/extensions/test_results.rb +89 -0
  124. data/test/integration/extensions/test_scenarios.rb +88 -0
  125. data/test/integration/extensions/test_transformations.rb +113 -0
  126. data/test/integration/extensions/test_transformers.rb +80 -0
  127. data/test/integration/test_field.rb +45 -0
  128. data/test/integration/test_import.rb +78 -0
  129. data/test/integration/test_running_scenarios.rb +379 -0
  130. data/test/integration/test_transformation.rb +56 -0
  131. data/test/integration/test_transforming.rb +154 -0
  132. data/test/table_sets.rb +76 -0
  133. data/test/unit/models/test_common_model.rb +130 -0
  134. data/test/unit/models/test_comparison.rb +619 -0
  135. data/test/unit/models/test_connection.rb +115 -0
  136. data/test/unit/models/test_field.rb +99 -0
  137. data/test/unit/models/test_import.rb +130 -0
  138. data/test/unit/models/test_job.rb +115 -0
  139. data/test/unit/models/test_matcher.rb +82 -0
  140. data/test/unit/models/test_project.rb +102 -0
  141. data/test/unit/models/test_resource.rb +564 -0
  142. data/test/unit/models/test_result.rb +90 -0
  143. data/test/unit/models/test_scenario.rb +199 -0
  144. data/test/unit/models/test_transformation.rb +193 -0
  145. data/test/unit/models/test_transformer.rb +188 -0
  146. data/test/unit/test_base.rb +60 -0
  147. data/test/unit/test_data_uploader.rb +27 -0
  148. data/test/unit/test_database.rb +23 -0
  149. data/test/unit/test_helpers.rb +58 -0
  150. data/test/unit/test_logger.rb +10 -0
  151. data/test/unit/test_models.rb +12 -0
  152. data/test/unit/test_runner.rb +76 -0
  153. data/test/unit/test_scheduler.rb +66 -0
  154. data/uploads/.gitignore +2 -0
  155. data/vendor/java/.gitignore +5 -0
  156. data/webroot/public/css/960.css +1 -0
  157. data/webroot/public/css/dataTables.css +1057 -0
  158. data/webroot/public/css/jquery-ui.css +572 -0
  159. data/webroot/public/css/jquery.treeview.css +68 -0
  160. data/webroot/public/css/reset.css +1 -0
  161. data/webroot/public/css/style.css +504 -0
  162. data/webroot/public/css/text.css +1 -0
  163. data/webroot/public/favicon.ico +0 -0
  164. data/webroot/public/images/12_col.gif +0 -0
  165. data/webroot/public/images/16_col.gif +0 -0
  166. data/webroot/public/images/add.png +0 -0
  167. data/webroot/public/images/ajax-loader.gif +0 -0
  168. data/webroot/public/images/cog.png +0 -0
  169. data/webroot/public/images/coupler.png +0 -0
  170. data/webroot/public/images/foo.png +0 -0
  171. data/webroot/public/images/hammer.png +0 -0
  172. data/webroot/public/images/header.png +0 -0
  173. data/webroot/public/images/home.gif +0 -0
  174. data/webroot/public/images/jobs.gif +0 -0
  175. data/webroot/public/images/sidebar-bottom.png +0 -0
  176. data/webroot/public/images/sidebar.png +0 -0
  177. data/webroot/public/images/treeview-default-line.gif +0 -0
  178. data/webroot/public/images/treeview-default.gif +0 -0
  179. data/webroot/public/images/ui-anim_basic_16x16.gif +0 -0
  180. data/webroot/public/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  181. data/webroot/public/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
  182. data/webroot/public/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
  183. data/webroot/public/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  184. data/webroot/public/images/ui-bg_glass_75_dadada_1x400.png +0 -0
  185. data/webroot/public/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  186. data/webroot/public/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  187. data/webroot/public/images/ui-bg_highlight-hard_30_565356_1x100.png +0 -0
  188. data/webroot/public/images/ui-bg_highlight-hard_75_888588_1x100.png +0 -0
  189. data/webroot/public/images/ui-bg_highlight-soft_30_6e3b3a_1x100.png +0 -0
  190. data/webroot/public/images/ui-bg_highlight-soft_35_8e8b8e_1x100.png +0 -0
  191. data/webroot/public/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  192. data/webroot/public/images/ui-icons_222222_256x240.png +0 -0
  193. data/webroot/public/images/ui-icons_2e83ff_256x240.png +0 -0
  194. data/webroot/public/images/ui-icons_454545_256x240.png +0 -0
  195. data/webroot/public/images/ui-icons_888888_256x240.png +0 -0
  196. data/webroot/public/images/ui-icons_cd0a0a_256x240.png +0 -0
  197. data/webroot/public/images/ui-icons_ffffff_256x240.png +0 -0
  198. data/webroot/public/js/ajaxupload.js +673 -0
  199. data/webroot/public/js/application.js +40 -0
  200. data/webroot/public/js/jquery-ui.combobox.js +98 -0
  201. data/webroot/public/js/jquery-ui.js +9867 -0
  202. data/webroot/public/js/jquery-ui.min.js +559 -0
  203. data/webroot/public/js/jquery.dataTables.min.js +587 -0
  204. data/webroot/public/js/jquery.min.js +154 -0
  205. data/webroot/public/js/jquery.timeago.js +140 -0
  206. data/webroot/public/js/jquery.tooltip.min.js +19 -0
  207. data/webroot/public/js/jquery.treeview.min.js +15 -0
  208. data/webroot/public/js/resource.js +11 -0
  209. data/webroot/public/js/results.js +56 -0
  210. data/webroot/public/js/transformations.js +95 -0
  211. data/webroot/views/connections/index.erb +5 -0
  212. data/webroot/views/connections/list.erb +34 -0
  213. data/webroot/views/connections/new.erb +55 -0
  214. data/webroot/views/connections/show.erb +36 -0
  215. data/webroot/views/imports/edit.erb +60 -0
  216. data/webroot/views/imports/form.erb +81 -0
  217. data/webroot/views/imports/new.erb +89 -0
  218. data/webroot/views/index.erb +12 -0
  219. data/webroot/views/jobs/index.erb +7 -0
  220. data/webroot/views/jobs/list.erb +24 -0
  221. data/webroot/views/layout.erb +38 -0
  222. data/webroot/views/matchers/form.erb +250 -0
  223. data/webroot/views/matchers/list.erb +32 -0
  224. data/webroot/views/projects/form.erb +14 -0
  225. data/webroot/views/projects/index.erb +96 -0
  226. data/webroot/views/projects/show.erb +24 -0
  227. data/webroot/views/resources/edit.erb +88 -0
  228. data/webroot/views/resources/index.erb +5 -0
  229. data/webroot/views/resources/list.erb +27 -0
  230. data/webroot/views/resources/new.erb +121 -0
  231. data/webroot/views/resources/show.erb +86 -0
  232. data/webroot/views/resources/transform.erb +2 -0
  233. data/webroot/views/results/csv.erb +12 -0
  234. data/webroot/views/results/details.erb +15 -0
  235. data/webroot/views/results/index.erb +2 -0
  236. data/webroot/views/results/list.erb +22 -0
  237. data/webroot/views/results/record.erb +24 -0
  238. data/webroot/views/results/show.erb +68 -0
  239. data/webroot/views/scenarios/index.erb +5 -0
  240. data/webroot/views/scenarios/list.erb +20 -0
  241. data/webroot/views/scenarios/new.erb +99 -0
  242. data/webroot/views/scenarios/run.erb +2 -0
  243. data/webroot/views/scenarios/show.erb +50 -0
  244. data/webroot/views/sidebar.erb +106 -0
  245. data/webroot/views/transformations/create.erb +115 -0
  246. data/webroot/views/transformations/for.erb +16 -0
  247. data/webroot/views/transformations/index.erb +2 -0
  248. data/webroot/views/transformations/list.erb +29 -0
  249. data/webroot/views/transformations/new.erb +126 -0
  250. data/webroot/views/transformations/preview.erb +46 -0
  251. data/webroot/views/transformers/edit.erb +6 -0
  252. data/webroot/views/transformers/form.erb +58 -0
  253. data/webroot/views/transformers/index.erb +2 -0
  254. data/webroot/views/transformers/list.erb +25 -0
  255. data/webroot/views/transformers/new.erb +5 -0
  256. data/webroot/views/transformers/preview.erb +23 -0
  257. data/webroot/views/transformers/show.erb +0 -0
  258. metadata +558 -0
@@ -0,0 +1,78 @@
1
+ require 'helper'
2
+
3
+ class TestImport < Coupler::Test::IntegrationTest
4
+
5
+ test "import!" do
6
+ project = Project.create(:name => "foo")
7
+ import = Import.create(:data => fixture_file_upload("people.csv"), :project => project)
8
+ import.import!
9
+ assert_not_nil import.occurred_at
10
+
11
+ project.local_database do |db|
12
+ name = :"import_#{import.id}"
13
+ assert db.tables.include?(name)
14
+ schema = db.schema(name)
15
+ assert_equal [:integer, true], schema.assoc(:id)[1].values_at(:type, :primary_key)
16
+ assert_equal :string, schema.assoc(:first_name)[1][:type]
17
+ assert_equal :string, schema.assoc(:last_name)[1][:type]
18
+ assert_equal :integer, schema.assoc(:age)[1][:type]
19
+
20
+ ds = db[name]
21
+ assert_equal 50, ds.count
22
+ end
23
+ end
24
+
25
+ test "flags duplicate primary keys" do
26
+ tempfile = Tempfile.new('coupler-import')
27
+ tempfile.write("id,foo,bar\n1,abc,def\n2,ghi,jkl\n2,mno,pqr")
28
+ tempfile.close
29
+
30
+ project = Project.create(:name => "foo")
31
+ import = Import.create(:data => file_upload(tempfile.path), :project => project)
32
+
33
+ assert !import.import!
34
+ assert import.has_duplicate_keys
35
+
36
+ project.local_database do |db|
37
+ ds = db[:"import_#{import.id}"]
38
+ assert ds.filter(:id => 2).select_map(:dup_key_count).all?
39
+ end
40
+ end
41
+
42
+ test "repair duplicate keys" do
43
+ project = Project.create(:name => "foo")
44
+ import = Import.create(:data => fixture_file_upload('duplicate-keys.csv'), :project => project)
45
+ import.import!
46
+
47
+ import.repair_duplicate_keys!(nil)
48
+ import.dataset do |ds|
49
+ assert !ds.columns!.include?(:dup_key_count)
50
+ assert_equal 1, ds.filter(:id => 2).count
51
+ assert_equal 1, ds.filter(:id => 4).count
52
+ end
53
+ project.local_database do |db|
54
+ assert db.schema(:"import_#{import.id}").assoc(:id)[1][:primary_key]
55
+ end
56
+ end
57
+
58
+ test "repair duplicate keys with deletions" do
59
+ tempfile = Tempfile.new('coupler-import')
60
+ tempfile.write("id,foo,bar\n1,2,3\n1,4,5\n1,6,7\n123,456,789\n")
61
+ tempfile.close
62
+
63
+ project = Project.create(:name => "foo")
64
+ import = Import.create(:data => file_upload(tempfile.path), :project => project)
65
+ import.import!
66
+
67
+ import.repair_duplicate_keys!({"1" => ["1"]})
68
+ import.dataset do |ds|
69
+ assert !ds.columns!.include?(:dup_key_count)
70
+ assert_equal 1, ds.filter(:id => 1).count
71
+ assert_equal 1, ds.filter(:id => 124).count
72
+ assert_equal 0, ds.filter(:id => 125).count
73
+ end
74
+ project.local_database do |db|
75
+ assert db.schema(:"import_#{import.id}").assoc(:id)[1][:primary_key]
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,379 @@
1
+ require 'helper'
2
+
3
+ class TestRunningScenarios < Coupler::Test::IntegrationTest
4
+
5
+ def self.startup
6
+ super
7
+ Connection.delete
8
+ each_adapter do |adapter, config|
9
+ conn = new_connection(adapter, :name => "#{adapter} connection").save!
10
+ conn.database do |db|
11
+ tables = db.tables
12
+ db.drop_table(:basic_self_linkage) if tables.include?(:basic_self_linkage)
13
+ db.drop_table(:basic_cross_linkage) if tables.include?(:basic_cross_linkage)
14
+ TableMaker.new(db, :basic_self_linkage, <<-EOF)
15
+ +-------------+-------------+
16
+ | id(Integer) | foo(String) |
17
+ +=============+=============+
18
+ | 1 | foo |
19
+ | 2 | foo |
20
+ | 3 | foo |
21
+ | 4 | foo |
22
+ | 5 | foo |
23
+ | 6 | bar |
24
+ | 7 | bar |
25
+ | 8 | bar |
26
+ | 9 | bar |
27
+ | 10 | bar |
28
+ | 11 | baz |
29
+ | 12 | baz |
30
+ | 13 | baz |
31
+ | 14 | baz |
32
+ | 15 | baz |
33
+ | 16 | abc |
34
+ | 17 | def |
35
+ | 18 | ghi |
36
+ | 19 | jkl |
37
+ | 20 | mno |
38
+ +-------------+-------------+
39
+ EOF
40
+
41
+ TableMaker.new(db, :basic_cross_linkage, <<-EOF)
42
+ +-------------+-------------+-------------+
43
+ | id(Integer) | foo(String) | bar(String) |
44
+ +=============+=============+=============+
45
+ | 18 | foo | |
46
+ | 16 | foo | |
47
+ | 17 | foo | |
48
+ | 15 | | foo |
49
+ | 13 | | foo |
50
+ | 10 | | foo |
51
+ | 19 | | foo |
52
+ | 25 | bar | |
53
+ | 23 | bar | |
54
+ | 20 | bar | |
55
+ | 29 | bar | |
56
+ | 28 | | bar |
57
+ | 26 | | bar |
58
+ | 27 | | bar |
59
+ | 38 | baz | |
60
+ | 35 | | baz |
61
+ | 33 | | baz |
62
+ | 30 | | baz |
63
+ | 39 | | baz |
64
+ | 45 | quux | |
65
+ | 43 | quux | |
66
+ | 40 | quux | |
67
+ | 49 | quux | |
68
+ | 48 | | quux |
69
+ | 55 | ditto | ditto |
70
+ | 53 | ditto | ditto |
71
+ +-------------+-------------+-------------+
72
+ EOF
73
+
74
+ db.create_table!(:resource_1) do
75
+ primary_key :id
76
+ String :ssn, :index => true
77
+ String :dob, :index => true
78
+ Integer :foo, :index => true
79
+ Integer :bar, :index => true
80
+ Integer :age, :index => true
81
+ Integer :height, :index => true
82
+ index [:id, :ssn]
83
+ index [:id, :ssn, :dob]
84
+ index [:id, :foo, :bar]
85
+ index [:id, :age]
86
+ end
87
+ rows = Array.new(11000) do |i|
88
+ [
89
+ i < 10500 ? "1234567%02d" % (i / 350) : "9876%05d" % i,
90
+ i < 10500 ? "2000-01-%02d" % (i / 525) : nil,
91
+ i < 1000 ? ((x = i / 125) == 2 ? 123 : x) : nil,
92
+ i < 1000 ? i % 10 : nil,
93
+ i % 20 + 25,
94
+ i % 50 + 125,
95
+ ]
96
+ end
97
+ db[:resource_1].import([:ssn, :dob, :foo, :bar, :age, :height], rows)
98
+
99
+ db.create_table!(:resource_2) do
100
+ primary_key :id
101
+ String :SocialSecurityNumber
102
+ index [:id, :SocialSecurityNumber]
103
+ end
104
+ rows = Array.new(21000) do |i|
105
+ [
106
+ i < 10500 ? "1234567%02d" % (i % 30) : "9876%05d" % i,
107
+ ]
108
+ end
109
+ db[:resource_2].import([:SocialSecurityNumber], rows)
110
+ end
111
+ end
112
+ end
113
+
114
+ #def self.shutdown
115
+ #each_adapter do |adapter, config|
116
+ #conn = Connection[:name => "#{adapter} connection"]
117
+ #conn.database do |db|
118
+ #db.drop_table(:basic_cross_linkage)
119
+ #db.drop_table(:basic_self_linkage)
120
+ #end
121
+ #end
122
+ #super
123
+ #end
124
+
125
+ def setup
126
+ super
127
+ @project = Project.create(:name => "foo")
128
+ end
129
+
130
+ each_adapter do |adapter, _|
131
+ adapter_test(adapter, "default csv export self linkage") do
132
+ conn = new_connection(adapter, :name => :"#{adapter} connection").save!
133
+ resource = Resource.create(:name => 'foo', :table_name => 'basic_self_linkage', :project => @project, :connection => conn)
134
+ scenario = Scenario.create(:name => 'foo', :resource_1 => resource, :project => @project)
135
+ foo_field = resource.fields_dataset[:name => 'foo']
136
+ matcher = Matcher.create({
137
+ :scenario => scenario,
138
+ :comparisons_attributes => [{
139
+ 'lhs_type' => 'field', 'raw_lhs_value' => foo_field.id, 'lhs_which' => 1,
140
+ 'rhs_type' => 'field', 'raw_rhs_value' => foo_field.id, 'rhs_which' => 2,
141
+ 'operator' => 'equals'
142
+ }]
143
+ })
144
+ scenario.run!
145
+ assert_not_nil scenario.last_run_at
146
+ assert_equal 1, scenario.run_count
147
+ assert_equal 1, scenario.results_dataset.count
148
+
149
+ result = scenario.results_dataset.first
150
+ csv = result.to_csv
151
+
152
+ # FIXME: add some actual csv tests you lazy bastard
153
+ end
154
+
155
+ adapter_test(adapter, "default csv export cross linkage") do
156
+ conn = new_connection(adapter, :name => :"#{adapter} connection").save!
157
+ resource = Resource.create(:name => 'foo', :table_name => 'basic_cross_linkage', :project => @project, :connection => conn)
158
+ scenario = Scenario.create(:name => 'foo', :resource_1 => resource, :project => @project)
159
+ foo_field = resource.fields_dataset[:name => 'foo']
160
+ bar_field = resource.fields_dataset[:name => 'bar']
161
+ matcher = Matcher.create({
162
+ :scenario => scenario,
163
+ :comparisons_attributes => [{
164
+ 'lhs_type' => 'field', 'raw_lhs_value' => foo_field.id, 'lhs_which' => 1,
165
+ 'rhs_type' => 'field', 'raw_rhs_value' => bar_field.id, 'rhs_which' => 2,
166
+ 'operator' => 'equals'
167
+ }]
168
+ })
169
+ scenario.run!
170
+ result = scenario.results_dataset.first
171
+ csv = result.to_csv
172
+
173
+ # FIXME: add some actual csv tests you lazy bastard
174
+ end
175
+
176
+ adapter_test(adapter, "self linkage with one comparison") do
177
+ conn = new_connection(adapter, :name => :"#{adapter} connection").save!
178
+ resource = Resource.create(:name => 'resource_1', :table_name => 'resource_1', :project => @project, :connection => conn)
179
+ scenario = Scenario.create(:name => 'foo', :resource_1 => resource, :project => @project)
180
+ field = resource.fields_dataset[:name => 'ssn']
181
+ matcher = Matcher.create({
182
+ :scenario => scenario, :comparisons_attributes => [{
183
+ 'lhs_type' => 'field', 'raw_lhs_value' => field.id, 'lhs_which' => 1,
184
+ 'rhs_type' => 'field', 'raw_rhs_value' => field.id, 'rhs_which' => 2,
185
+ 'operator' => 'equals'
186
+ }]
187
+ })
188
+ scenario.run!
189
+
190
+ groups = {}
191
+ scenario.local_database do |db|
192
+ assert db.tables.include?(:groups_records_1)
193
+ ds = db[:groups_records_1]
194
+ assert_equal 10500, ds.count
195
+ counts = ds.group_and_count(:group_id).all
196
+ assert_equal 30, counts.length
197
+ assert counts.all? { |g| g[:count] == 350 }
198
+ assert ds.group_and_count(:record_id).all? { |r| r[:count] == 1 }
199
+ ds.each do |row|
200
+ record_id = groups[row[:group_id]]
201
+ if record_id
202
+ assert_equal (record_id - 1) / 350, (row[:record_id].to_i - 1) / 350, "Record #{row[:record_id]} should not have been in the same group as Record #{record_id}."
203
+ else
204
+ groups[row[:group_id]] = row[:record_id].to_i
205
+ end
206
+ end
207
+ end
208
+ end
209
+
210
+ adapter_test(adapter, "self linkage with two comparisons") do
211
+ conn = new_connection(adapter, :name => :"#{adapter} connection").save!
212
+ resource = Resource.create(:name => 'resource_1', :table_name => 'resource_1', :project => @project, :connection => conn)
213
+ scenario = Scenario.create(:name => 'foo', :resource_1 => resource, :project => @project)
214
+ field_1 = resource.fields_dataset[:name => 'ssn']
215
+ field_2 = resource.fields_dataset[:name => 'dob']
216
+ matcher = Matcher.create({
217
+ :scenario => scenario, :comparisons_attributes => [
218
+ {
219
+ 'lhs_type' => 'field', 'raw_lhs_value' => field_1.id, 'lhs_which' => 1,
220
+ 'rhs_type' => 'field', 'raw_rhs_value' => field_1.id, 'rhs_which' => 2,
221
+ 'operator' => 'equals'
222
+ },
223
+ {
224
+ 'lhs_type' => 'field', 'raw_lhs_value' => field_2.id, 'lhs_which' => 1,
225
+ 'rhs_type' => 'field', 'raw_rhs_value' => field_2.id, 'rhs_which' => 2,
226
+ 'operator' => 'equals'
227
+ },
228
+ ]
229
+ })
230
+ scenario.run!
231
+
232
+ groups = {}
233
+ scenario.local_database do |db|
234
+ assert db.tables.include?(:groups_records_1)
235
+ ds = db[:groups_records_1]
236
+ assert_equal 10500, ds.count
237
+
238
+ counts = ds.group_and_count(:group_id)
239
+ assert_equal 20, counts.having(:count => 175).count
240
+ assert_equal 20, counts.having(:count => 350).count
241
+ assert ds.group_and_count(:record_id).all? { |r| r[:count] == 1 }
242
+ ds.each do |row|
243
+ record_id = groups[row[:group_id]]
244
+ if record_id
245
+ assert_equal (record_id - 1) / 350, (row[:record_id].to_i - 1) / 350, "Record #{row[:record_id]} should not have been in the same group as Record #{record_id}."
246
+ assert_equal (record_id - 1) / 525, (row[:record_id].to_i - 1) / 525, "Record #{row[:record_id]} should not have been in the same group as Record #{record_id}."
247
+ else
248
+ groups[row[:group_id]] = row[:record_id].to_i
249
+ end
250
+ end
251
+ end
252
+ end
253
+
254
+ adapter_test(adapter, "self linkage with cross match") do
255
+ conn = new_connection(adapter, :name => :"#{adapter} connection").save!
256
+ resource = Resource.create(:name => 'resource_1', :table_name => 'resource_1', :project => @project, :connection => conn)
257
+ scenario = Scenario.create(:name => 'foo', :resource_1 => resource, :project => @project)
258
+ field_1 = resource.fields_dataset[:name => 'foo']
259
+ field_2 = resource.fields_dataset[:name => 'bar']
260
+ matcher = Matcher.create({
261
+ :scenario => scenario, :comparisons_attributes => [
262
+ {
263
+ 'lhs_type' => 'field', 'raw_lhs_value' => field_1.id, 'lhs_which' => 1,
264
+ 'rhs_type' => 'field', 'raw_rhs_value' => field_2.id, 'rhs_which' => 2,
265
+ 'operator' => 'equals'
266
+ },
267
+ ]
268
+ })
269
+ scenario.run!
270
+
271
+ groups = {}
272
+ scenario.local_database do |db|
273
+ assert db.tables.include?(:groups_records_1)
274
+ join_ds = db[:groups_records_1]
275
+ # Breakdown of groups_records_1
276
+ # - Values that should match each other: 0, 1, 3, 4, 5, 6, 7
277
+ # - For each value, there are 125 records that should match in foo
278
+ # Subtotal: 875
279
+ # - For each value, there are 100 records that should match in bar
280
+ # Subtotal: 700
281
+ # * Expected Total: 1575
282
+ assert_equal 1575, join_ds.count
283
+
284
+ assert db.tables.include?(:groups_1)
285
+ group_ds = db[:groups_1]
286
+ assert_equal 7, group_ds.count
287
+ group_ds.each do |group_row|
288
+ assert_equal 125, group_row[:"resource_1_count"], "Row counts didn't match"
289
+ assert_equal 100, group_row[:"resource_2_count"], "Row counts didn't match"
290
+ end
291
+
292
+ assert_equal 0, join_ds.group_and_count(:group_id).having(:count => 1).count
293
+ end
294
+ end
295
+
296
+ adapter_test(adapter, "self linkage with blocking") do
297
+ conn = new_connection(adapter, :name => :"#{adapter} connection").save!
298
+ resource = Resource.create(:name => 'resource_1', :table_name => 'resource_1', :project => @project, :connection => conn)
299
+ scenario = Scenario.create(:name => 'foo', :resource_1 => resource, :project => @project)
300
+ field_1 = resource.fields_dataset[:name => 'age']
301
+ field_2 = resource.fields_dataset[:name => 'height']
302
+ matcher = Matcher.create({
303
+ :scenario => scenario, :comparisons_attributes => [
304
+ {
305
+ 'lhs_type' => 'field', 'raw_lhs_value' => field_1.id, 'lhs_which' => 1,
306
+ 'rhs_type' => 'field', 'raw_rhs_value' => field_1.id, 'rhs_which' => 2,
307
+ 'operator' => 'equals'
308
+ },
309
+ {
310
+ 'lhs_type' => 'field', 'raw_lhs_value' => field_1.id, 'lhs_which' => 1,
311
+ 'rhs_type' => 'integer', 'raw_rhs_value' => 30,
312
+ 'operator' => 'greater_than'
313
+ },
314
+ {
315
+ 'lhs_type' => 'field', 'raw_lhs_value' => field_2.id, 'lhs_which' => 1,
316
+ 'rhs_type' => 'integer', 'raw_rhs_value' => 150,
317
+ 'operator' => 'greater_than'
318
+ },
319
+ ]
320
+ })
321
+ scenario.run!
322
+
323
+ groups = {}
324
+ scenario.local_database do |db|
325
+ assert db.tables.include?(:groups_records_1)
326
+ ds = db[:groups_records_1]
327
+ assert ds.group_and_count(:record_id).all? { |r| r[:count] == 1 }
328
+ ds.each do |row|
329
+ index = row[:record_id].to_i - 1
330
+ assert index % 20 > 5, "#{row[:record_id]}'s age is too small"
331
+ assert index % 50 > 25, "#{row[:record_id]}'s height is too small"
332
+
333
+ record_id = groups[row[:group_id]]
334
+ if record_id
335
+ assert_equal (record_id - 1) % 20 + 25, (row[:record_id].to_i - 1) % 20 + 25, "Record #{row[:record_id]} should not have been in the same group as Record #{record_id}."
336
+ else
337
+ groups[row[:group_id]] = row[:record_id].to_i
338
+ end
339
+ end
340
+ end
341
+ end
342
+
343
+ adapter_test(adapter, "dual linkage with one comparison") do
344
+ conn = new_connection(adapter, :name => :"#{adapter} connection").save!
345
+ resource_1 = Resource.create(:name => 'resource_1', :table_name => 'resource_1', :project => @project, :connection => conn)
346
+ resource_2 = Resource.create(:name => 'resource_2', :table_name => 'resource_2', :project => @project, :connection => conn)
347
+ scenario = Scenario.create(:name => 'foo', :resource_1 => resource_1, :resource_2 => resource_2, :project => @project)
348
+ field_1 = resource_1.fields_dataset[:name => 'ssn']
349
+ field_2 = resource_2.fields_dataset[:name => 'SocialSecurityNumber']
350
+ assert field_1, "ssn field couldn't be found: #{resource_1.fields_dataset.collect(&:name).inspect}"
351
+ assert field_2, "socialsecuritynumber field couldn't be found: #{resource_2.fields_dataset.collect(&:name).inspect}"
352
+ matcher = Matcher.create({
353
+ :scenario => scenario, :comparisons_attributes => [{
354
+ 'lhs_type' => 'field', 'raw_lhs_value' => field_1.id, 'lhs_which' => 1,
355
+ 'rhs_type' => 'field', 'raw_rhs_value' => field_2.id, 'rhs_which' => 2,
356
+ 'operator' => 'equals'
357
+ }]
358
+ })
359
+ scenario.run!
360
+
361
+ groups = {}
362
+ scenario.local_database do |db|
363
+ assert db.tables.include?(:groups_records_1)
364
+ ds = db[:groups_records_1]
365
+ assert_equal 22000, ds.count
366
+
367
+ counts = ds.group_and_count(:group_id).all
368
+ assert_equal 530, counts.length
369
+ counts = counts.inject({}) { |h, r| h[r[:count]] ||= 0; h[r[:count]] += 1; h }
370
+ assert_equal 30, counts[700]
371
+ assert_equal 500, counts[2]
372
+ assert ds.group_and_count(:record_id, :which).all? { |r| r[:count] == 1 }
373
+
374
+ ds = db[:groups_1]
375
+ assert_equal 530, ds.count
376
+ end
377
+ end
378
+ end
379
+ end
@@ -0,0 +1,56 @@
1
+ require 'helper'
2
+
3
+ class TestTransformation < Coupler::Test::IntegrationTest
4
+ def self.startup
5
+ super
6
+ Connection.delete
7
+ each_adapter do |adapter, config|
8
+ data = Array.new(50) { [Forgery(:name).first_name, Forgery(:name).last_name] }
9
+ conn = new_connection(adapter, :name => "#{adapter} connection").save!
10
+ conn.database do |db|
11
+ db.create_table!(:test_data) do
12
+ primary_key :id
13
+ String :first_name
14
+ String :last_name
15
+ end
16
+ db[:test_data].import([:first_name, :last_name], data)
17
+ end
18
+ end
19
+ end
20
+
21
+ def adapter_setup(adapter)
22
+ @connection = new_connection(adapter, :name => "#{adapter} connection").save!
23
+ @project = Project.create(:name => 'foo')
24
+ @resource = Resource.create(:name => 'test', :connection => @connection, :table_name => 'test_data', :project => @project)
25
+ end
26
+
27
+ each_adapter do |adapter, _|
28
+ adapter_test(adapter, "uses local db type to determine field type") do
29
+ adapter_setup(adapter)
30
+ string_to_int = Transformer.create(:name => 'string_to_int', :allowed_types => %w{string}, :code => "value.length", :result_type => "integer")
31
+ int_to_string = Transformer.create(:name => 'int_to_string', :allowed_types => %w{integer}, :code => "value.to_s", :result_type => "string")
32
+ field = @resource.fields_dataset[:name => 'first_name']
33
+ xformation_1 = Transformation.create(:transformer => string_to_int, :source_field => field, :resource => @resource)
34
+ xformation_2 = Transformation.new(:transformer => int_to_string, :source_field => field, :resource => @resource)
35
+ assert xformation_2.valid?, xformation_2.errors.full_messages.join("; ")
36
+ end
37
+
38
+ adapter_test(adapter, "accepts nested attributes for result field") do
39
+ adapter_setup(adapter)
40
+ transformer = Factory(:transformer, :code => %{value}, :result_type => 'same')
41
+ field = @resource.fields_dataset[:name => 'first_name']
42
+ transformation = Transformation.create({
43
+ :transformer => transformer,
44
+ :resource => @resource,
45
+ :source_field => field,
46
+ :result_field_attributes => { :name => 'new_first_name' }
47
+ })
48
+
49
+ result_field = transformation.result_field.refresh
50
+ assert_equal field[:type], result_field[:type]
51
+ assert_equal field[:db_type], result_field[:db_type]
52
+ assert result_field[:is_generated]
53
+ end
54
+
55
+ end
56
+ end
@@ -0,0 +1,154 @@
1
+ require 'helper'
2
+
3
+ class TestTransforming < Coupler::Test::IntegrationTest
4
+
5
+ def self.startup
6
+ super
7
+ Connection.delete
8
+ each_adapter do |adapter, config|
9
+ data = Array.new(50) { [Forgery(:name).first_name, Forgery(:name).last_name] }
10
+ conn = new_connection(adapter, :name => "#{adapter} connection").save!
11
+ conn.database do |db|
12
+ db.create_table!(:test_data) do
13
+ primary_key :id
14
+ String :first_name
15
+ String :last_name
16
+ end
17
+ db[:test_data].import([:first_name, :last_name], data)
18
+ end
19
+ end
20
+ end
21
+
22
+ def setup
23
+ super
24
+ @project = Project.create(:name => "Test project")
25
+ end
26
+
27
+ def adapter_setup(adapter)
28
+ @connection = new_connection(adapter, :name => "#{adapter} connection").save!
29
+ @resource = Resource.create(:name => "Test resource", :table_name => 'test_data', :project => @project, :connection => @connection)
30
+ @transformer = Transformer.create(:name => "downcaser", :allowed_types => %w{string}, :code => 'value.downcase', :result_type => 'same')
31
+ end
32
+
33
+ each_adapter do |adapter, _|
34
+ adapter_test(adapter, "single transformation") do
35
+ adapter_setup(adapter)
36
+ transformation = Transformation.create({
37
+ :resource => @resource, :transformer => @transformer,
38
+ :source_field => @resource.fields_dataset[:name => 'first_name']
39
+ })
40
+
41
+ @resource.transform!
42
+ assert @resource.transformed_at
43
+ assert_equal "#{transformation.id}", @resource.transformed_with
44
+ assert_equal "ok", @resource.status
45
+
46
+ @connection.database do |source_db|
47
+ @project.local_database do |local_db|
48
+ table_name = :"resource_#{@resource.id}"
49
+ assert_equal :string, local_db.schema(table_name).assoc(:first_name)[1][:type]
50
+ local_ds = local_db[table_name]
51
+ source_db[:test_data].each do |source_row|
52
+ local_row = local_ds[:id => source_row[:id]]
53
+ assert_equal source_row[:first_name].downcase, local_row[:first_name]
54
+ end
55
+ end
56
+ end
57
+ end
58
+
59
+ adapter_test(adapter, "transform goes in order of position") do
60
+ adapter_setup(adapter)
61
+ first_name = @resource.fields_dataset[:name => 'first_name']
62
+ foo_transformer = Transformer.create(:name => "foo", :allowed_types => %w{string}, :code => '"FOO"', :result_type => 'same')
63
+ xformation_1 = Transformation.create(:resource => @resource, :transformer => @transformer, :source_field => first_name)
64
+ xformation_2 = Transformation.create(:resource => @resource, :transformer => foo_transformer, :source_field => first_name)
65
+ xformation_1.update(:position => 2)
66
+ xformation_2.update(:position => 1)
67
+
68
+ @resource.transform!
69
+ @resource.local_dataset do |ds|
70
+ ds.each { |r| assert_equal "foo", r[:first_name] }
71
+ end
72
+ end
73
+
74
+ adapter_test(adapter, "transform into new result field") do
75
+ adapter_setup(adapter)
76
+ transformation = Transformation.create({
77
+ :resource => @resource, :transformer => @transformer,
78
+ :source_field => @resource.fields_dataset[:name => 'first_name'],
79
+ :result_field_attributes => { :name => 'downcased_first_name' }
80
+ })
81
+ @resource.transform!
82
+ @resource.local_dataset do |ds|
83
+ ds.each { |r| assert_equal r[:first_name].downcase, r[:downcased_first_name] }
84
+ end
85
+ end
86
+
87
+ adapter_test(adapter, "transforming only gets specified columns") do
88
+ adapter_setup(adapter)
89
+ @resource.fields_dataset.filter(["name IN ?", %w{last_name}]).update(:is_selected => false)
90
+ transformation = Transformation.create({
91
+ :resource => @resource, :transformer => @transformer,
92
+ :source_field => @resource.fields_dataset[:name => 'first_name']
93
+ })
94
+ @resource.transform!
95
+ @project.local_database do |db|
96
+ schema = db.schema(:"resource_#{@resource.id}")
97
+ assert_equal [:id, :first_name], schema.collect(&:first)
98
+ end
99
+ end
100
+
101
+ adapter_test(adapter, "preview inplace transformation") do
102
+ adapter_setup(adapter)
103
+ strlen = Transformer.create(:name => "strlen", :code => 'value.length', :allowed_types => %w{string}, :result_type => 'integer')
104
+ square = Transformer.create(:name => 'square', :code => "value ** 2", :allowed_types => %w{integer}, :result_type => 'integer')
105
+ first_name = @resource.fields_dataset[:name => 'first_name']
106
+ transformation_1 = Transformation.create({
107
+ :resource => @resource, :transformer => strlen,
108
+ :source_field => first_name
109
+ })
110
+ transformation_2 = Transformation.new({
111
+ :resource => @resource, :transformer => square,
112
+ :source_field => first_name, :result_field => first_name
113
+ })
114
+ arr = @resource.preview_transformation(transformation_2)
115
+ assert_equal [:id, :first_name, :last_name], arr[:fields]
116
+ assert_equal 50, arr[:data].length
117
+ @resource.source_dataset do |ds|
118
+ ds.limit(50).each_with_index do |row, i|
119
+ before = row.merge(:first_name => row[:first_name].length)
120
+ after = before.merge(:first_name => before[:first_name] ** 2)
121
+ result = arr[:data].find { |r| r[:before][:id] == row[:id] }
122
+ assert_equal before, result[:before], "Before didn't match"
123
+ assert_equal after, result[:after], "After didn't match"
124
+ end
125
+ end
126
+ end
127
+
128
+ adapter_test(adapter, "preview transformation with invalid transformer") do
129
+ adapter_setup(adapter)
130
+ first_name = @resource.fields_dataset[:name => 'first_name']
131
+ transformation = Transformation.new({
132
+ :resource => @resource, :source_field => first_name,
133
+ :result_field => first_name, :transformer => nil,
134
+ :transformer_attributes => { :name => "foo", :code => "HAY", :allowed_types => %w{string}, :result_type => "string" }
135
+ })
136
+ result = @resource.preview_transformation(transformation)
137
+ assert_kind_of Exception, result
138
+ end
139
+
140
+ adapter_test(adapter, "transform with progress callback") do
141
+ adapter_setup(adapter)
142
+ first_name = @resource.fields_dataset[:name => 'first_name']
143
+ transformation = Transformation.create({
144
+ :resource => @resource, :source_field => first_name,
145
+ :result_field => first_name, :transformer => @transformer
146
+ })
147
+ count = 0
148
+ @resource.transform! do |n|
149
+ count += 1
150
+ end
151
+ assert count > 0
152
+ end
153
+ end
154
+ end