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.
Files changed (242) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +226 -117
  3. data/LICENSE +21 -0
  4. data/README.md +35 -83
  5. data/bin/conceptql +2 -1
  6. data/bin/conceptql_utils +6 -0
  7. data/config/multiple_vocabularies.csv +4 -0
  8. data/config/provenance.yml +489 -0
  9. data/config/race.yml +50 -0
  10. data/config/vocabularies.csv +87 -0
  11. data/lib/conceptql/behaviors/code_lister.rb +23 -0
  12. data/lib/conceptql/behaviors/drugish.rb +17 -0
  13. data/lib/conceptql/behaviors/labish.rb +19 -0
  14. data/lib/conceptql/behaviors/metadatable.rb +226 -73
  15. data/lib/conceptql/behaviors/provenanceable.rb +26 -0
  16. data/lib/conceptql/behaviors/single_event.rb +11 -0
  17. data/lib/conceptql/behaviors/timeless.rb +11 -0
  18. data/lib/conceptql/behaviors/utilizable.rb +206 -0
  19. data/lib/conceptql/behaviors/windowable.rb +27 -0
  20. data/lib/conceptql/cli.rb +117 -108
  21. data/lib/conceptql/columnizer.rb +75 -0
  22. data/lib/conceptql/data_model/base.rb +462 -0
  23. data/lib/conceptql/data_model/gdm.rb +155 -0
  24. data/lib/conceptql/data_model/gdm_wide.rb +42 -0
  25. data/lib/conceptql/data_model/omopv4_plus.rb +12 -0
  26. data/lib/conceptql/data_model.rb +30 -0
  27. data/lib/conceptql/database.rb +214 -0
  28. data/lib/conceptql/date_adjuster.rb +66 -18
  29. data/lib/conceptql/diagram.rb +243 -0
  30. data/lib/conceptql/lexicon/lexicon_gdm.rb +47 -0
  31. data/lib/conceptql/lexicon/lexicon_no_db.rb +49 -0
  32. data/lib/conceptql/lexicon/lexicon_ohdsi.rb +63 -0
  33. data/lib/conceptql/lexicon/lexicon_strategy.rb +116 -0
  34. data/lib/conceptql/lexicon.rb +111 -0
  35. data/lib/conceptql/nodifier.rb +65 -24
  36. data/lib/conceptql/operators/after.rb +34 -14
  37. data/lib/conceptql/operators/any_overlap.rb +8 -6
  38. data/lib/conceptql/operators/before.rb +34 -10
  39. data/lib/conceptql/operators/binary_operator_operator.rb +66 -23
  40. data/lib/conceptql/operators/casting_operator.rb +51 -36
  41. data/lib/conceptql/operators/co_reported.rb +111 -0
  42. data/lib/conceptql/operators/concurrent_within.rb +69 -0
  43. data/lib/conceptql/operators/contains.rb +12 -8
  44. data/lib/conceptql/operators/count.rb +17 -9
  45. data/lib/conceptql/operators/date_range.rb +48 -14
  46. data/lib/conceptql/operators/death.rb +14 -4
  47. data/lib/conceptql/operators/dedup.rb +31 -0
  48. data/lib/conceptql/operators/during.rb +7 -12
  49. data/lib/conceptql/operators/episode.rb +146 -0
  50. data/lib/conceptql/operators/equal.rb +9 -3
  51. data/lib/conceptql/operators/ethnicity.rb +49 -0
  52. data/lib/conceptql/operators/except.rb +19 -12
  53. data/lib/conceptql/operators/filter.rb +23 -8
  54. data/lib/conceptql/operators/first.rb +16 -3
  55. data/lib/conceptql/operators/from.rb +52 -4
  56. data/lib/conceptql/operators/gender.rb +34 -11
  57. data/lib/conceptql/operators/hospice.rb +19 -0
  58. data/lib/conceptql/operators/hospitalization.rb +19 -0
  59. data/lib/conceptql/operators/information_periods.rb +36 -0
  60. data/lib/conceptql/operators/invalid.rb +35 -0
  61. data/lib/conceptql/operators/last.rb +16 -3
  62. data/lib/conceptql/operators/match.rb +91 -0
  63. data/lib/conceptql/operators/multiple_vocabularies.rb +172 -0
  64. data/lib/conceptql/operators/numeric.rb +65 -23
  65. data/lib/conceptql/operators/numeric_filter.rb +68 -0
  66. data/lib/conceptql/operators/occurrence.rb +153 -32
  67. data/lib/conceptql/operators/one_in_two_out.rb +237 -38
  68. data/lib/conceptql/operators/operator.rb +624 -178
  69. data/lib/conceptql/operators/pass_thru.rb +17 -3
  70. data/lib/conceptql/operators/person.rb +18 -6
  71. data/lib/conceptql/operators/person_filter.rb +9 -3
  72. data/lib/conceptql/operators/place_of_service_filter.rb +43 -0
  73. data/lib/conceptql/operators/provenance.rb +55 -0
  74. data/lib/conceptql/operators/provider_filter.rb +62 -0
  75. data/lib/conceptql/operators/race.rb +55 -7
  76. data/lib/conceptql/operators/read.rb +168 -0
  77. data/lib/conceptql/operators/recall.rb +49 -14
  78. data/lib/conceptql/operators/snf.rb +19 -0
  79. data/lib/conceptql/operators/sum.rb +29 -12
  80. data/lib/conceptql/operators/temporal_operator.rb +108 -16
  81. data/lib/conceptql/operators/time_window.rb +20 -32
  82. data/lib/conceptql/operators/trim_date.rb +71 -0
  83. data/lib/conceptql/operators/trim_date_end.rb +28 -40
  84. data/lib/conceptql/operators/trim_date_start.rb +28 -41
  85. data/lib/conceptql/operators/union.rb +67 -6
  86. data/lib/conceptql/operators/vocabulary.rb +151 -0
  87. data/lib/conceptql/paths.rb +41 -0
  88. data/lib/conceptql/query.rb +191 -26
  89. data/lib/conceptql/query_modifiers/gdm/admission_date_query_modifier.rb +46 -0
  90. data/lib/conceptql/query_modifiers/gdm/drug_query_modifier.rb +54 -0
  91. data/lib/conceptql/query_modifiers/gdm/lab_query_modifier.rb +57 -0
  92. data/lib/conceptql/query_modifiers/gdm/pos_query_modifier.rb +46 -0
  93. data/lib/conceptql/query_modifiers/gdm/provenance_query_modifier.rb +45 -0
  94. data/lib/conceptql/query_modifiers/gdm/provider_query_modifier.rb +53 -0
  95. data/lib/conceptql/query_modifiers/gdm_wide/admission_date_query_modifier.rb +42 -0
  96. data/lib/conceptql/query_modifiers/gdm_wide/drug_query_modifier.rb +52 -0
  97. data/lib/conceptql/query_modifiers/gdm_wide/lab_query_modifier.rb +55 -0
  98. data/lib/conceptql/query_modifiers/gdm_wide/pos_query_modifier.rb +37 -0
  99. data/lib/conceptql/query_modifiers/gdm_wide/provenance_query_modifier.rb +38 -0
  100. data/lib/conceptql/query_modifiers/omopv4_plus/admission_date_query_modifier.rb +45 -0
  101. data/lib/conceptql/query_modifiers/omopv4_plus/drug_query_modifier.rb +80 -0
  102. data/lib/conceptql/query_modifiers/omopv4_plus/pos_query_modifier.rb +47 -0
  103. data/lib/conceptql/query_modifiers/omopv4_plus/provenance_query_modifier.rb +45 -0
  104. data/lib/conceptql/query_modifiers/omopv4_plus/provider_query_modifier.rb +26 -0
  105. data/lib/conceptql/query_modifiers/query_modifier.rb +20 -0
  106. data/lib/conceptql/rdbms/duckdb.rb +25 -0
  107. data/lib/conceptql/rdbms/generic.rb +120 -0
  108. data/lib/conceptql/rdbms/postgres.rb +51 -0
  109. data/lib/conceptql/rdbms/spark.rb +22 -0
  110. data/lib/conceptql/rdbms.rb +22 -0
  111. data/lib/conceptql/scope.rb +481 -31
  112. data/lib/conceptql/sql_formatters/formatter.rb +25 -0
  113. data/lib/conceptql/sql_formatters.rb +22 -0
  114. data/lib/conceptql/utils.rb +113 -0
  115. data/lib/conceptql/utils_cli.rb +17 -0
  116. data/lib/conceptql/version.rb +3 -1
  117. data/lib/conceptql/visitors/metadata_extractor.rb +54 -0
  118. data/lib/conceptql/vocabularies/behaviors/costish.rb +24 -0
  119. data/lib/conceptql/vocabularies/behaviors/gdmish.rb +19 -0
  120. data/lib/conceptql/vocabularies/behaviors/omopish.rb +32 -0
  121. data/lib/conceptql/vocabularies/dynamic_vocabularies.rb +63 -0
  122. data/lib/conceptql/vocabularies/entry.rb +138 -0
  123. data/lib/conceptql/vocabularies/vocabulary_class_factory.rb +85 -0
  124. data/lib/conceptql/window/base.rb +26 -0
  125. data/lib/conceptql/window/date_range.rb +25 -0
  126. data/lib/conceptql/window/table.rb +116 -0
  127. data/lib/conceptql/window.rb +72 -0
  128. data/lib/conceptql.rb +65 -2
  129. data/lib/sequel/extensions/pg_ctas_explain.rb +22 -0
  130. data/lib/sequel/extensions/pg_vacuum_table.rb +23 -0
  131. data/schemas/gdm.yml +882 -0
  132. data/schemas/gdm_wide.yml +757 -0
  133. data/schemas/ohdsi_vocabs.yml +143 -0
  134. data/schemas/omopv4_plus.yml +729 -0
  135. data/schemas/omopv4_plus_more_ignorables.tsv +13 -0
  136. metadata +267 -207
  137. data/.gitignore +0 -23
  138. data/Gemfile +0 -4
  139. data/Guardfile +0 -56
  140. data/LICENSE.txt +0 -22
  141. data/Rakefile +0 -1
  142. data/conceptql.gemspec +0 -32
  143. data/doc/ConceptQL Specification (alpha).pdf +0 -0
  144. data/doc/diagram_0.png +0 -0
  145. data/doc/implementation_notes.md +0 -39
  146. data/doc/spec.md +0 -1344
  147. data/lib/conceptql/behaviors/debuggable.rb +0 -70
  148. data/lib/conceptql/behaviors/dottable.rb +0 -103
  149. data/lib/conceptql/behaviors/preppable.rb +0 -20
  150. data/lib/conceptql/converter.rb +0 -65
  151. data/lib/conceptql/debugger.rb +0 -48
  152. data/lib/conceptql/fake_grapher.rb +0 -20
  153. data/lib/conceptql/graph.rb +0 -52
  154. data/lib/conceptql/graph_nodifier.rb +0 -203
  155. data/lib/conceptql/logger.rb +0 -10
  156. data/lib/conceptql/operators/complement.rb +0 -38
  157. data/lib/conceptql/operators/concept.rb +0 -70
  158. data/lib/conceptql/operators/condition_type.rb +0 -71
  159. data/lib/conceptql/operators/cpt.rb +0 -25
  160. data/lib/conceptql/operators/drug_type_concept.rb +0 -21
  161. data/lib/conceptql/operators/from_seer_visits.rb +0 -23
  162. data/lib/conceptql/operators/hcpcs.rb +0 -25
  163. data/lib/conceptql/operators/icd10.rb +0 -28
  164. data/lib/conceptql/operators/icd9.rb +0 -28
  165. data/lib/conceptql/operators/icd9_procedure.rb +0 -25
  166. data/lib/conceptql/operators/intersect.rb +0 -34
  167. data/lib/conceptql/operators/loinc.rb +0 -25
  168. data/lib/conceptql/operators/medcode.rb +0 -28
  169. data/lib/conceptql/operators/medcode_procedure.rb +0 -27
  170. data/lib/conceptql/operators/ndc.rb +0 -29
  171. data/lib/conceptql/operators/observation_by_enttype.rb +0 -29
  172. data/lib/conceptql/operators/observation_period.rb +0 -30
  173. data/lib/conceptql/operators/overlapped_by.rb +0 -23
  174. data/lib/conceptql/operators/overlaps.rb +0 -19
  175. data/lib/conceptql/operators/place_of_service_code.rb +0 -24
  176. data/lib/conceptql/operators/procedure_occurrence.rb +0 -25
  177. data/lib/conceptql/operators/prodcode.rb +0 -29
  178. data/lib/conceptql/operators/rxnorm.rb +0 -24
  179. data/lib/conceptql/operators/snomed.rb +0 -24
  180. data/lib/conceptql/operators/snomed_condition.rb +0 -26
  181. data/lib/conceptql/operators/source_vocabulary_operator.rb +0 -58
  182. data/lib/conceptql/operators/standard_vocabulary_operator.rb +0 -45
  183. data/lib/conceptql/operators/started_by.rb +0 -22
  184. data/lib/conceptql/operators/to_seer_visits.rb +0 -24
  185. data/lib/conceptql/operators/visit.rb +0 -15
  186. data/lib/conceptql/operators/visit_occurrence.rb +0 -30
  187. data/lib/conceptql/tree.rb +0 -53
  188. data/lib/conceptql/utils/temp_table.rb +0 -72
  189. data/spec/conceptql/behaviors/dottable_spec.rb +0 -99
  190. data/spec/conceptql/converter_spec.rb +0 -51
  191. data/spec/conceptql/date_adjuster_spec.rb +0 -68
  192. data/spec/conceptql/operators/after_spec.rb +0 -16
  193. data/spec/conceptql/operators/before_spec.rb +0 -16
  194. data/spec/conceptql/operators/casting_operator_spec.rb +0 -68
  195. data/spec/conceptql/operators/complement_spec.rb +0 -15
  196. data/spec/conceptql/operators/concept_spec.rb +0 -40
  197. data/spec/conceptql/operators/condition_type_spec.rb +0 -128
  198. data/spec/conceptql/operators/contains_spec.rb +0 -19
  199. data/spec/conceptql/operators/cpt_spec.rb +0 -29
  200. data/spec/conceptql/operators/date_range_spec.rb +0 -33
  201. data/spec/conceptql/operators/death_spec.rb +0 -10
  202. data/spec/conceptql/operators/during_spec.rb +0 -30
  203. data/spec/conceptql/operators/except_spec.rb +0 -15
  204. data/spec/conceptql/operators/first_spec.rb +0 -35
  205. data/spec/conceptql/operators/from_spec.rb +0 -13
  206. data/spec/conceptql/operators/gender_spec.rb +0 -27
  207. data/spec/conceptql/operators/hcpcs_spec.rb +0 -29
  208. data/spec/conceptql/operators/icd10_spec.rb +0 -34
  209. data/spec/conceptql/operators/icd9_procedure_spec.rb +0 -29
  210. data/spec/conceptql/operators/icd9_spec.rb +0 -34
  211. data/spec/conceptql/operators/intersect_spec.rb +0 -28
  212. data/spec/conceptql/operators/last_spec.rb +0 -36
  213. data/spec/conceptql/operators/loinc_spec.rb +0 -29
  214. data/spec/conceptql/operators/medcode_procedure_spec.rb +0 -34
  215. data/spec/conceptql/operators/medcode_spec.rb +0 -34
  216. data/spec/conceptql/operators/observation_period_spec.rb +0 -10
  217. data/spec/conceptql/operators/occurrence_spec.rb +0 -87
  218. data/spec/conceptql/operators/overlapped_by_spec.rb +0 -32
  219. data/spec/conceptql/operators/overlaps_spec.rb +0 -21
  220. data/spec/conceptql/operators/person_filter_spec.rb +0 -15
  221. data/spec/conceptql/operators/person_spec.rb +0 -10
  222. data/spec/conceptql/operators/place_of_service_code_spec.rb +0 -24
  223. data/spec/conceptql/operators/procedure_occurrence_spec.rb +0 -10
  224. data/spec/conceptql/operators/prodcode_spec.rb +0 -35
  225. data/spec/conceptql/operators/query_double.rb +0 -20
  226. data/spec/conceptql/operators/query_double_spec.rb +0 -7
  227. data/spec/conceptql/operators/race_spec.rb +0 -21
  228. data/spec/conceptql/operators/rxnorm_spec.rb +0 -29
  229. data/spec/conceptql/operators/snomed_spec.rb +0 -29
  230. data/spec/conceptql/operators/source_vocabulary_operator_spec.rb +0 -35
  231. data/spec/conceptql/operators/standard_vocabulary_operator_spec.rb +0 -35
  232. data/spec/conceptql/operators/started_by_spec.rb +0 -22
  233. data/spec/conceptql/operators/temporal_operator_spec.rb +0 -51
  234. data/spec/conceptql/operators/time_window_spec.rb +0 -77
  235. data/spec/conceptql/operators/union_spec.rb +0 -21
  236. data/spec/conceptql/operators/visit_occurrence_spec.rb +0 -10
  237. data/spec/conceptql/query_spec.rb +0 -22
  238. data/spec/conceptql/tree_spec.rb +0 -50
  239. data/spec/doubles/stream_for_casting_double.rb +0 -9
  240. data/spec/doubles/stream_for_occurrence_double.rb +0 -25
  241. data/spec/doubles/stream_for_temporal_double.rb +0 -6
  242. data/spec/spec_helper.rb +0 -102
@@ -1,10 +1,24 @@
1
- require_relative 'operator'
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
- def types
7
- upstreams.map(&:types).flatten.uniq
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
- require_relative 'casting_operator'
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
- desc 'Returns all people in the database, or if given a upstream, converts all results to the set of patients contained in those results.'
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
- types :person
16
+ domains :person
9
17
 
10
- def my_type
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 types we use, but the default behavior of casting,
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
- # types will be converted to person
36
+ # domains will be converted to person
25
37
  []
26
38
  end
27
39
  end
@@ -1,12 +1,18 @@
1
- require_relative 'binary_operator_operator'
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
- desc 'Only passes through a result from the LHR if the person appears in the RHR.'
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
- require_relative 'operator'
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
- desc 'Finds all people that match the races'
13
- argument :races, type: :codelist, vocab: 'Race'
14
- types :person
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
- db.from(:person___p)
18
- .join(:vocabulary__concept___vc, { vc__concept_id: :p__race_concept_id })
19
- .where(Sequel.function(:lower, :vc__concept_name) => arguments.map(&:downcase))
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
- require_relative 'pass_thru'
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 results cached in the corresponding table
13
+ # "define" operator and will fetch the records cached in the corresponding table
12
14
  class Recall < Operator
13
- desc <<-EOF
14
- Recalls a set of named results that were previously stored using the Define operator.
15
- Must be surrounded by the same Let operator as surrounds the corresponding Define operator.
16
- EOF
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 'Variable Assignment'
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 columns(query, local_type)
25
- COLUMNS
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
- require_relative 'pass_thru'
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
- desc <<-EOF
7
- Sums value_as_number across all results that match on all but start_date, end_date.
8
- For start_date and end_date the min and max of each respectively is returned.'
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(*(COLUMNS - [:start_date, :end_date, :criterion_id, :value_as_number]))
14
- .select_append(Sequel.lit('?', 0).as(:criterion_id))
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
-