conceptql 0.4.0 → 0.5.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 (45) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +64 -0
  3. data/config/vocabularies.csv +87 -87
  4. data/doc/spec.md +1951 -0
  5. data/lib/conceptql/behaviors/utilizable.rb +0 -2
  6. data/lib/conceptql/cli.rb +1 -1
  7. data/lib/conceptql/data_model/base.rb +1 -167
  8. data/lib/conceptql/data_model.rb +20 -4
  9. data/lib/conceptql/database.rb +2 -0
  10. data/lib/conceptql/lexicon/lexicon_strategy.rb +23 -3
  11. data/lib/conceptql/lexicon.rb +9 -1
  12. data/lib/conceptql/operators/co_reported.rb +4 -31
  13. data/lib/conceptql/operators/hospice.rb +1 -2
  14. data/lib/conceptql/operators/hospitalization.rb +1 -2
  15. data/lib/conceptql/operators/information_periods.rb +1 -3
  16. data/lib/conceptql/operators/one_in_two_out.rb +1 -2
  17. data/lib/conceptql/operators/operator.rb +3 -11
  18. data/lib/conceptql/operators/provenance.rb +1 -2
  19. data/lib/conceptql/operators/race.rb +2 -6
  20. data/lib/conceptql/operators/read.rb +1 -100
  21. data/lib/conceptql/operators/snf.rb +1 -2
  22. data/lib/conceptql/operators/vocabulary.rb +15 -2
  23. data/lib/conceptql/paths.rb +6 -0
  24. data/lib/conceptql/query.rb +5 -2
  25. data/lib/conceptql/rdbms/duckdb.rb +0 -4
  26. data/lib/conceptql/rdbms/generic.rb +0 -4
  27. data/lib/conceptql/rdbms/postgres.rb +0 -4
  28. data/lib/conceptql/rdbms/spark.rb +0 -4
  29. data/lib/conceptql/utils.rb +0 -43
  30. data/lib/conceptql/version.rb +1 -1
  31. data/lib/conceptql/vocabularies/entry.rb +0 -20
  32. data/lib/conceptql/vocabularies/vocabulary_class_factory.rb +2 -15
  33. metadata +16 -13
  34. data/lib/conceptql/data_model/omopv4_plus.rb +0 -12
  35. data/lib/conceptql/query_modifiers/omopv4_plus/admission_date_query_modifier.rb +0 -45
  36. data/lib/conceptql/query_modifiers/omopv4_plus/drug_query_modifier.rb +0 -80
  37. data/lib/conceptql/query_modifiers/omopv4_plus/pos_query_modifier.rb +0 -47
  38. data/lib/conceptql/query_modifiers/omopv4_plus/provenance_query_modifier.rb +0 -45
  39. data/lib/conceptql/query_modifiers/omopv4_plus/provider_query_modifier.rb +0 -26
  40. data/lib/conceptql/sql_formatters/formatter.rb +0 -25
  41. data/lib/conceptql/sql_formatters.rb +0 -22
  42. data/lib/conceptql/vocabularies/behaviors/costish.rb +0 -24
  43. data/lib/conceptql/vocabularies/behaviors/omopish.rb +0 -32
  44. data/schemas/omopv4_plus.yml +0 -729
  45. data/schemas/omopv4_plus_more_ignorables.tsv +0 -13
@@ -170,8 +170,6 @@ module ConceptQL
170
170
  end
171
171
 
172
172
  def override_columns
173
- return unless gdm?
174
-
175
173
  {
176
174
  length_of_stay: (rdbms.days_between(Sequel[:admission_date],
177
175
  discharge_date_column) + 1).as(:length_of_stay),
data/lib/conceptql/cli.rb CHANGED
@@ -94,7 +94,7 @@ module ConceptQL
94
94
  given by the connection options (or sequelizer's configuration).
95
95
  DESC
96
96
  option :counts, type: :boolean, default: false, desc: "annotate with per-domain row/person counts (needs a DB)"
97
- option :data_model, desc: "data model: gdm, gdm_wide, omopv4_plus (default: $CONCEPTQL_DATA_MODEL or gdm)"
97
+ option :data_model, desc: "data model: gdm, gdm_wide (default: $CONCEPTQL_DATA_MODEL or gdm)"
98
98
  def render_json(statement_file)
99
99
  cdb = if options[:counts]
100
100
  ConceptQL::Database.new(db(options.except("counts", "data_model")),
@@ -10,6 +10,7 @@ module ConceptQL
10
10
  def_delegators :nodifier, :cdb
11
11
  def_delegators :cdb, :lexicon, :source_table_exists?
12
12
  def_delegators :lexicon,
13
+ :code_meanings,
13
14
  :concept_codes,
14
15
  :concept_ids,
15
16
  :concept_ids_by_name,
@@ -47,79 +48,14 @@ module ConceptQL
47
48
  db[table]
48
49
  end
49
50
 
50
- # The data columns whose concept ids the display-name joins look up, by
51
- # table. A data model without those joins has none.
52
- def name_concept_columns
53
- {}
54
- end
55
-
56
- def query_modifier_for(column)
57
- {
58
- visit_source_concept_id: ConceptQL::QueryModifiers::Omopv4Plus::PoSQueryModifier,
59
- provider_id: ConceptQL::QueryModifiers::Omopv4Plus::ProviderQueryModifier,
60
- context_specialty_concept_id: ConceptQL::QueryModifiers::Omopv4Plus::ProviderQueryModifier,
61
- drug_name: ConceptQL::QueryModifiers::Omopv4Plus::DrugQueryModifier,
62
- admission_date: ConceptQL::QueryModifiers::Omopv4Plus::AdmissionDateQueryModifier,
63
- provenance_type: ConceptQL::QueryModifiers::Omopv4Plus::ProvenanceQueryModifier
64
- }[column]
65
- end
66
-
67
- def criterion_id
68
- :criterion_id
69
- end
70
-
71
- def table_id(table = nil)
72
- return :person_id if table.to_sym == :death
73
-
74
- :criterion_id if table.nil?
75
- end
76
-
77
- def person_table
78
- :person
79
- end
80
-
81
- def make_table_id(table)
82
- "#{table}_id".to_sym
83
- end
84
-
85
51
  def make_fk_id(table)
86
52
  make_table_id(table)
87
53
  end
88
54
 
89
- def person_id(table)
90
- case table.to_sym
91
- when :procedure_cost, :drug_cost
92
- nil
93
- else
94
- :person_id
95
- end
96
- end
97
-
98
55
  def schema
99
56
  SCHEMAS[data_model]
100
57
  end
101
58
 
102
- def data_model
103
- :omopv4_plus
104
- end
105
-
106
- def pk_by_domain(domain)
107
- case domain.to_sym
108
- when :death
109
- :person_id
110
- when :procedure_cost
111
- :procedure_occurrence_id
112
- when :drug_cost
113
- :drug_exposure_id
114
- else
115
- "#{domain}_id".to_sym
116
- end
117
- end
118
-
119
- def fk_by_domain(domain)
120
- "#{domain}_id".to_sym
121
- end
122
-
123
59
  def query_columns
124
60
  @query_columns ||= Hash[operator.scope.query_columns.zip(operator.scope.query_columns)]
125
61
  end
@@ -231,10 +167,6 @@ module ConceptQL
231
167
  }
232
168
  end
233
169
 
234
- def table_to_domain(table)
235
- table
236
- end
237
-
238
170
  def replace(replace_hash)
239
171
  return {} unless replace_hash
240
172
 
@@ -276,14 +208,6 @@ module ConceptQL
276
208
  end
277
209
  end
278
210
 
279
- def start_date_column(query, domain)
280
- start_date_columns.merge(person: person_date_of_birth(query))[domain]
281
- end
282
-
283
- def end_date_column(query, domain)
284
- end_date_columns.merge(person: person_date_of_birth(query))[domain]
285
- end
286
-
287
211
  def source_value_columns
288
212
  @source_value_columns ||= assign_column_to_table do |table, columns|
289
213
  reggy = /#{table.to_s.split('_').first}_source_value$/
@@ -292,81 +216,10 @@ module ConceptQL
292
216
  end
293
217
  end
294
218
 
295
- def source_vocabulary_ids
296
- @source_vocabulary_ids = assign_column_to_table do |table, columns|
297
- next if %w[person death].any? { |tn| table.to_s =~ /#{tn}/ }
298
-
299
- reggy = /#{table.to_s.split('_').first}_source_vocabulary_id/
300
- column = columns.select { |k| k =~ reggy }.first
301
- column || columns.select { |k| k =~ /_source_vocabulary_id/ }.first
302
- end
303
- end
304
-
305
- def id_columns
306
- @id_columns ||= assign_column_to_table do |table, columns|
307
- reggy = /#{table}_id/
308
- columns.select { |k| k =~ reggy }.first
309
- end
310
- end
311
-
312
- def id_column(table)
313
- id_columns[table]
314
- end
315
-
316
- def type_concept_id_columns
317
- @type_concept_id_columns ||= assign_column_to_table do |_table, columns|
318
- columns.select { |c| c =~ /_type_concept_id$/ }.first
319
- end
320
- end
321
-
322
- def type_concept_id_column(domain)
323
- type_concept_id_columns[table_by_domain(domain)]
324
- end
325
-
326
- def table_by_domain(domain)
327
- domain
328
- end
329
-
330
- def period_table
331
- :observation_period
332
- end
333
-
334
219
  def source_value_column(table)
335
220
  source_value_columns[table]
336
221
  end
337
222
 
338
- def code_provenance_type_column(_query, domain)
339
- {
340
- condition_occurrence: :condition_type_concept_id,
341
- death: :death_type_concept_id,
342
- drug_exposure: :drug_type_concept_id,
343
- observation: :observation_type_concept_id,
344
- procedure_occurrence: :procedure_type_concept_id
345
- }[domain]
346
- end
347
-
348
- def file_provenance_type_column(_query, domain)
349
- {
350
- condition_occurrence: :condition_type_concept_id,
351
- death: :death_type_concept_id,
352
- drug_exposure: :drug_type_concept_id,
353
- observation: :observation_type_concept_id,
354
- procedure_occurrence: :procedure_type_concept_id
355
- }[domain]
356
- end
357
-
358
- def provider_id_column(_query, domain)
359
- {
360
- condition_occurrence: :associated_provider_id,
361
- death: :death_type_concept_id,
362
- drug_exposure: :prescribing_provider_id,
363
- observation: :associated_provider_id,
364
- person: :provider_id,
365
- procedure_occurrence: :associated_provider_id,
366
- provider: :provider_id
367
- }[domain]
368
- end
369
-
370
223
  def place_of_service_concept_id_column(_query, domain)
371
224
  return nil if domain.nil?
372
225
  return Sequel.cast(:visit_source_concept_id, :Bigint) if table_cols(domain).include?(pos_table_fk)
@@ -374,15 +227,6 @@ module ConceptQL
374
227
  nil
375
228
  end
376
229
 
377
- def pos_table_fk
378
- :visit_occurrence_id
379
- end
380
-
381
- def person_date_of_birth(_query)
382
- rdbms.cast_date(ConceptQL::Utils.assemble_date(:year_of_birth, :month_of_birth, :day_of_birth,
383
- database_type: nodifier.database_type))
384
- end
385
-
386
230
  def date_columns(query, table = nil)
387
231
  sd = start_date_column(query, table)
388
232
  sd = rdbms.cast_date(Sequel.expr(sd)).as(:start_date) unless sd == :start_date
@@ -428,10 +272,6 @@ module ConceptQL
428
272
  source_vocabulary_ids[table_to_sym(table)]
429
273
  end
430
274
 
431
- def table_is_missing?(db)
432
- !(source_table_exists?(db, :observation_period) && source_table_exists?(db, :condition_occurrence))
433
- end
434
-
435
275
  def concept_id(table)
436
276
  table_cols(table).find do |col|
437
277
  col = col.to_s
@@ -451,12 +291,6 @@ module ConceptQL
451
291
  end
452
292
  end
453
293
  end
454
-
455
- def information_period_where_clause(arguments)
456
- return if arguments.empty?
457
-
458
- { plan_source_value: arguments }
459
- end
460
294
  end
461
295
  end
462
296
  end
@@ -2,11 +2,29 @@
2
2
 
3
3
  require_relative "data_model/gdm"
4
4
  require_relative "data_model/gdm_wide"
5
- require_relative "data_model/omopv4_plus"
6
5
  require_relative "rdbms"
7
6
 
8
7
  module ConceptQL
9
8
  module DataModel
9
+ SUPPORTED = %i[gdm gdm_wide].freeze
10
+
11
+ # Raised when a caller asks for a data model that ConceptQL does not have.
12
+ class UnsupportedError < ArgumentError
13
+ REMOVED = { omopv4_plus: "0.5.0" }.freeze
14
+
15
+ def initialize(data_model)
16
+ message = "ConceptQL has no data model #{data_model.inspect}. Use one of: #{SUPPORTED.join(', ')}."
17
+ if (version = REMOVED[data_model])
18
+ message = "The #{data_model} data model was removed in ConceptQL #{version}. Use one of: #{SUPPORTED.join(', ')}."
19
+ end
20
+ super(message)
21
+ end
22
+ end
23
+
24
+ def self.assert_supported!(data_model)
25
+ raise UnsupportedError, data_model unless SUPPORTED.include?(data_model)
26
+ end
27
+
10
28
  def self.get(data_model, opts = {})
11
29
  operator = opts[:operator]
12
30
  nodifier = opts[:nodifier]
@@ -16,10 +34,8 @@ module ConceptQL
16
34
  Gdm.new(operator, nodifier)
17
35
  when :gdm_wide
18
36
  GdmWide.new(operator, nodifier)
19
- when :omopv4_plus
20
- Omopv4Plus.new(operator, nodifier)
21
37
  else
22
- raise "No DataModel defined for #{data_model.inspect}"
38
+ raise UnsupportedError, data_model
23
39
  end
24
40
  end
25
41
 
@@ -28,6 +28,7 @@ module ConceptQL
28
28
  @opts = ConceptQL::Utils.rekey(opts, rekey_values: true)
29
29
 
30
30
  @opts[:data_model] ||= (ENV["CONCEPTQL_DATA_MODEL"] || ConceptQL::DEFAULT_DATA_MODEL).to_sym
31
+ ConceptQL::DataModel.assert_supported!(@opts[:data_model])
31
32
 
32
33
  self.class.db_extensions(db, @opts[:data_model], @opts[:use_cold_col])
33
34
  db_type = db ? db.database_type.to_sym : :postgres
@@ -139,6 +140,7 @@ module ConceptQL
139
140
  # differs from this Database's.
140
141
  def operators(data_model = opts[:data_model])
141
142
  data_model = data_model.to_sym
143
+ ConceptQL::DataModel.assert_supported!(data_model)
142
144
  @operators ||= {}
143
145
  @operators[data_model] ||= begin
144
146
  vocabulary_operators = Vocabularies::DynamicVocabularies.new(lexicon).operators(data_model)
@@ -8,9 +8,9 @@ module ConceptQL
8
8
  @db = db
9
9
  end
10
10
 
11
- # A lookup is always scoped to a vocabulary. `vocabulary_id` is polymorphic:
12
- # a String, an Integer (ReadOmop reports 17), or an Array of vocabulary names
13
- # (the provenance-type pairs), which Sequel turns into an IN clause.
11
+ # A lookup is always scoped to a vocabulary. `vocabulary_id` is a String or
12
+ # an Array of vocabulary names (the provenance-type pairs), which Sequel turns
13
+ # into an IN clause.
14
14
  #
15
15
  # There is deliberately no wildcard. `'*'` is meaningful elsewhere in the
16
16
  # operator family, but only in `arguments` — Vocabulary#select_all? reads it
@@ -98,6 +98,26 @@ module ConceptQL
98
98
  concepts(db, vocabulary_id, codes).select_map(%i[concept_code concept_text])
99
99
  end
100
100
 
101
+ # The distinct names of each code that has more than one name, as
102
+ # { downcased code => [name, ...] } with the names sorted. A vocabulary
103
+ # can reuse a code for a new meaning: DRG 410 meant chemotherapy until
104
+ # 2007-09-30 and a biliary tract procedure after that. OHDSI keeps each
105
+ # meaning as its own concept row, and the GDM vocabulary keeps both names.
106
+ # Names that differ only in case or spacing count as one name.
107
+ # A caller can answer "is the concept table missing?" with a block.
108
+ def code_meanings(data_db, vocabulary_id, codes)
109
+ return {} if codes.blank? || db.nil? || db_is_mock?(data_db)
110
+ return {} if block_given? ? yield : table_is_missing?(data_db)
111
+
112
+ concepts(db, vocabulary_id, codes)
113
+ .select_map(%i[concept_code concept_text])
114
+ .group_by { |code, _| code.downcase }
115
+ .transform_values { |rows| rows.map(&:last).compact.uniq { |name| name.downcase.split.join(" ") }.sort }
116
+ .select { |_, names| names.length > 1 }
117
+ rescue Sequel::DatabaseError
118
+ {}
119
+ end
120
+
101
121
  def vocabulary_is_empty?(data_db, vocabulary_id)
102
122
  concepts_table(data_db).where(vocabulary_id: vocabulary_id).count.zero?
103
123
  end
@@ -14,6 +14,7 @@ module ConceptQL
14
14
  # cache never outlives one export (seed conceptql-a5f). Methods that return
15
15
  # datasets are not cached: their query runs later, in the caller.
16
16
  CACHED_LOOKUPS = %i[
17
+ code_meanings
17
18
  concept_codes
18
19
  concept_ids
19
20
  concept_ids_by_name
@@ -43,7 +44,7 @@ module ConceptQL
43
44
  @cache = {} if cache
44
45
  end
45
46
 
46
- (CACHED_LOOKUPS - %i[concepts_to_codes known_codes table_is_missing?]).each do |name|
47
+ (CACHED_LOOKUPS - %i[code_meanings concepts_to_codes known_codes table_is_missing?]).each do |name|
47
48
  define_method(name) do |*args|
48
49
  lookup(name, args) { @strategy.public_send(name, *args) }
49
50
  end
@@ -65,6 +66,13 @@ module ConceptQL
65
66
  end
66
67
  end
67
68
 
69
+ # Like concepts_to_codes, it asks about the concept table through the block.
70
+ def code_meanings(data_db, vocabulary_id, codes)
71
+ lookup(:code_meanings, [ data_db, vocabulary_id, codes ]) do
72
+ @strategy.code_meanings(data_db, vocabulary_id, codes) { table_is_missing?(data_db) }
73
+ end
74
+ end
75
+
68
76
  # Only "the table is there" is kept. A missing table (or a database error,
69
77
  # which Sequel's table_exists? turns into "missing") is checked again on
70
78
  # the next call, so one transient error does not last the whole export.
@@ -17,7 +17,10 @@ module ConceptQL
17
17
  vocabularies.length > 1
18
18
  end
19
19
 
20
- desc "Passes along all events that were co-reported in the same record in the source data."
20
+ desc "Passes along the events of its upstream streams that were reported in the same source record " \
21
+ "(the same context). A record is kept only if every upstream stream " \
22
+ "has at least one event in it. If any one upstream has no event in a record, the events of all " \
23
+ "streams in that record are dropped, without a warning."
21
24
 
22
25
  allows_many_upstreams
23
26
  category "Combine Streams"
@@ -27,14 +30,6 @@ module ConceptQL
27
30
  require_column :context_id
28
31
 
29
32
  def query(db)
30
- if gdm?
31
- gdm(db)
32
- else
33
- events_with_common_visits(db).inject { |q, query| q.union(query, all: true) }
34
- end
35
- end
36
-
37
- def gdm(db)
38
33
  contexteds = upstreams.map do |stream|
39
34
  [ stream.cte_name(:contextified), contextify(db, stream) ]
40
35
  end.to_h
@@ -84,28 +79,6 @@ module ConceptQL
84
79
  # .select_append(Sequel[:c][:context_id].as(:context_id))
85
80
  # .from_self
86
81
  end
87
-
88
- private
89
-
90
- def visit_occurrence_ids_in_common(db)
91
- @visit_occurrence_ids_in_common ||= upstream_queries(db).map do |q|
92
- q.select(:visit_occurrence_id)
93
- end.inject do |q, query|
94
- q.from_self(alias: :tab1)
95
- .join(query.as(:tab2), visit_occurrence_id: :visit_occurrence_id)
96
- .select(Sequel[:tab1][:visit_occurrence_id].as(:visit_occurrence_id))
97
- end
98
- end
99
-
100
- def events_with_common_visits(db)
101
- upstream_queries(db).map { |q| q.where(visit_occurrence_id: visit_occurrence_ids_in_common(db)).from_self }
102
- end
103
-
104
- def upstream_queries(db)
105
- @upstream_queries ||= upstreams.map do |expression|
106
- expression.evaluate(db).select(*query_cols).from_self
107
- end
108
- end
109
82
  end
110
83
  end
111
84
  end
@@ -5,8 +5,7 @@ require_relative "operator"
5
5
  module ConceptQL
6
6
  module Operators
7
7
  class Hospice < Operator
8
- # GDM only: omopv4_plus has no provenance vocabularies (conceptql-myp).
9
- register __FILE__, :gdm, :gdm_wide
8
+ register __FILE__
10
9
  include ConceptQL::Behaviors::Utilizable
11
10
 
12
11
  desc "Selects admission records of type 'hospice'"
@@ -5,8 +5,7 @@ require_relative "operator"
5
5
  module ConceptQL
6
6
  module Operators
7
7
  class Hospitalization < Operator
8
- # GDM only: omopv4_plus has no provenance vocabularies (conceptql-myp).
9
- register __FILE__, :gdm, :gdm_wide
8
+ register __FILE__
10
9
  include ConceptQL::Behaviors::Utilizable
11
10
 
12
11
  desc "Selects admission records of type 'inpatient'"
@@ -23,9 +23,7 @@ module ConceptQL
23
23
  end
24
24
 
25
25
  def source_table
26
- tab = dm.table_by_domain(domain)
27
- tab = :payer_plan_period if tab == :observation_period && arguments.present?
28
- tab
26
+ dm.table_by_domain(domain)
29
27
  end
30
28
 
31
29
  def where_clause
@@ -10,8 +10,7 @@ module ConceptQL
10
10
  class OneInTwoOut < Operator
11
11
  include ConceptQL::Behaviors::SingleEvent
12
12
 
13
- # GDM only: omopv4_plus has no provenance vocabularies (conceptql-myp).
14
- register __FILE__, :gdm, :gdm_wide
13
+ register __FILE__
15
14
 
16
15
  desc "Identifies an event that appears either once in an inpatient setting or twice within a specified interval in an outpatient setting."
17
16
 
@@ -9,7 +9,7 @@ module ConceptQL
9
9
  # Built-in operators only: the classes that call Operator.register when
10
10
  # their file loads. Vocabulary operators are not in here; each
11
11
  # ConceptQL::Database builds its own from its lexicon (Database#operators).
12
- STATIC_OPERATORS = { omopv4_plus: {}, gdm: {}, gdm_wide: {} }.freeze
12
+ STATIC_OPERATORS = { gdm: {}, gdm_wide: {} }.freeze
13
13
 
14
14
  SELECTED_COLUMNS = %i[person_id
15
15
  criterion_id
@@ -348,14 +348,6 @@ module ConceptQL
348
348
  end
349
349
  end
350
350
 
351
- def omopv4_plus?
352
- data_model == :omopv4_plus
353
- end
354
-
355
- def gdm?
356
- %i[gdm gdm_wide].include?(data_model)
357
- end
358
-
359
351
  def wide?
360
352
  %i[gdm_wide].include?(data_model)
361
353
  end
@@ -467,10 +459,10 @@ module ConceptQL
467
459
  cast_column(:file_provenance_type, dm.provenance_type_column(query, table))
468
460
  end
469
461
 
470
- def provider_id(query, table)
462
+ def provider_id(query, _table)
471
463
  return :provider_id if query_columns(query).include?(:provider_id)
472
464
 
473
- cast_column(:provider_id, dm.provider_id_column(query, table))
465
+ cast_column(:provider_id)
474
466
  end
475
467
 
476
468
  def determine_tables
@@ -16,8 +16,7 @@ module ConceptQL
16
16
  #
17
17
  # Multiple provenances can be specified at once
18
18
  class Provenance < Operator
19
- # GDM only: omopv4_plus has no provenance vocabularies (conceptql-myp).
20
- register __FILE__, :gdm, :gdm_wide
19
+ register __FILE__
21
20
 
22
21
  desc "Passes along records with the indicated provenance (e.g. inpatient, outpatient, file type)."
23
22
 
@@ -38,11 +38,7 @@ module ConceptQL
38
38
  end
39
39
 
40
40
  def source_table
41
- if gdm?
42
- :patients
43
- else
44
- :person
45
- end
41
+ :patients
46
42
  end
47
43
 
48
44
  def query(db)
@@ -52,7 +48,7 @@ module ConceptQL
52
48
 
53
49
  c_ids = (concept_ids + actual_ids).map { |i| [ i, race_descendents[i] ] }.flatten.compact.uniq.sort
54
50
 
55
- c_ids = dm.related_concept_ids(db, c_ids) if gdm?
51
+ c_ids = dm.related_concept_ids(db, c_ids)
56
52
 
57
53
  q = db.from(source_table)
58
54
  if words.any? { |w| w.match(/unknown/i) }
@@ -18,25 +18,15 @@ module ConceptQL
18
18
  conceptql_spec_id "vocabulary"
19
19
 
20
20
  def query(db)
21
- gdm? ? gdm(db) : omopv4(db)
22
- end
23
-
24
- def gdm(db)
25
21
  ops(db).first.evaluate(db)
26
22
  end
27
23
 
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
24
  def domains(db)
35
25
  ops.first.domains(db)
36
26
  end
37
27
 
38
28
  def query_cols
39
- gdm? ? ops.first.query_cols : ops.map(&:query_cols).flatten.uniq
29
+ ops.first.query_cols
40
30
  end
41
31
 
42
32
  def required_columns
@@ -53,40 +43,11 @@ module ConceptQL
53
43
  ops.flat_map { |op| op.describe_codes(db, codes) }
54
44
  end
55
45
 
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
46
  class ReadBase < ConceptQL::Operators::Vocabulary
66
47
  preferred_name "READ"
67
48
  argument :read_codes, type: :codelist, vocab: "Read"
68
49
  end
69
50
 
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
51
  class ReadGDM < ReadBase
91
52
  include ConceptQL::Vocabularies::Behaviors::Gdmish
92
53
  include ConceptQL::Behaviors::Labish
@@ -103,66 +64,6 @@ module ConceptQL
103
64
  [ :condition_occurrence ]
104
65
  end
105
66
  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
67
  end
167
68
  end
168
69
  end