conceptql 0.1.1 → 0.2.0

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 (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
+