conceptql 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/CHANGELOG.md +226 -117
- data/LICENSE +21 -0
- data/README.md +35 -83
- data/bin/conceptql +2 -1
- data/bin/conceptql_utils +6 -0
- data/config/multiple_vocabularies.csv +4 -0
- data/config/provenance.yml +489 -0
- data/config/race.yml +50 -0
- data/config/vocabularies.csv +87 -0
- data/lib/conceptql/behaviors/code_lister.rb +23 -0
- data/lib/conceptql/behaviors/drugish.rb +17 -0
- data/lib/conceptql/behaviors/labish.rb +19 -0
- data/lib/conceptql/behaviors/metadatable.rb +226 -73
- data/lib/conceptql/behaviors/provenanceable.rb +26 -0
- data/lib/conceptql/behaviors/single_event.rb +11 -0
- data/lib/conceptql/behaviors/timeless.rb +11 -0
- data/lib/conceptql/behaviors/utilizable.rb +206 -0
- data/lib/conceptql/behaviors/windowable.rb +27 -0
- data/lib/conceptql/cli.rb +117 -108
- data/lib/conceptql/columnizer.rb +75 -0
- data/lib/conceptql/data_model/base.rb +462 -0
- data/lib/conceptql/data_model/gdm.rb +155 -0
- data/lib/conceptql/data_model/gdm_wide.rb +42 -0
- data/lib/conceptql/data_model/omopv4_plus.rb +12 -0
- data/lib/conceptql/data_model.rb +30 -0
- data/lib/conceptql/database.rb +214 -0
- data/lib/conceptql/date_adjuster.rb +66 -18
- data/lib/conceptql/diagram.rb +243 -0
- data/lib/conceptql/lexicon/lexicon_gdm.rb +47 -0
- data/lib/conceptql/lexicon/lexicon_no_db.rb +49 -0
- data/lib/conceptql/lexicon/lexicon_ohdsi.rb +63 -0
- data/lib/conceptql/lexicon/lexicon_strategy.rb +116 -0
- data/lib/conceptql/lexicon.rb +111 -0
- data/lib/conceptql/nodifier.rb +65 -24
- data/lib/conceptql/operators/after.rb +34 -14
- data/lib/conceptql/operators/any_overlap.rb +8 -6
- data/lib/conceptql/operators/before.rb +34 -10
- data/lib/conceptql/operators/binary_operator_operator.rb +66 -23
- data/lib/conceptql/operators/casting_operator.rb +51 -36
- data/lib/conceptql/operators/co_reported.rb +111 -0
- data/lib/conceptql/operators/concurrent_within.rb +69 -0
- data/lib/conceptql/operators/contains.rb +12 -8
- data/lib/conceptql/operators/count.rb +17 -9
- data/lib/conceptql/operators/date_range.rb +48 -14
- data/lib/conceptql/operators/death.rb +14 -4
- data/lib/conceptql/operators/dedup.rb +31 -0
- data/lib/conceptql/operators/during.rb +7 -12
- data/lib/conceptql/operators/episode.rb +146 -0
- data/lib/conceptql/operators/equal.rb +9 -3
- data/lib/conceptql/operators/ethnicity.rb +49 -0
- data/lib/conceptql/operators/except.rb +19 -12
- data/lib/conceptql/operators/filter.rb +23 -8
- data/lib/conceptql/operators/first.rb +16 -3
- data/lib/conceptql/operators/from.rb +52 -4
- data/lib/conceptql/operators/gender.rb +34 -11
- data/lib/conceptql/operators/hospice.rb +19 -0
- data/lib/conceptql/operators/hospitalization.rb +19 -0
- data/lib/conceptql/operators/information_periods.rb +36 -0
- data/lib/conceptql/operators/invalid.rb +35 -0
- data/lib/conceptql/operators/last.rb +16 -3
- data/lib/conceptql/operators/match.rb +91 -0
- data/lib/conceptql/operators/multiple_vocabularies.rb +172 -0
- data/lib/conceptql/operators/numeric.rb +65 -23
- data/lib/conceptql/operators/numeric_filter.rb +68 -0
- data/lib/conceptql/operators/occurrence.rb +153 -32
- data/lib/conceptql/operators/one_in_two_out.rb +237 -38
- data/lib/conceptql/operators/operator.rb +624 -178
- data/lib/conceptql/operators/pass_thru.rb +17 -3
- data/lib/conceptql/operators/person.rb +18 -6
- data/lib/conceptql/operators/person_filter.rb +9 -3
- data/lib/conceptql/operators/place_of_service_filter.rb +43 -0
- data/lib/conceptql/operators/provenance.rb +55 -0
- data/lib/conceptql/operators/provider_filter.rb +62 -0
- data/lib/conceptql/operators/race.rb +55 -7
- data/lib/conceptql/operators/read.rb +168 -0
- data/lib/conceptql/operators/recall.rb +49 -14
- data/lib/conceptql/operators/snf.rb +19 -0
- data/lib/conceptql/operators/sum.rb +29 -12
- data/lib/conceptql/operators/temporal_operator.rb +108 -16
- data/lib/conceptql/operators/time_window.rb +20 -32
- data/lib/conceptql/operators/trim_date.rb +71 -0
- data/lib/conceptql/operators/trim_date_end.rb +28 -40
- data/lib/conceptql/operators/trim_date_start.rb +28 -41
- data/lib/conceptql/operators/union.rb +67 -6
- data/lib/conceptql/operators/vocabulary.rb +151 -0
- data/lib/conceptql/paths.rb +41 -0
- data/lib/conceptql/query.rb +191 -26
- data/lib/conceptql/query_modifiers/gdm/admission_date_query_modifier.rb +46 -0
- data/lib/conceptql/query_modifiers/gdm/drug_query_modifier.rb +54 -0
- data/lib/conceptql/query_modifiers/gdm/lab_query_modifier.rb +57 -0
- data/lib/conceptql/query_modifiers/gdm/pos_query_modifier.rb +46 -0
- data/lib/conceptql/query_modifiers/gdm/provenance_query_modifier.rb +45 -0
- data/lib/conceptql/query_modifiers/gdm/provider_query_modifier.rb +53 -0
- data/lib/conceptql/query_modifiers/gdm_wide/admission_date_query_modifier.rb +42 -0
- data/lib/conceptql/query_modifiers/gdm_wide/drug_query_modifier.rb +52 -0
- data/lib/conceptql/query_modifiers/gdm_wide/lab_query_modifier.rb +55 -0
- data/lib/conceptql/query_modifiers/gdm_wide/pos_query_modifier.rb +37 -0
- data/lib/conceptql/query_modifiers/gdm_wide/provenance_query_modifier.rb +38 -0
- data/lib/conceptql/query_modifiers/omopv4_plus/admission_date_query_modifier.rb +45 -0
- data/lib/conceptql/query_modifiers/omopv4_plus/drug_query_modifier.rb +80 -0
- data/lib/conceptql/query_modifiers/omopv4_plus/pos_query_modifier.rb +47 -0
- data/lib/conceptql/query_modifiers/omopv4_plus/provenance_query_modifier.rb +45 -0
- data/lib/conceptql/query_modifiers/omopv4_plus/provider_query_modifier.rb +26 -0
- data/lib/conceptql/query_modifiers/query_modifier.rb +20 -0
- data/lib/conceptql/rdbms/duckdb.rb +25 -0
- data/lib/conceptql/rdbms/generic.rb +120 -0
- data/lib/conceptql/rdbms/postgres.rb +51 -0
- data/lib/conceptql/rdbms/spark.rb +22 -0
- data/lib/conceptql/rdbms.rb +22 -0
- data/lib/conceptql/scope.rb +481 -31
- data/lib/conceptql/sql_formatters/formatter.rb +25 -0
- data/lib/conceptql/sql_formatters.rb +22 -0
- data/lib/conceptql/utils.rb +113 -0
- data/lib/conceptql/utils_cli.rb +17 -0
- data/lib/conceptql/version.rb +3 -1
- data/lib/conceptql/visitors/metadata_extractor.rb +54 -0
- data/lib/conceptql/vocabularies/behaviors/costish.rb +24 -0
- data/lib/conceptql/vocabularies/behaviors/gdmish.rb +19 -0
- data/lib/conceptql/vocabularies/behaviors/omopish.rb +32 -0
- data/lib/conceptql/vocabularies/dynamic_vocabularies.rb +63 -0
- data/lib/conceptql/vocabularies/entry.rb +138 -0
- data/lib/conceptql/vocabularies/vocabulary_class_factory.rb +85 -0
- data/lib/conceptql/window/base.rb +26 -0
- data/lib/conceptql/window/date_range.rb +25 -0
- data/lib/conceptql/window/table.rb +116 -0
- data/lib/conceptql/window.rb +72 -0
- data/lib/conceptql.rb +65 -2
- data/lib/sequel/extensions/pg_ctas_explain.rb +22 -0
- data/lib/sequel/extensions/pg_vacuum_table.rb +23 -0
- data/schemas/gdm.yml +882 -0
- data/schemas/gdm_wide.yml +757 -0
- data/schemas/ohdsi_vocabs.yml +143 -0
- data/schemas/omopv4_plus.yml +729 -0
- data/schemas/omopv4_plus_more_ignorables.tsv +13 -0
- metadata +267 -207
- data/.gitignore +0 -23
- data/Gemfile +0 -4
- data/Guardfile +0 -56
- data/LICENSE.txt +0 -22
- data/Rakefile +0 -1
- data/conceptql.gemspec +0 -32
- data/doc/ConceptQL Specification (alpha).pdf +0 -0
- data/doc/diagram_0.png +0 -0
- data/doc/implementation_notes.md +0 -39
- data/doc/spec.md +0 -1344
- data/lib/conceptql/behaviors/debuggable.rb +0 -70
- data/lib/conceptql/behaviors/dottable.rb +0 -103
- data/lib/conceptql/behaviors/preppable.rb +0 -20
- data/lib/conceptql/converter.rb +0 -65
- data/lib/conceptql/debugger.rb +0 -48
- data/lib/conceptql/fake_grapher.rb +0 -20
- data/lib/conceptql/graph.rb +0 -52
- data/lib/conceptql/graph_nodifier.rb +0 -203
- data/lib/conceptql/logger.rb +0 -10
- data/lib/conceptql/operators/complement.rb +0 -38
- data/lib/conceptql/operators/concept.rb +0 -70
- data/lib/conceptql/operators/condition_type.rb +0 -71
- data/lib/conceptql/operators/cpt.rb +0 -25
- data/lib/conceptql/operators/drug_type_concept.rb +0 -21
- data/lib/conceptql/operators/from_seer_visits.rb +0 -23
- data/lib/conceptql/operators/hcpcs.rb +0 -25
- data/lib/conceptql/operators/icd10.rb +0 -28
- data/lib/conceptql/operators/icd9.rb +0 -28
- data/lib/conceptql/operators/icd9_procedure.rb +0 -25
- data/lib/conceptql/operators/intersect.rb +0 -34
- data/lib/conceptql/operators/loinc.rb +0 -25
- data/lib/conceptql/operators/medcode.rb +0 -28
- data/lib/conceptql/operators/medcode_procedure.rb +0 -27
- data/lib/conceptql/operators/ndc.rb +0 -29
- data/lib/conceptql/operators/observation_by_enttype.rb +0 -29
- data/lib/conceptql/operators/observation_period.rb +0 -30
- data/lib/conceptql/operators/overlapped_by.rb +0 -23
- data/lib/conceptql/operators/overlaps.rb +0 -19
- data/lib/conceptql/operators/place_of_service_code.rb +0 -24
- data/lib/conceptql/operators/procedure_occurrence.rb +0 -25
- data/lib/conceptql/operators/prodcode.rb +0 -29
- data/lib/conceptql/operators/rxnorm.rb +0 -24
- data/lib/conceptql/operators/snomed.rb +0 -24
- data/lib/conceptql/operators/snomed_condition.rb +0 -26
- data/lib/conceptql/operators/source_vocabulary_operator.rb +0 -58
- data/lib/conceptql/operators/standard_vocabulary_operator.rb +0 -45
- data/lib/conceptql/operators/started_by.rb +0 -22
- data/lib/conceptql/operators/to_seer_visits.rb +0 -24
- data/lib/conceptql/operators/visit.rb +0 -15
- data/lib/conceptql/operators/visit_occurrence.rb +0 -30
- data/lib/conceptql/tree.rb +0 -53
- data/lib/conceptql/utils/temp_table.rb +0 -72
- data/spec/conceptql/behaviors/dottable_spec.rb +0 -99
- data/spec/conceptql/converter_spec.rb +0 -51
- data/spec/conceptql/date_adjuster_spec.rb +0 -68
- data/spec/conceptql/operators/after_spec.rb +0 -16
- data/spec/conceptql/operators/before_spec.rb +0 -16
- data/spec/conceptql/operators/casting_operator_spec.rb +0 -68
- data/spec/conceptql/operators/complement_spec.rb +0 -15
- data/spec/conceptql/operators/concept_spec.rb +0 -40
- data/spec/conceptql/operators/condition_type_spec.rb +0 -128
- data/spec/conceptql/operators/contains_spec.rb +0 -19
- data/spec/conceptql/operators/cpt_spec.rb +0 -29
- data/spec/conceptql/operators/date_range_spec.rb +0 -33
- data/spec/conceptql/operators/death_spec.rb +0 -10
- data/spec/conceptql/operators/during_spec.rb +0 -30
- data/spec/conceptql/operators/except_spec.rb +0 -15
- data/spec/conceptql/operators/first_spec.rb +0 -35
- data/spec/conceptql/operators/from_spec.rb +0 -13
- data/spec/conceptql/operators/gender_spec.rb +0 -27
- data/spec/conceptql/operators/hcpcs_spec.rb +0 -29
- data/spec/conceptql/operators/icd10_spec.rb +0 -34
- data/spec/conceptql/operators/icd9_procedure_spec.rb +0 -29
- data/spec/conceptql/operators/icd9_spec.rb +0 -34
- data/spec/conceptql/operators/intersect_spec.rb +0 -28
- data/spec/conceptql/operators/last_spec.rb +0 -36
- data/spec/conceptql/operators/loinc_spec.rb +0 -29
- data/spec/conceptql/operators/medcode_procedure_spec.rb +0 -34
- data/spec/conceptql/operators/medcode_spec.rb +0 -34
- data/spec/conceptql/operators/observation_period_spec.rb +0 -10
- data/spec/conceptql/operators/occurrence_spec.rb +0 -87
- data/spec/conceptql/operators/overlapped_by_spec.rb +0 -32
- data/spec/conceptql/operators/overlaps_spec.rb +0 -21
- data/spec/conceptql/operators/person_filter_spec.rb +0 -15
- data/spec/conceptql/operators/person_spec.rb +0 -10
- data/spec/conceptql/operators/place_of_service_code_spec.rb +0 -24
- data/spec/conceptql/operators/procedure_occurrence_spec.rb +0 -10
- data/spec/conceptql/operators/prodcode_spec.rb +0 -35
- data/spec/conceptql/operators/query_double.rb +0 -20
- data/spec/conceptql/operators/query_double_spec.rb +0 -7
- data/spec/conceptql/operators/race_spec.rb +0 -21
- data/spec/conceptql/operators/rxnorm_spec.rb +0 -29
- data/spec/conceptql/operators/snomed_spec.rb +0 -29
- data/spec/conceptql/operators/source_vocabulary_operator_spec.rb +0 -35
- data/spec/conceptql/operators/standard_vocabulary_operator_spec.rb +0 -35
- data/spec/conceptql/operators/started_by_spec.rb +0 -22
- data/spec/conceptql/operators/temporal_operator_spec.rb +0 -51
- data/spec/conceptql/operators/time_window_spec.rb +0 -77
- data/spec/conceptql/operators/union_spec.rb +0 -21
- data/spec/conceptql/operators/visit_occurrence_spec.rb +0 -10
- data/spec/conceptql/query_spec.rb +0 -22
- data/spec/conceptql/tree_spec.rb +0 -50
- data/spec/doubles/stream_for_casting_double.rb +0 -9
- data/spec/doubles/stream_for_occurrence_double.rb +0 -25
- data/spec/doubles/stream_for_temporal_double.rb +0 -6
- data/spec/spec_helper.rb +0 -102
|
@@ -1,62 +1,261 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "operator"
|
|
4
|
+
require_relative "../date_adjuster"
|
|
5
|
+
require_relative "../behaviors/provenanceable"
|
|
6
|
+
require_relative "../behaviors/single_event"
|
|
3
7
|
|
|
4
8
|
module ConceptQL
|
|
5
9
|
module Operators
|
|
6
10
|
class OneInTwoOut < Operator
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
11
|
+
include ConceptQL::Behaviors::SingleEvent
|
|
12
|
+
|
|
13
|
+
# GDM only: omopv4_plus has no provenance vocabularies (conceptql-myp).
|
|
14
|
+
register __FILE__, :gdm, :gdm_wide
|
|
15
|
+
|
|
16
|
+
desc "Identifies an event that appears either once in an inpatient setting or twice within a specified interval in an outpatient setting."
|
|
17
|
+
|
|
12
18
|
allows_one_upstream
|
|
13
|
-
|
|
19
|
+
validate_one_upstream
|
|
20
|
+
validate_no_arguments
|
|
21
|
+
category "Filter Single Stream"
|
|
22
|
+
basic_type :temporal
|
|
14
23
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
24
|
+
option :inpatient_length_of_stay, type: :integer, min: 0, default: 0,
|
|
25
|
+
desc: "Minimum length of inpatient stay (in days) required for inpatient event to be valid", label: "Inpatient Length of Stay (Days)"
|
|
26
|
+
option :outpatient_minimum_gap, type: :string, default: "30d",
|
|
27
|
+
instructions: 'Enter a numeric value and specify "d", "m", or "y" for "days", "months", or "years".'
|
|
28
|
+
option :outpatient_maximum_gap, type: :string, default: "365d",
|
|
29
|
+
instructions: 'Enter a numeric value and specify "d", "m", or "y" for "days", "months", or "years".'
|
|
30
|
+
option :outpatient_event_to_return, type: :string, options: [ "Initial Event", "Confirming Event" ],
|
|
31
|
+
default: "Initial Event", desc: "Which event to pass downstream"
|
|
32
|
+
|
|
33
|
+
validate_option DateAdjuster::VALID_INPUT, :outpatient_minimum_gap, :outpatient_maximum_gap
|
|
34
|
+
|
|
35
|
+
default_query_columns
|
|
36
|
+
|
|
37
|
+
include ConceptQL::Provenanceable
|
|
38
|
+
|
|
39
|
+
require_column :admission_date
|
|
40
|
+
require_column :discharge_date
|
|
41
|
+
|
|
42
|
+
# Each event is copied once per tag, by a cross join with a constant
|
|
43
|
+
# table, so the upstream is read only once. The copies sort by date
|
|
44
|
+
# and, at equal dates, by tag: a lower gap bound, then an event, then
|
|
45
|
+
# an upper gap bound. Running sums over that order then count, at each
|
|
46
|
+
# bound, the events on or after the lower bound and on or before the
|
|
47
|
+
# upper bound, which is the old self-join's condition.
|
|
48
|
+
LOWER = 0
|
|
49
|
+
EVENT = 1
|
|
50
|
+
UPPER = 2
|
|
51
|
+
INPATIENT = 3
|
|
52
|
+
|
|
53
|
+
attr_reader :db
|
|
18
54
|
|
|
19
55
|
def query(db)
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
56
|
+
@db = db
|
|
57
|
+
first_valid_event.from_self
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
private
|
|
61
|
+
|
|
62
|
+
def initial_mode?
|
|
63
|
+
options[:outpatient_event_to_return] == "Initial Event"
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def min_gap
|
|
67
|
+
(options[:outpatient_minimum_gap] || "30d").to_s
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def max_gap
|
|
71
|
+
options[:outpatient_maximum_gap].to_s
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# In the old self-join, a bare START or END resolved to the subquery's
|
|
75
|
+
# table. In Initial Event mode that is the confirming event, so the
|
|
76
|
+
# "gap" compares the confirming event with itself. It filters which
|
|
77
|
+
# events can confirm and bounds nothing.
|
|
78
|
+
def keyword_gap?(gap)
|
|
79
|
+
initial_mode? && %w[start end].include?(gap.downcase)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def adjusted(gap)
|
|
83
|
+
DateAdjuster.new(self, gap).adjust(:start_date)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# The earliest date a partner event may have. NULL when the bound is
|
|
87
|
+
# NULL, as the old comparison then failed.
|
|
88
|
+
def lower_bound
|
|
89
|
+
return Sequel[:start_date] if min_gap.empty? || keyword_gap?(min_gap)
|
|
90
|
+
|
|
91
|
+
bound = adjusted(min_gap)
|
|
92
|
+
Sequel.case([ [ Sequel.expr(bound) > :start_date, bound ], [ Sequel.expr(bound) <= :start_date, :start_date ] ], nil)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# The latest date a partner event may have, or nil for no limit.
|
|
96
|
+
def upper_bound
|
|
97
|
+
return if max_gap.empty? || keyword_gap?(max_gap)
|
|
98
|
+
|
|
99
|
+
adjusted(max_gap)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def partner_filter
|
|
103
|
+
conditions = []
|
|
104
|
+
conditions << (Sequel.expr(:start_date) >= adjusted(min_gap)) if keyword_gap?(min_gap)
|
|
105
|
+
conditions << (Sequel.expr(:start_date) <= adjusted(max_gap)) if keyword_gap?(max_gap)
|
|
106
|
+
return true if conditions.empty?
|
|
107
|
+
|
|
108
|
+
Sequel.&(*conditions)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def inpatient_condition
|
|
112
|
+
condition = build_where_from_codes(db, [ "inpatient" ])
|
|
113
|
+
return condition if options[:inpatient_length_of_stay].nil? || options[:inpatient_length_of_stay].to_i.zero?
|
|
114
|
+
|
|
115
|
+
Sequel.&(condition, rdbms.days_between(:admission_date, :discharge_date) > options[:inpatient_length_of_stay].to_i)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# The old self-join compared these columns with = and <>, so a NULL in
|
|
119
|
+
# any of them kept an event from pairing.
|
|
120
|
+
def outpatient_condition
|
|
121
|
+
not_null = (matching_columns + [ :criterion_id ]).map { |c| Sequel.~(c => nil) }
|
|
122
|
+
Sequel.&(build_where_from_codes(db, %w[carrier_claim outpatient]), *not_null)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def flag(condition)
|
|
126
|
+
Sequel.case([ [ condition, 1 ] ], 0)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def condition_events
|
|
130
|
+
db[stream.evaluate(db)]
|
|
131
|
+
.where(criterion_domain: "condition_occurrence")
|
|
132
|
+
.where(Sequel.|(inpatient_condition, outpatient_condition))
|
|
133
|
+
.select(*query_cols)
|
|
134
|
+
.select_append(flag(inpatient_condition).as(:is_inpatient), flag(outpatient_condition).as(:is_outpatient))
|
|
135
|
+
.select_append(lower_bound.as(:lower_bound))
|
|
136
|
+
.select_append(flag(partner_filter).as(:can_partner))
|
|
137
|
+
.then { |ds| initial_mode? ? ds.select_append(event_key) : ds }
|
|
138
|
+
.from_self
|
|
139
|
+
# The upper bound gets its own SELECT. DuckDB compares 1 month as
|
|
140
|
+
# equal to 30 days, so its optimizer merges date + 30 days and
|
|
141
|
+
# date + 1 month in one SELECT list into one expression (bead
|
|
142
|
+
# conceptql-a4j, seed conceptql-3wy). sequel-duckdb is to emit month
|
|
143
|
+
# intervals in a form that avoids this; the split stays as a second
|
|
144
|
+
# guard.
|
|
145
|
+
.select_append(Sequel.as(upper_bound || Sequel.cast(nil, Date), :upper_bound))
|
|
28
146
|
.from_self
|
|
29
|
-
|
|
147
|
+
end
|
|
30
148
|
|
|
31
|
-
|
|
32
|
-
|
|
149
|
+
# Tells an event's copies apart from other events' copies. Only Initial
|
|
150
|
+
# Event mode needs it, to bring its two bounds' counts together.
|
|
151
|
+
def event_key
|
|
152
|
+
Sequel.function(:row_number).over(partition: matching_columns, order: %i[start_date criterion_id]).as(:event_key)
|
|
33
153
|
end
|
|
34
154
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
155
|
+
def tags
|
|
156
|
+
[ LOWER, EVENT, UPPER, INPATIENT ]
|
|
157
|
+
.map { |tag| db.select(Sequel.as(tag, :tag)) }
|
|
158
|
+
.inject { |all, one| all.union(one, all: true, from_self: false) }
|
|
38
159
|
end
|
|
39
160
|
|
|
40
|
-
def
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
161
|
+
def bounds_valid
|
|
162
|
+
valid = Sequel.~(lower_bound: nil)
|
|
163
|
+
return valid unless upper_bound
|
|
164
|
+
|
|
165
|
+
Sequel.&(valid, Sequel.~(upper_bound: nil), Sequel.expr(:lower_bound) <= :upper_bound)
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def copies
|
|
169
|
+
bound_tags = upper_bound ? [ LOWER, UPPER ] : [ LOWER ]
|
|
170
|
+
condition_events
|
|
171
|
+
.cross_join(tags, table_alias: :tags)
|
|
172
|
+
.where(Sequel.|(
|
|
173
|
+
Sequel.&({ tag: INPATIENT }, { is_inpatient: 1 }),
|
|
174
|
+
Sequel.&({ tag: EVENT }, { is_outpatient: 1 }, Sequel.~(start_date: nil)),
|
|
175
|
+
Sequel.&({ tag: bound_tags }, { is_outpatient: 1 }, bounds_valid)
|
|
176
|
+
))
|
|
177
|
+
.select_all(:t1)
|
|
178
|
+
.select_append(:tag)
|
|
179
|
+
.select_append(Sequel.case({ LOWER => :lower_bound, UPPER => :upper_bound }, :start_date, :tag).as(:sweep_date))
|
|
180
|
+
.select_append(weight.as(:weight))
|
|
45
181
|
.from_self
|
|
46
182
|
end
|
|
47
183
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
184
|
+
# Initial Event mode counts events at its bounds. Confirming Event mode
|
|
185
|
+
# counts the bound intervals that are open at each event.
|
|
186
|
+
def weight
|
|
187
|
+
if initial_mode?
|
|
188
|
+
Sequel.case([ [ Sequel.&({ tag: EVENT }, { can_partner: 1 }), 1 ] ], 0)
|
|
189
|
+
else
|
|
190
|
+
Sequel.case({ LOWER => 1, UPPER => -1 }, 0, :tag)
|
|
53
191
|
end
|
|
192
|
+
end
|
|
54
193
|
|
|
55
|
-
|
|
56
|
-
|
|
194
|
+
def running_sum(partition)
|
|
195
|
+
Sequel.function(:sum, :weight).over(partition: partition, order: %i[sweep_date tag])
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def total_sum(partition)
|
|
199
|
+
Sequel.function(:sum, :weight).over(partition: partition)
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
# Counts from events with a different criterion_id only: the old
|
|
203
|
+
# self-join excluded pairs that shared one.
|
|
204
|
+
def counted_copies
|
|
205
|
+
same = matching_columns + [ :criterion_id ]
|
|
206
|
+
ds = copies.select_append((running_sum(matching_columns) - running_sum(same)).as(:partners))
|
|
207
|
+
if initial_mode? && !upper_bound
|
|
208
|
+
ds = ds.select_append((total_sum(matching_columns) - total_sum(same)).as(:all_partners))
|
|
57
209
|
end
|
|
210
|
+
ds.from_self
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def valid_condition
|
|
214
|
+
return Sequel.&({ tag: EVENT }, Sequel.expr(:partners) >= 1) unless initial_mode?
|
|
215
|
+
|
|
216
|
+
Sequel.&({ tag: EVENT }, Sequel.expr(:upper_partners) - :lower_partners >= 1)
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
def bound_partners(tag)
|
|
220
|
+
Sequel.function(:max, Sequel.case([ [ { tag: tag }, :partners ] ], nil))
|
|
221
|
+
.over(partition: matching_columns + [ :event_key ])
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
def judged_copies
|
|
225
|
+
return counted_copies unless initial_mode?
|
|
226
|
+
|
|
227
|
+
upper = upper_bound ? bound_partners(UPPER) : Sequel.function(:max, :all_partners).over(partition: matching_columns + [ :event_key ])
|
|
228
|
+
counted_copies
|
|
229
|
+
.select_append(bound_partners(LOWER).as(:lower_partners), upper.as(:upper_partners))
|
|
230
|
+
.from_self
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
def output_column(column)
|
|
234
|
+
case column
|
|
235
|
+
when :start_date then Sequel.case([ [ { tag: INPATIENT }, :admission_date ] ], :start_date).as(:start_date)
|
|
236
|
+
when :end_date then Sequel.case([ [ { tag: INPATIENT }, :discharge_date ] ], :end_date).as(:end_date)
|
|
237
|
+
else column
|
|
238
|
+
end
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
def all_valid_events
|
|
242
|
+
judged_copies
|
|
243
|
+
.where(Sequel.|({ tag: INPATIENT }, valid_condition))
|
|
244
|
+
.select(*dynamic_columns.map { |column| output_column(column) })
|
|
245
|
+
.from_self
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
def first_valid_event
|
|
249
|
+
all_valid_events
|
|
250
|
+
.select_append do |o|
|
|
251
|
+
o.row_number.function.over(partition: matching_columns,
|
|
252
|
+
order: [
|
|
253
|
+
:start_date, :criterion_id
|
|
254
|
+
]).as(:rn)
|
|
255
|
+
end
|
|
256
|
+
.from_self
|
|
257
|
+
.where(rn: 1)
|
|
58
258
|
end
|
|
59
259
|
end
|
|
60
260
|
end
|
|
61
261
|
end
|
|
62
|
-
|