Skip to content

Commit

Permalink
Merge branch 'release/1.10.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
mbentz-uf committed Aug 31, 2022
2 parents a4e3310 + 3eb8f9c commit 51deedf
Show file tree
Hide file tree
Showing 10 changed files with 188 additions and 19 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,7 @@ build/

# Anything else is probably a preference. Think twice and then ask three people
# before adding something else.

run_*/
nacculator_cfg.ini
.DS_STORE
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ Contributors at the University of Florida

* Tarun Gupta Akirala
* Christopher P. Barnes
* Michael Bentz
* Naomi Braun
* Philip Chase
* Samantha Emerson
Expand Down
14 changes: 14 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
Changelog
=========
## [1.10.0] - 2022-08-31
### Summary
This release introduces minor fixes to the FTLD and LBD Short Version modules.

### Added
* Add missing field for #3 in LBD Short Version FVP form E2L in the builder file (Samantha Emerson)
* Add ability to run filters on a local redcap export csv rather than automatically downloading via redcap api (Samantha Emerson)

### Changed
* Update gitignore (mbentz-uf)
* Update allowable_favlues on FTDRATIO in ftld forms.py files to account for error code (Samantha Emerson)
* Re-adjust Z1X spacing on FVP LBDSV to account for invisible deprecated C1 form (Samantha Emerson)
* Update Z1X positions in forms.py for lbd short version (Samantha Emerson)

## [1.9.0] - 2022-06-24

### Summary
Expand Down
31 changes: 30 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,37 @@ The filters can be run all at once with your REDCap API token using:
You can find more details on `nacculator_filters` under the section:
HOW TO Acquire current-db-subjects.csv for the filters

Or they can be run one at a time on a `.csv` file with the `-f` and `-meta`

RUNNING ALL FILTERS ON A LOCAL FILE
------------------------------------------------------

REDCap has an export size limit that can be exceeded with a large project like
the ADRC. When the size of the project surpasses the REDCap limit, the
`nacculator_filters` command will no longer work. The data must be manually
exported from the project in chunks (whether by event or by ptid). However you
choose to export the data, keep in mind that all of the fields in a packet need
to be present in the input csv you use. So, for example, the A1 and A2 forms in
the IVP cannot be exported and run separately through NACCulator.

You can still run all the filters using your config file on a REDCap-exported
csv, even when not using `nacculator_filters`. The command to use this filter
locally is:

$ python3 nacc/local_filters.py nacculator_cfg.ini redcap_input.csv

where `redcap_input.csv` is the location of the file you want to filter. The
filter will then run as normal, creating a `run_CURRENT-DATE` folder and
depositing each stage of the filter process in this folder. The final output
of the filter process is a csv file called `final_Update.csv` which can then
be run through NACCulator.


RUNNING INDIVIDUAL FILTERS
------------------------------------------------------

The filters can also be run one at a time on a `.csv` file with the `-f` and `-meta`
flags.

For example, to run the fixHeaders filter:

$ redcap2nacc -f fixHeaders -meta nacculator_cfg.ini <data_input.csv >filtered_output.csv
Expand Down
2 changes: 1 addition & 1 deletion nacc/ftld/fvp/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ def __init__(self):
self.fields['FTDSENPR'] = nacc.uds3.Field(name='FTDSENPR', typename='Num', position=(96, 97), length=2, inclusive_range=(0, 20), allowable_values=[], blanks=['Blank if Question 5a FTDSENAS = 95-98'])
self.fields['FTDNOUNC'] = nacc.uds3.Field(name='FTDNOUNC', typename='Num', position=(99, 100), length=2, inclusive_range=(0, 16), allowable_values=['95', '96', '97', '98'], blanks=[])
self.fields['FTDVERBC'] = nacc.uds3.Field(name='FTDVERBC', typename='Num', position=(102, 103), length=2, inclusive_range=(0, 16), allowable_values=[], blanks=['Blank if Question 6a FTDNOUNC = 95-98'])
self.fields['FTDRATIO'] = nacc.uds3.Field(name='FTDRATIO', typename='Num', position=(105, 109), length=5, inclusive_range=(0,16), allowable_values=[], blanks=['Blank if Question 6a FTDNOUNC = 95-98'])
self.fields['FTDRATIO'] = nacc.uds3.Field(name='FTDRATIO', typename='Num', position=(105, 109), length=5, inclusive_range=(0,16), allowable_values=[88.88], blanks=['Blank if Question 6a FTDNOUNC = 95-98'])
self.fields['FTDREAAS'] = nacc.uds3.Field(name='FTDREAAS', typename='Num', position=(111, 112), length=2, inclusive_range=(0, 5), allowable_values=['95', '96', '97', '98'], blanks=[])
self.fields['FTDREAOS'] = nacc.uds3.Field(name='FTDREAOS', typename='Num', position=(114, 115), length=2, inclusive_range=(0, 37), allowable_values=[], blanks=['Blank if Question 7a FTDREAAS = 95-98'])
self.fields['FTDREASR'] = nacc.uds3.Field(name='FTDREASR', typename='Num', position=(117, 118), length=2, inclusive_range=(0, 20), allowable_values=[], blanks=['Blank if Question 7a FTDREAAS = 95-98'])
Expand Down
2 changes: 1 addition & 1 deletion nacc/ftld/ivp/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ def __init__(self):
self.fields['FTDSENPR'] = nacc.uds3.Field(name='FTDSENPR', typename='Num', position=(96, 97), length=2, inclusive_range=(0, 20), allowable_values=[], blanks=['Blank if Question 5a FTDSENAS = 95-98'])
self.fields['FTDNOUNC'] = nacc.uds3.Field(name='FTDNOUNC', typename='Num', position=(99, 100), length=2, inclusive_range=(0, 16), allowable_values=['95', '96', '97', '98'], blanks=[])
self.fields['FTDVERBC'] = nacc.uds3.Field(name='FTDVERBC', typename='Num', position=(102, 103), length=2, inclusive_range=(0, 16), allowable_values=[], blanks=['Blank if Question 6a FTDNOUNC = 95-98'])
self.fields['FTDRATIO'] = nacc.uds3.Field(name='FTDRATIO', typename='Num', position=(105, 109), length=5, inclusive_range=(0,16), allowable_values=[], blanks=['Blank if Question 6a FTDNOUNC = 95-98'])
self.fields['FTDRATIO'] = nacc.uds3.Field(name='FTDRATIO', typename='Num', position=(105, 109), length=5, inclusive_range=(0,16), allowable_values=[88.88], blanks=['Blank if Question 6a FTDNOUNC = 95-98'])
self.fields['FTDREAAS'] = nacc.uds3.Field(name='FTDREAAS', typename='Num', position=(111, 112), length=2, inclusive_range=(0, 5), allowable_values=['95', '96', '97', '98'], blanks=[])
self.fields['FTDREAOS'] = nacc.uds3.Field(name='FTDREAOS', typename='Num', position=(114, 115), length=2, inclusive_range=(0, 37), allowable_values=[], blanks=['Blank if Question 7a FTDREAAS = 95-98'])
self.fields['FTDREASR'] = nacc.uds3.Field(name='FTDREASR', typename='Num', position=(117, 118), length=2, inclusive_range=(0, 20), allowable_values=[], blanks=['Blank if Question 7a FTDREAAS = 95-98'])
Expand Down
1 change: 1 addition & 0 deletions nacc/lbd/v3_1/fvp/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,7 @@ def add_e2l(record,packet):
E2L.LBIFPtPP = record['fu_LBIFPtPP'.lower()]
E2L.LBIFPISL = record['fu_LBIFPISL'.lower()]
E2L.LBIFPAVL = record['fu_LBIFPAVL'.lower()]
E2L.LBIAPet = record['fu_LBIAPet'.lower()]
E2L.LBIAPAVL = record['fu_LBIAPAVL'.lower()]
E2L.LBItPet = record['fu_LBItPet'.lower()]
E2L.LBItPAVL = record['fu_LBItPAVL'.lower()]
Expand Down
30 changes: 15 additions & 15 deletions nacc/lbd/v3_1/fvp/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,21 +46,21 @@ def __init__(self):
self.fields['LANGA4'] = nacc.uds3.Field(name='LANGA4', typename='Num', position=(61, 61), length=1, inclusive_range=(1, 2), allowable_values=['1', '2'], blanks=['Blank if Question 4b A4SUB = 0 (No)'])
self.fields['A4SUB'] = nacc.uds3.Field(name='A4SUB', typename='Num', position=(63, 63), length=1, inclusive_range=(0, 1), allowable_values=['0', '1'], blanks=[])
self.fields['A4NOT'] = nacc.uds3.Field(name='A4NOT', typename='Num', position=(65, 66), length=2, inclusive_range=None, allowable_values=['95', '96', '97', '98'], blanks=['Blank if Question 4b A4SUB = 1 (Yes)'])
self.fields['LANGB1'] = nacc.uds3.Field(name='LANGB1', typename='Num', position=(70, 70), length=1, inclusive_range=(1, 2), allowable_values=['1', '2'], blanks=['Blank if Question 6b B1SUB = 0 (No)'])
self.fields['B1SUB'] = nacc.uds3.Field(name='B1SUB', typename='Num', position=(72, 72), length=1, inclusive_range=(0, 1), allowable_values=['0', '1'], blanks=[])
self.fields['B1NOT'] = nacc.uds3.Field(name='B1NOT', typename='Num', position=(74, 75), length=2, inclusive_range=None, allowable_values=['95', '96', '97', '98'], blanks=['Blank if Question 6b B1SUB = 1 (Yes)'])
self.fields['LANGB4'] = nacc.uds3.Field(name='LANGB4', typename='Num', position=(77, 77), length=1, inclusive_range=(1, 2), allowable_values=['1', '2'], blanks=[])
self.fields['LANGB5'] = nacc.uds3.Field(name='LANGB5', typename='Num', position=(79, 79), length=1, inclusive_range=(1, 2), allowable_values=['1', '2'], blanks=['Blank if Question 8b B5SUB = 0 (No)'])
self.fields['B5SUB'] = nacc.uds3.Field(name='B5SUB', typename='Num', position=(81, 81), length=1, inclusive_range=(0, 1), allowable_values=['0', '1'], blanks=[])
self.fields['B5NOT'] = nacc.uds3.Field(name='B5NOT', typename='Num', position=(83, 84), length=2, inclusive_range=None, allowable_values=['95', '96', '97', '98'], blanks=['Blank if Question 8b B5SUB = 1 (Yes)'])
self.fields['LANGB6'] = nacc.uds3.Field(name='LANGB6', typename='Num', position=(86, 86), length=1, inclusive_range=(1, 2), allowable_values=['1', '2'], blanks=['Blank if Question 9b B6SUB = 0 (No)'])
self.fields['B6SUB'] = nacc.uds3.Field(name='B6SUB', typename='Num', position=(88, 88), length=1, inclusive_range=(0, 1), allowable_values=['0', '1'], blanks=[])
self.fields['B6NOT'] = nacc.uds3.Field(name='B6NOT', typename='Num', position=(90, 91), length=2, inclusive_range=None, allowable_values=['95', '96', '97', '98'], blanks=['Blank if Question 9b B6SUB = 1 (Yes)'])
self.fields['LANGB7'] = nacc.uds3.Field(name='LANGB7', typename='Num', position=(93, 93), length=1, inclusive_range=(1, 2), allowable_values=['1', '2'], blanks=['Blank if Question 10b B7SUB = 0 (No)'])
self.fields['B7SUB'] = nacc.uds3.Field(name='B7SUB', typename='Num', position=(95, 95), length=1, inclusive_range=(0, 1), allowable_values=['0', '1'], blanks=[])
self.fields['B7NOT'] = nacc.uds3.Field(name='B7NOT', typename='Num', position=(97, 98), length=2, inclusive_range=None, allowable_values=['95', '96', '97', '98'], blanks=['Blank if Question 10b B7SUB = 1 (Yes)'])
self.fields['LANGB8'] = nacc.uds3.Field(name='LANGB8', typename='Num', position=(100, 100), length=1, inclusive_range=(1, 2), allowable_values=['1', '2'], blanks=[])
self.fields['LANGB9'] = nacc.uds3.Field(name='LANGB9', typename='Num', position=(102, 102), length=1, inclusive_range=(1, 2), allowable_values=['1', '2'], blanks=[])
self.fields['LANGB1'] = nacc.uds3.Field(name='LANGB1', typename='Num', position=(68, 68), length=1, inclusive_range=(1, 2), allowable_values=['1', '2'], blanks=['Blank if Question 6b B1SUB = 0 (No)'])
self.fields['B1SUB'] = nacc.uds3.Field(name='B1SUB', typename='Num', position=(70, 70), length=1, inclusive_range=(0, 1), allowable_values=['0', '1'], blanks=[])
self.fields['B1NOT'] = nacc.uds3.Field(name='B1NOT', typename='Num', position=(72, 73), length=2, inclusive_range=None, allowable_values=['95', '96', '97', '98'], blanks=['Blank if Question 6b B1SUB = 1 (Yes)'])
self.fields['LANGB4'] = nacc.uds3.Field(name='LANGB4', typename='Num', position=(75, 75), length=1, inclusive_range=(1, 2), allowable_values=['1', '2'], blanks=[])
self.fields['LANGB5'] = nacc.uds3.Field(name='LANGB5', typename='Num', position=(77, 77), length=1, inclusive_range=(1, 2), allowable_values=['1', '2'], blanks=['Blank if Question 8b B5SUB = 0 (No)'])
self.fields['B5SUB'] = nacc.uds3.Field(name='B5SUB', typename='Num', position=(79, 79), length=1, inclusive_range=(0, 1), allowable_values=['0', '1'], blanks=[])
self.fields['B5NOT'] = nacc.uds3.Field(name='B5NOT', typename='Num', position=(81, 82), length=2, inclusive_range=None, allowable_values=['95', '96', '97', '98'], blanks=['Blank if Question 8b B5SUB = 1 (Yes)'])
self.fields['LANGB6'] = nacc.uds3.Field(name='LANGB6', typename='Num', position=(84, 84), length=1, inclusive_range=(1, 2), allowable_values=['1', '2'], blanks=['Blank if Question 9b B6SUB = 0 (No)'])
self.fields['B6SUB'] = nacc.uds3.Field(name='B6SUB', typename='Num', position=(86, 86), length=1, inclusive_range=(0, 1), allowable_values=['0', '1'], blanks=[])
self.fields['B6NOT'] = nacc.uds3.Field(name='B6NOT', typename='Num', position=(88, 89), length=2, inclusive_range=None, allowable_values=['95', '96', '97', '98'], blanks=['Blank if Question 9b B6SUB = 1 (Yes)'])
self.fields['LANGB7'] = nacc.uds3.Field(name='LANGB7', typename='Num', position=(91, 91), length=1, inclusive_range=(1, 2), allowable_values=['1', '2'], blanks=['Blank if Question 10b B7SUB = 0 (No)'])
self.fields['B7SUB'] = nacc.uds3.Field(name='B7SUB', typename='Num', position=(93, 93), length=1, inclusive_range=(0, 1), allowable_values=['0', '1'], blanks=[])
self.fields['B7NOT'] = nacc.uds3.Field(name='B7NOT', typename='Num', position=(95, 96), length=2, inclusive_range=None, allowable_values=['95', '96', '97', '98'], blanks=['Blank if Question 10b B7SUB = 1 (Yes)'])
self.fields['LANGB8'] = nacc.uds3.Field(name='LANGB8', typename='Num', position=(98, 98), length=1, inclusive_range=(1, 2), allowable_values=['1', '2'], blanks=[])
self.fields['LANGB9'] = nacc.uds3.Field(name='LANGB9', typename='Num', position=(100, 100), length=1, inclusive_range=(1, 2), allowable_values=['1', '2'], blanks=[])
self.fields['LANGC2'] = nacc.uds3.Field(name='LANGC2', typename='Num', position=(104, 104), length=1, inclusive_range=(1, 2), allowable_values=['1', '2'], blanks=[])
self.fields['LANGD1'] = nacc.uds3.Field(name='LANGD1', typename='Num', position=(106, 106), length=1, inclusive_range=(1, 2), allowable_values=['1', '2'], blanks=[])
self.fields['LANGD2'] = nacc.uds3.Field(name='LANGD2', typename='Num', position=(108, 108), length=1, inclusive_range=(1, 2), allowable_values=['1', '2'], blanks=[])
Expand Down
120 changes: 120 additions & 0 deletions nacc/local_filters.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import os
import sys
import csv
import datetime
import configparser
from nacc.uds3.filters import *


# Creating a folder which contains Intermediate files
def recent_run_folder(out_dir):
# Check if directory exists. If not, create it.
if not os.path.exists(out_dir):
try:
os.makedirs(out_dir)
except Exception as e:
raise e


def get_headers(input_ptr):
reader = csv.DictReader(input_ptr)
headers = reader.fieldnames
print(headers)


def run_all_filters(folder_name, config, input_name):
# Calling Filters
try:
print("--------------Removing subjects already in current--------------------", file=sys.stderr)
if input_name:
input_path = input_name
else:
input_path = os.path.join(folder_name, "redcap_input.csv")
output_path = os.path.join(folder_name, "clean.csv")
print("Processing", file=sys.stderr)
with open(output_path, 'w') as output_ptr, open(input_path, 'r') as input_ptr:
filter_clean_ptid(input_ptr, config, output_ptr)

print("--------------Replacing drug IDs--------------------", file=sys.stderr)
input_path = os.path.join(folder_name, "clean.csv")
output_path = os.path.join(folder_name, "drugs.csv")
with open(output_path, 'w') as output_ptr, open(input_path, 'r') as input_ptr:
filter_replace_drug_id(input_ptr, config, output_ptr)

print("--------------Fixing Headers--------------------", file=sys.stderr)
input_path = os.path.join(folder_name, "drugs.csv")
output_path = os.path.join(folder_name, "clean_headers.csv")
with open(output_path, 'w') as output_ptr, open(input_path, 'r') as input_ptr:
filter_fix_headers(input_ptr, config, output_ptr)

print("--------------Filling in Defaults--------------------", file=sys.stderr)
input_path = os.path.join(folder_name, "clean_headers.csv")
output_path = os.path.join(folder_name, "default.csv")
with open(output_path, 'w') as output_ptr, open(input_path, 'r') as input_ptr:
filter_fill_default(input_ptr, config, output_ptr)

print("--------------Updating fields--------------------", file=sys.stderr)
input_path = os.path.join(folder_name, "default.csv")
output_path = os.path.join(folder_name, "update_fields.csv")
with open(output_path, 'w') as output_ptr, open(input_path, 'r') as input_ptr:
filter_update_field(input_ptr, config, output_ptr)

print("--------------Fixing Visit Dates--------------------", file=sys.stderr)
input_path = os.path.join(folder_name, "update_fields.csv")
output_path = os.path.join(folder_name, "proper_visitdate.csv")
with open(output_path, 'w') as output_ptr, open(input_path, 'r') as input_ptr:
filter_fix_visitdate(input_ptr, config, output_ptr)

print("--------------Removing Unnecessary Records--------------------", file=sys.stderr)
input_path = os.path.join(folder_name, "proper_visitdate.csv")
output_path = os.path.join(folder_name, "CleanedPtid_Update.csv")
with open(output_path, 'w') as output_ptr, open(input_path, 'r') as input_ptr:
filter_remove_ptid(input_ptr, config, output_ptr)

print("--------------Removing Records without VisitDate--------------------", file=sys.stderr)
input_path = os.path.join(folder_name, "CleanedPtid_Update.csv")
output_path = os.path.join(folder_name, "final_Update.csv")
with open(output_path, 'w') as output_ptr, open(input_path, 'r') as input_ptr:
filter_eliminate_empty_date(input_ptr, config, output_ptr)

except Exception as e:
print("Error in Opening a file")
print(e)

return


def read_config(config_path):
config = configparser.ConfigParser()
config.read(config_path)
return config


def main():
currentdate = datetime.datetime.now().strftime('%m-%d-%Y')
folder_name = "run_" + currentdate
input_name = ""
print("Recent folder " + folder_name, file=sys.stderr)

current_directory = os.getcwd()
identified_folder = os.path.join(current_directory, folder_name)

if not os.path.exists(identified_folder):
recent_run_folder(identified_folder)

# Reading from Config and Accessing the necessary Data
config_path = sys.argv[1]
try:
if sys.argv[2]:
input_name = sys.argv[2]
except IndexError:
pass
# config = read_config(config_path)

run_all_filters(folder_name, config_path, input_name)

exit()


if __name__ == '__main__':
main()
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

from setuptools import setup, find_packages

VERSION = "1.9.0"
VERSION = "1.10.0"

setup(
name="nacculator",
Expand Down

0 comments on commit 51deedf

Please sign in to comment.