conceptql 0.4.1 → 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 (37) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +44 -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/version.rb +1 -1
  25. data/lib/conceptql/vocabularies/entry.rb +0 -20
  26. data/lib/conceptql/vocabularies/vocabulary_class_factory.rb +2 -15
  27. metadata +2 -11
  28. data/lib/conceptql/data_model/omopv4_plus.rb +0 -12
  29. data/lib/conceptql/query_modifiers/omopv4_plus/admission_date_query_modifier.rb +0 -45
  30. data/lib/conceptql/query_modifiers/omopv4_plus/drug_query_modifier.rb +0 -80
  31. data/lib/conceptql/query_modifiers/omopv4_plus/pos_query_modifier.rb +0 -47
  32. data/lib/conceptql/query_modifiers/omopv4_plus/provenance_query_modifier.rb +0 -45
  33. data/lib/conceptql/query_modifiers/omopv4_plus/provider_query_modifier.rb +0 -26
  34. data/lib/conceptql/vocabularies/behaviors/costish.rb +0 -24
  35. data/lib/conceptql/vocabularies/behaviors/omopish.rb +0 -32
  36. data/schemas/omopv4_plus.yml +0 -729
  37. data/schemas/omopv4_plus_more_ignorables.tsv +0 -13
@@ -5,8 +5,7 @@ require_relative "operator"
5
5
  module ConceptQL
6
6
  module Operators
7
7
  class Snf < 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 'SNF'"
@@ -38,10 +38,23 @@ module ConceptQL
38
38
  args -= bad_arguments
39
39
 
40
40
  missing_args = args - dm.known_codes(db, vocabulary_id, args)
41
+ add_warning("unknown code(s)", *missing_args) unless missing_args.empty?
41
42
 
42
- return if missing_args.empty?
43
+ warn_about_reused_codes(db, args - missing_args)
44
+ end
43
45
 
44
- add_warning("unknown code(s)", *missing_args)
46
+ # A code that the vocabulary reused for a new meaning selects records of
47
+ # both meanings: DRG 410 selects chemotherapy stays before 2007-10-01 and
48
+ # biliary tract procedures after it (conceptql-ttk). Each item names the
49
+ # code and all of its names, so the user sees the problem without a
50
+ # vocabulary lookup.
51
+ def warn_about_reused_codes(db, codes)
52
+ meanings = dm.code_meanings(db, vocabulary_id, codes)
53
+ reused = codes.filter_map do |code|
54
+ names = meanings[code.downcase]
55
+ "#{code}: #{names.join(' | ')}" if names
56
+ end
57
+ add_warning("code(s) with more than one meaning", *reused) unless reused.empty?
45
58
  end
46
59
 
47
60
  def describe_codes(db, codes)
@@ -37,5 +37,11 @@ module ConceptQL
37
37
  def race_file
38
38
  ConceptQL.config_dir / "race.yml"
39
39
  end
40
+
41
+ # The ConceptQL specification, shipped in the gem for applications that
42
+ # serve it to readers (the Jigsaw diagram editor serves it over MCP).
43
+ def spec_path
44
+ root / "doc" / "spec.md"
45
+ end
40
46
  end
41
47
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ConceptQL
4
- VERSION = "0.4.1"
4
+ VERSION = "0.5.0"
5
5
  end
@@ -9,7 +9,6 @@ module ConceptQL
9
9
 
10
10
  METHODS = %i[
11
11
  id
12
- omopv4_vocabulary_id
13
12
  omopv5_vocabulary_id
14
13
  domain
15
14
  vocabulary_short_name
@@ -28,11 +27,6 @@ module ConceptQL
28
27
  predominant_domains
29
28
  ]
30
29
 
31
- COST_RELATED_VOCABS = [
32
- "Revenue Code",
33
- "DRG"
34
- ].freeze
35
-
36
30
  def initialize(hash)
37
31
  @hash = hash
38
32
  @hash[:omopv5_vocabulary_id] ||= @hash[:id]
@@ -54,12 +48,6 @@ module ConceptQL
54
48
  self.class.new(hash.merge(other_entry.hash))
55
49
  end
56
50
 
57
- def belongs_in_omopv4_plus?
58
- @belongs_in_omopv4_plus ||= (!from_lexicon? || from_csv?) \
59
- && has_domain? \
60
- && visible?
61
- end
62
-
63
51
  def to_hash
64
52
  METADATA_METHODS.to_h do |meth|
65
53
  [ meth, send(meth) ]
@@ -114,10 +102,6 @@ module ConceptQL
114
102
  hash[:domain] =~ /drug_exposure/i
115
103
  end
116
104
 
117
- def is_costish?
118
- belongs_in_omopv4_plus? && COST_RELATED_VOCABS.include?(omopv5_vocabulary_id)
119
- end
120
-
121
105
  def visible?
122
106
  !hidden?
123
107
  end
@@ -126,10 +110,6 @@ module ConceptQL
126
110
  omopv5_vocabulary_id || id
127
111
  end
128
112
 
129
- def omopv4_id
130
- omopv4_vocabulary_id
131
- end
132
-
133
113
  def get_klasses
134
114
  VocabularyClassFactory.new(self).get_klasses
135
115
  end
@@ -3,8 +3,6 @@
3
3
  require_relative "../behaviors/drugish"
4
4
  require_relative "../behaviors/labish"
5
5
  require_relative "behaviors/gdmish"
6
- require_relative "behaviors/omopish"
7
- require_relative "behaviors/costish"
8
6
  require_relative "../operators/vocabulary"
9
7
 
10
8
  module ConceptQL
@@ -19,9 +17,8 @@ module ConceptQL
19
17
  def get_klasses
20
18
  {
21
19
  gdm: get_gdm_klass,
22
- gdm_wide: get_gdm_klass,
23
- omopv4_plus: get_omopv4_plus_klass
24
- }.compact
20
+ gdm_wide: get_gdm_klass
21
+ }
25
22
  end
26
23
 
27
24
  def get_gdm_klass
@@ -30,16 +27,6 @@ module ConceptQL
30
27
  end
31
28
  end
32
29
 
33
- def get_omopv4_plus_klass
34
- return nil unless entry.belongs_in_omopv4_plus?
35
-
36
- get_klass do
37
- include Behaviors::Omopish
38
-
39
- include Behaviors::Costish if entry.is_costish?
40
- end
41
- end
42
-
43
30
  def get_klass(&block)
44
31
  ventry = entry
45
32
  Class.new(ConceptQL::Operators::Vocabulary) do |klass|
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: conceptql
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.1
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ryan Duryea
@@ -328,6 +328,7 @@ files:
328
328
  - config/provenance.yml
329
329
  - config/race.yml
330
330
  - config/vocabularies.csv
331
+ - doc/spec.md
331
332
  - lib/conceptql.rb
332
333
  - lib/conceptql/behaviors/code_lister.rb
333
334
  - lib/conceptql/behaviors/drugish.rb
@@ -344,7 +345,6 @@ files:
344
345
  - lib/conceptql/data_model/base.rb
345
346
  - lib/conceptql/data_model/gdm.rb
346
347
  - lib/conceptql/data_model/gdm_wide.rb
347
- - lib/conceptql/data_model/omopv4_plus.rb
348
348
  - lib/conceptql/database.rb
349
349
  - lib/conceptql/date_adjuster.rb
350
350
  - lib/conceptql/diagram.rb
@@ -418,11 +418,6 @@ files:
418
418
  - lib/conceptql/query_modifiers/gdm_wide/lab_query_modifier.rb
419
419
  - lib/conceptql/query_modifiers/gdm_wide/pos_query_modifier.rb
420
420
  - lib/conceptql/query_modifiers/gdm_wide/provenance_query_modifier.rb
421
- - lib/conceptql/query_modifiers/omopv4_plus/admission_date_query_modifier.rb
422
- - lib/conceptql/query_modifiers/omopv4_plus/drug_query_modifier.rb
423
- - lib/conceptql/query_modifiers/omopv4_plus/pos_query_modifier.rb
424
- - lib/conceptql/query_modifiers/omopv4_plus/provenance_query_modifier.rb
425
- - lib/conceptql/query_modifiers/omopv4_plus/provider_query_modifier.rb
426
421
  - lib/conceptql/query_modifiers/query_modifier.rb
427
422
  - lib/conceptql/rdbms.rb
428
423
  - lib/conceptql/rdbms/duckdb.rb
@@ -434,9 +429,7 @@ files:
434
429
  - lib/conceptql/utils_cli.rb
435
430
  - lib/conceptql/version.rb
436
431
  - lib/conceptql/visitors/metadata_extractor.rb
437
- - lib/conceptql/vocabularies/behaviors/costish.rb
438
432
  - lib/conceptql/vocabularies/behaviors/gdmish.rb
439
- - lib/conceptql/vocabularies/behaviors/omopish.rb
440
433
  - lib/conceptql/vocabularies/dynamic_vocabularies.rb
441
434
  - lib/conceptql/vocabularies/entry.rb
442
435
  - lib/conceptql/vocabularies/vocabulary_class_factory.rb
@@ -449,8 +442,6 @@ files:
449
442
  - schemas/gdm.yml
450
443
  - schemas/gdm_wide.yml
451
444
  - schemas/ohdsi_vocabs.yml
452
- - schemas/omopv4_plus.yml
453
- - schemas/omopv4_plus_more_ignorables.tsv
454
445
  homepage: https://github.com/outcomesinsights/conceptql
455
446
  licenses:
456
447
  - MIT
@@ -1,12 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "base"
4
- module ConceptQL
5
- module DataModel
6
- class Omopv4Plus < Base
7
- def base
8
- :omop
9
- end
10
- end
11
- end
12
- end
@@ -1,45 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "../query_modifier"
4
-
5
- module ConceptQL
6
- module QueryModifiers
7
- module Omopv4Plus
8
- class AdmissionDateQueryModifier < QueryModifier
9
- def self.provided_columns
10
- %i[
11
- admission_date
12
- discharge_date
13
- ]
14
- end
15
-
16
- def self.has_required_columns?(cols)
17
- cols.map(&:to_s).any? { |col| col =~ /start_date/i }
18
- end
19
-
20
- def modified_query
21
- cols = dm.table_cols(source_table).map(&:to_s)
22
- return query unless cols.any? { |col| col =~ /start_date/i }
23
-
24
- start_col = cols.find { |col| col =~ /start_date/i }.to_sym
25
- end_col = cols.find { |col| col =~ /end_date/i }.to_sym
26
-
27
- query.from_self(alias: :t)
28
- .select_all(:t)
29
- .select_append(Sequel[:t][start_col].as(:admission_date))
30
- .select_append(Sequel.function(:coalesce, Sequel[:t][end_col],
31
- Sequel[:t][start_col]).as(:discharge_date))
32
- .from_self
33
- end
34
-
35
- private
36
-
37
- def domain
38
- op.domain
39
- rescue StandardError
40
- nil
41
- end
42
- end
43
- end
44
- end
45
- end
@@ -1,80 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "../query_modifier"
4
-
5
- module ConceptQL
6
- module QueryModifiers
7
- module Omopv4Plus
8
- class DrugQueryModifier < QueryModifier
9
- attr_reader :db
10
-
11
- def self.provided_columns
12
- %i[
13
- drug_name
14
- drug_amount
15
- drug_amount_units
16
- drug_quantity
17
- drug_days_supply
18
- ]
19
- end
20
-
21
- def self.has_required_columns?(cols)
22
- needed = [ :drug_concept_id ]
23
- found = needed & cols
24
- needed == found
25
- end
26
-
27
- def initialize(*args)
28
- super
29
- @db = query.db
30
- end
31
-
32
- def modified_query
33
- return query unless dm.table_cols(source_table).include?(:drug_concept_id)
34
-
35
- query.db
36
- drug_concept_ids = query.select(:drug_concept_id).distinct
37
- query.from_self(alias: :de)
38
- .left_join(micro_table(drug_concept_ids).as(:mt), drug_concept_id: :drug_concept_id)
39
- .select_all(:de)
40
- .select_append(Sequel[:de][:quantity].as(:drug_quantity))
41
- .select_append(Sequel[:de][:days_supply].as(:drug_days_supply))
42
- .select_append(Sequel[:mt][:amount_value].as(:drug_amount))
43
- .select_append(Sequel[:mt][:amount_unit].as(:drug_amount_units))
44
- .select_append(Sequel[:mt][:drug_name].as(:drug_name))
45
- .from_self
46
- end
47
-
48
- private
49
-
50
- def micro_table(drug_concept_ids)
51
- # TODO: Does drug_strength only have RXNORM concept_ids?
52
- # TODO: What is vocabulary for units? Can we shrink concept table to just that vocab before joining?
53
- collapsed_strengths = db[:drug_strength]
54
- .where(drug_concept_id: drug_concept_ids)
55
- .select_group(:drug_concept_id)
56
- .select_append(
57
- Sequel.function(:min, :amount_value).as(:amount_value),
58
- Sequel.function(:min, :amount_unit).as(:amount_unit),
59
- Sequel.function(:count, 1).as(:dcount)
60
- )
61
-
62
- value_case = Sequel.case({ 1 => :amount_value }, nil, :dcount).as(:amount_value)
63
- unit_case = Sequel.case({ 1 => :amount_unit }, nil, :dcount).as(:amount_unit)
64
-
65
- collapsed_strengths = collapsed_strengths.from_self.select(:drug_concept_id, value_case, unit_case)
66
-
67
- db.from(Sequel[:concept].as(:dc))
68
- .left_join(collapsed_strengths, { drug_concept_id: :concept_id }, { table_alias: :ds })
69
- .where(Sequel[:dc][:concept_id] => drug_concept_ids)
70
- .select(
71
- Sequel[:dc][:concept_id].as(:drug_concept_id),
72
- Sequel[:dc][:concept_name].as(:drug_name),
73
- Sequel[:ds][:amount_value].as(:amount_value),
74
- Sequel[:ds][:amount_unit].as(:amount_unit)
75
- )
76
- end
77
- end
78
- end
79
- end
80
- end
@@ -1,47 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "../query_modifier"
4
-
5
- module ConceptQL
6
- module QueryModifiers
7
- module Omopv4Plus
8
- class PoSQueryModifier < QueryModifier
9
- attr_reader :join_id, :table, :source_column, :column
10
-
11
- def self.provided_columns
12
- [ :visit_source_concept_id ]
13
- end
14
-
15
- def self.has_required_columns?(cols)
16
- needed = [ :visit_occurrence_id ]
17
- found = needed & cols
18
- needed == found
19
- end
20
-
21
- def initialize(*args)
22
- super
23
- @column = :visit_source_concept_id
24
- @join_id = :visit_occurrence_id
25
- @table = :visit_occurrence
26
- @source_column = :visit_source_concept_id
27
- end
28
-
29
- def modified_query
30
- return query unless dm.table_cols(source_table).include?(join_id)
31
- return query if dm.table_cols(source_table).include?(column)
32
-
33
- left_alias = "tab1".to_sym
34
- right_alias = "tab2".to_sym
35
-
36
- extra_table = query.db.from(table).select(Sequel.as(source_column, column), join_id)
37
- ds = query.from_self(alias: left_alias)
38
- ds.left_join(extra_table.as(right_alias),
39
- Sequel.qualify(left_alias, join_id) => Sequel.qualify(right_alias, join_id))
40
- .select_all(left_alias)
41
- .select_append(Sequel.qualify(right_alias, column).as(column))
42
- .from_self
43
- end
44
- end
45
- end
46
- end
47
- end
@@ -1,45 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "../query_modifier"
4
-
5
- module ConceptQL
6
- module QueryModifiers
7
- module Omopv4Plus
8
- class ProvenanceQueryModifier < QueryModifier
9
- RELATED_COLUMNS = %w[
10
- condition_type_concept_id
11
- procedure_type_concept_id
12
- death_type_concept_id
13
- observation_type_concept_id
14
- drug_type_concept_id
15
- ].sort.map(&:to_sym)
16
-
17
- attr_reader :db
18
-
19
- def self.provided_columns
20
- %i[
21
- file_provenance_type
22
- code_provenance_type
23
- ]
24
- end
25
-
26
- def self.has_required_columns?(cols)
27
- !(RELATED_COLUMNS & cols).empty?
28
- end
29
-
30
- def initialize(*args)
31
- super
32
- @db = query.db
33
- end
34
-
35
- def modified_query
36
- return query unless self.class.has_required_columns?(dm.table_cols(source_table))
37
-
38
- query.select_append(Sequel[dm.file_provenance_type_column(query, source_table)].as(:file_provenance_type),
39
- Sequel[dm.code_provenance_type_column(query,
40
- source_table)].as(:code_provenance_type)).from_self
41
- end
42
- end
43
- end
44
- end
45
- end
@@ -1,26 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "../query_modifier"
4
-
5
- module ConceptQL
6
- module QueryModifiers
7
- module Omopv4Plus
8
- class ProviderQueryModifier < QueryModifier
9
- def self.provided_columns
10
- [ :provider_id ]
11
- end
12
-
13
- def self.has_required_columns?(cols)
14
- cols.find { |col| col.to_s =~ /provider_id/ }
15
- end
16
-
17
- def modified_query
18
- col = self.class.has_required_columns?(dm.table_cols(source_table))
19
- return query if col.nil? || col.to_sym == :provider_id
20
-
21
- query.select_append(Sequel[col].as(:provider_id)).from_self
22
- end
23
- end
24
- end
25
- end
26
- end
@@ -1,24 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "../../operators/vocabulary"
4
-
5
- module ConceptQL
6
- module Vocabularies
7
- module Behaviors
8
- module Costish
9
- def query(db)
10
- costs = db[:procedure_cost].where(column_to_search => values).select(:procedure_occurrence_id)
11
- db[:procedure_occurrence].where(procedure_occurrence_id: costs)
12
- end
13
-
14
- def table
15
- :procedure_occurrence
16
- end
17
-
18
- def column_to_search
19
- vocab_entry.id == "revenue_code" ? :revenue_code_source_value : :disease_class_source_value
20
- end
21
- end
22
- end
23
- end
24
- end
@@ -1,32 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module ConceptQL
4
- module Vocabularies
5
- module Behaviors
6
- module Omopish
7
- # Overrides Operator#tables
8
- def tables
9
- domains
10
- end
11
-
12
- def vocabulary_condition
13
- { dm.source_vocabulary_id(domain) => vocabulary_id.to_i }
14
- end
15
-
16
- def where_clause(_db)
17
- conds = vocabulary_condition
18
- conds[dm.source_value_column(domain)] = arguments unless select_all?
19
- conds
20
- end
21
-
22
- def query_cols
23
- dm.table_columns(domain)
24
- end
25
-
26
- def translated_vocabulary_id
27
- vocab_entry.omopv4_id
28
- end
29
- end
30
- end
31
- end
32
- end