line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Convert::Pheno::Mapping; |
2
|
|
|
|
|
|
|
|
3
|
6
|
|
|
6
|
|
50
|
use strict; |
|
6
|
|
|
|
|
12
|
|
|
6
|
|
|
|
|
182
|
|
4
|
6
|
|
|
6
|
|
37
|
use warnings; |
|
6
|
|
|
|
|
10
|
|
|
6
|
|
|
|
|
135
|
|
5
|
6
|
|
|
6
|
|
25
|
use autodie; |
|
6
|
|
|
|
|
10
|
|
|
6
|
|
|
|
|
30
|
|
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
#use Carp qw(confess); |
8
|
6
|
|
|
6
|
|
32001
|
use feature qw(say); |
|
6
|
|
|
|
|
20
|
|
|
6
|
|
|
|
|
401
|
|
9
|
6
|
|
|
6
|
|
37
|
use utf8; |
|
6
|
|
|
|
|
10
|
|
|
6
|
|
|
|
|
41
|
|
10
|
6
|
|
|
6
|
|
170
|
use Data::Dumper; |
|
6
|
|
|
|
|
11
|
|
|
6
|
|
|
|
|
364
|
|
11
|
6
|
|
|
6
|
|
3751
|
use JSON::XS; |
|
6
|
|
|
|
|
25856
|
|
|
6
|
|
|
|
|
381
|
|
12
|
6
|
|
|
6
|
|
3368
|
use Time::HiRes qw(gettimeofday); |
|
6
|
|
|
|
|
9472
|
|
|
6
|
|
|
|
|
22
|
|
13
|
6
|
|
|
6
|
|
4436
|
use POSIX qw(strftime); |
|
6
|
|
|
|
|
32223
|
|
|
6
|
|
|
|
|
41
|
|
14
|
6
|
|
|
6
|
|
9128
|
use Scalar::Util qw(looks_like_number); |
|
6
|
|
|
|
|
10
|
|
|
6
|
|
|
|
|
343
|
|
15
|
6
|
|
|
6
|
|
39
|
use List::Util qw(first); |
|
6
|
|
|
|
|
12
|
|
|
6
|
|
|
|
|
363
|
|
16
|
6
|
|
|
6
|
|
2815
|
use Convert::Pheno::SQLite; |
|
6
|
|
|
|
|
18
|
|
|
6
|
|
|
|
|
927
|
|
17
|
|
|
|
|
|
|
binmode STDOUT, ':encoding(utf-8)'; |
18
|
6
|
|
|
6
|
|
49
|
use Exporter 'import'; |
|
6
|
|
|
|
|
21
|
|
|
6
|
|
|
|
|
362
|
|
19
|
|
|
|
|
|
|
our @EXPORT = |
20
|
|
|
|
|
|
|
qw(map_ethnicity map_ontology dotify_and_coerce_number iso8601_time _map2iso8601 map_reference_range map_age_range map2redcap_dict map2ohdsi convert2boolean find_age randStr map_operator_concept_id map_info_field map_omop_visit_occurrence dot_date2iso remap_mapping_hash); |
21
|
|
|
|
|
|
|
|
22
|
6
|
|
|
6
|
|
39
|
use constant DEVEL_MODE => 0; |
|
6
|
|
|
|
|
16
|
|
|
6
|
|
|
|
|
16255
|
|
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
# Global hash |
25
|
|
|
|
|
|
|
my %seen = (); |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
############################# |
28
|
|
|
|
|
|
|
############################# |
29
|
|
|
|
|
|
|
# SUBROUTINES FOR MAPPING # |
30
|
|
|
|
|
|
|
############################# |
31
|
|
|
|
|
|
|
############################# |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
sub map_ethnicity { |
34
|
|
|
|
|
|
|
|
35
|
40
|
|
|
40
|
0
|
69
|
my $str = shift; |
36
|
40
|
|
|
|
|
81
|
my %ethnicity = ( map { $_ => 'NCIT:C41261' } ( 'caucasian', 'white' ) ); |
|
80
|
|
|
|
|
241
|
|
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
# 1, Caucasian | 2, Hispanic | 3, Asian | 4, African/African-American | 5, Indigenous American | 6, Mixed | 9, Other"; |
39
|
40
|
|
|
|
|
230
|
return { id => $ethnicity{ lc($str) }, label => $str }; |
40
|
|
|
|
|
|
|
} |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
sub map_ontology { |
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
# Most of the execution time goes to this subroutine |
45
|
|
|
|
|
|
|
# We will adopt two estragies to gain speed: |
46
|
|
|
|
|
|
|
# 1 - Prepare once, excute often (almost no gain in speed :/ ) |
47
|
|
|
|
|
|
|
# 2 - Create a global hash with "seen" queries (+++huge gain) |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
#return { id => 'dummy', label => 'dummy' } # test speed |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
# Checking for existance in %seen |
52
|
81000
|
|
|
81000
|
0
|
128023
|
my $tmp_query = $_[0]->{query}; |
53
|
|
|
|
|
|
|
say "Skipping searching for <$tmp_query> as it already exists" |
54
|
81000
|
|
|
|
|
86762
|
if DEVEL_MODE && exists $seen{$tmp_query}; |
55
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
# return if terms has already been searched and exists |
57
|
|
|
|
|
|
|
# Not a big fan of global stuff... |
58
|
|
|
|
|
|
|
# ¯\_(ツ)_/¯ |
59
|
|
|
|
|
|
|
# Premature return |
60
|
81000
|
100
|
|
|
|
222498
|
return $seen{$tmp_query} if exists $seen{$tmp_query}; # global |
61
|
|
|
|
|
|
|
|
62
|
206
|
|
|
|
|
230
|
say "searching for <$tmp_query>" if DEVEL_MODE; |
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
# return something if we know 'a priori' that the query won't exist |
65
|
|
|
|
|
|
|
#return { id => 'NCIT:NA000', label => $tmp_query } if $tmp_query =~ m/xx/; |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
# Ok, now it's time to start the subroutine |
68
|
206
|
|
|
|
|
378
|
my $arg = shift; |
69
|
206
|
|
|
|
|
344
|
my $column = $arg->{column}; |
70
|
206
|
|
|
|
|
328
|
my $ontology = $arg->{ontology}; |
71
|
206
|
|
|
|
|
312
|
my $self = $arg->{self}; |
72
|
206
|
|
|
|
|
410
|
my $search = $self->{search}; |
73
|
206
|
|
|
|
|
416
|
my $print_hidden_labels = $self->{print_hidden_labels}; |
74
|
206
|
|
|
|
|
398
|
my $text_similarity_method = $self->{text_similarity_method}; |
75
|
206
|
|
|
|
|
326
|
my $min_text_similarity_score = $self->{min_text_similarity_score}; |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
# Die if user wants OHDSI w/o flag -ohdsi-db |
78
|
|
|
|
|
|
|
die |
79
|
|
|
|
|
|
|
"Could not find the concept_id:<$tmp_query> in the provided <CONCEPT> table.\nPlease use the flag <--ohdsi-db> to enable searching at Athena-OHDSI database\n" |
80
|
206
|
50
|
33
|
|
|
568
|
if ( $ontology eq 'ohdsi' && !$self->{ohdsi_db} ); |
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
# Perform query |
83
|
|
|
|
|
|
|
my ( $id, $label ) = get_ontology( |
84
|
|
|
|
|
|
|
{ |
85
|
206
|
|
|
|
|
1680
|
sth_column_ref => $self->{sth}{$ontology}{$column}, |
86
|
|
|
|
|
|
|
query => $tmp_query, |
87
|
|
|
|
|
|
|
ontology => $ontology, |
88
|
|
|
|
|
|
|
column => $column, |
89
|
|
|
|
|
|
|
search => $search, |
90
|
|
|
|
|
|
|
text_similarity_method => $text_similarity_method, |
91
|
|
|
|
|
|
|
min_text_similarity_score => $min_text_similarity_score |
92
|
|
|
|
|
|
|
} |
93
|
|
|
|
|
|
|
); |
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
# Add result to global %seen |
96
|
206
|
|
|
|
|
1657
|
$seen{$tmp_query} = { id => $id, label => $label }; # global |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
# id and label come from <db> _label is the original string (can change on partial matches) |
99
|
206
|
50
|
|
|
|
1409
|
return $print_hidden_labels |
100
|
|
|
|
|
|
|
? { id => $id, label => $label, _label => $tmp_query } |
101
|
|
|
|
|
|
|
: { id => $id, label => $label }; |
102
|
|
|
|
|
|
|
} |
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
sub dotify_and_coerce_number { |
105
|
|
|
|
|
|
|
|
106
|
1828645
|
|
|
1828645
|
0
|
1986931
|
my $val = shift; |
107
|
1828645
|
|
|
|
|
2174378
|
( my $tr_val = $val ) =~ tr/,/./; |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
# looks_like_number does not work with commas so we must tr first |
110
|
|
|
|
|
|
|
#say "$val === ", looks_like_number($val); |
111
|
|
|
|
|
|
|
# coercing to number $tr_val and avoiding value = "" |
112
|
|
|
|
|
|
|
return |
113
|
1828645
|
100
|
|
|
|
4354280
|
looks_like_number($tr_val) ? 0 + $tr_val |
|
|
100
|
|
|
|
|
|
114
|
|
|
|
|
|
|
: $val eq '' ? undef |
115
|
|
|
|
|
|
|
: $val; |
116
|
|
|
|
|
|
|
} |
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
sub iso8601_time { |
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
# Standard modules (gmtime()===>Coordinated Universal Time(UTC)) |
121
|
|
|
|
|
|
|
# NB: The T separates the date portion from the time-of-day portion. |
122
|
|
|
|
|
|
|
# The Z on the end means UTC (that is, an offset-from-UTC of zero hours-minutes-seconds). |
123
|
|
|
|
|
|
|
# - The Z is pronounced “Zulu”. |
124
|
1
|
|
|
1
|
0
|
10
|
my $now = time(); |
125
|
1
|
|
|
|
|
244
|
return strftime( '%Y-%m-%dT%H:%M:%SZ', gmtime($now) ); |
126
|
|
|
|
|
|
|
} |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
sub _map2iso8601 { |
129
|
|
|
|
|
|
|
|
130
|
69770
|
|
|
69770
|
|
395336
|
my ( $date, $time ) = split /\s+/, shift; |
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
# UTC |
133
|
69770
|
50
|
66
|
|
|
412343
|
return $date |
134
|
|
|
|
|
|
|
. ( ( defined $time && $time =~ m/^T(.+)Z$/ ) ? $time : 'T00:00:00Z' ); |
135
|
|
|
|
|
|
|
} |
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
sub map_reference_range { |
138
|
|
|
|
|
|
|
|
139
|
2785
|
|
|
2785
|
0
|
3612
|
my $arg = shift; |
140
|
2785
|
|
|
|
|
3700
|
my $field = $arg->{field}; |
141
|
2785
|
|
|
|
|
2982
|
my $redcap_dict = $arg->{redcap_dict}; |
142
|
2785
|
|
|
|
|
3101
|
my $unit = $arg->{unit}; |
143
|
2785
|
|
|
|
|
5457
|
my %hash = ( low => 'Text Validation Min', high => 'Text Validation Max' ); |
144
|
|
|
|
|
|
|
my $hashref = { |
145
|
|
|
|
|
|
|
unit => $unit, |
146
|
2785
|
|
|
|
|
4007
|
map { $_ => undef } qw(low high) |
|
5570
|
|
|
|
|
13102
|
|
147
|
|
|
|
|
|
|
}; # Initialize low,high to undef |
148
|
2785
|
|
|
|
|
4788
|
for my $range (qw (low high)) { |
149
|
|
|
|
|
|
|
$hashref->{$range} = |
150
|
5570
|
|
|
|
|
10200
|
dotify_and_coerce_number( $redcap_dict->{$field}{ $hash{$range} } ); |
151
|
|
|
|
|
|
|
} |
152
|
|
|
|
|
|
|
|
153
|
2785
|
|
|
|
|
12345
|
return $hashref; |
154
|
|
|
|
|
|
|
} |
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
sub map_age_range { |
157
|
|
|
|
|
|
|
|
158
|
80
|
|
|
80
|
0
|
167
|
my $str = shift; |
159
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
# Premature return if not range |
161
|
80
|
50
|
|
|
|
622
|
return { age => |
162
|
|
|
|
|
|
|
{ iso8601duration => 'P' . dotify_and_coerce_number($str) . 'Y' } } |
163
|
|
|
|
|
|
|
unless $str =~ m/\-|\+/; |
164
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
# if range |
166
|
80
|
|
|
|
|
208
|
$str =~ s/\+/\-999/; # from '70+' '70-999' |
167
|
80
|
|
|
|
|
307
|
my ( $start, $end ) = split /\-/, $str; |
168
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
return { |
170
|
80
|
|
|
|
|
228
|
ageRange => { |
171
|
|
|
|
|
|
|
start => { |
172
|
|
|
|
|
|
|
iso8601duration => 'P' . dotify_and_coerce_number($start) . 'Y' |
173
|
|
|
|
|
|
|
}, |
174
|
|
|
|
|
|
|
end => |
175
|
|
|
|
|
|
|
{ iso8601duration => 'P' . dotify_and_coerce_number($end) . 'Y' } |
176
|
|
|
|
|
|
|
} |
177
|
|
|
|
|
|
|
}; |
178
|
|
|
|
|
|
|
} |
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
sub map2redcap_dict { |
181
|
|
|
|
|
|
|
|
182
|
20670
|
|
|
20670
|
0
|
24295
|
my $arg = shift; |
183
|
|
|
|
|
|
|
my ( $redcap_dict, $participant, $field, $labels ) = ( |
184
|
|
|
|
|
|
|
$arg->{redcap_dict}, $arg->{participant}, |
185
|
|
|
|
|
|
|
$arg->{field}, $arg->{labels} |
186
|
20670
|
|
|
|
|
32901
|
); |
187
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
# Options: |
189
|
|
|
|
|
|
|
# labels = 1 |
190
|
|
|
|
|
|
|
# _labels |
191
|
|
|
|
|
|
|
# labels = 0 |
192
|
|
|
|
|
|
|
# 'Field Note' |
193
|
|
|
|
|
|
|
return $labels |
194
|
|
|
|
|
|
|
? $redcap_dict->{$field}{_labels}{ $participant->{$field} } |
195
|
20670
|
100
|
|
|
|
74137
|
: $redcap_dict->{$field}{'Field Note'}; |
196
|
|
|
|
|
|
|
} |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
sub map2ohdsi { |
199
|
|
|
|
|
|
|
|
200
|
139999
|
|
|
139999
|
0
|
181479
|
my $arg = shift; |
201
|
|
|
|
|
|
|
my ( $ohdsi_dic, $concept_id, $self ) = |
202
|
139999
|
|
|
|
|
229752
|
( $arg->{ohdsi_dic}, $arg->{concept_id}, $arg->{self} ); |
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
####################### |
205
|
|
|
|
|
|
|
# OPTION A: <CONCEPT> # |
206
|
|
|
|
|
|
|
####################### |
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
# NB1: Here we don't win any speed over using %seen as ... |
209
|
|
|
|
|
|
|
# .. we are already searching in a hash |
210
|
|
|
|
|
|
|
# NB2: $concept_id is stringified by hash |
211
|
139999
|
|
|
|
|
244772
|
my ( $data, $id, $label, $vocabulary ) = ( (undef) x 4 ); |
212
|
139999
|
50
|
|
|
|
290136
|
if ( exists $ohdsi_dic->{$concept_id} ) { |
213
|
139999
|
|
|
|
|
239639
|
$id = $ohdsi_dic->{$concept_id}{concept_code}; |
214
|
139999
|
|
|
|
|
191160
|
$label = $ohdsi_dic->{$concept_id}{concept_name}; |
215
|
139999
|
|
|
|
|
190973
|
$vocabulary = $ohdsi_dic->{$concept_id}{vocabulary_id}; |
216
|
139999
|
|
|
|
|
351442
|
$data = { id => qq($vocabulary:$id), label => $label }; |
217
|
|
|
|
|
|
|
} |
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
###################### |
220
|
|
|
|
|
|
|
# OPTION B: External # |
221
|
|
|
|
|
|
|
###################### |
222
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
else { |
224
|
0
|
|
|
|
|
0
|
$data = map_ontology( |
225
|
|
|
|
|
|
|
{ |
226
|
|
|
|
|
|
|
query => $concept_id, |
227
|
|
|
|
|
|
|
column => 'concept_id', |
228
|
|
|
|
|
|
|
ontology => 'ohdsi', |
229
|
|
|
|
|
|
|
self => $self |
230
|
|
|
|
|
|
|
} |
231
|
|
|
|
|
|
|
); |
232
|
|
|
|
|
|
|
} |
233
|
139999
|
|
|
|
|
288263
|
return $data; |
234
|
|
|
|
|
|
|
} |
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
sub convert2boolean { |
237
|
|
|
|
|
|
|
|
238
|
40
|
|
|
40
|
0
|
113
|
my $val = lc(shift); |
239
|
|
|
|
|
|
|
return |
240
|
40
|
50
|
66
|
|
|
374
|
( $val eq 'true' || $val eq 'yes' ) ? JSON::XS::true |
|
|
100
|
33
|
|
|
|
|
241
|
|
|
|
|
|
|
: ( $val eq 'false' || $val eq 'no' ) ? JSON::XS::false |
242
|
|
|
|
|
|
|
: undef; # unknown = undef |
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
} |
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
sub find_age { |
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
# Not using any CPAN module for now |
249
|
|
|
|
|
|
|
# Adapted from https://www.perlmonks.org/?node_id=9995 |
250
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
# Assuming $birth_month is 0..11 |
252
|
70059
|
|
|
70059
|
0
|
88164
|
my $arg = shift; |
253
|
70059
|
|
|
|
|
83245
|
my $birth = $arg->{birth_day}; |
254
|
70059
|
|
|
|
|
79838
|
my $date = $arg->{date}; |
255
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
# Not a big fan of premature return, but it works here... |
257
|
|
|
|
|
|
|
# ¯\_(ツ)_/¯ |
258
|
70059
|
50
|
33
|
|
|
213029
|
return unless ( $birth && $date ); |
259
|
|
|
|
|
|
|
|
260
|
70059
|
|
|
|
|
556335
|
my ( $birth_year, $birth_month, $birth_day ) = |
261
|
|
|
|
|
|
|
( split /\-|\s+/, $birth )[ 0 .. 2 ]; |
262
|
70059
|
|
|
|
|
207900
|
my ( $year, $month, $day ) = ( split /\-/, $date )[ 0 .. 2 ]; |
263
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
#my ($day, $month, $year) = (localtime)[3..5]; |
265
|
|
|
|
|
|
|
#$year += 1900; |
266
|
|
|
|
|
|
|
|
267
|
70059
|
|
|
|
|
157146
|
my $age = $year - $birth_year; |
268
|
70059
|
100
|
|
|
|
317738
|
$age-- |
269
|
|
|
|
|
|
|
unless sprintf( "%02d%02d", $month, $day ) >= |
270
|
|
|
|
|
|
|
sprintf( "%02d%02d", $birth_month, $birth_day ); |
271
|
70059
|
|
|
|
|
324109
|
return $age . 'Y'; |
272
|
|
|
|
|
|
|
} |
273
|
|
|
|
|
|
|
|
274
|
|
|
|
|
|
|
sub randStr { |
275
|
|
|
|
|
|
|
|
276
|
|
|
|
|
|
|
#https://www.perlmonks.org/?node_id=233023 |
277
|
|
|
|
|
|
|
return join( '', |
278
|
1
|
|
|
1
|
0
|
5
|
map { ( 'a' .. 'z', 'A' .. 'Z', 0 .. 9 )[ rand 62 ] } 0 .. shift ); |
|
9
|
|
|
|
|
28
|
|
279
|
|
|
|
|
|
|
} |
280
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
sub map_operator_concept_id { |
282
|
|
|
|
|
|
|
|
283
|
0
|
|
|
0
|
0
|
0
|
my $arg = shift; |
284
|
0
|
|
|
|
|
0
|
my $id = $arg->{operator_concept_id}; |
285
|
0
|
|
|
|
|
0
|
my $val = $arg->{value_as_number}; |
286
|
0
|
|
|
|
|
0
|
my $unit = $arg->{unit}; |
287
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
# Define hash for possible values |
289
|
0
|
|
|
|
|
0
|
my %operator_concept_id = ( 4172704 => 'GT', 4172756 => 'LT' ); |
290
|
|
|
|
|
|
|
|
291
|
|
|
|
|
|
|
# 4172703 => 'EQ'; |
292
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
# $hasref will be used for return |
294
|
0
|
|
|
|
|
0
|
my $hashref = undef; |
295
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
# Only for GT || LT |
297
|
0
|
0
|
|
|
|
0
|
if ( exists $operator_concept_id{$id} ) { |
298
|
|
|
|
|
|
|
$hashref = { |
299
|
|
|
|
|
|
|
unit => $unit, |
300
|
0
|
|
|
|
|
0
|
map { $_ => undef } qw(low high) |
|
0
|
|
|
|
|
0
|
|
301
|
|
|
|
|
|
|
}; # Initialize low,high to undef |
302
|
0
|
0
|
|
|
|
0
|
if ( $operator_concept_id{$id} eq 'GT' ) { |
303
|
0
|
|
|
|
|
0
|
$hashref->{high} = dotify_and_coerce_number($val); |
304
|
|
|
|
|
|
|
} |
305
|
|
|
|
|
|
|
else { |
306
|
0
|
|
|
|
|
0
|
$hashref->{low} = dotify_and_coerce_number($val); |
307
|
|
|
|
|
|
|
} |
308
|
|
|
|
|
|
|
} |
309
|
0
|
|
|
|
|
0
|
return $hashref; |
310
|
|
|
|
|
|
|
} |
311
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
sub map_omop_visit_occurrence { |
313
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
# key eq 'visit_occurrence_id' |
315
|
|
|
|
|
|
|
# { '85' => |
316
|
|
|
|
|
|
|
# { |
317
|
|
|
|
|
|
|
# 'admitting_source_concept_id' => 0, |
318
|
|
|
|
|
|
|
# 'admitting_source_value' => undef, |
319
|
|
|
|
|
|
|
# 'care_site_id' => '\\N', |
320
|
|
|
|
|
|
|
# 'discharge_to_concept_id' => 0, |
321
|
|
|
|
|
|
|
# 'discharge_to_source_value' => undef, |
322
|
|
|
|
|
|
|
# 'person_id' => 1, |
323
|
|
|
|
|
|
|
# 'preceding_visit_occurrence_id' => 82, |
324
|
|
|
|
|
|
|
# 'provider_id' => '\\N', |
325
|
|
|
|
|
|
|
# 'visit_concept_id' => 9201, |
326
|
|
|
|
|
|
|
# 'visit_end_date' => '1981-08-19', |
327
|
|
|
|
|
|
|
# 'visit_end_datetime' => '1981-08-19 00:00:00', |
328
|
|
|
|
|
|
|
# 'visit_occurrence_id' => 85, |
329
|
|
|
|
|
|
|
# 'visit_source_concept_id' => 0, |
330
|
|
|
|
|
|
|
# 'visit_source_value' => '7879d5b2-1af2-49a7-a801-121de124c6af', |
331
|
|
|
|
|
|
|
# 'visit_start_date' => '1981-08-18', |
332
|
|
|
|
|
|
|
# 'visit_start_datetime' => '1981-08-18 00:00:00', |
333
|
|
|
|
|
|
|
# 'visit_type_concept_id' => 44818517 |
334
|
|
|
|
|
|
|
# } |
335
|
|
|
|
|
|
|
# } |
336
|
|
|
|
|
|
|
|
337
|
69193
|
|
|
69193
|
0
|
98965
|
my $arg = shift; |
338
|
69193
|
|
|
|
|
88908
|
my $self = $arg->{self}; |
339
|
69193
|
|
|
|
|
76523
|
my $ohdsi_dic = $arg->{ohdsi_dic}; |
340
|
69193
|
|
|
|
|
82384
|
my $person_id = $arg->{person_id}; |
341
|
69193
|
|
|
|
|
78039
|
my $visit_occurrence_id = $arg->{visit_occurrence_id}; |
342
|
69193
|
|
|
|
|
86489
|
my $visit_occurrence = $self->{visit_occurrence}; |
343
|
|
|
|
|
|
|
|
344
|
|
|
|
|
|
|
# Premature return |
345
|
69193
|
100
|
|
|
|
120451
|
return undef if $visit_occurrence_id eq '\\N'; # perlcritic Severity: 5 |
346
|
|
|
|
|
|
|
|
347
|
|
|
|
|
|
|
# *** IMPORTANT *** |
348
|
|
|
|
|
|
|
# EUNOMIA instance has mismatches between the person_id -- visit_occurrence_id |
349
|
|
|
|
|
|
|
# For instance, person_id = 1 has only visit_occurrence_id = 85, but on tables it has: |
350
|
|
|
|
|
|
|
# 82, 84, 42, 54, 41, 25, 76 and 81 |
351
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
# warn if we don't have $visit_occurrence_id in VISIT_OCURRENCE |
353
|
68663
|
100
|
|
|
|
177945
|
unless ( exists $visit_occurrence->{$visit_occurrence_id} ) { |
354
|
68414
|
|
|
|
|
65592
|
warn |
355
|
|
|
|
|
|
|
"Sorry, but <visit_occurrence_id:$visit_occurrence_id> does not exist for <person_id:$person_id>\n" |
356
|
|
|
|
|
|
|
if DEVEL_MODE; |
357
|
|
|
|
|
|
|
|
358
|
|
|
|
|
|
|
# Premature return |
359
|
68414
|
|
|
|
|
148561
|
return undef; # perlcritic Severity: 5 |
360
|
|
|
|
|
|
|
} |
361
|
|
|
|
|
|
|
|
362
|
|
|
|
|
|
|
# Getting pointer to the hash element |
363
|
249
|
|
|
|
|
913
|
my $hashref = $visit_occurrence->{$visit_occurrence_id}; |
364
|
|
|
|
|
|
|
|
365
|
|
|
|
|
|
|
my $concept = map2ohdsi( |
366
|
|
|
|
|
|
|
{ |
367
|
|
|
|
|
|
|
ohdsi_dic => $ohdsi_dic, |
368
|
|
|
|
|
|
|
concept_id => $hashref->{visit_concept_id}, |
369
|
249
|
|
|
|
|
1469
|
self => $self |
370
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
} |
372
|
|
|
|
|
|
|
); |
373
|
|
|
|
|
|
|
|
374
|
|
|
|
|
|
|
# *** IMPORTANT *** |
375
|
|
|
|
|
|
|
# Ad hoc to avoid using --ohdsi-db while we find a solution to EUNOMIA not being self-contained |
376
|
249
|
|
|
|
|
933
|
my $ad_hoc_44818517 = { |
377
|
|
|
|
|
|
|
id => "Visit Type:OMOP4822465", |
378
|
|
|
|
|
|
|
label => "Visit derived from encounter on claim" |
379
|
|
|
|
|
|
|
}; |
380
|
|
|
|
|
|
|
my $type = |
381
|
|
|
|
|
|
|
$hashref->{visit_type_concept_id} == 44818517 |
382
|
|
|
|
|
|
|
? $ad_hoc_44818517 |
383
|
|
|
|
|
|
|
: map2ohdsi( |
384
|
|
|
|
|
|
|
{ |
385
|
|
|
|
|
|
|
ohdsi_dic => $ohdsi_dic, |
386
|
|
|
|
|
|
|
concept_id => $hashref->{visit_type_concept_id}, |
387
|
249
|
50
|
|
|
|
1030
|
self => $self |
388
|
|
|
|
|
|
|
|
389
|
|
|
|
|
|
|
} |
390
|
|
|
|
|
|
|
); |
391
|
249
|
|
|
|
|
671
|
my $start_date = _map2iso8601( $hashref->{visit_start_date} ); |
392
|
249
|
|
|
|
|
720
|
my $end_date = _map2iso8601( $hashref->{visit_end_date} ); |
393
|
249
|
|
|
|
|
984
|
my $info = { VISIT_OCCURENCE => { OMOP_columns => $hashref } }; |
394
|
|
|
|
|
|
|
|
395
|
|
|
|
|
|
|
return { |
396
|
|
|
|
|
|
|
_info => $info, |
397
|
|
|
|
|
|
|
id => $visit_occurrence_id, |
398
|
|
|
|
|
|
|
concept => $concept, |
399
|
|
|
|
|
|
|
type => $type, |
400
|
|
|
|
|
|
|
start_date => $start_date, |
401
|
|
|
|
|
|
|
end_date => $end_date, |
402
|
|
|
|
|
|
|
occurrence_id => $hashref->{visit_occurrence_id} |
403
|
249
|
|
|
|
|
2084
|
}; |
404
|
|
|
|
|
|
|
} |
405
|
|
|
|
|
|
|
|
406
|
|
|
|
|
|
|
sub dot_date2iso { |
407
|
|
|
|
|
|
|
|
408
|
|
|
|
|
|
|
# We can get |
409
|
|
|
|
|
|
|
# '', '1990.12.25', '1990-12-25' |
410
|
330
|
|
100
|
330
|
0
|
755
|
my $date = shift // ''; |
411
|
|
|
|
|
|
|
|
412
|
|
|
|
|
|
|
# Premature returns |
413
|
330
|
100
|
|
|
|
639
|
return '1900-01-01' if $date eq ''; |
414
|
255
|
100
|
|
|
|
966
|
return $date if $date =~ m/^(\d{4})\-(\d{2})\-(\d{2})$/; |
415
|
|
|
|
|
|
|
|
416
|
|
|
|
|
|
|
# Split '1990.12.25' |
417
|
153
|
|
|
|
|
514
|
my ( $d, $m, $y ) = split /\./, $date; |
418
|
|
|
|
|
|
|
|
419
|
|
|
|
|
|
|
# YYYYMMDD |
420
|
153
|
|
|
|
|
497
|
return qq/$y-$m-$d/; |
421
|
|
|
|
|
|
|
} |
422
|
|
|
|
|
|
|
|
423
|
|
|
|
|
|
|
sub is_multidimensional { |
424
|
|
|
|
|
|
|
|
425
|
0
|
0
|
|
0
|
0
|
0
|
return ref shift ? 1 : 0; |
426
|
|
|
|
|
|
|
} |
427
|
|
|
|
|
|
|
|
428
|
|
|
|
|
|
|
sub remap_mapping_hash { |
429
|
|
|
|
|
|
|
|
430
|
1260
|
|
|
1260
|
0
|
2114
|
my ( $mapping_file, $term ) = @_; |
431
|
|
|
|
|
|
|
my %hash_out = map { |
432
|
1260
|
|
|
|
|
1794
|
$_, exists $mapping_file->{$term}{$_} |
433
|
5040
|
100
|
|
|
|
12928
|
? $mapping_file->{$term}{$_} |
434
|
|
|
|
|
|
|
: undef |
435
|
|
|
|
|
|
|
} (qw/fields dict map radio/); |
436
|
|
|
|
|
|
|
$hash_out{ontology} = |
437
|
|
|
|
|
|
|
exists $mapping_file->{$term}{ontology} |
438
|
|
|
|
|
|
|
? $mapping_file->{$term}{ontology} |
439
|
1260
|
100
|
|
|
|
3521
|
: $mapping_file->{project}{ontology}; |
440
|
|
|
|
|
|
|
$hash_out{routesOfAdministration} = |
441
|
|
|
|
|
|
|
$mapping_file->{$term}{routesOfAdministration} |
442
|
1260
|
100
|
|
|
|
2653
|
if $term eq 'treatments'; |
443
|
1260
|
|
|
|
|
3323
|
return \%hash_out; |
444
|
|
|
|
|
|
|
} |
445
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
1; |