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
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
require_relative 'casting_operator'
|
|
2
|
+
|
|
3
|
+
module ConceptQL
|
|
4
|
+
module Operators
|
|
5
|
+
class Death < CastingOperator
|
|
6
|
+
desc 'Generates all death records, or, if fed a stream, fetches all death records for the people represented in the incoming result set.'
|
|
7
|
+
types :death
|
|
8
|
+
allows_one_upstream
|
|
9
|
+
|
|
10
|
+
def my_type
|
|
11
|
+
:death
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def i_point_at
|
|
15
|
+
[ :person ]
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def these_point_at_me
|
|
19
|
+
[]
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
require_relative 'operator'
|
|
2
|
+
|
|
3
|
+
module ConceptQL
|
|
4
|
+
module Operators
|
|
5
|
+
class DrugTypeConcept < Operator
|
|
6
|
+
desc 'Given a set of concept IDs in RxNorm, returns that set of drug exposures'
|
|
7
|
+
argument :concept_ids, type: :codelist, vocab: 'RxNorm'
|
|
8
|
+
|
|
9
|
+
def type
|
|
10
|
+
:drug_exposure
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def query(db)
|
|
14
|
+
db.from(:drug_exposure)
|
|
15
|
+
.where(drug_type_concept_id: arguments)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
|
|
@@ -1,8 +1,14 @@
|
|
|
1
|
-
require_relative '
|
|
1
|
+
require_relative 'temporal_operator'
|
|
2
2
|
|
|
3
3
|
module ConceptQL
|
|
4
|
-
module
|
|
5
|
-
class During <
|
|
4
|
+
module Operators
|
|
5
|
+
class During < TemporalOperator
|
|
6
|
+
desc <<-EOF
|
|
7
|
+
Compares all results on a person-by-person basis between the left hand results (LHR) and the right hand resuls (RHR).
|
|
8
|
+
For any result in the LHR whose start_date and end_date occur within the start_date and end_date of a RHR row, that result is passed through.
|
|
9
|
+
All other results are discarded, including all results in the RHR.
|
|
10
|
+
EOF
|
|
11
|
+
|
|
6
12
|
def where_clause
|
|
7
13
|
if inclusive?
|
|
8
14
|
Sequel.expr(Sequel.expr(Proc.new { r__start_date <= l__start_date}).&(Sequel.expr( Proc.new { l__start_date <= r__end_date })))
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
require_relative 'temporal_operator'
|
|
2
|
+
|
|
3
|
+
module ConceptQL
|
|
4
|
+
module Operators
|
|
5
|
+
class Equal < TemporalOperator
|
|
6
|
+
desc 'If a LHR result has the same value_as_number as a RHR result, it is passed through'
|
|
7
|
+
|
|
8
|
+
def where_clause
|
|
9
|
+
{ r__value_as_number: :l__value_as_number }
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
require_relative 'binary_operator_operator'
|
|
2
|
+
|
|
3
|
+
module ConceptQL
|
|
4
|
+
module Operators
|
|
5
|
+
class Except < BinaryOperatorOperator
|
|
6
|
+
desc 'If a LHR result appears in the RHR result, it is removed from the output result set.'
|
|
7
|
+
category 'Set Logic'
|
|
8
|
+
|
|
9
|
+
def query(db)
|
|
10
|
+
if ignore_dates?
|
|
11
|
+
query = db.from(Sequel.as(left.evaluate(db), :l))
|
|
12
|
+
.left_join(Sequel.as(right.evaluate(db), :r), l__criterion_id: :r__criterion_id, l__criterion_type: :r__criterion_type)
|
|
13
|
+
.where(r__criterion_id: nil)
|
|
14
|
+
.select_all(:l)
|
|
15
|
+
db.from(query)
|
|
16
|
+
else
|
|
17
|
+
left.evaluate(db).except(right.evaluate(db))
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
def ignore_dates?
|
|
24
|
+
options[:ignore_dates]
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
require_relative 'binary_operator_operator'
|
|
2
|
+
|
|
3
|
+
module ConceptQL
|
|
4
|
+
module Operators
|
|
5
|
+
class Filter < BinaryOperatorOperator
|
|
6
|
+
desc 'Only pass through results from the LHR that have a corresponding RHR with the same person, criterion_id, and criterion_type'
|
|
7
|
+
|
|
8
|
+
def query(db)
|
|
9
|
+
rhs = right.evaluate(db)
|
|
10
|
+
rhs = rhs.select_group(:person_id, :criterion_id, :criterion_type)
|
|
11
|
+
query = db.from(Sequel.as(left.evaluate(db), :l))
|
|
12
|
+
query = query
|
|
13
|
+
.left_join(Sequel.as(rhs, :r), l__person_id: :r__person_id, l__criterion_id: :r__criterion_id, l__criterion_type: :r__criterion_type)
|
|
14
|
+
.exclude(r__criterion_id: nil)
|
|
15
|
+
.select_all(:l)
|
|
16
|
+
db.from(query)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
@@ -1,20 +1,22 @@
|
|
|
1
1
|
require_relative 'occurrence'
|
|
2
2
|
|
|
3
3
|
module ConceptQL
|
|
4
|
-
module
|
|
5
|
-
# Represents a
|
|
4
|
+
module Operators
|
|
5
|
+
# Represents a operator that will grab the first occurrence of something
|
|
6
6
|
#
|
|
7
|
-
# The
|
|
7
|
+
# The operator treats all streams as a single, large stream. It partitions
|
|
8
8
|
# that larget stream by person_id, then sorts within those groupings
|
|
9
9
|
# by start_date and then select at most one row per person, regardless
|
|
10
|
-
# of how many different types of streams enter the
|
|
10
|
+
# of how many different types of streams enter the operator
|
|
11
11
|
#
|
|
12
12
|
# If two rows have the same start_date, the order of their ranking
|
|
13
13
|
# is arbitrary
|
|
14
14
|
#
|
|
15
15
|
# If we ask for the first occurrence of something and a person has no
|
|
16
|
-
# occurrences, this
|
|
16
|
+
# occurrences, this operator returns nothing for that person
|
|
17
17
|
class First < Occurrence
|
|
18
|
+
desc 'Only passes through the row with the earliest start_date per person. If more than one row qualifies, result is arbitrarily chosen.'
|
|
19
|
+
|
|
18
20
|
def occurrence
|
|
19
21
|
1
|
|
20
22
|
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
require_relative 'operator'
|
|
2
|
+
|
|
3
|
+
module ConceptQL
|
|
4
|
+
module Operators
|
|
5
|
+
class FromSeerVisits < Operator
|
|
6
|
+
def type
|
|
7
|
+
:observation
|
|
8
|
+
end
|
|
9
|
+
def query(db)
|
|
10
|
+
visit_ids = stream.evaluate(db)
|
|
11
|
+
.from_self
|
|
12
|
+
.where(criterion_type: 'visit_occurrence')
|
|
13
|
+
query = db[:observation].where(visit_occurrence_id: visit_ids.select(:criterion_id))
|
|
14
|
+
arguments.inject(query) do |q, key|
|
|
15
|
+
q.where(observation_source_value: key.to_s.upcase)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
require_relative '
|
|
1
|
+
require_relative 'operator'
|
|
2
2
|
|
|
3
3
|
module ConceptQL
|
|
4
|
-
module
|
|
5
|
-
class Gender <
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
4
|
+
module Operators
|
|
5
|
+
class Gender < Operator
|
|
6
|
+
desc 'Returns all person records that match the selected gender.'
|
|
7
|
+
argument :gender, type: :string, options: ['Male', 'Female']
|
|
8
|
+
types :person
|
|
9
9
|
|
|
10
10
|
def query(db)
|
|
11
11
|
gender_concept_ids = values.map do |value|
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
require_relative 'standard_vocabulary_operator'
|
|
2
|
+
|
|
3
|
+
module ConceptQL
|
|
4
|
+
module Operators
|
|
5
|
+
class Hcpcs < StandardVocabularyOperator
|
|
6
|
+
preferred_name 'HCPCS'
|
|
7
|
+
desc 'Searches the procedure_occurrence table for all procedures with matching HCPCS codes'
|
|
8
|
+
argument :hcpcs, type: :codelist, vocab: 'HCPCS'
|
|
9
|
+
predominant_types :procedure_occurrence
|
|
10
|
+
|
|
11
|
+
def table
|
|
12
|
+
:procedure_occurrence
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def vocabulary_id
|
|
16
|
+
5
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def concept_column
|
|
20
|
+
:procedure_concept_id
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
require_relative 'source_vocabulary_operator'
|
|
2
|
+
|
|
3
|
+
module ConceptQL
|
|
4
|
+
module Operators
|
|
5
|
+
class Icd10 < SourceVocabularyOperator
|
|
6
|
+
preferred_name 'ICD-10 CM'
|
|
7
|
+
desc 'Searches the condition_occurrence table for the given set of ICD-10 codes.'
|
|
8
|
+
argument :icd10s, type: :codelist, vocab: 'ICD10CM'
|
|
9
|
+
predominant_types :condition_occurrence
|
|
10
|
+
|
|
11
|
+
def table
|
|
12
|
+
:condition_occurrence
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def vocabulary_id
|
|
16
|
+
34
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def source_column
|
|
20
|
+
:condition_source_value
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def concept_column
|
|
24
|
+
:condition_concept_id
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
require_relative 'source_vocabulary_operator'
|
|
2
|
+
|
|
3
|
+
module ConceptQL
|
|
4
|
+
module Operators
|
|
5
|
+
class Icd9 < SourceVocabularyOperator
|
|
6
|
+
preferred_name 'ICD-9 CM'
|
|
7
|
+
desc 'Searches the condition_occurrence table for the given set of ICD-9 codes.'
|
|
8
|
+
argument :icd9s, type: :codelist, vocab: 'ICD9CM'
|
|
9
|
+
predominant_types :condition_occurrence
|
|
10
|
+
|
|
11
|
+
def table
|
|
12
|
+
:condition_occurrence
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def vocabulary_id
|
|
16
|
+
2
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def source_column
|
|
20
|
+
:condition_source_value
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def concept_column
|
|
24
|
+
:condition_concept_id
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
require_relative 'standard_vocabulary_operator'
|
|
2
|
+
|
|
3
|
+
module ConceptQL
|
|
4
|
+
module Operators
|
|
5
|
+
class Icd9Procedure < StandardVocabularyOperator
|
|
6
|
+
preferred_name 'ICD-9 Proc'
|
|
7
|
+
desc 'Searches the procedure_occurrence table for the given set of ICD-9 codes.'
|
|
8
|
+
argument :icd9s, type: :codelist, vocab: 'ICD9Proc'
|
|
9
|
+
predominant_types :procedure_occurrence
|
|
10
|
+
|
|
11
|
+
def table
|
|
12
|
+
:procedure_occurrence
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def vocabulary_id
|
|
16
|
+
3
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def concept_column
|
|
20
|
+
:procedure_concept_id
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
require_relative 'pass_thru'
|
|
2
2
|
|
|
3
3
|
module ConceptQL
|
|
4
|
-
module
|
|
4
|
+
module Operators
|
|
5
5
|
class Intersect < PassThru
|
|
6
|
+
desc 'Passes thru any result row that appears in all incoming result sets.'
|
|
7
|
+
allows_many_upstreams
|
|
8
|
+
category 'Set Logic'
|
|
9
|
+
|
|
6
10
|
def types
|
|
7
|
-
|
|
11
|
+
upstreams.map(&:types).flatten.uniq
|
|
8
12
|
end
|
|
9
13
|
|
|
10
14
|
def query(db)
|
|
11
15
|
exprs = {}
|
|
12
|
-
|
|
16
|
+
upstreams.each do |expression|
|
|
13
17
|
evaled = expression.evaluate(db)
|
|
14
18
|
expression.types.each do |type|
|
|
15
19
|
(exprs[type] ||= []) << evaled
|
|
@@ -1,20 +1,22 @@
|
|
|
1
1
|
require_relative 'occurrence'
|
|
2
2
|
|
|
3
3
|
module ConceptQL
|
|
4
|
-
module
|
|
5
|
-
# Represents a
|
|
4
|
+
module Operators
|
|
5
|
+
# Represents a operator that will grab the last occurrence of something
|
|
6
6
|
#
|
|
7
|
-
# The
|
|
7
|
+
# The operator treats all streams as a single, large stream. It partitions
|
|
8
8
|
# that larget stream by person_id, then sorts within those groupings
|
|
9
9
|
# by start_date and then select at most one row per person, regardless
|
|
10
|
-
# of how many different types of streams enter the
|
|
10
|
+
# of how many different types of streams enter the operator
|
|
11
11
|
#
|
|
12
12
|
# If two rows have the same start_date, the order of their ranking
|
|
13
13
|
# is arbitrary
|
|
14
14
|
#
|
|
15
15
|
# If we ask for the last occurrence of something and a person has no
|
|
16
|
-
# occurrences, this
|
|
16
|
+
# occurrences, this operator returns nothing for that person
|
|
17
17
|
class Last < Occurrence
|
|
18
|
+
desc 'Only passes through the row with the most recent start_date per person. If more than one row qualifies, result is arbitrarily chosen.'
|
|
19
|
+
|
|
18
20
|
def occurrence
|
|
19
21
|
-1
|
|
20
22
|
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
require_relative 'standard_vocabulary_operator'
|
|
2
|
+
|
|
3
|
+
module ConceptQL
|
|
4
|
+
module Operators
|
|
5
|
+
class Loinc < StandardVocabularyOperator
|
|
6
|
+
preferred_name 'LOINC'
|
|
7
|
+
desc 'Searches the observation table for all observations with matching LOINC codes'
|
|
8
|
+
argument :loincs, type: :codelist, vocab: 'LOINC'
|
|
9
|
+
predominant_types :observation
|
|
10
|
+
|
|
11
|
+
def table
|
|
12
|
+
:observation
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def vocabulary_id
|
|
16
|
+
6
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def concept_column
|
|
20
|
+
:observation_concept_id
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
require_relative 'source_vocabulary_operator'
|
|
2
|
+
|
|
3
|
+
module ConceptQL
|
|
4
|
+
module Operators
|
|
5
|
+
class Medcode < SourceVocabularyOperator
|
|
6
|
+
desc 'Searches the condition_occurrence table for all conditions with matching Medcodes'
|
|
7
|
+
argument :medcodes, type: :codelist, vocab_id: '203'
|
|
8
|
+
predominant_types :condition_occurrence
|
|
9
|
+
|
|
10
|
+
def table
|
|
11
|
+
:condition_occurrence
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def vocabulary_id
|
|
15
|
+
203
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def source_column
|
|
19
|
+
:condition_source_value
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def concept_column
|
|
23
|
+
:condition_concept_id
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
require_relative 'source_vocabulary_operator'
|
|
2
|
+
|
|
3
|
+
module ConceptQL
|
|
4
|
+
module Operators
|
|
5
|
+
class MedcodeProcedure < SourceVocabularyOperator
|
|
6
|
+
desc 'Searches the procedure_occurrence table for all procedures with matching Medcodes'
|
|
7
|
+
argument :medcodes, type: :codelist, vocab: '204'
|
|
8
|
+
predominant_types :procedure_occurrence
|
|
9
|
+
|
|
10
|
+
def table
|
|
11
|
+
:procedure_occurrence
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def vocabulary_id
|
|
15
|
+
204
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def source_column
|
|
19
|
+
:procedure_source_value
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def concept_column
|
|
23
|
+
:procedure_concept_id
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
require_relative 'source_vocabulary_operator'
|
|
2
|
+
|
|
3
|
+
module ConceptQL
|
|
4
|
+
module Operators
|
|
5
|
+
class Ndc < SourceVocabularyOperator
|
|
6
|
+
preferred_name 'NDC'
|
|
7
|
+
desc 'Searches the drug_exposure table for all procedures with matching NDC codes'
|
|
8
|
+
argument :ndcs, type: :codelist, vocab: 'NDC'
|
|
9
|
+
predominant_types :drug_exposure
|
|
10
|
+
|
|
11
|
+
def table
|
|
12
|
+
:drug_exposure
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def vocabulary_id
|
|
16
|
+
9
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def source_column
|
|
20
|
+
:drug_source_value
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def concept_column
|
|
24
|
+
:drug_concept_id
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
require_relative 'pass_thru'
|
|
2
|
+
|
|
3
|
+
module ConceptQL
|
|
4
|
+
module Operators
|
|
5
|
+
# Represents a operator that will either:
|
|
6
|
+
# - create a value_as_number value for every person in the database
|
|
7
|
+
# - change the value_as_number value for every every result passed in
|
|
8
|
+
# - either to a number
|
|
9
|
+
# - or a value from a column in the origin row
|
|
10
|
+
#
|
|
11
|
+
# Accepts two params:
|
|
12
|
+
# - Either a number value or a symbol representing a column name
|
|
13
|
+
# - An optional stream
|
|
14
|
+
class Numeric < PassThru
|
|
15
|
+
desc <<-EOF
|
|
16
|
+
Represents a operator that will either:
|
|
17
|
+
- create a value_as_number value for every person in the database
|
|
18
|
+
- change the value_as_number value for every every result passed in
|
|
19
|
+
- either to a number
|
|
20
|
+
- or a value from a column in the origin row
|
|
21
|
+
|
|
22
|
+
Accepts two params:
|
|
23
|
+
- Either a number value or a symbol representing a column name
|
|
24
|
+
- An optional stream
|
|
25
|
+
EOF
|
|
26
|
+
argument :value, type: :float
|
|
27
|
+
allows_one_upstream
|
|
28
|
+
|
|
29
|
+
def query(db)
|
|
30
|
+
stream.nil? ? as_criterion(db) : with_kids(db)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def types
|
|
34
|
+
stream.nil? ? [:person] : super
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
def with_kids(db)
|
|
39
|
+
db.from(stream.evaluate(db))
|
|
40
|
+
.select(*(COLUMNS - [:value_as_number]))
|
|
41
|
+
.select_append(Sequel.lit('?', arguments.first).cast(Float).as(:value_as_number))
|
|
42
|
+
.from_self
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def as_criterion(db)
|
|
46
|
+
db.from(select_it(db.from(:person), :person))
|
|
47
|
+
.select(*(COLUMNS - [:value_as_number]))
|
|
48
|
+
.select_append(Sequel.lit('?', arguments.first).cast(Float).as(:value_as_number))
|
|
49
|
+
.from_self
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
require_relative 'source_vocabulary_operator'
|
|
2
|
+
|
|
3
|
+
module ConceptQL
|
|
4
|
+
module Operators
|
|
5
|
+
class ObservationByEnttype < SourceVocabularyOperator
|
|
6
|
+
desc 'Searches the observation table for all observations with matching Enttype'
|
|
7
|
+
argument :enttypes, type: :codelist, vocab_id: [206, 207]
|
|
8
|
+
predominant_types :observation
|
|
9
|
+
|
|
10
|
+
def table
|
|
11
|
+
:observation
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def vocabulary_id
|
|
15
|
+
[206, 207]
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def source_column
|
|
19
|
+
:observation_source_value
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def concept_column
|
|
23
|
+
:observation_concept_id
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
require_relative 'casting_operator'
|
|
2
|
+
|
|
3
|
+
module ConceptQL
|
|
4
|
+
module Operators
|
|
5
|
+
class ObservationPeriod < CastingOperator
|
|
6
|
+
desc 'Generates all observation_period records, or, if fed a stream, fetches all observation_period records for the people represented in the incoming result set.'
|
|
7
|
+
types :observation_period
|
|
8
|
+
allows_one_upstream
|
|
9
|
+
|
|
10
|
+
def my_type
|
|
11
|
+
:observation_period
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def i_point_at
|
|
15
|
+
[]
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def these_point_at_me
|
|
19
|
+
# I could list ALL the types we use, but the default behavior of casting,
|
|
20
|
+
# when there is no explicit casting defined, is to convert everything to
|
|
21
|
+
# person IDs
|
|
22
|
+
#
|
|
23
|
+
# So by defining no known castable relationships in this operator, all
|
|
24
|
+
# types will be converted to person
|
|
25
|
+
[]
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
require_relative 'operator'
|
|
2
|
+
|
|
3
|
+
module ConceptQL
|
|
4
|
+
module Operators
|
|
5
|
+
# Represents a operator that will grab the Nth occurrence of something
|
|
6
|
+
#
|
|
7
|
+
# Specify occurrences as integers, excluding O
|
|
8
|
+
# 1 => first
|
|
9
|
+
# 2 => second
|
|
10
|
+
# ...
|
|
11
|
+
# -1 => last
|
|
12
|
+
# -2 => second-to-last
|
|
13
|
+
#
|
|
14
|
+
# The operator treats all streams as a single, large stream. It partitions
|
|
15
|
+
# that larget stream by person_id, then sorts within those groupings
|
|
16
|
+
# by start_date and then select at most one row per person, regardless
|
|
17
|
+
# of how many different types of streams enter the operator
|
|
18
|
+
#
|
|
19
|
+
# If two rows have the same start_date, the order of their ranking
|
|
20
|
+
# is arbitrary
|
|
21
|
+
#
|
|
22
|
+
# If we ask for the second occurrence of something and a person has only one
|
|
23
|
+
# occurrence, this operator returns nothing for that person
|
|
24
|
+
class Occurrence < Operator
|
|
25
|
+
preferred_name 'Nth Occurrence'
|
|
26
|
+
desc <<-EOF
|
|
27
|
+
Groups all results by person, then orders by start_date, then finds the nth occurrence.
|
|
28
|
+
nth occurrence can be positive or negative.
|
|
29
|
+
1 => first
|
|
30
|
+
2 => second
|
|
31
|
+
...
|
|
32
|
+
-1 => last
|
|
33
|
+
-2 => second-to-last
|
|
34
|
+
|
|
35
|
+
If two rows have the same start_date, the order of their ranking
|
|
36
|
+
is arbitrary
|
|
37
|
+
|
|
38
|
+
If we ask for the second occurrence of something and a person has only one
|
|
39
|
+
occurrence, this operator returns nothing for that person
|
|
40
|
+
EOF
|
|
41
|
+
argument :occurrence, type: :integer
|
|
42
|
+
allows_one_upstream
|
|
43
|
+
category %w(Temporal Occurrence)
|
|
44
|
+
|
|
45
|
+
def query(db)
|
|
46
|
+
db[:occurrences]
|
|
47
|
+
.with(:occurrences,
|
|
48
|
+
stream.evaluate(db)
|
|
49
|
+
.from_self
|
|
50
|
+
.select_append { |o| o.row_number(:over, partition: :person_id, order: ordered_columns){}.as(:rn) })
|
|
51
|
+
.where(rn: occurrence.abs)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def occurrence
|
|
55
|
+
@occurrence ||= arguments.first
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
private
|
|
59
|
+
def asc_or_desc
|
|
60
|
+
occurrence < 0 ? :desc : :asc
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def ordered_columns
|
|
64
|
+
ordered_columns = [Sequel.send(asc_or_desc, :start_date)]
|
|
65
|
+
ordered_columns += [:criterion_type, :criterion_id]
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|