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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/CHANGELOG.md +53 -1
- data/Guardfile +52 -24
- data/conceptql.gemspec +5 -3
- data/doc/spec.md +2 -2
- data/lib/conceptql/behaviors/debuggable.rb +70 -0
- data/lib/conceptql/behaviors/dottable.rb +28 -14
- data/lib/conceptql/behaviors/metadatable.rb +97 -0
- data/lib/conceptql/behaviors/preppable.rb +20 -0
- data/lib/conceptql/cli.rb +31 -5
- data/lib/conceptql/converter.rb +65 -0
- data/lib/conceptql/debugger.rb +48 -0
- data/lib/conceptql/graph.rb +14 -13
- data/lib/conceptql/graph_nodifier.rb +49 -17
- data/lib/conceptql/nodifier.rb +31 -6
- data/lib/conceptql/operators/after.rb +25 -0
- data/lib/conceptql/operators/any_overlap.rb +15 -0
- data/lib/conceptql/operators/before.rb +21 -0
- data/lib/conceptql/{nodes/binary_operator_node.rb → operators/binary_operator_operator.rb} +12 -8
- data/lib/conceptql/{nodes/casting_node.rb → operators/casting_operator.rb} +8 -7
- data/lib/conceptql/{nodes → operators}/complement.rb +14 -11
- data/lib/conceptql/operators/concept.rb +70 -0
- data/lib/conceptql/{nodes → operators}/condition_type.rb +13 -5
- data/lib/conceptql/operators/contains.rb +17 -0
- data/lib/conceptql/operators/count.rb +26 -0
- data/lib/conceptql/operators/cpt.rb +25 -0
- data/lib/conceptql/{nodes → operators}/date_range.rb +11 -6
- data/lib/conceptql/operators/death.rb +23 -0
- data/lib/conceptql/operators/drug_type_concept.rb +21 -0
- data/lib/conceptql/{nodes → operators}/during.rb +9 -3
- data/lib/conceptql/operators/equal.rb +13 -0
- data/lib/conceptql/operators/except.rb +28 -0
- data/lib/conceptql/operators/filter.rb +21 -0
- data/lib/conceptql/{nodes → operators}/first.rb +7 -5
- data/lib/conceptql/{nodes → operators}/from.rb +2 -2
- data/lib/conceptql/operators/from_seer_visits.rb +23 -0
- data/lib/conceptql/{nodes → operators}/gender.rb +6 -6
- data/lib/conceptql/operators/hcpcs.rb +25 -0
- data/lib/conceptql/operators/icd10.rb +28 -0
- data/lib/conceptql/operators/icd9.rb +28 -0
- data/lib/conceptql/operators/icd9_procedure.rb +25 -0
- data/lib/conceptql/{nodes → operators}/intersect.rb +7 -3
- data/lib/conceptql/{nodes → operators}/last.rb +7 -5
- data/lib/conceptql/operators/loinc.rb +25 -0
- data/lib/conceptql/operators/medcode.rb +28 -0
- data/lib/conceptql/operators/medcode_procedure.rb +27 -0
- data/lib/conceptql/operators/ndc.rb +29 -0
- data/lib/conceptql/operators/numeric.rb +54 -0
- data/lib/conceptql/operators/observation_by_enttype.rb +29 -0
- data/lib/conceptql/operators/observation_period.rb +30 -0
- data/lib/conceptql/operators/occurrence.rb +70 -0
- data/lib/conceptql/operators/one_in_two_out.rb +62 -0
- data/lib/conceptql/{nodes/node.rb → operators/operator.rb} +84 -52
- data/lib/conceptql/operators/overlapped_by.rb +23 -0
- data/lib/conceptql/operators/overlaps.rb +19 -0
- data/lib/conceptql/operators/pass_thru.rb +11 -0
- data/lib/conceptql/{nodes → operators}/person.rb +8 -4
- data/lib/conceptql/operators/person_filter.rb +13 -0
- data/lib/conceptql/{nodes → operators}/place_of_service_code.rb +7 -7
- data/lib/conceptql/operators/procedure_occurrence.rb +25 -0
- data/lib/conceptql/operators/prodcode.rb +29 -0
- data/lib/conceptql/{nodes → operators}/race.rb +7 -7
- data/lib/conceptql/operators/recall.rb +38 -0
- data/lib/conceptql/operators/rxnorm.rb +24 -0
- data/lib/conceptql/operators/snomed.rb +24 -0
- data/lib/conceptql/operators/snomed_condition.rb +26 -0
- data/lib/conceptql/{nodes/source_vocabulary_node.rb → operators/source_vocabulary_operator.rb} +7 -4
- data/lib/conceptql/{nodes/standard_vocabulary_node.rb → operators/standard_vocabulary_operator.rb} +6 -4
- data/lib/conceptql/{nodes → operators}/started_by.rb +9 -3
- data/lib/conceptql/{nodes → operators}/sum.rb +9 -4
- data/lib/conceptql/{nodes/temporal_node.rb → operators/temporal_operator.rb} +7 -4
- data/lib/conceptql/{nodes → operators}/time_window.rb +19 -7
- data/lib/conceptql/operators/to_seer_visits.rb +24 -0
- data/lib/conceptql/operators/trim_date_end.rb +55 -0
- data/lib/conceptql/operators/trim_date_start.rb +56 -0
- data/lib/conceptql/{nodes → operators}/union.rb +6 -2
- data/lib/conceptql/operators/visit.rb +15 -0
- data/lib/conceptql/{nodes → operators}/visit_occurrence.rb +7 -3
- data/lib/conceptql/query.rb +19 -17
- data/lib/conceptql/scope.rb +69 -0
- data/lib/conceptql/tree.rb +33 -18
- data/lib/conceptql/utils/temp_table.rb +72 -0
- data/lib/conceptql/version.rb +1 -1
- data/spec/conceptql/behaviors/dottable_spec.rb +39 -51
- data/spec/conceptql/converter_spec.rb +51 -0
- data/spec/conceptql/date_adjuster_spec.rb +15 -15
- data/spec/conceptql/operators/after_spec.rb +16 -0
- data/spec/conceptql/operators/before_spec.rb +16 -0
- data/spec/conceptql/{nodes/casting_node_spec.rb → operators/casting_operator_spec.rb} +16 -20
- data/spec/conceptql/operators/complement_spec.rb +15 -0
- data/spec/conceptql/operators/concept_spec.rb +40 -0
- data/spec/conceptql/{nodes → operators}/condition_type_spec.rb +39 -24
- data/spec/conceptql/operators/contains_spec.rb +19 -0
- data/spec/conceptql/operators/cpt_spec.rb +29 -0
- data/spec/conceptql/operators/date_range_spec.rb +33 -0
- data/spec/conceptql/operators/death_spec.rb +10 -0
- data/spec/conceptql/operators/during_spec.rb +30 -0
- data/spec/conceptql/operators/except_spec.rb +15 -0
- data/spec/conceptql/operators/first_spec.rb +35 -0
- data/spec/conceptql/operators/from_spec.rb +13 -0
- data/spec/conceptql/operators/gender_spec.rb +27 -0
- data/spec/conceptql/operators/hcpcs_spec.rb +29 -0
- data/spec/conceptql/operators/icd10_spec.rb +34 -0
- data/spec/conceptql/operators/icd9_procedure_spec.rb +29 -0
- data/spec/conceptql/operators/icd9_spec.rb +34 -0
- data/spec/conceptql/operators/intersect_spec.rb +28 -0
- data/spec/conceptql/operators/last_spec.rb +36 -0
- data/spec/conceptql/operators/loinc_spec.rb +29 -0
- data/spec/conceptql/operators/medcode_procedure_spec.rb +34 -0
- data/spec/conceptql/operators/medcode_spec.rb +34 -0
- data/spec/conceptql/operators/observation_period_spec.rb +10 -0
- data/spec/conceptql/operators/occurrence_spec.rb +87 -0
- data/spec/conceptql/operators/overlapped_by_spec.rb +32 -0
- data/spec/conceptql/operators/overlaps_spec.rb +21 -0
- data/spec/conceptql/operators/person_filter_spec.rb +15 -0
- data/spec/conceptql/operators/person_spec.rb +10 -0
- data/spec/conceptql/{nodes → operators}/place_of_service_code_spec.rb +6 -8
- data/spec/conceptql/operators/procedure_occurrence_spec.rb +10 -0
- data/spec/conceptql/operators/prodcode_spec.rb +35 -0
- data/spec/conceptql/operators/query_double.rb +20 -0
- data/spec/conceptql/operators/query_double_spec.rb +7 -0
- data/spec/conceptql/operators/race_spec.rb +21 -0
- data/spec/conceptql/operators/rxnorm_spec.rb +29 -0
- data/spec/conceptql/operators/snomed_spec.rb +29 -0
- data/spec/conceptql/operators/source_vocabulary_operator_spec.rb +35 -0
- data/spec/conceptql/operators/standard_vocabulary_operator_spec.rb +35 -0
- data/spec/conceptql/operators/started_by_spec.rb +22 -0
- data/spec/conceptql/{nodes/temporal_node_spec.rb → operators/temporal_operator_spec.rb} +11 -17
- data/spec/conceptql/operators/time_window_spec.rb +77 -0
- data/spec/conceptql/operators/union_spec.rb +21 -0
- data/spec/conceptql/operators/visit_occurrence_spec.rb +10 -0
- data/spec/conceptql/query_spec.rb +10 -9
- data/spec/conceptql/tree_spec.rb +24 -28
- data/spec/doubles/stream_for_casting_double.rb +1 -1
- data/spec/doubles/stream_for_occurrence_double.rb +1 -1
- data/spec/doubles/stream_for_temporal_double.rb +1 -1
- data/spec/spec_helper.rb +74 -58
- metadata +202 -133
- data/lib/conceptql/nodes/after.rb +0 -12
- data/lib/conceptql/nodes/before.rb +0 -11
- data/lib/conceptql/nodes/concept.rb +0 -38
- data/lib/conceptql/nodes/count.rb +0 -23
- data/lib/conceptql/nodes/cpt.rb +0 -20
- data/lib/conceptql/nodes/death.rb +0 -19
- data/lib/conceptql/nodes/define.rb +0 -96
- data/lib/conceptql/nodes/drug_type_concept.rb +0 -18
- data/lib/conceptql/nodes/equal.rb +0 -11
- data/lib/conceptql/nodes/except.rb +0 -11
- data/lib/conceptql/nodes/hcpcs.rb +0 -20
- data/lib/conceptql/nodes/icd10.rb +0 -23
- data/lib/conceptql/nodes/icd9.rb +0 -23
- data/lib/conceptql/nodes/icd9_procedure.rb +0 -20
- data/lib/conceptql/nodes/loinc.rb +0 -20
- data/lib/conceptql/nodes/numeric.rb +0 -40
- data/lib/conceptql/nodes/occurrence.rb +0 -49
- data/lib/conceptql/nodes/pass_thru.rb +0 -11
- data/lib/conceptql/nodes/person_filter.rb +0 -12
- data/lib/conceptql/nodes/procedure_occurrence.rb +0 -21
- data/lib/conceptql/nodes/recall.rb +0 -50
- data/lib/conceptql/nodes/rxnorm.rb +0 -20
- data/lib/conceptql/nodes/snomed.rb +0 -19
- data/lib/conceptql/nodes/visit.rb +0 -11
- data/spec/conceptql/nodes/after_spec.rb +0 -18
- data/spec/conceptql/nodes/before_spec.rb +0 -18
- data/spec/conceptql/nodes/complement_spec.rb +0 -15
- data/spec/conceptql/nodes/concept_spec.rb +0 -34
- data/spec/conceptql/nodes/cpt_spec.rb +0 -31
- data/spec/conceptql/nodes/date_range_spec.rb +0 -35
- data/spec/conceptql/nodes/death_spec.rb +0 -12
- data/spec/conceptql/nodes/during_spec.rb +0 -32
- data/spec/conceptql/nodes/except_spec.rb +0 -18
- data/spec/conceptql/nodes/first_spec.rb +0 -37
- data/spec/conceptql/nodes/from_spec.rb +0 -15
- data/spec/conceptql/nodes/gender_spec.rb +0 -29
- data/spec/conceptql/nodes/hcpcs_spec.rb +0 -31
- data/spec/conceptql/nodes/icd10_spec.rb +0 -36
- data/spec/conceptql/nodes/icd9_procedure_spec.rb +0 -31
- data/spec/conceptql/nodes/icd9_spec.rb +0 -36
- data/spec/conceptql/nodes/intersect_spec.rb +0 -33
- data/spec/conceptql/nodes/last_spec.rb +0 -38
- data/spec/conceptql/nodes/loinc_spec.rb +0 -31
- data/spec/conceptql/nodes/occurrence_spec.rb +0 -89
- data/spec/conceptql/nodes/person_filter_spec.rb +0 -18
- data/spec/conceptql/nodes/person_spec.rb +0 -12
- data/spec/conceptql/nodes/procedure_occurrence_spec.rb +0 -12
- data/spec/conceptql/nodes/query_double.rb +0 -19
- data/spec/conceptql/nodes/race_spec.rb +0 -23
- data/spec/conceptql/nodes/rxnorm_spec.rb +0 -31
- data/spec/conceptql/nodes/snomed_spec.rb +0 -31
- data/spec/conceptql/nodes/source_vocabulary_node_spec.rb +0 -37
- data/spec/conceptql/nodes/standard_vocabulary_node_spec.rb +0 -40
- data/spec/conceptql/nodes/started_by_spec.rb +0 -25
- data/spec/conceptql/nodes/time_window_spec.rb +0 -85
- data/spec/conceptql/nodes/union_spec.rb +0 -25
- 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:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: bb925d74153217fcb29ee4c4e5874827005246be
|
|
4
|
+
data.tar.gz: 41ae6d964b8a6ae82b37098adcda8548917543bd
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f62d0381dbd1ceabed67e9fff58d95975bf4cff27a957f03aa0ba495ca7726b22af3727d26acd4f683ca048a17dac858dffd2bef7f2e0707cae7e5cfd29e6677
|
|
7
|
+
data.tar.gz: 004743713781e5ea7cf7281ea071597ac8fa0959714571639d97d69a9fdd67b03692b10b9e1c77802230f57149212e9daf5077459d02db86814759051b44cc62
|
data/.gitignore
CHANGED
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
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
#
|
|
19
|
-
|
|
20
|
-
#
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
#
|
|
27
|
-
|
|
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 '
|
|
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 '
|
|
29
|
-
spec.add_development_dependency 'guard-
|
|
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
|
|
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 '
|
|
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 '
|
|
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
|
-
|
|
19
|
-
|
|
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.
|
|
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
|
|
43
|
-
@
|
|
44
|
-
me = g.add_nodes(
|
|
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,
|
|
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(
|
|
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
|
-
|
|
71
|
-
|
|
82
|
+
upstreams.each do |upstream|
|
|
83
|
+
upstream.graph_it(g, db)
|
|
72
84
|
end
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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
|
+
|
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.
|
|
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.
|
|
114
|
+
puts q.sql
|
|
95
115
|
STDIN.gets
|
|
96
|
-
results = q.
|
|
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:
|
|
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
|
+
|