Skip to content

Commit 413b1f3

Browse files
[pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
1 parent 9d14df0 commit 413b1f3

File tree

2 files changed

+116
-44
lines changed

2 files changed

+116
-44
lines changed

reproschema/reproschema2redcap.py

Lines changed: 86 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -84,14 +84,24 @@ def find_Ftype_and_colH(item, row_data, response_options):
8484

8585
return row_data
8686

87-
def process_item(item, item_properties, activity_name, activity_preamble, contextfile, http_kwargs, compute_item=False, compute_expr=None):
87+
88+
def process_item(
89+
item,
90+
item_properties,
91+
activity_name,
92+
activity_preamble,
93+
contextfile,
94+
http_kwargs,
95+
compute_item=False,
96+
compute_expr=None,
97+
):
8898
"""
8999
Process an item in JSON format and extract relevant information into a dictionary.
90100
Only includes non-empty/non-None values to match clean_dict_nans behavior.
91101
"""
92102
if activity_name.endswith("_schema"):
93103
activity_name = activity_name[:-7]
94-
104+
95105
# Initialize with only required fields
96106
row_data = {
97107
"var_name": item.id,
@@ -112,7 +122,9 @@ def process_item(item, item_properties, activity_name, activity_preamble, contex
112122
if "ResponseOption" in resp["category"]:
113123
response_options = ResponseOption(**resp)
114124
else:
115-
raise Exception(f"Expected to have ResponseOption but got {resp['category']}")
125+
raise Exception(
126+
f"Expected to have ResponseOption but got {resp['category']}"
127+
)
116128
else:
117129
response_options = item.responseOptions
118130

@@ -122,19 +134,27 @@ def process_item(item, item_properties, activity_name, activity_preamble, contex
122134
row_data["val_min"] = response_options.minValue
123135
if response_options.maxValue is not None:
124136
row_data["val_max"] = response_options.maxValue
125-
137+
126138
# Handle choices
127139
choices = response_options.choices
128140
if choices and not isinstance(choices, str):
129141
if isinstance(choices, list):
130-
item_choices = [f"{ch.value}, {ch.name.get('en', '')}" for ch in choices if ch.value is not None]
142+
item_choices = [
143+
f"{ch.value}, {ch.name.get('en', '')}"
144+
for ch in choices
145+
if ch.value is not None
146+
]
131147
if item_choices:
132148
row_data["choices"] = " | ".join(item_choices)
133149

134150
# Add valueRequired if explicitly True
135-
if item_properties and "valueRequired" in item_properties and item_properties["valueRequired"] is True:
151+
if (
152+
item_properties
153+
and "valueRequired" in item_properties
154+
and item_properties["valueRequired"] is True
155+
):
136156
row_data["required"] = "y"
137-
157+
138158
var_name = str(item.id).split("/")[-1] # Get the last part of the id path
139159
if var_name.endswith("_total_score"):
140160
row_data["isVis_logic"] = False # This will make the field hidden
@@ -143,7 +163,11 @@ def process_item(item, item_properties, activity_name, activity_preamble, contex
143163
row_data["isVis_logic"] = item_properties["isVis"]
144164

145165
# Handle description
146-
if item.description and "en" in item.description and item.description["en"]:
166+
if (
167+
item.description
168+
and "en" in item.description
169+
and item.description["en"]
170+
):
147171
row_data["field_notes"] = item.description["en"]
148172

149173
# Handle preamble
@@ -227,11 +251,13 @@ def get_csv_data(dir_path, contextfile, http_kwargs):
227251

228252
# Get activity name without adding extra _schema
229253
activity_name = act.id.split("/")[-1]
230-
if activity_name.endswith('_schema.jsonld'):
231-
activity_name = activity_name[:-12] # Remove _schema.jsonld
232-
elif activity_name.endswith('.jsonld'):
254+
if activity_name.endswith("_schema.jsonld"):
255+
activity_name = activity_name[
256+
:-12
257+
] # Remove _schema.jsonld
258+
elif activity_name.endswith(".jsonld"):
233259
activity_name = activity_name[:-7] # Remove .jsonld
234-
260+
235261
items_properties.update(
236262
{
237263
el["isAbout"]: el
@@ -245,22 +271,30 @@ def get_csv_data(dir_path, contextfile, http_kwargs):
245271
item_order = [("ord", el) for el in act.ui.order]
246272
item_calc = [("calc", el) for el in act.compute]
247273

248-
computed_fields = {calc_item.variableName for _, calc_item in item_calc}
249-
274+
computed_fields = {
275+
calc_item.variableName
276+
for _, calc_item in item_calc
277+
}
250278

251279
for tp, item in item_order + item_calc:
252280
try:
253281
if tp == "calc":
254282
js_expr = item.jsExpression
255283
var_name = item.variableName
256-
284+
257285
# Find the corresponding item properties
258286
if var_name in items_properties:
259-
item = items_properties[var_name]["isAbout"]
287+
item = items_properties[var_name][
288+
"isAbout"
289+
]
260290
# Ensure computed fields are marked as hidden
261-
items_properties[var_name]["isVis"] = False
291+
items_properties[var_name][
292+
"isVis"
293+
] = False
262294
else:
263-
print(f"WARNING: no item properties found for computed field {var_name} in {activity_name}")
295+
print(
296+
f"WARNING: no item properties found for computed field {var_name} in {activity_name}"
297+
)
264298
continue
265299
item_calc = True
266300
else:
@@ -269,7 +303,7 @@ def get_csv_data(dir_path, contextfile, http_kwargs):
269303
it_prop = items_properties.get(item)
270304
if not _is_url(item):
271305
item = Path(activity_path).parent / item
272-
306+
273307
try:
274308
item_json = load_file(
275309
item,
@@ -285,9 +319,15 @@ def get_csv_data(dir_path, contextfile, http_kwargs):
285319
print(f"Error loading item: {item}")
286320
print(f"Error details: {str(e)}")
287321
continue
288-
289-
activity_name = act.id.split("/")[-1].split(".")[0]
290-
activity_preamble = act.preamble.get("en", "").strip() if hasattr(act, 'preamble') else ""
322+
323+
activity_name = act.id.split("/")[-1].split(
324+
"."
325+
)[0]
326+
activity_preamble = (
327+
act.preamble.get("en", "").strip()
328+
if hasattr(act, "preamble")
329+
else ""
330+
)
291331

292332
row_data = process_item(
293333
itm,
@@ -302,12 +342,15 @@ def get_csv_data(dir_path, contextfile, http_kwargs):
302342
csv_data.append(row_data)
303343

304344
except Exception as e:
305-
print(f"Error processing item {item}: {str(e)}")
345+
print(
346+
f"Error processing item {item}: {str(e)}"
347+
)
306348
continue
307349
# Break after finding the first _schema file
308350
break
309351
return csv_data
310352

353+
311354
def write_to_csv(csv_data, output_csv_filename):
312355
# REDCap-specific headers
313356
headers = [
@@ -328,23 +371,25 @@ def write_to_csv(csv_data, output_csv_filename):
328371
"Question Number (surveys only)",
329372
"Matrix Group Name",
330373
"Matrix Ranking?",
331-
"Field Annotation"
374+
"Field Annotation",
332375
]
333376

334377
# Writing to the CSV file
335-
with open(output_csv_filename, "w", newline="", encoding="utf-8") as csvfile:
378+
with open(
379+
output_csv_filename, "w", newline="", encoding="utf-8"
380+
) as csvfile:
336381
writer = csv.DictWriter(csvfile, fieldnames=headers)
337382
writer.writeheader()
338-
383+
339384
for row in csv_data:
340385
redcap_row = {}
341-
386+
342387
# Handle var_name URL conversion
343388
var_name = row["var_name"]
344389
if _is_url(var_name):
345390
var_name = var_name.split("/")[-1].split(".")[0]
346391
redcap_row["Variable / Field Name"] = var_name
347-
392+
348393
# Handle form name
349394
activity_name = row["activity"]
350395
if activity_name.endswith("_schema"):
@@ -365,15 +410,21 @@ def write_to_csv(csv_data, output_csv_filename):
365410
"isVis_logic": "Branching Logic (Show field only if...)",
366411
"field_annotation": "Field Annotation",
367412
"matrix_group": "Matrix Group Name",
368-
"matrix_ranking": "Matrix Ranking?"
413+
"matrix_ranking": "Matrix Ranking?",
369414
}
370415

371416
# Add mapped fields only if they exist and aren't empty
372417
for src_key, dest_key in field_mappings.items():
373-
if src_key in row and row[src_key] is not None and row[src_key] != "":
418+
if (
419+
src_key in row
420+
and row[src_key] is not None
421+
and row[src_key] != ""
422+
):
374423
# Special handling for visibility logic
375424
if src_key == "isVis_logic":
376-
if row[src_key] is not True: # Only add if not default True
425+
if (
426+
row[src_key] is not True
427+
): # Only add if not default True
377428
redcap_row[dest_key] = row[src_key]
378429
# Special handling for required field
379430
elif src_key == "required":
@@ -382,7 +433,9 @@ def write_to_csv(csv_data, output_csv_filename):
382433
elif src_key == "field_annotation":
383434
current_annotation = redcap_row.get(dest_key, "")
384435
if current_annotation:
385-
redcap_row[dest_key] = f"{current_annotation} {row[src_key]}"
436+
redcap_row[dest_key] = (
437+
f"{current_annotation} {row[src_key]}"
438+
)
386439
else:
387440
redcap_row[dest_key] = row[src_key]
388441
else:
@@ -392,6 +445,7 @@ def write_to_csv(csv_data, output_csv_filename):
392445

393446
print("The CSV file was written successfully")
394447

448+
395449
def reproschema2redcap(input_dir_path, output_csv_filename):
396450
contextfile = CONTEXTFILE_URL # todo, give an option
397451
http_kwargs = {}

reproschema/tests/test_rs2redcap_redcap2rs.py

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -236,25 +236,37 @@ def compare_protocols(prot_tree_orig, prot_tree_final):
236236
if key == "valueRequired":
237237
# Debug print
238238
print(f"\nDebug - Activity: {act_name}, Item: {nm}")
239-
print(f"Original valueRequired: {orig_value}, type: {type(orig_value)}")
240-
print(f"Final valueRequired: {final_value}, type: {type(final_value)}")
241-
239+
print(
240+
f"Original valueRequired: {orig_value}, type: {type(orig_value)}"
241+
)
242+
print(
243+
f"Final valueRequired: {final_value}, type: {type(final_value)}"
244+
)
245+
242246
# Compare only True values
243247
if orig_value is True:
244248
if final_value is not True:
245249
error = True
246-
print(f"Error case 1: orig=True, final={final_value}")
250+
print(
251+
f"Error case 1: orig=True, final={final_value}"
252+
)
247253
elif final_value is True:
248254
if orig_value is not True:
249255
error = True
250-
print(f"Error case 2: orig={orig_value}, final=True")
256+
print(
257+
f"Error case 2: orig={orig_value}, final=True"
258+
)
251259

252260
elif key == "isVis":
253261
# Original isVis handling
254262
if orig_value is not None:
255-
if normalize_condition(orig_value) != normalize_condition(final_value):
263+
if normalize_condition(
264+
orig_value
265+
) != normalize_condition(final_value):
256266
error = True
257-
elif final_value is not None and final_value is not True:
267+
elif (
268+
final_value is not None and final_value is not True
269+
):
258270
error = True
259271

260272
if error:
@@ -326,12 +338,18 @@ def compare_protocols(prot_tree_orig, prot_tree_final):
326338
# Handle cases where one might be NaN/None and the other empty string
327339
orig_q = act_items_orig[nm]["obj"].question.get("en", "")
328340
final_q = el["obj"].question.get("en", "")
329-
341+
330342
# Convert None/NaN to empty string for comparison
331-
orig_q = "" if pd.isna(orig_q) or orig_q is None else orig_q
332-
final_q = "" if pd.isna(final_q) or final_q is None else final_q
333-
334-
if normalize_condition(orig_q) != normalize_condition(final_q):
343+
orig_q = (
344+
"" if pd.isna(orig_q) or orig_q is None else orig_q
345+
)
346+
final_q = (
347+
"" if pd.isna(final_q) or final_q is None else final_q
348+
)
349+
350+
if normalize_condition(orig_q) != normalize_condition(
351+
final_q
352+
):
335353
if "<br><br>" in normalize_condition(orig_q):
336354
warnings_list.append(
337355
print_return_msg(

0 commit comments

Comments
 (0)