conceptql 0.2.0 → 0.4.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 +5 -5
- data/CHANGELOG.md +226 -117
- data/LICENSE +21 -0
- data/README.md +35 -83
- data/bin/conceptql +2 -1
- data/bin/conceptql_utils +6 -0
- data/config/multiple_vocabularies.csv +4 -0
- data/config/provenance.yml +489 -0
- data/config/race.yml +50 -0
- data/config/vocabularies.csv +87 -0
- data/lib/conceptql/behaviors/code_lister.rb +23 -0
- data/lib/conceptql/behaviors/drugish.rb +17 -0
- data/lib/conceptql/behaviors/labish.rb +19 -0
- data/lib/conceptql/behaviors/metadatable.rb +226 -73
- data/lib/conceptql/behaviors/provenanceable.rb +26 -0
- data/lib/conceptql/behaviors/single_event.rb +11 -0
- data/lib/conceptql/behaviors/timeless.rb +11 -0
- data/lib/conceptql/behaviors/utilizable.rb +206 -0
- data/lib/conceptql/behaviors/windowable.rb +27 -0
- data/lib/conceptql/cli.rb +117 -108
- data/lib/conceptql/columnizer.rb +75 -0
- data/lib/conceptql/data_model/base.rb +462 -0
- data/lib/conceptql/data_model/gdm.rb +155 -0
- data/lib/conceptql/data_model/gdm_wide.rb +42 -0
- data/lib/conceptql/data_model/omopv4_plus.rb +12 -0
- data/lib/conceptql/data_model.rb +30 -0
- data/lib/conceptql/database.rb +214 -0
- data/lib/conceptql/date_adjuster.rb +66 -18
- data/lib/conceptql/diagram.rb +243 -0
- data/lib/conceptql/lexicon/lexicon_gdm.rb +47 -0
- data/lib/conceptql/lexicon/lexicon_no_db.rb +49 -0
- data/lib/conceptql/lexicon/lexicon_ohdsi.rb +63 -0
- data/lib/conceptql/lexicon/lexicon_strategy.rb +116 -0
- data/lib/conceptql/lexicon.rb +111 -0
- data/lib/conceptql/nodifier.rb +65 -24
- data/lib/conceptql/operators/after.rb +34 -14
- data/lib/conceptql/operators/any_overlap.rb +8 -6
- data/lib/conceptql/operators/before.rb +34 -10
- data/lib/conceptql/operators/binary_operator_operator.rb +66 -23
- data/lib/conceptql/operators/casting_operator.rb +51 -36
- data/lib/conceptql/operators/co_reported.rb +111 -0
- data/lib/conceptql/operators/concurrent_within.rb +69 -0
- data/lib/conceptql/operators/contains.rb +12 -8
- data/lib/conceptql/operators/count.rb +17 -9
- data/lib/conceptql/operators/date_range.rb +48 -14
- data/lib/conceptql/operators/death.rb +14 -4
- data/lib/conceptql/operators/dedup.rb +31 -0
- data/lib/conceptql/operators/during.rb +7 -12
- data/lib/conceptql/operators/episode.rb +146 -0
- data/lib/conceptql/operators/equal.rb +9 -3
- data/lib/conceptql/operators/ethnicity.rb +49 -0
- data/lib/conceptql/operators/except.rb +19 -12
- data/lib/conceptql/operators/filter.rb +23 -8
- data/lib/conceptql/operators/first.rb +16 -3
- data/lib/conceptql/operators/from.rb +52 -4
- data/lib/conceptql/operators/gender.rb +34 -11
- data/lib/conceptql/operators/hospice.rb +19 -0
- data/lib/conceptql/operators/hospitalization.rb +19 -0
- data/lib/conceptql/operators/information_periods.rb +36 -0
- data/lib/conceptql/operators/invalid.rb +35 -0
- data/lib/conceptql/operators/last.rb +16 -3
- data/lib/conceptql/operators/match.rb +91 -0
- data/lib/conceptql/operators/multiple_vocabularies.rb +172 -0
- data/lib/conceptql/operators/numeric.rb +65 -23
- data/lib/conceptql/operators/numeric_filter.rb +68 -0
- data/lib/conceptql/operators/occurrence.rb +153 -32
- data/lib/conceptql/operators/one_in_two_out.rb +237 -38
- data/lib/conceptql/operators/operator.rb +624 -178
- data/lib/conceptql/operators/pass_thru.rb +17 -3
- data/lib/conceptql/operators/person.rb +18 -6
- data/lib/conceptql/operators/person_filter.rb +9 -3
- data/lib/conceptql/operators/place_of_service_filter.rb +43 -0
- data/lib/conceptql/operators/provenance.rb +55 -0
- data/lib/conceptql/operators/provider_filter.rb +62 -0
- data/lib/conceptql/operators/race.rb +55 -7
- data/lib/conceptql/operators/read.rb +168 -0
- data/lib/conceptql/operators/recall.rb +49 -14
- data/lib/conceptql/operators/snf.rb +19 -0
- data/lib/conceptql/operators/sum.rb +29 -12
- data/lib/conceptql/operators/temporal_operator.rb +108 -16
- data/lib/conceptql/operators/time_window.rb +20 -32
- data/lib/conceptql/operators/trim_date.rb +71 -0
- data/lib/conceptql/operators/trim_date_end.rb +28 -40
- data/lib/conceptql/operators/trim_date_start.rb +28 -41
- data/lib/conceptql/operators/union.rb +67 -6
- data/lib/conceptql/operators/vocabulary.rb +151 -0
- data/lib/conceptql/paths.rb +41 -0
- data/lib/conceptql/query.rb +191 -26
- data/lib/conceptql/query_modifiers/gdm/admission_date_query_modifier.rb +46 -0
- data/lib/conceptql/query_modifiers/gdm/drug_query_modifier.rb +54 -0
- data/lib/conceptql/query_modifiers/gdm/lab_query_modifier.rb +57 -0
- data/lib/conceptql/query_modifiers/gdm/pos_query_modifier.rb +46 -0
- data/lib/conceptql/query_modifiers/gdm/provenance_query_modifier.rb +45 -0
- data/lib/conceptql/query_modifiers/gdm/provider_query_modifier.rb +53 -0
- data/lib/conceptql/query_modifiers/gdm_wide/admission_date_query_modifier.rb +42 -0
- data/lib/conceptql/query_modifiers/gdm_wide/drug_query_modifier.rb +52 -0
- data/lib/conceptql/query_modifiers/gdm_wide/lab_query_modifier.rb +55 -0
- data/lib/conceptql/query_modifiers/gdm_wide/pos_query_modifier.rb +37 -0
- data/lib/conceptql/query_modifiers/gdm_wide/provenance_query_modifier.rb +38 -0
- data/lib/conceptql/query_modifiers/omopv4_plus/admission_date_query_modifier.rb +45 -0
- data/lib/conceptql/query_modifiers/omopv4_plus/drug_query_modifier.rb +80 -0
- data/lib/conceptql/query_modifiers/omopv4_plus/pos_query_modifier.rb +47 -0
- data/lib/conceptql/query_modifiers/omopv4_plus/provenance_query_modifier.rb +45 -0
- data/lib/conceptql/query_modifiers/omopv4_plus/provider_query_modifier.rb +26 -0
- data/lib/conceptql/query_modifiers/query_modifier.rb +20 -0
- data/lib/conceptql/rdbms/duckdb.rb +25 -0
- data/lib/conceptql/rdbms/generic.rb +120 -0
- data/lib/conceptql/rdbms/postgres.rb +51 -0
- data/lib/conceptql/rdbms/spark.rb +22 -0
- data/lib/conceptql/rdbms.rb +22 -0
- data/lib/conceptql/scope.rb +481 -31
- data/lib/conceptql/sql_formatters/formatter.rb +25 -0
- data/lib/conceptql/sql_formatters.rb +22 -0
- data/lib/conceptql/utils.rb +113 -0
- data/lib/conceptql/utils_cli.rb +17 -0
- data/lib/conceptql/version.rb +3 -1
- data/lib/conceptql/visitors/metadata_extractor.rb +54 -0
- data/lib/conceptql/vocabularies/behaviors/costish.rb +24 -0
- data/lib/conceptql/vocabularies/behaviors/gdmish.rb +19 -0
- data/lib/conceptql/vocabularies/behaviors/omopish.rb +32 -0
- data/lib/conceptql/vocabularies/dynamic_vocabularies.rb +63 -0
- data/lib/conceptql/vocabularies/entry.rb +138 -0
- data/lib/conceptql/vocabularies/vocabulary_class_factory.rb +85 -0
- data/lib/conceptql/window/base.rb +26 -0
- data/lib/conceptql/window/date_range.rb +25 -0
- data/lib/conceptql/window/table.rb +116 -0
- data/lib/conceptql/window.rb +72 -0
- data/lib/conceptql.rb +65 -2
- data/lib/sequel/extensions/pg_ctas_explain.rb +22 -0
- data/lib/sequel/extensions/pg_vacuum_table.rb +23 -0
- data/schemas/gdm.yml +882 -0
- data/schemas/gdm_wide.yml +757 -0
- data/schemas/ohdsi_vocabs.yml +143 -0
- data/schemas/omopv4_plus.yml +729 -0
- data/schemas/omopv4_plus_more_ignorables.tsv +13 -0
- metadata +267 -207
- data/.gitignore +0 -23
- data/Gemfile +0 -4
- data/Guardfile +0 -56
- data/LICENSE.txt +0 -22
- data/Rakefile +0 -1
- data/conceptql.gemspec +0 -32
- data/doc/ConceptQL Specification (alpha).pdf +0 -0
- data/doc/diagram_0.png +0 -0
- data/doc/implementation_notes.md +0 -39
- data/doc/spec.md +0 -1344
- data/lib/conceptql/behaviors/debuggable.rb +0 -70
- data/lib/conceptql/behaviors/dottable.rb +0 -103
- data/lib/conceptql/behaviors/preppable.rb +0 -20
- data/lib/conceptql/converter.rb +0 -65
- data/lib/conceptql/debugger.rb +0 -48
- data/lib/conceptql/fake_grapher.rb +0 -20
- data/lib/conceptql/graph.rb +0 -52
- data/lib/conceptql/graph_nodifier.rb +0 -203
- data/lib/conceptql/logger.rb +0 -10
- data/lib/conceptql/operators/complement.rb +0 -38
- data/lib/conceptql/operators/concept.rb +0 -70
- data/lib/conceptql/operators/condition_type.rb +0 -71
- data/lib/conceptql/operators/cpt.rb +0 -25
- data/lib/conceptql/operators/drug_type_concept.rb +0 -21
- data/lib/conceptql/operators/from_seer_visits.rb +0 -23
- data/lib/conceptql/operators/hcpcs.rb +0 -25
- data/lib/conceptql/operators/icd10.rb +0 -28
- data/lib/conceptql/operators/icd9.rb +0 -28
- data/lib/conceptql/operators/icd9_procedure.rb +0 -25
- data/lib/conceptql/operators/intersect.rb +0 -34
- data/lib/conceptql/operators/loinc.rb +0 -25
- data/lib/conceptql/operators/medcode.rb +0 -28
- data/lib/conceptql/operators/medcode_procedure.rb +0 -27
- data/lib/conceptql/operators/ndc.rb +0 -29
- data/lib/conceptql/operators/observation_by_enttype.rb +0 -29
- data/lib/conceptql/operators/observation_period.rb +0 -30
- data/lib/conceptql/operators/overlapped_by.rb +0 -23
- data/lib/conceptql/operators/overlaps.rb +0 -19
- data/lib/conceptql/operators/place_of_service_code.rb +0 -24
- data/lib/conceptql/operators/procedure_occurrence.rb +0 -25
- data/lib/conceptql/operators/prodcode.rb +0 -29
- data/lib/conceptql/operators/rxnorm.rb +0 -24
- data/lib/conceptql/operators/snomed.rb +0 -24
- data/lib/conceptql/operators/snomed_condition.rb +0 -26
- data/lib/conceptql/operators/source_vocabulary_operator.rb +0 -58
- data/lib/conceptql/operators/standard_vocabulary_operator.rb +0 -45
- data/lib/conceptql/operators/started_by.rb +0 -22
- data/lib/conceptql/operators/to_seer_visits.rb +0 -24
- data/lib/conceptql/operators/visit.rb +0 -15
- data/lib/conceptql/operators/visit_occurrence.rb +0 -30
- data/lib/conceptql/tree.rb +0 -53
- data/lib/conceptql/utils/temp_table.rb +0 -72
- data/spec/conceptql/behaviors/dottable_spec.rb +0 -99
- data/spec/conceptql/converter_spec.rb +0 -51
- data/spec/conceptql/date_adjuster_spec.rb +0 -68
- data/spec/conceptql/operators/after_spec.rb +0 -16
- data/spec/conceptql/operators/before_spec.rb +0 -16
- data/spec/conceptql/operators/casting_operator_spec.rb +0 -68
- data/spec/conceptql/operators/complement_spec.rb +0 -15
- data/spec/conceptql/operators/concept_spec.rb +0 -40
- data/spec/conceptql/operators/condition_type_spec.rb +0 -128
- data/spec/conceptql/operators/contains_spec.rb +0 -19
- data/spec/conceptql/operators/cpt_spec.rb +0 -29
- data/spec/conceptql/operators/date_range_spec.rb +0 -33
- data/spec/conceptql/operators/death_spec.rb +0 -10
- data/spec/conceptql/operators/during_spec.rb +0 -30
- data/spec/conceptql/operators/except_spec.rb +0 -15
- data/spec/conceptql/operators/first_spec.rb +0 -35
- data/spec/conceptql/operators/from_spec.rb +0 -13
- data/spec/conceptql/operators/gender_spec.rb +0 -27
- data/spec/conceptql/operators/hcpcs_spec.rb +0 -29
- data/spec/conceptql/operators/icd10_spec.rb +0 -34
- data/spec/conceptql/operators/icd9_procedure_spec.rb +0 -29
- data/spec/conceptql/operators/icd9_spec.rb +0 -34
- data/spec/conceptql/operators/intersect_spec.rb +0 -28
- data/spec/conceptql/operators/last_spec.rb +0 -36
- data/spec/conceptql/operators/loinc_spec.rb +0 -29
- data/spec/conceptql/operators/medcode_procedure_spec.rb +0 -34
- data/spec/conceptql/operators/medcode_spec.rb +0 -34
- data/spec/conceptql/operators/observation_period_spec.rb +0 -10
- data/spec/conceptql/operators/occurrence_spec.rb +0 -87
- data/spec/conceptql/operators/overlapped_by_spec.rb +0 -32
- data/spec/conceptql/operators/overlaps_spec.rb +0 -21
- data/spec/conceptql/operators/person_filter_spec.rb +0 -15
- data/spec/conceptql/operators/person_spec.rb +0 -10
- data/spec/conceptql/operators/place_of_service_code_spec.rb +0 -24
- data/spec/conceptql/operators/procedure_occurrence_spec.rb +0 -10
- data/spec/conceptql/operators/prodcode_spec.rb +0 -35
- data/spec/conceptql/operators/query_double.rb +0 -20
- data/spec/conceptql/operators/query_double_spec.rb +0 -7
- data/spec/conceptql/operators/race_spec.rb +0 -21
- data/spec/conceptql/operators/rxnorm_spec.rb +0 -29
- data/spec/conceptql/operators/snomed_spec.rb +0 -29
- data/spec/conceptql/operators/source_vocabulary_operator_spec.rb +0 -35
- data/spec/conceptql/operators/standard_vocabulary_operator_spec.rb +0 -35
- data/spec/conceptql/operators/started_by_spec.rb +0 -22
- data/spec/conceptql/operators/temporal_operator_spec.rb +0 -51
- data/spec/conceptql/operators/time_window_spec.rb +0 -77
- data/spec/conceptql/operators/union_spec.rb +0 -21
- data/spec/conceptql/operators/visit_occurrence_spec.rb +0 -10
- data/spec/conceptql/query_spec.rb +0 -22
- data/spec/conceptql/tree_spec.rb +0 -50
- data/spec/doubles/stream_for_casting_double.rb +0 -9
- data/spec/doubles/stream_for_occurrence_double.rb +0 -25
- data/spec/doubles/stream_for_temporal_double.rb +0 -6
- data/spec/spec_helper.rb +0 -102
|
@@ -1,10 +1,24 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "operator"
|
|
2
4
|
|
|
3
5
|
module ConceptQL
|
|
4
6
|
module Operators
|
|
7
|
+
# The base class of operators whose domains and columns are those of their
|
|
8
|
+
# upstreams (union, count, sum, numeric, dedup, episode, time_window and
|
|
9
|
+
# co_reported). It has no query method, so it does not register: pass_thru
|
|
10
|
+
# is not an operator name.
|
|
5
11
|
class PassThru < Operator
|
|
6
|
-
|
|
7
|
-
|
|
12
|
+
basic_type :set
|
|
13
|
+
no_desc
|
|
14
|
+
|
|
15
|
+
def domains(db)
|
|
16
|
+
doms = upstreams.compact.flat_map { |up| up.domains(db) }.uniq
|
|
17
|
+
doms.empty? ? [ :invalid ] : doms
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def query_cols
|
|
21
|
+
upstreams.map(&:query_cols).inject(:|)
|
|
8
22
|
end
|
|
9
23
|
end
|
|
10
24
|
end
|
|
@@ -1,27 +1,39 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "casting_operator"
|
|
2
4
|
|
|
3
5
|
module ConceptQL
|
|
4
6
|
module Operators
|
|
5
7
|
class Person < CastingOperator
|
|
6
|
-
|
|
8
|
+
include ConceptQL::Behaviors::Windowable
|
|
9
|
+
include ConceptQL::Behaviors::Timeless
|
|
10
|
+
|
|
11
|
+
register __FILE__
|
|
12
|
+
|
|
13
|
+
desc "Selects all records in the person table."
|
|
14
|
+
|
|
7
15
|
allows_one_upstream
|
|
8
|
-
|
|
16
|
+
domains :person
|
|
9
17
|
|
|
10
|
-
def
|
|
18
|
+
def my_domain
|
|
11
19
|
:person
|
|
12
20
|
end
|
|
13
21
|
|
|
22
|
+
def source_table
|
|
23
|
+
dm.table_by_domain(my_domain)
|
|
24
|
+
end
|
|
25
|
+
|
|
14
26
|
def i_point_at
|
|
15
27
|
[]
|
|
16
28
|
end
|
|
17
29
|
|
|
18
30
|
def these_point_at_me
|
|
19
|
-
# I could list ALL the
|
|
31
|
+
# I could list ALL the domains we use, but the default behavior of casting,
|
|
20
32
|
# when there is no explicit casting defined, is to convert everything to
|
|
21
33
|
# person IDs
|
|
22
34
|
#
|
|
23
35
|
# So by defining no known castable relationships in this operator, all
|
|
24
|
-
#
|
|
36
|
+
# domains will be converted to person
|
|
25
37
|
[]
|
|
26
38
|
end
|
|
27
39
|
end
|
|
@@ -1,12 +1,18 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "binary_operator_operator"
|
|
2
4
|
|
|
3
5
|
module ConceptQL
|
|
4
6
|
module Operators
|
|
5
7
|
class PersonFilter < BinaryOperatorOperator
|
|
6
|
-
|
|
8
|
+
register __FILE__
|
|
9
|
+
|
|
10
|
+
desc "Passes along left hand records with a corresponding person_id in the right hand records."
|
|
11
|
+
default_query_columns
|
|
12
|
+
|
|
7
13
|
def query(db)
|
|
8
14
|
db.from(left.evaluate(db))
|
|
9
|
-
.where(person_id: right.evaluate(db).select_group(:person_id))
|
|
15
|
+
.where(person_id: right.evaluate(db).from_self.select_group(:person_id))
|
|
10
16
|
end
|
|
11
17
|
end
|
|
12
18
|
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "operator"
|
|
4
|
+
|
|
5
|
+
module ConceptQL
|
|
6
|
+
module Operators
|
|
7
|
+
# Filters the incoming stream of records to only those that have a
|
|
8
|
+
# an associated visit_occurrence with the matching place_of_service
|
|
9
|
+
#
|
|
10
|
+
# Provenance related concepts are the ones found in the xxx_type_concept_id
|
|
11
|
+
# field.
|
|
12
|
+
#
|
|
13
|
+
# If the record has NULL for the provenance-related field, they are filtered
|
|
14
|
+
# out.
|
|
15
|
+
#
|
|
16
|
+
# Multiple provenances can be specified at once
|
|
17
|
+
class PlaceOfServiceFilter < Operator
|
|
18
|
+
register __FILE__
|
|
19
|
+
|
|
20
|
+
desc "Passes along records that match one or more of the Medicare Place Of Service values."
|
|
21
|
+
|
|
22
|
+
argument :places_of_service, type: :codelist, vocab: "Place of Service"
|
|
23
|
+
category "Filter Single Stream"
|
|
24
|
+
basic_type :temporal
|
|
25
|
+
allows_one_upstream
|
|
26
|
+
validate_one_upstream
|
|
27
|
+
validate_at_least_one_argument
|
|
28
|
+
require_column :visit_source_concept_id
|
|
29
|
+
default_query_columns
|
|
30
|
+
|
|
31
|
+
def query(db)
|
|
32
|
+
db.from(stream.evaluate(db))
|
|
33
|
+
.where(visit_source_concept_id: place_of_service_concept_ids(db))
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def place_of_service_concept_ids(db)
|
|
39
|
+
dm.concept_ids(db, "Place of Service", arguments.map(&:to_s))
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "operator"
|
|
4
|
+
require_relative "../behaviors/provenanceable"
|
|
5
|
+
|
|
6
|
+
module ConceptQL
|
|
7
|
+
module Operators
|
|
8
|
+
# Filters the incoming stream of records to only those that have a
|
|
9
|
+
# provenance-related concept_id.
|
|
10
|
+
#
|
|
11
|
+
# Provenance related concepts are the ones found in the xxx_type_concept_id
|
|
12
|
+
# field.
|
|
13
|
+
#
|
|
14
|
+
# If the record has NULL for the provenance-related field, they are filtered
|
|
15
|
+
# out.
|
|
16
|
+
#
|
|
17
|
+
# Multiple provenances can be specified at once
|
|
18
|
+
class Provenance < Operator
|
|
19
|
+
# GDM only: omopv4_plus has no provenance vocabularies (conceptql-myp).
|
|
20
|
+
register __FILE__, :gdm, :gdm_wide
|
|
21
|
+
|
|
22
|
+
desc "Passes along records with the indicated provenance (e.g. inpatient, outpatient, file type)."
|
|
23
|
+
|
|
24
|
+
argument :provenance_types, label: "Provenance Types", type: :codelist
|
|
25
|
+
category "Filter Single Stream"
|
|
26
|
+
basic_type :temporal
|
|
27
|
+
allows_one_upstream
|
|
28
|
+
validate_one_upstream
|
|
29
|
+
default_query_columns
|
|
30
|
+
|
|
31
|
+
include ConceptQL::Provenanceable
|
|
32
|
+
|
|
33
|
+
attr_reader :db
|
|
34
|
+
|
|
35
|
+
def query(db)
|
|
36
|
+
@db = db
|
|
37
|
+
db.from(stream.evaluate(db)).where(build_where_from_codes(db, arguments))
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
|
|
42
|
+
def additional_validation(db, _opts = {})
|
|
43
|
+
# With no lexicon there are no provenance concepts to check against,
|
|
44
|
+
# and every keyword would be reported as unrecognized. Vocabulary
|
|
45
|
+
# operators skip their unknown-code check in the same situation.
|
|
46
|
+
return if dm.lexicon.strategy == :no_db
|
|
47
|
+
|
|
48
|
+
bad_keywords = find_bad_keywords(db, arguments)
|
|
49
|
+
return unless bad_keywords.present?
|
|
50
|
+
|
|
51
|
+
add_error("unrecognized keywords", *bad_keywords.uniq)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "operator"
|
|
4
|
+
|
|
5
|
+
module ConceptQL
|
|
6
|
+
module Operators
|
|
7
|
+
# Filters incoming records to only those that have been associated with
|
|
8
|
+
# providers matching the given criteria.
|
|
9
|
+
class ProviderFilter < Operator
|
|
10
|
+
register __FILE__
|
|
11
|
+
|
|
12
|
+
desc "Passes along records where the provider's specialty matches a given set of specialty_concept_ids.\n\nA single record may have multiple specialties, so the operator will return all records that match any of the given specialties, which may lead to duplicate records."
|
|
13
|
+
option :specialties, type: :string
|
|
14
|
+
option :roles, type: :string
|
|
15
|
+
category "Filter Single Stream"
|
|
16
|
+
basic_type :temporal
|
|
17
|
+
allows_one_upstream
|
|
18
|
+
validate_one_upstream
|
|
19
|
+
validate_required_options :specialties, :roles
|
|
20
|
+
require_column :provider_id
|
|
21
|
+
require_column :context_specialty_concept_id
|
|
22
|
+
require_column :practitioner_specialty_concept_id
|
|
23
|
+
require_column :role_type_concept_id
|
|
24
|
+
default_query_columns
|
|
25
|
+
|
|
26
|
+
def query(db)
|
|
27
|
+
ds = stream.evaluate(db).from_self(alias: :upstream)
|
|
28
|
+
|
|
29
|
+
specialty_concept_ids = options[:specialties].split(/\s*,\s*/)
|
|
30
|
+
role_type_concept_ids = options[:roles].split(/\s*,\s*/)
|
|
31
|
+
|
|
32
|
+
unless specialty_concept_ids.include?("*")
|
|
33
|
+
spec_con_ids = specialty_concept_ids.map(&:to_i)
|
|
34
|
+
ds = ds.where(
|
|
35
|
+
Sequel.or(
|
|
36
|
+
[
|
|
37
|
+
[ :context_specialty_concept_id, spec_con_ids ],
|
|
38
|
+
[ :practitioner_specialty_concept_id, spec_con_ids ]
|
|
39
|
+
]
|
|
40
|
+
)
|
|
41
|
+
)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
unless role_type_concept_ids.include?("*")
|
|
45
|
+
role_con_ids = role_type_concept_ids.map(&:to_i)
|
|
46
|
+
ds = ds.where(role_type_concept_id: role_con_ids)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
ds
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def query_columns
|
|
53
|
+
default_columns + %i[
|
|
54
|
+
context_specialty_concept_id
|
|
55
|
+
practitioner_specialty_concept_id
|
|
56
|
+
role_type_concept_id
|
|
57
|
+
provider_id
|
|
58
|
+
]
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "operator"
|
|
4
|
+
require "yaml"
|
|
2
5
|
|
|
3
6
|
module ConceptQL
|
|
4
7
|
module Operators
|
|
@@ -9,14 +12,59 @@ module ConceptQL
|
|
|
9
12
|
# concept_name column of the concept table. If you misspell the race name
|
|
10
13
|
# you won't get any matches
|
|
11
14
|
class Race < Operator
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
+
register __FILE__
|
|
16
|
+
|
|
17
|
+
include ConceptQL::Behaviors::Windowable
|
|
18
|
+
include ConceptQL::Behaviors::Timeless
|
|
19
|
+
|
|
20
|
+
desc "Selects person records that match the given set of Race codes."
|
|
21
|
+
argument :races, type: :codelist, vocab: "Race"
|
|
22
|
+
domains :person
|
|
23
|
+
category "Select by Property"
|
|
24
|
+
basic_type :selection
|
|
25
|
+
validate_no_upstreams
|
|
26
|
+
validate_at_least_one_argument
|
|
27
|
+
|
|
28
|
+
def race_descendents
|
|
29
|
+
@race_descendents ||= YAML.load_file(ConceptQL.race_file)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def query_cols
|
|
33
|
+
table_columns(table)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def table
|
|
37
|
+
source_table
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def source_table
|
|
41
|
+
if gdm?
|
|
42
|
+
:patients
|
|
43
|
+
else
|
|
44
|
+
:person
|
|
45
|
+
end
|
|
46
|
+
end
|
|
15
47
|
|
|
16
48
|
def query(db)
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
49
|
+
words = arguments - actual_ids
|
|
50
|
+
|
|
51
|
+
concept_ids = dm.concept_ids_by_name(db, words)
|
|
52
|
+
|
|
53
|
+
c_ids = (concept_ids + actual_ids).map { |i| [ i, race_descendents[i] ] }.flatten.compact.uniq.sort
|
|
54
|
+
|
|
55
|
+
c_ids = dm.related_concept_ids(db, c_ids) if gdm?
|
|
56
|
+
|
|
57
|
+
q = db.from(source_table)
|
|
58
|
+
if words.any? { |w| w.match(/unknown/i) }
|
|
59
|
+
c_ids += [ 0 ]
|
|
60
|
+
q = q.where(race_concept_id: nil)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
q.where(race_concept_id: c_ids)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def actual_ids
|
|
67
|
+
@actual_ids ||= arguments.select { |i| i.to_s.match(/^\d+$/) }
|
|
20
68
|
end
|
|
21
69
|
end
|
|
22
70
|
end
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "sequel"
|
|
4
|
+
require "sequel/adapters/mock"
|
|
5
|
+
require_relative "../behaviors/labish"
|
|
6
|
+
|
|
7
|
+
module ConceptQL
|
|
8
|
+
module Operators
|
|
9
|
+
class Read < Operator
|
|
10
|
+
include ConceptQL::Behaviors::CodeLister
|
|
11
|
+
register __FILE__
|
|
12
|
+
|
|
13
|
+
preferred_name "READ"
|
|
14
|
+
desc "Selects records based on the Read vocabulary."
|
|
15
|
+
argument :read_codes, type: :codelist, vocab: "Read"
|
|
16
|
+
basic_type :selection
|
|
17
|
+
category "Select by Clinical Codes"
|
|
18
|
+
conceptql_spec_id "vocabulary"
|
|
19
|
+
|
|
20
|
+
def query(db)
|
|
21
|
+
gdm? ? gdm(db) : omopv4(db)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def gdm(db)
|
|
25
|
+
ops(db).first.evaluate(db)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def omopv4(db)
|
|
29
|
+
streams = ops(db).map { |op| op.evaluate(db) }
|
|
30
|
+
|
|
31
|
+
streams.inject { |q, query| q.union(query, all: true) }.from_self
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def domains(db)
|
|
35
|
+
ops.first.domains(db)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def query_cols
|
|
39
|
+
gdm? ? ops.first.query_cols : ops.map(&:query_cols).flatten.uniq
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def required_columns
|
|
43
|
+
ops.flat_map(&:required_columns).uniq
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
private
|
|
47
|
+
|
|
48
|
+
def ops(_db = nil)
|
|
49
|
+
[ ReadGDM.new(nodifier, "read_condition_occurrence", *arguments) ]
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def describe_codes(db, codes)
|
|
53
|
+
ops.flat_map { |op| op.describe_codes(db, codes) }
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def klasses
|
|
57
|
+
@klasses ||= {
|
|
58
|
+
condition_occurrence: ReadCondition,
|
|
59
|
+
procedure_occurrence: ReadProcedure,
|
|
60
|
+
drug_exposure: ReadDrug,
|
|
61
|
+
observation: ReadObservation
|
|
62
|
+
}
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
class ReadBase < ConceptQL::Operators::Vocabulary
|
|
66
|
+
preferred_name "READ"
|
|
67
|
+
argument :read_codes, type: :codelist, vocab: "Read"
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
class ReadOmopBase < ReadBase
|
|
71
|
+
include ConceptQL::Vocabularies::Behaviors::Omopish
|
|
72
|
+
|
|
73
|
+
def vocabulary_id
|
|
74
|
+
17
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def source_table
|
|
78
|
+
table
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def domain
|
|
82
|
+
table
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def domains(_db)
|
|
86
|
+
Array(domain)
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
class ReadGDM < ReadBase
|
|
91
|
+
include ConceptQL::Vocabularies::Behaviors::Gdmish
|
|
92
|
+
include ConceptQL::Behaviors::Labish
|
|
93
|
+
|
|
94
|
+
def vocabulary_id
|
|
95
|
+
"Read"
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def domain_map(_v_id)
|
|
99
|
+
:condition_occurrence
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def domains(_db)
|
|
103
|
+
[ :condition_occurrence ]
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
class ReadCondition < ReadOmopBase
|
|
108
|
+
def table
|
|
109
|
+
:procedure_occurrence
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def source_column
|
|
113
|
+
:procedure_source_value
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def concept_column
|
|
117
|
+
:procedure_concept_id
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
class ReadProcedure < ReadOmopBase
|
|
122
|
+
def table
|
|
123
|
+
:procedure_occurrence
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def source_column
|
|
127
|
+
:procedure_source_value
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def concept_column
|
|
131
|
+
:procedure_concept_id
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
class ReadObservation < ReadOmopBase
|
|
136
|
+
include ConceptQL::Behaviors::Labish
|
|
137
|
+
|
|
138
|
+
def table
|
|
139
|
+
:observation
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def source_column
|
|
143
|
+
:observation_source_value
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def concept_column
|
|
147
|
+
:observation_concept_id
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
class ReadDrug < ReadOmopBase
|
|
152
|
+
include ConceptQL::Behaviors::Drugish
|
|
153
|
+
|
|
154
|
+
def table
|
|
155
|
+
:drug_exposure
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def source_column
|
|
159
|
+
:drug_source_value
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def concept_column
|
|
163
|
+
:drug_concept_id
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
end
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "pass_thru"
|
|
2
4
|
|
|
3
5
|
module ConceptQL
|
|
4
6
|
module Operators
|
|
@@ -8,31 +10,64 @@ module ConceptQL
|
|
|
8
10
|
# that complex concept into a set of sub-concepts to better understand it.
|
|
9
11
|
#
|
|
10
12
|
# This operator will look for a sub-concept that has been created through the
|
|
11
|
-
# "define" operator and will fetch the
|
|
13
|
+
# "define" operator and will fetch the records cached in the corresponding table
|
|
12
14
|
class Recall < Operator
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
register __FILE__
|
|
16
|
+
|
|
17
|
+
desc "Recalls a set of records from a labeled operator in the statement"
|
|
18
|
+
|
|
17
19
|
argument :name, type: :string
|
|
18
|
-
category
|
|
20
|
+
category "Get Related Data"
|
|
21
|
+
basic_type :selection
|
|
22
|
+
validate_no_upstreams
|
|
23
|
+
validate_one_argument
|
|
24
|
+
default_query_columns
|
|
19
25
|
|
|
20
26
|
def query(db)
|
|
21
27
|
scope.from(db, source)
|
|
22
28
|
end
|
|
23
29
|
|
|
24
|
-
def
|
|
25
|
-
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
def types
|
|
29
|
-
scope.types(source)
|
|
30
|
+
def domains(db)
|
|
31
|
+
scope.domains(source, db)
|
|
30
32
|
end
|
|
31
33
|
|
|
32
34
|
def source
|
|
33
35
|
arguments.first
|
|
34
36
|
end
|
|
37
|
+
|
|
38
|
+
def annotate(db, opts = {})
|
|
39
|
+
@annotate ||= if valid?(db) && replaced?
|
|
40
|
+
original.annotate(db, opts)
|
|
41
|
+
else
|
|
42
|
+
super
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def original
|
|
47
|
+
nodifier.scope.fetch_operator(source)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def code_list(db)
|
|
51
|
+
original.code_list(db)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
private
|
|
55
|
+
|
|
56
|
+
def additional_validation(_db, _opts = {})
|
|
57
|
+
return unless arguments.length == 1
|
|
58
|
+
|
|
59
|
+
if scope.fetch_operator(source)
|
|
60
|
+
scope.recall_dependencies[source].each do |d|
|
|
61
|
+
add_error("mutually referential recalls", d) if scope.recall_dependencies[d].include?(source)
|
|
62
|
+
end
|
|
63
|
+
else
|
|
64
|
+
add_error("no matching label", source)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def replaced?
|
|
69
|
+
options[:replaced]
|
|
70
|
+
end
|
|
35
71
|
end
|
|
36
72
|
end
|
|
37
73
|
end
|
|
38
|
-
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "operator"
|
|
4
|
+
|
|
5
|
+
module ConceptQL
|
|
6
|
+
module Operators
|
|
7
|
+
class Snf < Operator
|
|
8
|
+
# GDM only: omopv4_plus has no provenance vocabularies (conceptql-myp).
|
|
9
|
+
register __FILE__, :gdm, :gdm_wide
|
|
10
|
+
include ConceptQL::Behaviors::Utilizable
|
|
11
|
+
|
|
12
|
+
desc "Selects admission records of type 'SNF'"
|
|
13
|
+
|
|
14
|
+
def collection_type
|
|
15
|
+
"SNF"
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -1,29 +1,46 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "pass_thru"
|
|
2
4
|
|
|
3
5
|
module ConceptQL
|
|
4
6
|
module Operators
|
|
5
7
|
class Sum < PassThru
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
8
|
+
register __FILE__
|
|
9
|
+
|
|
10
|
+
def events_per_patient
|
|
11
|
+
:multiple
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def multiple_vocabularies
|
|
15
|
+
return true if upstreams.any?(&:multiple_vocabularies)
|
|
16
|
+
|
|
17
|
+
vocabularies.length > 1
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
desc <<~EOF
|
|
21
|
+
Sums value_as_number across all records that match on all but start_date, end_date.
|
|
22
|
+
For start_date and end_date the min and max of each respectively is returned.'
|
|
9
23
|
EOF
|
|
24
|
+
default_query_columns
|
|
25
|
+
validate_at_least_one_upstream
|
|
26
|
+
validate_no_arguments
|
|
27
|
+
require_column :value_as_number
|
|
10
28
|
|
|
11
29
|
def query(db)
|
|
12
30
|
db.from(unioned(db))
|
|
13
|
-
.select_group(*(
|
|
14
|
-
.select_append(Sequel.lit(
|
|
15
|
-
.select_append{ min(start_date).as(:start_date) }
|
|
16
|
-
.select_append{ max(end_date).as(:end_date) }
|
|
17
|
-
.select_append{sum(value_as_number).as(:value_as_number)}
|
|
31
|
+
.select_group(*(dynamic_columns - %i[start_date end_date criterion_id value_as_number]))
|
|
32
|
+
.select_append(Sequel.lit("?", 0).as(:criterion_id))
|
|
33
|
+
.select_append { min(start_date).as(:start_date) }
|
|
34
|
+
.select_append { max(end_date).as(:end_date) }
|
|
35
|
+
.select_append { sum(value_as_number).as(:value_as_number) }
|
|
18
36
|
.from_self
|
|
19
37
|
end
|
|
20
38
|
|
|
21
39
|
def unioned(db)
|
|
22
|
-
upstreams.map { |c| c.evaluate(db) }.inject do |uni, q|
|
|
23
|
-
uni.union(q)
|
|
40
|
+
upstreams.map { |c| c.evaluate(db).select(*query_cols) }.inject do |uni, q|
|
|
41
|
+
uni.union(q.from_self)
|
|
24
42
|
end
|
|
25
43
|
end
|
|
26
44
|
end
|
|
27
45
|
end
|
|
28
46
|
end
|
|
29
|
-
|