Skip to content

Commit 7af6c6b

Browse files
committed
Merge branch 'release/1.5.0'
2 parents 431390f + e6ea9e9 commit 7af6c6b

9 files changed

+209
-45
lines changed

CHANGELOG

+38
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,43 @@
11
Changelog
22
=========
3+
## [1.5.0] - 2020-12-08
4+
### Summary
5+
6+
This version introduces several smaller maintenance-level updates.
7+
The FTLD builder files now function similar to the UDS3 module builders,
8+
where NACCulator will first scan the Z1X to see which forms are present
9+
in the record rather than automatically trying to process all possible
10+
forms. This will prevent users from having to repeatedly manually delete
11+
empty optional forms that will never be filled out.
12+
13+
NACCulator's event detection has been generalized to be more compatible
14+
with more centers. The requirement for the initial visit packet's event
15+
name to contain "initial_visit" has been changed to "initial", and the
16+
requirement for the followup visit packet's event event name to contain
17+
"followup_visit" has been changed to just "follow" (to account for variants
18+
with either "followup" OR "follow_up").
19+
20+
The README has been updated to include more information about what data
21+
NACCulator needs from REDCap in order to run correctly (such as the filled
22+
Z1X form), and updated documentation on running the filters. I corrected the
23+
example commands for running the individual filters (with an input csv AND an
24+
output csv) so that they work properly now. All filter commands now require an
25+
argument specifying the config file so that the "validate" function in
26+
filters.py works properly. This makes the command structure consistent across
27+
all filters.
28+
29+
### added
30+
* Add note about Z1X to README
31+
* Add optional form logic to fvp builder
32+
* Add optional form logic to ftld ivp builder
33+
34+
### changed
35+
* Fix typo in README
36+
* Edit NACCulator capitalization to be consistent within README
37+
* Update documentation on filters and adjust example config file to make it easier to comment/uncomment filters
38+
* Fixed typo fukid9agd and fusib17pdx to have underscores in uds fvp builder
39+
* Update compatible redcap_event_names to be more general (initial, follow)
40+
341
## [1.4.0] - 2020-10-21
442
### Summary
543

README.md

+32-10
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,22 @@ Once the project data is exported from REDCap to the CSV file `data.csv`, run:
2121
$ redcap2nacc <data.csv >data.txt
2222

2323
This command will work only in the simplest case; UDS3 IVP data only.
24-
Nacculator will automatically skip PTIDs with errors, so the output `data.txt`
24+
NACCulator will automatically skip PTIDs with errors, so the output `data.txt`
2525
file will be ready to submit to NACC.
26-
In order to properly filter the data in the csv, nacculator is expecting that
26+
In order to properly filter the data in the csv, NACCulator is expecting that
2727
REDCap visits (denoted by `redcap_event_name`) contain certain keywords:
28-
"initial_visit" for initial visit packets,
29-
"followup_visit" for all followups,
28+
"initial" for initial visit packets,
29+
"follow" for all followups,
3030
"milestone" for milestone packets,
3131
"neuropath" for neuropathology packets,
3232
"telephone" for telephone followup packets
3333

34+
NACCulator collects data from the Z1X form first and uses that to determine the
35+
presence of other forms in the packet. The Z1X form for that record must be
36+
marked "Unverified" or "Complete" for NACCulator to recognize the record, and
37+
each optional form must be marked as submitted within the Z1X for NACCulator to
38+
find those forms.
39+
3440
_Note: output is written to `STDOUT`; errors are written to `STDERR`; input is
3541
expected to be from `STDIN` (the command line) unless a file is specified using
3642
the `-file` flag._
@@ -80,7 +86,7 @@ Both LBD / LBDSV and FTLD forms can have IVP or FVP arguments.
8086

8187
**Example** - Run data through the `cleanPtid` filter:
8288

83-
$ redcap2nacc -f cleanPtid -meta nacculator_cfg.ini <data.csv >data.txt
89+
$ redcap2nacc -f cleanPtid -meta nacculator_cfg.ini <data.csv >filtered_data.csv
8490

8591

8692
HOW TO Filter Data Using NACCulator
@@ -96,12 +102,26 @@ remove the `.example` portion, and then fill in your center's information.
96102
The config file contains sections with in-code filter function name. Each of
97103
these sections contains elements necessary for the filter to run.
98104
The filters described below will discuss what is required, if anything.
105+
106+
The filters can be run all at once with your REDCap API token using:
107+
108+
$ nacculator_filters nacculator_cfg.ini
109+
110+
You can find more details on `nacculator_filters` under the section:
111+
HOW TO Acquire current-db-subjects.csv for the filters
112+
113+
Or they can be run one at a time on a `.csv` file with the `-f` and `-meta`
114+
flags.
115+
For example, to run the fixHeaders filter:
116+
117+
$ redcap2nacc -f fixHeaders -meta nacculator_cfg.ini <data_input.csv >filtered_output.csv
118+
99119
If the filter requires the config, it must be passed with the `-meta` flag like
100120
the example above shows.
101121

122+
102123
* **cleanPtid**
103124

104-
**Filter config required**
105125
This filter requires a section in the config called `filter_clean_ptid`. This
106126
section will contain a single key `filepath` which will point to a csv
107127
(usually called `current-db-subjects.csv`) file of ptids to be removed. All
@@ -117,16 +137,15 @@ the example above shows.
117137
110003,I,001,Current
118138
110003,F,002,Current
119139

140+
120141
* **replaceDrugId**
121142

122143
This filter replaces the first character of non empty fields of columns
123144
`drugid_1` to `drugid_30` with character "**d**".
124145

125-
This filter does not require any meta data file as of now.
126146

127147
* **fixHeaders**
128148

129-
**Filter config required**
130149
This filter requires a section in the config called `filter_fix_headers` with
131150
as many keys as needed to replace the necessary columns. See example below.
132151
This filter fixes the column names of any column found in the filter mapping.
@@ -142,6 +161,8 @@ the example above shows.
142161
fu_otherneur: fu_othneur
143162
fu_otherneurx: fu_othneurxs
144163
fu_strokedec: fu_strokdec
164+
fukid9agd: fu_kid9agd
165+
fusib17pdx: fu_sib17pdx
145166

146167

147168
* **fillDefault**
@@ -154,14 +175,15 @@ the example above shows.
154175

155176
*If field is blank, it will be updated to default value.*
156177

178+
157179
* **updateField**
158180

159181
This filter is used to update fields that already had a value in the REDCap
160182
export. Currently, only `adcid` is updated.
161183

184+
162185
* **removePtid**
163186

164-
**Filter config required**
165187
This filter requires a section in the config called `filter_remove_ptid` with
166188
a single key called `ptid_format`. The value for that key is a regex string
167189
to match ptids that are to be kept.
@@ -211,7 +233,7 @@ move it to whatever location you specified in your `nacculator_cfg.ini` file.
211233

212234
The csv is used by the filter_clean_ptid filter to identify and cull all
213235
packets already in NACC's Current database from your input csv. It is used to
214-
make nacculator run faster for very large databases.
236+
make NACCulator run faster for very large databases.
215237

216238

217239
Example Workflow

nacc/ftld/fvp/builder.py

+56-6
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
###############################################################################
2-
# Copyright 2015-2019 University of Florida. All rights reserved.
2+
# Copyright 2015-2020 University of Florida. All rights reserved.
33
# This file is part of UF CTS-IT's NACCulator project.
44
# Use of this source code is governed by the license found in the LICENSE file.
55
###############################################################################
66

7+
import sys
8+
79
from nacc.ftld.fvp import forms as ftld_fvp_forms
810
from nacc.uds3 import packet as ftld_fvp_packet
911

1012

11-
def build_ftld_fvp_form(record: dict):
13+
def build_ftld_fvp_form(record: dict, err=sys.stderr):
1214
''' Converts REDCap CSV data into a packet (list of FVP Form objects) '''
1315
packet = ftld_fvp_packet.Packet()
1416

@@ -21,6 +23,35 @@ def build_ftld_fvp_form(record: dict):
2123
and int(record['visitday']) >= 1):
2224
raise ValueError('Form date cannot precede March 1, 2015.')
2325

26+
add_z1x(record, packet)
27+
# Forms B3F, B9F, C1F, C2F, C3F, E2F, and E3F are REQUIRED.
28+
# Forms A3A, C4F, C5F, and C6F are OPTIONAL and must be specifically
29+
# marked as present for nacculator to process them
30+
if record['ivp_z1x_complete'] in ['1', '2']:
31+
if record['ftda3afs'] == '1':
32+
add_a3a(record, packet)
33+
add_b3f(record, packet)
34+
add_b9f(record, packet)
35+
add_c1f(record, packet)
36+
add_c2f(record, packet)
37+
add_c3f(record, packet)
38+
if record['ftdc4fs'] == '1':
39+
add_c4f(record, packet)
40+
if record['ftdc5fs'] == '1':
41+
add_c5f(record, packet)
42+
if record['ftdc6fs'] == '1':
43+
add_c6f(record, packet)
44+
else:
45+
print("ptid " + str(record['ptid']) +
46+
": No Z1X form found.", file=err)
47+
add_e2f(record, packet)
48+
add_e3f(record, packet)
49+
update_header(record, packet)
50+
51+
return packet
52+
53+
54+
def add_z1x(record, packet):
2455
Z1X = ftld_fvp_forms.FormZ1X()
2556
Z1X.LANGA1 = record['fu_langa1']
2657
Z1X.LANGA2 = record['fu_langa2']
@@ -71,8 +102,10 @@ def build_ftld_fvp_form(record: dict):
71102
Z1X.LANGE3F = record['fu_lange3f']
72103
Z1X.LANGCLS = record['fu_langcls']
73104
Z1X.CLSSUB = record['fu_clssub']
74-
packet.append(Z1X)
105+
packet.insert(0, Z1X)
75106

107+
108+
def add_a3a(record, packet):
76109
A3a = ftld_fvp_forms.FormA3a()
77110
A3a.FTDRELCO = record['fu_ftdrelco']
78111
A3a.FTDSIBBY = record['fu_ftdsibby']
@@ -82,6 +115,8 @@ def build_ftld_fvp_form(record: dict):
82115
A3a.FTDCOMME = record['fu_ftdcomme']
83116
packet.append(A3a)
84117

118+
119+
def add_b3f(record, packet):
85120
B3F = ftld_fvp_forms.FormB3F()
86121
B3F.FTDLTFAS = record['fu_ftdltfas']
87122
B3F.FTDLIMB = record['fu_ftdlimb']
@@ -93,6 +128,8 @@ def build_ftld_fvp_form(record: dict):
93128
B3F.FTDGTYPX = record['fu_ftdgtypx']
94129
packet.append(B3F)
95130

131+
132+
def add_b9f(record, packet):
96133
B9F = ftld_fvp_forms.FormB9F()
97134
B9F.FTDPPASL = record['fu_ftdppasl']
98135
B9F.FTDPPAPO = record['fu_ftdppapo']
@@ -122,6 +159,8 @@ def build_ftld_fvp_form(record: dict):
122159
B9F.FTDPABVF = record['fu_ftdpabvf']
123160
packet.append(B9F)
124161

162+
163+
def add_c1f(record, packet):
125164
C1F = ftld_fvp_forms.FormC1F()
126165
C1F.FTDWORRC = record['fu_ftdworrc']
127166
C1F.FTDWORRS = record['fu_ftdworrs']
@@ -150,6 +189,8 @@ def build_ftld_fvp_form(record: dict):
150189
C1F.FTDREAPR = record['fu_ftdreapr']
151190
packet.append(C1F)
152191

192+
193+
def add_c2f(record, packet):
153194
C2F = ftld_fvp_forms.FormC2F()
154195
C2F.FTDCPC2F = record['fu_ftdcpc2f']
155196
C2F.FTDhAIRD = record['fu_ftdhaird']
@@ -180,6 +221,8 @@ def build_ftld_fvp_form(record: dict):
180221
C2F.FTDSNRAT = record['fu_ftdsnrat']
181222
packet.append(C2F)
182223

224+
225+
def add_c3f(record, packet):
183226
C3F = ftld_fvp_forms.FormC3F()
184227
C3F.FTDSELF = record['fu_ftdself']
185228
C3F.FTDBADLY = record['fu_ftdbadly']
@@ -235,6 +278,8 @@ def build_ftld_fvp_form(record: dict):
235278
C3F.FTDLENGT = record['fu_ftdlengt']
236279
packet.append(C3F)
237280

281+
282+
def add_c4f(record, packet):
238283
C4F = ftld_fvp_forms.FormC4F()
239284
C4F.FTDCPC4F = record['fu_ftdcpc4f']
240285
C4F.FTDWORKU = record['fu_ftdworku']
@@ -247,6 +292,8 @@ def build_ftld_fvp_form(record: dict):
247292
C4F.FTDBIST = record['fu_ftdbist']
248293
packet.append(C4F)
249294

295+
296+
def add_c5f(record, packet):
250297
C5F = ftld_fvp_forms.FormC5F()
251298
C5F.FTDCPC5F = record['fu_ftdcpc5f']
252299
C5F.FTDINSEX = record['fu_ftdinsex']
@@ -271,6 +318,8 @@ def build_ftld_fvp_form(record: dict):
271318
C5F.FTDIRIPT = record['fu_ftdiript']
272319
packet.append(C5F)
273320

321+
322+
def add_c6f(record, packet):
274323
C6F = ftld_fvp_forms.FormC6F()
275324
C6F.FTDCPC6F = record['fu_ftdcpc6f']
276325
C6F.FTDALTER = record['fu_ftdalter']
@@ -291,6 +340,8 @@ def build_ftld_fvp_form(record: dict):
291340
C6F.FTDRSMST = record['fu_ftdrsmst']
292341
packet.append(C6F)
293342

343+
344+
def add_e2f(record, packet):
294345
E2F = ftld_fvp_forms.FormE2F()
295346
E2F.FTDSMRI = record['fu_ftdsmri']
296347
E2F.FTDSMMO = record['fu_ftdsmmo']
@@ -340,6 +391,8 @@ def build_ftld_fvp_form(record: dict):
340391
E2F.FTDOTANS = record['fu_ftdotans']
341392
packet.append(E2F)
342393

394+
395+
def add_e3f(record, packet):
343396
E3F = ftld_fvp_forms.FormE3F()
344397
E3F.FTDIDIAG = record['fu_ftdidiag']
345398
E3F.FTDSMRIO = record['fu_ftdsmrio']
@@ -402,9 +455,6 @@ def build_ftld_fvp_form(record: dict):
402455
E3F.FTDOThIS = record['fu_ftdothis']
403456
packet.append(E3F)
404457

405-
update_header(record,packet)
406-
return packet
407-
408458

409459
def update_header(record, packet):
410460
for header in packet:

0 commit comments

Comments
 (0)