conceptql 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (196) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/CHANGELOG.md +53 -1
  4. data/Guardfile +52 -24
  5. data/conceptql.gemspec +5 -3
  6. data/doc/spec.md +2 -2
  7. data/lib/conceptql/behaviors/debuggable.rb +70 -0
  8. data/lib/conceptql/behaviors/dottable.rb +28 -14
  9. data/lib/conceptql/behaviors/metadatable.rb +97 -0
  10. data/lib/conceptql/behaviors/preppable.rb +20 -0
  11. data/lib/conceptql/cli.rb +31 -5
  12. data/lib/conceptql/converter.rb +65 -0
  13. data/lib/conceptql/debugger.rb +48 -0
  14. data/lib/conceptql/graph.rb +14 -13
  15. data/lib/conceptql/graph_nodifier.rb +49 -17
  16. data/lib/conceptql/nodifier.rb +31 -6
  17. data/lib/conceptql/operators/after.rb +25 -0
  18. data/lib/conceptql/operators/any_overlap.rb +15 -0
  19. data/lib/conceptql/operators/before.rb +21 -0
  20. data/lib/conceptql/{nodes/binary_operator_node.rb → operators/binary_operator_operator.rb} +12 -8
  21. data/lib/conceptql/{nodes/casting_node.rb → operators/casting_operator.rb} +8 -7
  22. data/lib/conceptql/{nodes → operators}/complement.rb +14 -11
  23. data/lib/conceptql/operators/concept.rb +70 -0
  24. data/lib/conceptql/{nodes → operators}/condition_type.rb +13 -5
  25. data/lib/conceptql/operators/contains.rb +17 -0
  26. data/lib/conceptql/operators/count.rb +26 -0
  27. data/lib/conceptql/operators/cpt.rb +25 -0
  28. data/lib/conceptql/{nodes → operators}/date_range.rb +11 -6
  29. data/lib/conceptql/operators/death.rb +23 -0
  30. data/lib/conceptql/operators/drug_type_concept.rb +21 -0
  31. data/lib/conceptql/{nodes → operators}/during.rb +9 -3
  32. data/lib/conceptql/operators/equal.rb +13 -0
  33. data/lib/conceptql/operators/except.rb +28 -0
  34. data/lib/conceptql/operators/filter.rb +21 -0
  35. data/lib/conceptql/{nodes → operators}/first.rb +7 -5
  36. data/lib/conceptql/{nodes → operators}/from.rb +2 -2
  37. data/lib/conceptql/operators/from_seer_visits.rb +23 -0
  38. data/lib/conceptql/{nodes → operators}/gender.rb +6 -6
  39. data/lib/conceptql/operators/hcpcs.rb +25 -0
  40. data/lib/conceptql/operators/icd10.rb +28 -0
  41. data/lib/conceptql/operators/icd9.rb +28 -0
  42. data/lib/conceptql/operators/icd9_procedure.rb +25 -0
  43. data/lib/conceptql/{nodes → operators}/intersect.rb +7 -3
  44. data/lib/conceptql/{nodes → operators}/last.rb +7 -5
  45. data/lib/conceptql/operators/loinc.rb +25 -0
  46. data/lib/conceptql/operators/medcode.rb +28 -0
  47. data/lib/conceptql/operators/medcode_procedure.rb +27 -0
  48. data/lib/conceptql/operators/ndc.rb +29 -0
  49. data/lib/conceptql/operators/numeric.rb +54 -0
  50. data/lib/conceptql/operators/observation_by_enttype.rb +29 -0
  51. data/lib/conceptql/operators/observation_period.rb +30 -0
  52. data/lib/conceptql/operators/occurrence.rb +70 -0
  53. data/lib/conceptql/operators/one_in_two_out.rb +62 -0
  54. data/lib/conceptql/{nodes/node.rb → operators/operator.rb} +84 -52
  55. data/lib/conceptql/operators/overlapped_by.rb +23 -0
  56. data/lib/conceptql/operators/overlaps.rb +19 -0
  57. data/lib/conceptql/operators/pass_thru.rb +11 -0
  58. data/lib/conceptql/{nodes → operators}/person.rb +8 -4
  59. data/lib/conceptql/operators/person_filter.rb +13 -0
  60. data/lib/conceptql/{nodes → operators}/place_of_service_code.rb +7 -7
  61. data/lib/conceptql/operators/procedure_occurrence.rb +25 -0
  62. data/lib/conceptql/operators/prodcode.rb +29 -0
  63. data/lib/conceptql/{nodes → operators}/race.rb +7 -7
  64. data/lib/conceptql/operators/recall.rb +38 -0
  65. data/lib/conceptql/operators/rxnorm.rb +24 -0
  66. data/lib/conceptql/operators/snomed.rb +24 -0
  67. data/lib/conceptql/operators/snomed_condition.rb +26 -0
  68. data/lib/conceptql/{nodes/source_vocabulary_node.rb → operators/source_vocabulary_operator.rb} +7 -4
  69. data/lib/conceptql/{nodes/standard_vocabulary_node.rb → operators/standard_vocabulary_operator.rb} +6 -4
  70. data/lib/conceptql/{nodes → operators}/started_by.rb +9 -3
  71. data/lib/conceptql/{nodes → operators}/sum.rb +9 -4
  72. data/lib/conceptql/{nodes/temporal_node.rb → operators/temporal_operator.rb} +7 -4
  73. data/lib/conceptql/{nodes → operators}/time_window.rb +19 -7
  74. data/lib/conceptql/operators/to_seer_visits.rb +24 -0
  75. data/lib/conceptql/operators/trim_date_end.rb +55 -0
  76. data/lib/conceptql/operators/trim_date_start.rb +56 -0
  77. data/lib/conceptql/{nodes → operators}/union.rb +6 -2
  78. data/lib/conceptql/operators/visit.rb +15 -0
  79. data/lib/conceptql/{nodes → operators}/visit_occurrence.rb +7 -3
  80. data/lib/conceptql/query.rb +19 -17
  81. data/lib/conceptql/scope.rb +69 -0
  82. data/lib/conceptql/tree.rb +33 -18
  83. data/lib/conceptql/utils/temp_table.rb +72 -0
  84. data/lib/conceptql/version.rb +1 -1
  85. data/spec/conceptql/behaviors/dottable_spec.rb +39 -51
  86. data/spec/conceptql/converter_spec.rb +51 -0
  87. data/spec/conceptql/date_adjuster_spec.rb +15 -15
  88. data/spec/conceptql/operators/after_spec.rb +16 -0
  89. data/spec/conceptql/operators/before_spec.rb +16 -0
  90. data/spec/conceptql/{nodes/casting_node_spec.rb → operators/casting_operator_spec.rb} +16 -20
  91. data/spec/conceptql/operators/complement_spec.rb +15 -0
  92. data/spec/conceptql/operators/concept_spec.rb +40 -0
  93. data/spec/conceptql/{nodes → operators}/condition_type_spec.rb +39 -24
  94. data/spec/conceptql/operators/contains_spec.rb +19 -0
  95. data/spec/conceptql/operators/cpt_spec.rb +29 -0
  96. data/spec/conceptql/operators/date_range_spec.rb +33 -0
  97. data/spec/conceptql/operators/death_spec.rb +10 -0
  98. data/spec/conceptql/operators/during_spec.rb +30 -0
  99. data/spec/conceptql/operators/except_spec.rb +15 -0
  100. data/spec/conceptql/operators/first_spec.rb +35 -0
  101. data/spec/conceptql/operators/from_spec.rb +13 -0
  102. data/spec/conceptql/operators/gender_spec.rb +27 -0
  103. data/spec/conceptql/operators/hcpcs_spec.rb +29 -0
  104. data/spec/conceptql/operators/icd10_spec.rb +34 -0
  105. data/spec/conceptql/operators/icd9_procedure_spec.rb +29 -0
  106. data/spec/conceptql/operators/icd9_spec.rb +34 -0
  107. data/spec/conceptql/operators/intersect_spec.rb +28 -0
  108. data/spec/conceptql/operators/last_spec.rb +36 -0
  109. data/spec/conceptql/operators/loinc_spec.rb +29 -0
  110. data/spec/conceptql/operators/medcode_procedure_spec.rb +34 -0
  111. data/spec/conceptql/operators/medcode_spec.rb +34 -0
  112. data/spec/conceptql/operators/observation_period_spec.rb +10 -0
  113. data/spec/conceptql/operators/occurrence_spec.rb +87 -0
  114. data/spec/conceptql/operators/overlapped_by_spec.rb +32 -0
  115. data/spec/conceptql/operators/overlaps_spec.rb +21 -0
  116. data/spec/conceptql/operators/person_filter_spec.rb +15 -0
  117. data/spec/conceptql/operators/person_spec.rb +10 -0
  118. data/spec/conceptql/{nodes → operators}/place_of_service_code_spec.rb +6 -8
  119. data/spec/conceptql/operators/procedure_occurrence_spec.rb +10 -0
  120. data/spec/conceptql/operators/prodcode_spec.rb +35 -0
  121. data/spec/conceptql/operators/query_double.rb +20 -0
  122. data/spec/conceptql/operators/query_double_spec.rb +7 -0
  123. data/spec/conceptql/operators/race_spec.rb +21 -0
  124. data/spec/conceptql/operators/rxnorm_spec.rb +29 -0
  125. data/spec/conceptql/operators/snomed_spec.rb +29 -0
  126. data/spec/conceptql/operators/source_vocabulary_operator_spec.rb +35 -0
  127. data/spec/conceptql/operators/standard_vocabulary_operator_spec.rb +35 -0
  128. data/spec/conceptql/operators/started_by_spec.rb +22 -0
  129. data/spec/conceptql/{nodes/temporal_node_spec.rb → operators/temporal_operator_spec.rb} +11 -17
  130. data/spec/conceptql/operators/time_window_spec.rb +77 -0
  131. data/spec/conceptql/operators/union_spec.rb +21 -0
  132. data/spec/conceptql/operators/visit_occurrence_spec.rb +10 -0
  133. data/spec/conceptql/query_spec.rb +10 -9
  134. data/spec/conceptql/tree_spec.rb +24 -28
  135. data/spec/doubles/stream_for_casting_double.rb +1 -1
  136. data/spec/doubles/stream_for_occurrence_double.rb +1 -1
  137. data/spec/doubles/stream_for_temporal_double.rb +1 -1
  138. data/spec/spec_helper.rb +74 -58
  139. metadata +202 -133
  140. data/lib/conceptql/nodes/after.rb +0 -12
  141. data/lib/conceptql/nodes/before.rb +0 -11
  142. data/lib/conceptql/nodes/concept.rb +0 -38
  143. data/lib/conceptql/nodes/count.rb +0 -23
  144. data/lib/conceptql/nodes/cpt.rb +0 -20
  145. data/lib/conceptql/nodes/death.rb +0 -19
  146. data/lib/conceptql/nodes/define.rb +0 -96
  147. data/lib/conceptql/nodes/drug_type_concept.rb +0 -18
  148. data/lib/conceptql/nodes/equal.rb +0 -11
  149. data/lib/conceptql/nodes/except.rb +0 -11
  150. data/lib/conceptql/nodes/hcpcs.rb +0 -20
  151. data/lib/conceptql/nodes/icd10.rb +0 -23
  152. data/lib/conceptql/nodes/icd9.rb +0 -23
  153. data/lib/conceptql/nodes/icd9_procedure.rb +0 -20
  154. data/lib/conceptql/nodes/loinc.rb +0 -20
  155. data/lib/conceptql/nodes/numeric.rb +0 -40
  156. data/lib/conceptql/nodes/occurrence.rb +0 -49
  157. data/lib/conceptql/nodes/pass_thru.rb +0 -11
  158. data/lib/conceptql/nodes/person_filter.rb +0 -12
  159. data/lib/conceptql/nodes/procedure_occurrence.rb +0 -21
  160. data/lib/conceptql/nodes/recall.rb +0 -50
  161. data/lib/conceptql/nodes/rxnorm.rb +0 -20
  162. data/lib/conceptql/nodes/snomed.rb +0 -19
  163. data/lib/conceptql/nodes/visit.rb +0 -11
  164. data/spec/conceptql/nodes/after_spec.rb +0 -18
  165. data/spec/conceptql/nodes/before_spec.rb +0 -18
  166. data/spec/conceptql/nodes/complement_spec.rb +0 -15
  167. data/spec/conceptql/nodes/concept_spec.rb +0 -34
  168. data/spec/conceptql/nodes/cpt_spec.rb +0 -31
  169. data/spec/conceptql/nodes/date_range_spec.rb +0 -35
  170. data/spec/conceptql/nodes/death_spec.rb +0 -12
  171. data/spec/conceptql/nodes/during_spec.rb +0 -32
  172. data/spec/conceptql/nodes/except_spec.rb +0 -18
  173. data/spec/conceptql/nodes/first_spec.rb +0 -37
  174. data/spec/conceptql/nodes/from_spec.rb +0 -15
  175. data/spec/conceptql/nodes/gender_spec.rb +0 -29
  176. data/spec/conceptql/nodes/hcpcs_spec.rb +0 -31
  177. data/spec/conceptql/nodes/icd10_spec.rb +0 -36
  178. data/spec/conceptql/nodes/icd9_procedure_spec.rb +0 -31
  179. data/spec/conceptql/nodes/icd9_spec.rb +0 -36
  180. data/spec/conceptql/nodes/intersect_spec.rb +0 -33
  181. data/spec/conceptql/nodes/last_spec.rb +0 -38
  182. data/spec/conceptql/nodes/loinc_spec.rb +0 -31
  183. data/spec/conceptql/nodes/occurrence_spec.rb +0 -89
  184. data/spec/conceptql/nodes/person_filter_spec.rb +0 -18
  185. data/spec/conceptql/nodes/person_spec.rb +0 -12
  186. data/spec/conceptql/nodes/procedure_occurrence_spec.rb +0 -12
  187. data/spec/conceptql/nodes/query_double.rb +0 -19
  188. data/spec/conceptql/nodes/race_spec.rb +0 -23
  189. data/spec/conceptql/nodes/rxnorm_spec.rb +0 -31
  190. data/spec/conceptql/nodes/snomed_spec.rb +0 -31
  191. data/spec/conceptql/nodes/source_vocabulary_node_spec.rb +0 -37
  192. data/spec/conceptql/nodes/standard_vocabulary_node_spec.rb +0 -40
  193. data/spec/conceptql/nodes/started_by_spec.rb +0 -25
  194. data/spec/conceptql/nodes/time_window_spec.rb +0 -85
  195. data/spec/conceptql/nodes/union_spec.rb +0 -25
  196. data/spec/conceptql/nodes/visit_occurrence_spec.rb +0 -12
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7346b1988f7233add68cf70f2f5617a1b2df86fc
4
- data.tar.gz: 893e14fdc17bc4dfed78868017e7cebd5739ddc5
3
+ metadata.gz: bb925d74153217fcb29ee4c4e5874827005246be
4
+ data.tar.gz: 41ae6d964b8a6ae82b37098adcda8548917543bd
5
5
  SHA512:
6
- metadata.gz: a7618751466878c7a78d2c0ef738201ed3612eff584a811e0ebf42f4c3cbeb662cea01a60ce8c6b452d37842aada37923103b5559bd71e34db65f812cad212a8
7
- data.tar.gz: 7f26dee3ab73028146901d121c6fc54cc08bad65e49a890e7a1ff3f97a48d5f11e7c7014984e6de2c2ef0c18f1bd983e2953a8f8b7138b69f4f80052d93808cd
6
+ metadata.gz: f62d0381dbd1ceabed67e9fff58d95975bf4cff27a957f03aa0ba495ca7726b22af3727d26acd4f683ca048a17dac858dffd2bef7f2e0707cae7e5cfd29e6677
7
+ data.tar.gz: 004743713781e5ea7cf7281ea071597ac8fa0959714571639d97d69a9fdd67b03692b10b9e1c77802230f57149212e9daf5077459d02db86814759051b44cc62
data/.gitignore CHANGED
@@ -5,6 +5,7 @@
5
5
  .bundle
6
6
  .config
7
7
  .yardoc
8
+ .idea
8
9
  Gemfile.lock
9
10
  InstalledFiles
10
11
  _yardoc
data/CHANGELOG.md CHANGED
@@ -1,6 +1,58 @@
1
1
  # Changelog
2
2
  All notable changes to this project will be documented in this file.
3
3
 
4
+ ## Unreleased
5
+
6
+ ### Added
7
+ - Optimized After/Before nodes when multi-person results are in the right stream.
8
+ - Set end_date as coalesce(end_date, start_date) to make range if end_date missing.
9
+ - Nodes
10
+ - AnyOverlap
11
+ - Contains
12
+ - Filter
13
+ - Ndc
14
+ - ObservationPeriod
15
+ - OneInTwoOut
16
+ - OverlappedBy
17
+ - Overlaps
18
+ - TrimDateEnd
19
+ - TrimDateStart
20
+ - Nodes for CPRD
21
+ - Medcode
22
+ - MedcodeProcedure
23
+ - ObservationByEnttype
24
+ - Prodcode
25
+ - Nodes for SEER
26
+ - FromSeerVisits
27
+ - ToSeerVisits
28
+ - Ability to limit results to a set of patients by setting Tree#person_ids
29
+ - "units_source_value" and "source_value" columns in results
30
+ - TimeWindow supports date literals
31
+ - ConditionType supports search for "primary"
32
+ - Nodifier#to_metadata
33
+
34
+ ### Changed
35
+ - Except now allows :ignore_date option
36
+ - Comparison is only done on criterion_id/type
37
+ - DateRange START/END use observation_period instead of visit_occurrence
38
+ - Operator#columns value_as_numeric => value_as_number
39
+ - Syntax is now more "lispy"
40
+ - Recall now uses any labeled operator as if that operator was fed to "Define" operator
41
+ - Nodes now called Operators
42
+
43
+ ### Deprecated
44
+ - Nothing.
45
+
46
+ ### Removed
47
+ - Let/Define operators
48
+
49
+ ### Fixed
50
+ - Many broken specs.
51
+ - Union calls #from_self on incoming streams to avoid column issues
52
+ - Concept node now works again
53
+ - Define ensures tables are built
54
+
55
+
4
56
  ## 0.1.1 - 2014-09-18
5
57
 
6
58
  ### Added
@@ -13,7 +65,7 @@ All notable changes to this project will be documented in this file.
13
65
  - Nothing.
14
66
 
15
67
  ### Fixed
16
- - Calling Query#sql no longer creates a bunch of temporary tables
68
+ - Calling Query#sql no longer creates a bunch of temporary tables.
17
69
 
18
70
 
19
71
  ## 0.1.0 - 2014-09-04
data/Guardfile CHANGED
@@ -1,28 +1,56 @@
1
1
  # A sample Guardfile
2
2
  # More info at https://github.com/guard/guard#readme
3
3
 
4
- guard :minitest do
5
- # with Minitest::Unit
6
- watch(%r{^test/(.*)\/?test_(.*)\.rb$})
7
- watch(%r{^lib/(.*/)?([^/]+)\.rb$}) { |m| "test/#{m[1]}test_#{m[2]}.rb" }
8
- watch(%r{^test/test_helper\.rb$}) { 'test' }
9
-
10
- # with Minitest::Spec
11
- watch(%r{^spec/(.*)_spec\.rb$})
12
- watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
13
- watch(%r{^spec/spec_helper\.rb$}) { 'spec' }
14
-
15
- # Rails 4
16
- # watch(%r{^app/(.+)\.rb$}) { |m| "test/#{m[1]}_test.rb" }
17
- # watch(%r{^app/controllers/application_controller\.rb$}) { 'test/controllers' }
18
- # watch(%r{^app/controllers/(.+)_controller\.rb$}) { |m| "test/integration/#{m[1]}_test.rb" }
19
- # watch(%r{^app/views/(.+)_mailer/.+}) { |m| "test/mailers/#{m[1]}_mailer_test.rb" }
20
- # watch(%r{^lib/(.+)\.rb$}) { |m| "test/lib/#{m[1]}_test.rb" }
21
- # watch(%r{^test/.+_test\.rb$})
22
- # watch(%r{^test/test_helper\.rb$}) { 'test' }
23
-
24
- # Rails < 4
25
- # watch(%r{^app/controllers/(.*)\.rb$}) { |m| "test/functional/#{m[1]}_test.rb" }
26
- # watch(%r{^app/helpers/(.*)\.rb$}) { |m| "test/helpers/#{m[1]}_test.rb" }
27
- # watch(%r{^app/models/(.*)\.rb$}) { |m| "test/unit/#{m[1]}_test.rb" }
4
+
5
+ # Note: The cmd option is now required due to the increasing number of ways
6
+ # rspec may be run, below are examples of the most common uses.
7
+ # * bundler: 'bundle exec rspec'
8
+ # * bundler binstubs: 'bin/rspec'
9
+ # * spring: 'bin/rspec' (This will use spring if running and you have
10
+ # installed the spring binstubs per the docs)
11
+ # * zeus: 'zeus rspec' (requires the server to be started separately)
12
+ # * 'just' rspec: 'rspec'
13
+
14
+ guard :rspec, cmd: "bundle exec rspec" do
15
+ require "guard/rspec/dsl"
16
+ dsl = Guard::RSpec::Dsl.new(self)
17
+
18
+ # Feel free to open issues for suggestions and improvements
19
+
20
+ # RSpec files
21
+ rspec = dsl.rspec
22
+ watch(rspec.spec_helper) { rspec.spec_dir }
23
+ watch(rspec.spec_support) { rspec.spec_dir }
24
+ watch(rspec.spec_files)
25
+
26
+ # Ruby files
27
+ ruby = dsl.ruby
28
+ dsl.watch_spec_files_for(ruby.lib_files)
29
+
30
+ # Rails files
31
+ rails = dsl.rails(view_extensions: %w(erb haml slim))
32
+ dsl.watch_spec_files_for(rails.app_files)
33
+ dsl.watch_spec_files_for(rails.views)
34
+
35
+ watch(rails.controllers) do |m|
36
+ [
37
+ rspec.spec.("routing/#{m[1]}_routing"),
38
+ rspec.spec.("controllers/#{m[1]}_controller"),
39
+ rspec.spec.("acceptance/#{m[1]}")
40
+ ]
41
+ end
42
+
43
+ # Rails config changes
44
+ watch(rails.spec_helper) { rspec.spec_dir }
45
+ watch(rails.routes) { "#{rspec.spec_dir}/routing" }
46
+ watch(rails.app_controller) { "#{rspec.spec_dir}/controllers" }
47
+
48
+ # Capybara features specs
49
+ watch(rails.view_dirs) { |m| rspec.spec.("features/#{m[1]}") }
50
+
51
+ # Turnip features and steps
52
+ watch(%r{^spec/acceptance/(.+)\.feature$})
53
+ watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) do |m|
54
+ Dir[File.join("**/#{m[1]}.feature")][0] || "spec/acceptance"
55
+ end
28
56
  end
data/conceptql.gemspec CHANGED
@@ -18,13 +18,15 @@ Gem::Specification.new do |spec|
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ['lib']
20
20
 
21
- spec.add_dependency 'activesupport', '~> 4.1'
21
+ spec.add_dependency 'facets', '~> 3.0'
22
22
  spec.add_dependency 'sequelizer', '~> 0.0'
23
23
  spec.add_dependency 'thor', '~> 0.19'
24
24
  spec.add_dependency 'pg', '~> 0.17'
25
25
  spec.add_dependency 'ruby-graphviz', '~> 1.2'
26
+ spec.add_dependency 'csv2xlsx', '~> 0'
26
27
  spec.add_development_dependency 'bundler', '~> 1.5'
27
28
  spec.add_development_dependency 'rake', '~> 10.3'
28
- spec.add_development_dependency 'minitest', '~> 5.4'
29
- spec.add_development_dependency 'guard-minitest', '~> 2.3'
29
+ spec.add_development_dependency 'rspec', '~> 3.2'
30
+ spec.add_development_dependency 'guard-rspec', '~> 4.5'
31
+ spec.add_development_dependency 'byebug', '~> 4.0'
30
32
  end
data/doc/spec.md CHANGED
@@ -153,7 +153,7 @@ Virtually all other nodes add, remove, filter, or otherwise alter streams of res
153
153
  Because streams represent sets of results, its makes sense to include a nodes that operate on sets
154
154
 
155
155
  ### Union
156
- - Takes any number of child nodes and aggregates their streams
156
+ - Takes any number of upstream nodes and aggregates their streams
157
157
  - Unions together streams with identical types
158
158
  - Think of streams with the same type flowing together into a single stream
159
159
  - We're really just gathering the union of all IDs for identically-typed streams
@@ -1333,7 +1333,7 @@ Just like Filter has an :as option, add one to Except node. This would simplify
1333
1333
 
1334
1334
 
1335
1335
  ### How to Handle fact_relationship Table from CDMv5
1336
- Each relationship type could be a binary node box read as L <relationship> R. E.g. L 'parent of' R would take a L stream and only pass on parents of rows in R stream.
1336
+ Each relationship type could be a binary node box read as L <relationship> R. E.g. L 'downstream of' R would take a L stream and only pass on downstreams of rows in R stream.
1337
1337
 
1338
1338
  We could implement a single node that takes a relationship as an argument (on top of the L and R arguments) or we could create a node class for each relationship. I think it would be better to have a single relationship node class and take the relationship as the argument.
1339
1339
 
@@ -0,0 +1,70 @@
1
+ require_relative '../behaviors/dottable'
2
+ require_relative '../operators/operator'
3
+ require_relative '../operators/binary_operator_operator'
4
+ require 'csv'
5
+ module ConceptQL
6
+ module Behaviors
7
+ module Debuggable
8
+ include Dottable
9
+ class ResultPrinter
10
+ attr :db, :dir, :type, :watch_ids, :operator
11
+ def initialize(db, dir, type, watch_ids, operator)
12
+ @db = db
13
+ @dir = dir
14
+ @type = type
15
+ @watch_ids = watch_ids
16
+ @operator = operator
17
+ end
18
+
19
+ def make_file
20
+ CSV.open(file_path, 'w') do |csv|
21
+ csv << ConceptQL::Operators::Operator::COLUMNS
22
+ results.each do |result|
23
+ csv << result
24
+ end
25
+ end
26
+ file_path
27
+ end
28
+
29
+ def file_path
30
+ @file_path ||= dir + file_name
31
+ end
32
+
33
+ def file_name
34
+ @file_name ||= [operator.operator_name, abbreviate(type)].join('_')
35
+ end
36
+
37
+ def results
38
+ q = operator.evaluate(db)
39
+ .from_self
40
+ .where(criterion_type: type.to_s)
41
+ unless watch_ids.empty?
42
+ q = q.where(person_id: watch_ids)
43
+ end
44
+
45
+ q.order([:person_id, :criterion_type, :start_date, :end_date, :criterion_id])
46
+ .select_map(ConceptQL::Operators::Operator::COLUMNS)
47
+ end
48
+
49
+ def abbreviate(type)
50
+ type.to_s.split('_').map(&:chars).map(&:first).join('')
51
+ end
52
+ end
53
+
54
+ def print_results(db, dir, watch_ids)
55
+ print_prep(db) if respond_to?(:print_prep)
56
+ kids = upstreams
57
+ if self.is_a?(ConceptQL::Operators::BinaryOperatorOperator)
58
+ kids = [left, right]
59
+ end
60
+ files = kids.map do |upstream|
61
+ upstream.print_results(db, dir, watch_ids)
62
+ end
63
+ files += types.map do |type|
64
+ ResultPrinter.new(db, dir, type, watch_ids, self).make_file
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
70
+
@@ -1,4 +1,6 @@
1
- require 'active_support/inflector'
1
+ require 'facets/string/snakecase'
2
+ require 'facets/string/titlecase'
3
+
2
4
  module ConceptQL
3
5
  module Behaviors
4
6
  module Dottable
@@ -15,13 +17,23 @@ module ConceptQL
15
17
  observation: 'magenta',
16
18
  misc: 'black'
17
19
  }
18
- def node_name
19
- @__node_name ||= self.class.name.split('::').last.underscore.gsub(/\W/, '_').downcase + '_' + (@@counter += 1).to_s
20
+
21
+ def operator_number
22
+ @__operator_number ||= (@@counter += 1)
23
+ end
24
+
25
+ def reset_operator_number
26
+ @@counter = 0
27
+ end
28
+
29
+ def operator_name
30
+ @__operator_name ||= self.class.name.split('::').last.snakecase.gsub(/\W/, '_').downcase + "_#{operator_number}"
20
31
  end
21
32
 
22
33
  def display_name
23
34
  @__display_name ||= begin
24
- output = self.class.name.split('::').last.titleize
35
+ output = self.class.name.split('::').last.snakecase.titlecase
36
+ #output += " #{operator_number}"
25
37
  output += ": #{arguments.join(', ')}" unless arguments.empty?
26
38
  if output.length > 100
27
39
  parts = output.split
@@ -39,9 +51,9 @@ module ConceptQL
39
51
  types.length == 1 ? TYPE_COLORS[types.first] || 'black' : 'black'
40
52
  end
41
53
 
42
- def graph_node(g)
43
- @__graph_node ||= begin
44
- me = g.add_nodes(node_name)
54
+ def graph_operator(g)
55
+ @__graph_operator ||= begin
56
+ me = g.add_nodes(operator_name)
45
57
  me[:label] = display_name
46
58
  me[:color] = type_color(types)
47
59
  me[:shape] = shape if respond_to?(:shape)
@@ -49,7 +61,7 @@ module ConceptQL
49
61
  end
50
62
  end
51
63
 
52
- def link_to(g, dest_node, db = nil)
64
+ def link_to(g, dest_operator, db = nil)
53
65
  edge_options = {}
54
66
 
55
67
  types.each do |type|
@@ -60,23 +72,25 @@ module ConceptQL
60
72
  edge_options[:label] = label.join("\n")
61
73
  edge_options[:style] = 'dashed' if my_n.zero?
62
74
  end
63
- e = g.add_edges(graph_node(g), dest_node, edge_options)
75
+ e = g.add_edges(graph_operator(g), dest_operator, edge_options)
64
76
  e[:color] = type_color(type)
65
77
  end
66
78
  end
67
79
 
68
80
  def graph_it(g, db)
69
81
  graph_prep(db) if respond_to?(:graph_prep)
70
- children.each do |child|
71
- child.graph_it(g, db)
82
+ upstreams.each do |upstream|
83
+ upstream.graph_it(g, db)
72
84
  end
73
- graph_node(g)
74
- children.each do |child|
75
- child.link_to(g, graph_node(g), db)
85
+ operator = graph_operator(g)
86
+ upstreams.each do |upstream|
87
+ upstream.link_to(g, graph_operator(g), db)
76
88
  end
89
+ operator
77
90
  end
78
91
 
79
92
  def my_count(db, type)
93
+ puts "counting #{operator_name} #{type}"
80
94
  evaluate(db).from_self.where(criterion_type: type.to_s).count
81
95
  end
82
96
 
@@ -0,0 +1,97 @@
1
+ require 'facets/kernel/meta_def'
2
+ require 'facets/string/snakecase'
3
+
4
+ module Metadatable
5
+ def preferred_name(value = nil)
6
+ return @preferred_name unless value
7
+ @preferred_name = value
8
+ end
9
+
10
+ def desc(value = nil)
11
+ return @desc unless value
12
+ @desc = value
13
+ end
14
+
15
+ def predominant_types(*values)
16
+ return @predominant_types if values.empty?
17
+ @predominant_types = values
18
+ end
19
+
20
+ def argument(name, options = {})
21
+ (@arguments ||= [])
22
+ @arguments << [name, options]
23
+ end
24
+
25
+ def option(name, options = {})
26
+ @options ||= {}
27
+ @options[name] = options
28
+ end
29
+
30
+ def types(*type_list)
31
+ @types = type_list
32
+ define_method(:types) do
33
+ type_list
34
+ end
35
+ if type_list.length == 1
36
+ define_method(:type) do
37
+ type_list.first
38
+ end
39
+ end
40
+ end
41
+
42
+ def allows_many_upstreams
43
+ @max_upstreams = 99
44
+ end
45
+
46
+ def allows_one_upstream
47
+ @max_upstreams = 1
48
+ end
49
+
50
+ def just_class_name
51
+ self.to_s.split('::').last
52
+ end
53
+
54
+ def humanized_class_name
55
+ just_class_name.gsub(/([A-Z])/, ' \1').lstrip
56
+ end
57
+
58
+ def category(category)
59
+ (@categories ||= [])
60
+ @categories << Array(category)
61
+ end
62
+
63
+ def reset_categories
64
+ @categories = []
65
+ end
66
+
67
+ def inherited(upstream)
68
+ (@options || {}).each do |name, opt|
69
+ upstream.option name, opt
70
+ end
71
+
72
+ (@categories || []).each do |cat|
73
+ upstream.category cat
74
+ end
75
+
76
+ case @max_upstreams
77
+ when 1
78
+ upstream.allows_one_upstream
79
+ when 99
80
+ upstream.allows_many_upstreams
81
+ end
82
+ end
83
+
84
+ def to_metadata
85
+ {
86
+ preferred_name: @preferred_name || humanized_class_name,
87
+ operation: just_class_name.snakecase,
88
+ max_upstreams: @max_upstreams || 0,
89
+ arguments: @arguments || [],
90
+ options: @options || {},
91
+ predominant_types: @types || @predominant_types || [],
92
+ desc: @desc,
93
+ categories: @categories || []
94
+ }
95
+ end
96
+ end
97
+
@@ -0,0 +1,20 @@
1
+ module ConceptQL
2
+ module Behaviors
3
+ module Preppable
4
+ attr_accessor :prep_proc
5
+ def prep
6
+ prep_proc.call if prep_proc
7
+ end
8
+
9
+ def all(*args)
10
+ prep
11
+ super
12
+ end
13
+
14
+ def count(*args)
15
+ prep
16
+ super
17
+ end
18
+ end
19
+ end
20
+ end
data/lib/conceptql/cli.rb CHANGED
@@ -39,16 +39,18 @@ module ConceptQL
39
39
  q = ConceptQL::Query.new(db(options), criteria_from_file(statement_file))
40
40
  puts q.sql
41
41
  puts q.statement.to_yaml
42
- pp q.execute
42
+ pp q.all
43
43
  end
44
44
 
45
45
  desc 'show_graph statement_file', 'Reads the ConceptQL statement from the file and shows the contents as a ConceptQL graph'
46
+ option :watch_file
46
47
  def show_graph(file)
47
48
  graph_it(criteria_from_file(file))
48
49
  end
49
50
 
50
51
  desc 'show_and_tell_file statement_file', 'Reads the ConceptQL statement from the file and shows the contents as a ConceptQL graph, then executes the statement against the DB'
51
52
  option :full
53
+ option :watch_file
52
54
  def show_and_tell_file(file)
53
55
  show_and_tell(criteria_from_file(file), options)
54
56
  end
@@ -60,9 +62,28 @@ module ConceptQL
60
62
  system('open /tmp/graph.pdf')
61
63
  end
62
64
 
65
+ desc 'metadata', 'Generates the metadata.js file for the JAM'
66
+ def metadata
67
+ File.write('/tmp/metadata.js', "var metadata = #{ConceptQL::Nodifier.new.to_metadata.to_json};")
68
+ end
69
+
70
+ desc 'convert', 'Converts from hash-based syntax to list-based syntax'
71
+ def convert(file)
72
+ require 'conceptql/converter'
73
+ require 'json'
74
+ begin
75
+ puts JSON.pretty_generate(ConceptQL::Converter.new.convert(criteria_from_file(file)))
76
+ rescue
77
+ puts "Couldn't convert #{file}"
78
+ puts $!.message
79
+ puts $!.backtrace.join("\n")
80
+ end
81
+ end
82
+
63
83
  private
64
84
  desc 'show_and_tell_db conceptql_id', 'Fetches the ConceptQL from a DB and shows the contents as a ConceptQL graph, then executes the statement against our test database'
65
85
  option :full
86
+ option :watch_file
66
87
  def show_and_tell_db(conceptql_id)
67
88
  result = fetch_conceptql(conceptql_id, options)
68
89
  puts "Concept: #{result[:label]}"
@@ -88,12 +109,11 @@ module ConceptQL
88
109
  puts q.statement.to_yaml
89
110
  puts 'JSON'
90
111
  puts JSON.pretty_generate(q.statement)
91
- STDIN.gets
92
112
  graph_it(statement, title)
93
113
  STDIN.gets
94
- puts q.query.sql
114
+ puts q.sql
95
115
  STDIN.gets
96
- results = q.execute
116
+ results = q.all
97
117
  if options[:full]
98
118
  pp results
99
119
  else
@@ -105,12 +125,18 @@ module ConceptQL
105
125
  def graph_it(statement, title = nil)
106
126
  require_relative 'graph'
107
127
  require_relative 'tree'
128
+ conn = db(options)
108
129
  ConceptQL::Graph.new(statement,
109
130
  dangler: true,
110
131
  title: title,
111
- db: db(options)
132
+ db: conn
112
133
  ).graph_it('/tmp/graph')
113
134
  system('open /tmp/graph.pdf')
135
+ if options[:watch_file]
136
+ require_relative 'debugger'
137
+ debugger = ConceptQL::Debugger.new(statement, db: conn, watch_ids: File.readlines(options[:watch_file]).map(&:to_i))
138
+ debugger.capture_results('/tmp/debug.xlsx')
139
+ end
114
140
  end
115
141
 
116
142
  def criteria_from_file(file)
@@ -0,0 +1,65 @@
1
+ require 'facets/array/extract_options'
2
+ require 'facets/hash/deep_rekey'
3
+ require 'facets/hash/update_values'
4
+
5
+ module ConceptQL
6
+ class Converter
7
+ def convert(statement)
8
+ traverse(statement).to_list_syntax
9
+ end
10
+
11
+ private
12
+ class Operator
13
+ attr :type, :values, :options
14
+ def initialize(type, *values)
15
+ @type, @values = type, values.flatten
16
+ @options = @values.extract_options!.deep_rekey
17
+ @values = @values
18
+ end
19
+
20
+ def args
21
+ values.select { |s| !s.is_a?(Operator) }
22
+ end
23
+
24
+ def upstreams
25
+ values.select { |s| s.is_a?(Operator) }
26
+ end
27
+
28
+ def converted_options
29
+ options.update_values do |value|
30
+ if value.is_a?(Operator)
31
+ value.to_list_syntax
32
+ else
33
+ value
34
+ end
35
+ end
36
+ end
37
+
38
+ def to_list_syntax
39
+ stmt = [type]
40
+ stmt += args unless args.empty?
41
+ stmt += upstreams.map(&:to_list_syntax) unless upstreams.empty?
42
+ stmt << converted_options unless options.empty?
43
+ stmt
44
+ end
45
+ end
46
+
47
+ def traverse(obj)
48
+ case obj
49
+ when Hash
50
+ if obj.keys.length > 1
51
+ obj = Hash[obj.map { |key, value| [ key, traverse(value) ]}]
52
+ return obj
53
+ end
54
+ type = obj.keys.first
55
+ values = traverse(obj[type])
56
+ obj = Operator.new(type, values)
57
+ obj
58
+ when Array
59
+ obj.map { |value| traverse(value) }
60
+ else
61
+ obj
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,48 @@
1
+ require_relative 'tree'
2
+ require_relative 'operators/operator'
3
+ require_relative 'behaviors/debuggable'
4
+
5
+ module ConceptQL
6
+ class Debugger
7
+ attr :statement, :db, :watch_ids
8
+ def initialize(statement, opts = {})
9
+ @statement = statement
10
+ @db = opts.fetch(:db, nil)
11
+ @tree = opts.fetch(:tree, Tree.new)
12
+ ConceptQL::Operators::Operator.send(:include, ConceptQL::Behaviors::Debuggable)
13
+ @watch_ids = opts.fetch(:watch_ids, [])
14
+ raise "Please specify one or more person_ids you'd like to debug" unless @watch_ids
15
+ end
16
+
17
+ def capture_results(path)
18
+ raise "Please specify path for debug file" unless path
19
+ Dir.mktmpdir do |dir|
20
+ dir = Pathname.new(dir)
21
+ operators = tree.root(self)
22
+ operators.first.reset_operator_number
23
+ csv_files = operators.map.with_index do |last_operator, index|
24
+ last_operator.print_results(db, dir, watch_ids)
25
+ end.flatten
26
+ system("csv2xlsx #{path} #{csv_files.join(' ')}")
27
+ end
28
+ end
29
+
30
+ private
31
+ attr :yaml, :tree, :db
32
+
33
+ def build_graph(g)
34
+ tree.root(self).each.with_index do |last_operator, index|
35
+ last_operator.graph_it(g, db)
36
+ if dangler
37
+ blank_operator = g.add_nodes("_#{index}")
38
+ blank_operator[:shape] = 'none'
39
+ blank_operator[:height] = 0
40
+ blank_operator[:label] = ''
41
+ blank_operator[:fixedsize] = true
42
+ last_operator.link_to(g, blank_operator, db)
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+