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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +64 -0
- data/config/vocabularies.csv +87 -87
- data/doc/spec.md +1951 -0
- data/lib/conceptql/behaviors/utilizable.rb +0 -2
- data/lib/conceptql/cli.rb +1 -1
- data/lib/conceptql/data_model/base.rb +1 -167
- data/lib/conceptql/data_model.rb +20 -4
- data/lib/conceptql/database.rb +2 -0
- data/lib/conceptql/lexicon/lexicon_strategy.rb +23 -3
- data/lib/conceptql/lexicon.rb +9 -1
- data/lib/conceptql/operators/co_reported.rb +4 -31
- data/lib/conceptql/operators/hospice.rb +1 -2
- data/lib/conceptql/operators/hospitalization.rb +1 -2
- data/lib/conceptql/operators/information_periods.rb +1 -3
- data/lib/conceptql/operators/one_in_two_out.rb +1 -2
- data/lib/conceptql/operators/operator.rb +3 -11
- data/lib/conceptql/operators/provenance.rb +1 -2
- data/lib/conceptql/operators/race.rb +2 -6
- data/lib/conceptql/operators/read.rb +1 -100
- data/lib/conceptql/operators/snf.rb +1 -2
- data/lib/conceptql/operators/vocabulary.rb +15 -2
- data/lib/conceptql/paths.rb +6 -0
- data/lib/conceptql/query.rb +5 -2
- data/lib/conceptql/rdbms/duckdb.rb +0 -4
- data/lib/conceptql/rdbms/generic.rb +0 -4
- data/lib/conceptql/rdbms/postgres.rb +0 -4
- data/lib/conceptql/rdbms/spark.rb +0 -4
- data/lib/conceptql/utils.rb +0 -43
- data/lib/conceptql/version.rb +1 -1
- data/lib/conceptql/vocabularies/entry.rb +0 -20
- data/lib/conceptql/vocabularies/vocabulary_class_factory.rb +2 -15
- metadata +16 -13
- data/lib/conceptql/data_model/omopv4_plus.rb +0 -12
- data/lib/conceptql/query_modifiers/omopv4_plus/admission_date_query_modifier.rb +0 -45
- data/lib/conceptql/query_modifiers/omopv4_plus/drug_query_modifier.rb +0 -80
- data/lib/conceptql/query_modifiers/omopv4_plus/pos_query_modifier.rb +0 -47
- data/lib/conceptql/query_modifiers/omopv4_plus/provenance_query_modifier.rb +0 -45
- data/lib/conceptql/query_modifiers/omopv4_plus/provider_query_modifier.rb +0 -26
- data/lib/conceptql/sql_formatters/formatter.rb +0 -25
- data/lib/conceptql/sql_formatters.rb +0 -22
- data/lib/conceptql/vocabularies/behaviors/costish.rb +0 -24
- data/lib/conceptql/vocabularies/behaviors/omopish.rb +0 -32
- data/schemas/omopv4_plus.yml +0 -729
- 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
|
-
|
|
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
|
-
|
|
43
|
+
warn_about_reused_codes(db, args - missing_args)
|
|
44
|
+
end
|
|
43
45
|
|
|
44
|
-
|
|
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)
|
data/lib/conceptql/paths.rb
CHANGED
|
@@ -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
|
data/lib/conceptql/query.rb
CHANGED
|
@@ -4,7 +4,7 @@ require "json"
|
|
|
4
4
|
require "forwardable"
|
|
5
5
|
require_relative "scope"
|
|
6
6
|
require_relative "nodifier"
|
|
7
|
-
|
|
7
|
+
require "sequel/sqlformat"
|
|
8
8
|
|
|
9
9
|
module ConceptQL
|
|
10
10
|
class Query
|
|
@@ -208,8 +208,11 @@ module ConceptQL
|
|
|
208
208
|
end
|
|
209
209
|
end
|
|
210
210
|
|
|
211
|
+
# Formats inside the Ruby process with the sequel-sqlformat gem (the Rust
|
|
212
|
+
# sqlformat crate). It changes only whitespace and keyword case, and it never
|
|
213
|
+
# fails on unknown syntax, so DuckDB and Spark SQL format too.
|
|
211
214
|
def format(sql)
|
|
212
|
-
|
|
215
|
+
Sequel::SQLFormat.format(sql, dialect: Sequel::SQLFormat.dialect_for(cdb.database_type), indent: 2, uppercase: true)
|
|
213
216
|
end
|
|
214
217
|
end
|
|
215
218
|
end
|
|
@@ -59,10 +59,6 @@ module ConceptQL
|
|
|
59
59
|
end
|
|
60
60
|
end
|
|
61
61
|
|
|
62
|
-
def preferred_formatter
|
|
63
|
-
SqlFormatters::SQL_FORMATTER
|
|
64
|
-
end
|
|
65
|
-
|
|
66
62
|
# Returns from - to in days, NOT to - from, so datediff(later, earlier)
|
|
67
63
|
# is positive. Every adapter keeps this order. This default emits
|
|
68
64
|
# Spark's datediff(endDate, startDate), which returns endDate - startDate,
|
|
@@ -14,10 +14,6 @@ module ConceptQL
|
|
|
14
14
|
db.vacuum_table(table_name, analyze: true) if analyze_temp_tables?
|
|
15
15
|
end
|
|
16
16
|
|
|
17
|
-
def preferred_formatter
|
|
18
|
-
SqlFormatters::POSTGRES
|
|
19
|
-
end
|
|
20
|
-
|
|
21
17
|
# DISTINCT ON is Postgres-only. The other adapters inherit
|
|
22
18
|
# Generic#primary_concepts, which uses ROW_NUMBER() instead.
|
|
23
19
|
def primary_concepts(db, all_primary_ids)
|
data/lib/conceptql/utils.rb
CHANGED
|
@@ -4,8 +4,6 @@ require "active_support/core_ext/array/conversions"
|
|
|
4
4
|
require "active_support/core_ext/object/blank"
|
|
5
5
|
require "active_support/core_ext/string/inflections"
|
|
6
6
|
require "json"
|
|
7
|
-
require "open3"
|
|
8
|
-
require "timeout"
|
|
9
7
|
|
|
10
8
|
module ConceptQL
|
|
11
9
|
module Utils
|
|
@@ -31,47 +29,6 @@ module ConceptQL
|
|
|
31
29
|
end
|
|
32
30
|
end
|
|
33
31
|
|
|
34
|
-
def timed_capture(*commands)
|
|
35
|
-
# Heavily adapted from:
|
|
36
|
-
# https://gist.github.com/lpar/1032297#gistcomment-1738285
|
|
37
|
-
opts = extract_opts!(commands)
|
|
38
|
-
timeout = opts.fetch(:timeout)
|
|
39
|
-
|
|
40
|
-
stdin, stdout, stderr, wait_thread = Open3.popen3(*commands)
|
|
41
|
-
wait_thread[:timed_out] = false
|
|
42
|
-
stdin.puts opts[:stdin_data] if opts[:stdin_data]
|
|
43
|
-
stdin.close
|
|
44
|
-
|
|
45
|
-
# Purposefully NOT using Timeout.rb because in general it is a dangerous API!
|
|
46
|
-
# http://blog.headius.com/2008/02/rubys-threadraise-threadkill-timeoutrb.html
|
|
47
|
-
Thread.new do
|
|
48
|
-
sleep timeout
|
|
49
|
-
if wait_thread.alive?
|
|
50
|
-
begin
|
|
51
|
-
wait_thread[:timed_out] = true
|
|
52
|
-
# please note: we are assuming the command will create ONE process (not handling subprocs / proc groups)
|
|
53
|
-
command = "kill -9 #{wait_thread.pid}"
|
|
54
|
-
system(command)
|
|
55
|
-
rescue(Errno::ESRCH)
|
|
56
|
-
# Do nothing!
|
|
57
|
-
end
|
|
58
|
-
end
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
if (es = wait_thread.value.exitstatus) && !es.zero?
|
|
62
|
-
raise stderr.read
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
out = stdout.read
|
|
66
|
-
stdout.close
|
|
67
|
-
|
|
68
|
-
if wait_thread[:timed_out]
|
|
69
|
-
raise Timeout::Error, "Command #{commands} failed to complete after #{timeout} seconds"
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
out
|
|
73
|
-
end
|
|
74
|
-
|
|
75
32
|
def extract_opts!(arr)
|
|
76
33
|
return {} unless arr.last.is_a?(Hash)
|
|
77
34
|
|
data/lib/conceptql/version.rb
CHANGED
|
@@ -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
|
-
|
|
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
|
+
version: 0.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ryan Duryea
|
|
@@ -79,6 +79,20 @@ dependencies:
|
|
|
79
79
|
- - "~>"
|
|
80
80
|
- !ruby/object:Gem::Version
|
|
81
81
|
version: '5.66'
|
|
82
|
+
- !ruby/object:Gem::Dependency
|
|
83
|
+
name: sequel-sqlformat
|
|
84
|
+
requirement: !ruby/object:Gem::Requirement
|
|
85
|
+
requirements:
|
|
86
|
+
- - "~>"
|
|
87
|
+
- !ruby/object:Gem::Version
|
|
88
|
+
version: '0.1'
|
|
89
|
+
type: :runtime
|
|
90
|
+
prerelease: false
|
|
91
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
92
|
+
requirements:
|
|
93
|
+
- - "~>"
|
|
94
|
+
- !ruby/object:Gem::Version
|
|
95
|
+
version: '0.1'
|
|
82
96
|
- !ruby/object:Gem::Dependency
|
|
83
97
|
name: sequelizer
|
|
84
98
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -314,6 +328,7 @@ files:
|
|
|
314
328
|
- config/provenance.yml
|
|
315
329
|
- config/race.yml
|
|
316
330
|
- config/vocabularies.csv
|
|
331
|
+
- doc/spec.md
|
|
317
332
|
- lib/conceptql.rb
|
|
318
333
|
- lib/conceptql/behaviors/code_lister.rb
|
|
319
334
|
- lib/conceptql/behaviors/drugish.rb
|
|
@@ -330,7 +345,6 @@ files:
|
|
|
330
345
|
- lib/conceptql/data_model/base.rb
|
|
331
346
|
- lib/conceptql/data_model/gdm.rb
|
|
332
347
|
- lib/conceptql/data_model/gdm_wide.rb
|
|
333
|
-
- lib/conceptql/data_model/omopv4_plus.rb
|
|
334
348
|
- lib/conceptql/database.rb
|
|
335
349
|
- lib/conceptql/date_adjuster.rb
|
|
336
350
|
- lib/conceptql/diagram.rb
|
|
@@ -404,11 +418,6 @@ files:
|
|
|
404
418
|
- lib/conceptql/query_modifiers/gdm_wide/lab_query_modifier.rb
|
|
405
419
|
- lib/conceptql/query_modifiers/gdm_wide/pos_query_modifier.rb
|
|
406
420
|
- lib/conceptql/query_modifiers/gdm_wide/provenance_query_modifier.rb
|
|
407
|
-
- lib/conceptql/query_modifiers/omopv4_plus/admission_date_query_modifier.rb
|
|
408
|
-
- lib/conceptql/query_modifiers/omopv4_plus/drug_query_modifier.rb
|
|
409
|
-
- lib/conceptql/query_modifiers/omopv4_plus/pos_query_modifier.rb
|
|
410
|
-
- lib/conceptql/query_modifiers/omopv4_plus/provenance_query_modifier.rb
|
|
411
|
-
- lib/conceptql/query_modifiers/omopv4_plus/provider_query_modifier.rb
|
|
412
421
|
- lib/conceptql/query_modifiers/query_modifier.rb
|
|
413
422
|
- lib/conceptql/rdbms.rb
|
|
414
423
|
- lib/conceptql/rdbms/duckdb.rb
|
|
@@ -416,15 +425,11 @@ files:
|
|
|
416
425
|
- lib/conceptql/rdbms/postgres.rb
|
|
417
426
|
- lib/conceptql/rdbms/spark.rb
|
|
418
427
|
- lib/conceptql/scope.rb
|
|
419
|
-
- lib/conceptql/sql_formatters.rb
|
|
420
|
-
- lib/conceptql/sql_formatters/formatter.rb
|
|
421
428
|
- lib/conceptql/utils.rb
|
|
422
429
|
- lib/conceptql/utils_cli.rb
|
|
423
430
|
- lib/conceptql/version.rb
|
|
424
431
|
- lib/conceptql/visitors/metadata_extractor.rb
|
|
425
|
-
- lib/conceptql/vocabularies/behaviors/costish.rb
|
|
426
432
|
- lib/conceptql/vocabularies/behaviors/gdmish.rb
|
|
427
|
-
- lib/conceptql/vocabularies/behaviors/omopish.rb
|
|
428
433
|
- lib/conceptql/vocabularies/dynamic_vocabularies.rb
|
|
429
434
|
- lib/conceptql/vocabularies/entry.rb
|
|
430
435
|
- lib/conceptql/vocabularies/vocabulary_class_factory.rb
|
|
@@ -437,8 +442,6 @@ files:
|
|
|
437
442
|
- schemas/gdm.yml
|
|
438
443
|
- schemas/gdm_wide.yml
|
|
439
444
|
- schemas/ohdsi_vocabs.yml
|
|
440
|
-
- schemas/omopv4_plus.yml
|
|
441
|
-
- schemas/omopv4_plus_more_ignorables.tsv
|
|
442
445
|
homepage: https://github.com/outcomesinsights/conceptql
|
|
443
446
|
licenses:
|
|
444
447
|
- MIT
|
|
@@ -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,25 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module ConceptQL
|
|
4
|
-
module SqlFormatters
|
|
5
|
-
# An external SQL formatter, given as a command array that reads SQL on
|
|
6
|
-
# stdin, e.g. Formatter.new(%w[sql-formatter -l postgresql]).
|
|
7
|
-
class Formatter
|
|
8
|
-
attr_reader :command
|
|
9
|
-
|
|
10
|
-
def initialize(command)
|
|
11
|
-
@command = command
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
def available?
|
|
15
|
-
system("which", command.first, out: File::NULL, err: File::NULL)
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
def format(sql)
|
|
19
|
-
ConceptQL::Utils.timed_capture(*command, stdin_data: sql, timeout: 10)
|
|
20
|
-
rescue Timeout::Error
|
|
21
|
-
sql
|
|
22
|
-
end
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
end
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative "sql_formatters/formatter"
|
|
4
|
-
|
|
5
|
-
module ConceptQL
|
|
6
|
-
module SqlFormatters
|
|
7
|
-
SQL_FORMATTER = %w[sql-formatter].freeze
|
|
8
|
-
POSTGRES = %w[sql-formatter -l postgresql].freeze
|
|
9
|
-
SQLFORMAT = %w[sqlformat --reindent --use_space_around_operators --identifiers lower --keywords upper -].freeze
|
|
10
|
-
PG_FORMAT = %w[pg_format].freeze
|
|
11
|
-
|
|
12
|
-
# Formats with the rdbms's preferred formatter, else the first of the
|
|
13
|
-
# fallbacks that is installed, else returns the SQL untouched.
|
|
14
|
-
def self.format(sql, rdbms)
|
|
15
|
-
formatter = [ rdbms.preferred_formatter, SQL_FORMATTER, SQLFORMAT, PG_FORMAT ]
|
|
16
|
-
.compact
|
|
17
|
-
.map { |command| Formatter.new(command) }
|
|
18
|
-
.detect(&:available?)
|
|
19
|
-
formatter ? formatter.format(sql) : sql
|
|
20
|
-
end
|
|
21
|
-
end
|
|
22
|
-
end
|