From f7acb04e243669b7bac8168f2f12a909a5204514 Mon Sep 17 00:00:00 2001
From: Beliy Nikita <beliy.nikita@outlook.com>
Date: Sun, 22 Mar 2020 17:49:19 +0100
Subject: [PATCH] adapted prepare

---
 example1/resources/Appariement.xlsx          | Bin 5784 -> 5471 bytes
 example1/resources/participants_add.json     |  73 +++++
 example1/resources/participants_remove.json  |  64 +++++
 example1/resources/plugins/bidsify_plugin.py |  23 +-
 example1/resources/plugins/rename_plugin.py  | 287 ++++++++++++++-----
 5 files changed, 361 insertions(+), 86 deletions(-)
 create mode 100644 example1/resources/participants_add.json
 create mode 100644 example1/resources/participants_remove.json

diff --git a/example1/resources/Appariement.xlsx b/example1/resources/Appariement.xlsx
index 5c3c88a613e7a47fc3e84301ccf2230783fcbdfa..29036a340230af04b8190f9691673a48c4f9b02d 100644
GIT binary patch
delta 3549
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

delta 3862
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
KlidJadj1b;U+p3Q

diff --git a/example1/resources/participants_add.json b/example1/resources/participants_add.json
new file mode 100644
index 0000000..5c5bae2
--- /dev/null
+++ b/example1/resources/participants_add.json
@@ -0,0 +1,73 @@
+{
+  "participant_id":{
+    "LongName":"Participant Id",
+    "Description":"label identifying a particular subject"
+  },
+  "age":{
+    "LongName":"Age",
+    "Description":"Age of a subject",
+    "Units":"year"
+  },
+  "sex":{
+    "LongName":"Sex",
+    "Description":"Sex of a subject",
+    "Levels":{
+      "F":"Female",
+      "M":"Male"
+    }
+  },
+  "education":{
+    "LongName":"Education level",
+    "Description":"Education level"
+  },
+  "group":{
+    "LongName":"group",
+    "Description":"Group subject belongs",
+    "Levels":{
+      "patient":"patient",
+      "control":"control"
+    }
+  },
+  "handiness":{
+    "LongName":"Handiness of subject",
+    "Levels":{
+      "r":"right-handed",
+      "l":"left-handed"
+    }
+  },
+  "paired":{
+    "LongName":"Paired Id",
+    "Description":"Subject Id paired with this subject"
+  },
+  "ses_1":{
+    "LongName":"First session",
+    "Description":"Id of the first session taken by subject",
+    "Levels":{
+      "ses-LCL":"Low charge level",
+      "ses-HCL":"High charge level",
+      "ses-STROOP":"Multiparametric scan"
+    }
+  },
+  "ses_2":{
+    "LongName":"Second session",
+    "Description":"Id of the second session taken by subject",
+    "Levels":{
+      "ses-LCL":"Low charge level",
+      "ses-HCL":"High charge level",
+      "ses-STROOP":"Multiparametric scan"
+    }
+  },
+  "ses_3":{
+    "LongName":"Third session",
+    "Description":"Id of the second session taken by subject",
+    "Levels":{
+      "ses-LCL":"Low charge level",
+      "ses-HCL":"High charge level",
+      "ses-STROOP":"Multiparametric scan"
+    }
+  },
+  "random":{
+    "LongName":"Rundom number between 0 and 1",
+    "Description":"Test of adding columns in process/bidsify step"
+  }
+}
diff --git a/example1/resources/participants_remove.json b/example1/resources/participants_remove.json
new file mode 100644
index 0000000..317371d
--- /dev/null
+++ b/example1/resources/participants_remove.json
@@ -0,0 +1,64 @@
+{
+  "participant_id":{
+    "LongName":"Participant Id",
+    "Description":"label identifying a particular subject"
+  },
+  "sex":{
+    "LongName":"Sex",
+    "Description":"Sex of a subject",
+    "Levels":{
+      "F":"Female",
+      "M":"Male"
+    }
+  },
+  "education":{
+    "LongName":"Education level",
+    "Description":"Education level"
+  },
+  "group":{
+    "LongName":"group",
+    "Description":"Group subject belongs",
+    "Levels":{
+      "patient":"patient",
+      "control":"control"
+    }
+  },
+  "handiness":{
+    "LongName":"Handiness of subject",
+    "Levels":{
+      "r":"right-handed",
+      "l":"left-handed"
+    }
+  },
+  "paired":{
+    "LongName":"Paired Id",
+    "Description":"Subject Id paired with this subject"
+  },
+  "ses_1":{
+    "LongName":"First session",
+    "Description":"Id of the first session taken by subject",
+    "Levels":{
+      "ses-LCL":"Low charge level",
+      "ses-HCL":"High charge level",
+      "ses-STROOP":"Multiparametric scan"
+    }
+  },
+  "ses_2":{
+    "LongName":"Second session",
+    "Description":"Id of the second session taken by subject",
+    "Levels":{
+      "ses-LCL":"Low charge level",
+      "ses-HCL":"High charge level",
+      "ses-STROOP":"Multiparametric scan"
+    }
+  },
+  "ses_3":{
+    "LongName":"Third session",
+    "Description":"Id of the second session taken by subject",
+    "Levels":{
+      "ses-LCL":"Low charge level",
+      "ses-HCL":"High charge level",
+      "ses-STROOP":"Multiparametric scan"
+    }
+  }
+}
diff --git a/example1/resources/plugins/bidsify_plugin.py b/example1/resources/plugins/bidsify_plugin.py
index 7cfafc4..6703e2b 100644
--- a/example1/resources/plugins/bidsify_plugin.py
+++ b/example1/resources/plugins/bidsify_plugin.py
@@ -1,6 +1,7 @@
 import os
 import shutil
 import logging
+import random
 
 from definitions import checkSeries
 
@@ -26,27 +27,35 @@ def InitEP(source: str, destination: str, dry: bool) -> int:
     dry_run = dry
 
 
+def SubjectEP(scan):
+    sub_id = int(scan.subject[4:])
+    scan.subject = "sub-{:03d}".format(sub_id + 1)
+    scan.sub_values["sex"] = None
+    scan.sub_values["random"] = random.random()
+
+
 def SessionEP(scan):
     global series
     global sid
     sub = scan.subject
     ses = scan.session
-    path = os.path.join(rawfolder,
-                        sub, ses,
-                        "MRI")
+    # path = os.path.join(rawfolder,
+    #                     sub, ses,
+    #                     "MRI")
+    path = os.path.join(scan.in_path, "MRI")
     series = sorted(os.listdir(path))
-    series = [s.split("-",1)[1] for s in series]
+    series = [s.split("-", 1)[1] for s in series]
     sid = -1
     checkSeries(path, sub, ses, False)
     # copytng behevioral data
-    aux_input = os.path.join(rawfolder, sub, ses, "auxiliary")
+    aux_input = os.path.join(scan.in_path, "auxiliary")
     if ses in ("ses-LCL", "ses-HCL"):
         if not os.path.isdir(aux_input):
             logger.error("Session {}/{} do not contain auxiliary folder"
                          .format(sub, ses))
             raise FileNotFoundError("folder {} not found"
                                     .format(aux_input))
-        beh = os.path.join(bidsfolder, sub, ses, "beh")
+        beh = os.path.join(scan.in_path, "beh")
         if not dry_run:
             os.makedirs(beh, exist_ok=True)
         for old, new in (("FCsepNBack.tsv", "task-rest_events.tsv"),
@@ -81,7 +90,7 @@ def SequenceEP(recording):
     global Intended
     Intended = ""
     sid += 1
-    recid = series[sid] 
+    recid = series[sid]
     if recid != recording.recId():
         logger.warning("{}: Id mismatch folder {}"
                        .format(recording.recIdentity(False),
diff --git a/example1/resources/plugins/rename_plugin.py b/example1/resources/plugins/rename_plugin.py
index bafe39b..7264928 100644
--- a/example1/resources/plugins/rename_plugin.py
+++ b/example1/resources/plugins/rename_plugin.py
@@ -3,126 +3,229 @@ import pandas
 import logging
 import shutil
 
-from tools import tools 
-from bidsMeta import BIDSfieldLibrary
+from tools import tools
 from bids import BidsSession
 
-from definitions import Series, checkSeries
+from definitions import Series, checkSeries, plugin_root
 
+
+# defining logger this way will prefix plugin messages
+# with plugin name
 logger = logging.getLogger(__name__)
 
-# global variables
-rawfolder = ""
-bidsfolder = ""
-dry_run = False
 
+#############################
+# global bidscoin variables #
+#############################
 
-resources = os.path.join(os.path.dirname(__file__), "..")
+# Folder with source dataset
+rawfolder = None
+# folder with prepared dataset
+preparedfolder = None
+# switch if is a dry-run (test run)
+dry_run = False
 
-df_subjects = None
+###########################
+# global plugin variables #
+###########################
+
+# map of individual sessions
+#   key: source folde session (s01234)
+#   value: bidsified session (ses-HCL)
 scans_map = {}
 
 # scale to convert ms in log-files to seconds
 time_scale = 1e-3
 
-# list of subjects exel-file columns 
-excel_col_list = {'Patient' : 'pat',
-                  'S_A_E' : "pat_sae",
+# subject balck-list
+#   subject folders in this list will be skipped
+#   by plugin
+sub_black_list = []
+
+# subject xls table columns and their renaiming
+excel_col_list = {"Patient": "pat",
+                  "Sex": "pat_sex",
+                  "Age": "pat_age",
+                  "Education": "pat_edu",
                   1: "pat_1", 2: "pat_2", 3: "pat_3",
-                  'Control' : "cnt",
-                  'S_A_E.1': "cnt_sae",
-                  '1.1': "cnt_1", '2.1': "cnt_2", '3.1': "cnt_3",
+                  'Control': "cnt",
+                  "Sex.1": "cnt_sex",
+                  "Age.1": "cnt_age",
+                  "Education.1": "cnt_edu",
+                  "1.1": "cnt_1", "2.1": "cnt_2", "3.1": "cnt_3"
                   }
 
-# sub_columns = BIDSfieldLibrary()
+
+# columns prefixes for patient and control subjects
+#   0 == False == Control
+#   1 == True == Patient
+sub_prefix = ["cnt", "pat"]
+
+# pandas dataframe with list of subjects
+df_subjects = None
 
 
 def InitEP(source: str, destination: str,
            dry: bool,
-           subjects: str="") -> int:
+           subjects: str = "") -> int:
+    """
+    Initialisation of plugin
+
+    1. Saves source/destination folders and dry_run switch
+    2. Loads subjects xls table
+
+    Parameters
+    ----------
+    source: str
+        path to source dataset
+    destination:
+        path to prepared dataset
+    subjects: str
+        path to subjects xls file, if empty is looked
+        in source dataset folder
+    """
+
     global rawfolder
-    global bidsfolder
+    global preparefolder
     global dry_run
-    global subject_file
 
     rawfolder = source
-    bidsfolder = destination
+    preparefolder = destination
     dry_run = dry
+
+    #########################
+    # Loading subjects list #
+    #########################
     if subjects:
         subject_file = subjects
     else:
-        subject_file = os.path.join(resources, "Appariement.xlsx")
-    logger.info(subject_file)
-
+        subject_file = os.path.join(plugin_root, "Appariement.xlsx")
     if not os.path.isfile(subject_file):
         raise FileNotFoundError("Subject file '{}' not found"
                                 .format(subject_file))
 
-    # creating df for subjects
+    # creating dataframe for subjects
     global df_subjects
     df_subjects = pandas.read_excel(subject_file,
                                     sheet_name=0, header=0,
-                                    usecols=[0,1,2,3,4,5,6,7,8,9,10])
-    df_subjects.rename(index=str, columns=excel_col_list,inplace=True)
+                                    usecols="A:N"
+                                    )
+    df_subjects.rename(index=str, columns=excel_col_list, inplace=True)
     df_subjects = df_subjects[df_subjects['pat'].notnull()
                               | df_subjects['cnt'].notnull()]
 
 
 def SubjectEP(session: BidsSession) -> int:
-    sub_id = int(session.subject)
-    index = df_subjects.loc[df_subjects["pat"] == sub_id].index 
-    status = 0
+    """
+    Subject determination and initialisation
+
+    1. Checks if subject not in balck list
+    2. Loads demographics from subject table
+    3. Creates session parcing dictionary
+
+    Parameters
+    ----------
+    session: BidsSession
+
+    Returns
+    -------
+    int:
+        if 0, plugin succesfull
+        if > 0, plugin failed, an exception will be raised
+        if < 0, plugin failed, and subject will be skipped
+    """
+
+    #################################
+    # Skipping if in the black list #
+    #################################
+    if session.subject in sub_black_list:
+        logger.info("Subject '{}' is in black_list"
+                    .format(session.subject))
+        return -1
+
+    ################################
+    # Retriving subject from table #
+    ################################
+    try:
+        # in case if folder name in source dataset
+        # cannot be converted to integer
+        sub_id = int(session.subject)
+    except ValueError as e:
+        logger.error("Subject {}: Can't determine subject Id for: {}"
+                     .format(session.subject, e))
+        return -1
+
+    # storing bidsified subject id into session object
+    # optional, but can be easely retrieved
+    session.sub_values["participant_id"] = "sub-" + session.subject
+    # looking for subject in dataframe
     prefix = "pat"
+    index = df_subjects.loc[df_subjects[prefix] == sub_id].index
+    # storing participant group in session
+    session.sub_values["group"] = "patient"
+
     if len(index) == 0:
         # Subject not in patient list, looking in control
-        index = df_subjects.loc[df_subjects["cnt"] == sub_id].index
+        prefix = "cnt"
+        index = df_subjects.loc[df_subjects[prefix] == sub_id].index
+        session.sub_values["group"] = "control"
         if len(index) == 0:
             raise KeyError("Subject {} not found in table"
                            .format(sub_id))
-        status = 1
-        prefix = "cnt"
+    if len(index) > 1:
+        logger.warning("Subject {}: several column entries present"
+                       .format(sub_id))
     index = index[0]
 
     # retrieving demographics
-    # <sex>_<age>_<education>
-    line = df_subjects.loc[index, prefix + "_sae"].split("_")
-    sex = line[0]
-    age = int(line[1])
-    education = int(line[2])
-    session.sub_values["participant_id"] = "sub-" + session.subject
-    session.sub_values["sex"] = sex
-    session.sub_values["age"] = age
-    session.sub_values["education"] = education
+    sex = df_subjects.loc[index, prefix + "_sex"]
+    age = df_subjects.loc[index, prefix + "_age"]
+    education = df_subjects.loc[index, prefix + "_edu"]
+
+    # session initialised values are Null
+    # fill them only if they are retrieved from table
+    if pandas.notna(sex):
+        session.sub_values["sex"] = sex
+    if pandas.notna(age):
+        session.sub_values["age"] = float(age)
+    if pandas.notna(education):
+        session.sub_values["education"] = float(education)
 
     # looking for pairing
-    if status == 0:
-        session.sub_values["group"] = "patient"
-        session.sub_values["paired"] = "sub-{:03}".format(int(df_subjects
-                                                  .loc[index, "cnt"]))
-    else:
-        session.sub_values["group"] = "control"
-        session.sub_values["paired"] = "sub-{:03}".format(int(df_subjects
-                                                  .loc[index, "pat"]))
+    paired = df_subjects.loc[index, sub_prefix[prefix == "cnt"]]
+    if pandas.notna(paired):
+        session.sub_values["paired"] = "sub-{:03}".format(int(paired))
 
     # looking for order of sessions
-    global scans_map
-    scans_map = {}
+    scans_map.clear()
     scans_order = sorted([os.path.basename(s) for s in
                           tools.lsdirs(os.path.join(rawfolder,
                                                     session.subject),
                                        "s*")
                           ])
+    # looping over session defined in columns
     for ind, s in enumerate(("_1", "_2", "_3")):
         v = "ses-" + str(df_subjects.loc[index, prefix + s]).strip()
         ses = "ses" + s
         if v == "ses-nan":
+            # Session not defined in table, but existing
+            # in source dataset
             session.sub_values[ses] = ""
             logger.warning("Subject {}({}): missing {} value"
                            .format(session.sub_values["participant_id"],
                                    session.sub_values["group"],
                                    ses)
                            )
+        elif v == "ses-OUT":
+            # participant left study
+            logger.warning("Subject {}({}): seems to be abandoned study"
+                           .format(session.sub_values["participant_id"],
+                                   session.sub_values["group"],
+                                   ses)
+                           )
+            return -1
         elif v not in Series:
+            # invalid session name
             logger.critical("Subject {}({}): Invalid {}: {}"
                             .format(session.sub_values["participant_id"],
                                     session.sub_values["group"],
@@ -132,10 +235,13 @@ def SubjectEP(session: BidsSession) -> int:
             raise KeyError("Invalid {}: {}"
                            .format(ses, v))
         else:
+            # session retrieved, storing values
             session.sub_values[ses] = v
-            scans_map[scans_order[ind]] = session.sub_values[ses]
+            scans_map[scans_order[ind]] = v
 
     # checking if all scans are identifyable
+    # if not, additional scans will be stored
+    # with original names
     for scan in scans_order:
         if scan not in scans_map:
             logger.error("Subject {}({}): Can't identify session {}"
@@ -144,45 +250,68 @@ def SubjectEP(session: BidsSession) -> int:
                                  scan))
             scans_map[scan] = scan
 
+    # opional, the sub- prefix added automatically
+    # if not present
     session.subject = "sub-" + session.subject
-    return 0
 
 
 def SessionEP(session: BidsSession) -> int:
-    # retrieving correct session name
+    """
+    1. Set-up session name
+
+    Parameters
+    ----------
+    session: BidsSession
+    """
+    # Setting session name from map
     session.session = scans_map[session.session]
-    return 0
 
 
 def SessionEndEP(session: BidsSession):
-    path = os.path.join(bidsfolder,
+    """
+    1. Checks the series in the prepared folder
+    2. Extract KSS/VAS data from kss_dict to tsv file
+    3. Parces in-scan nBack and KSS/VAS log files
+    """
+    # path contain destination folder, where
+    # all data files are placed
+    path = os.path.join(preparefolder,
                         session.getPath(True))
     out_path = os.path.join(path,
                             "MRI")
-    checkSeries(out_path,
-                session.subject, session.session,
-                False)
 
-    # parcing log files
+    # checking if session contains correct series
+    if not dry_run:
+        checkSeries(out_path,
+                    session.subject, session.session,
+                    False)
+
+    ############################################
+    # Retrieving in-scan task and KSS/VAS data #
+    ############################################
     if session.session == "ses-STROOP":
         return 0
+    # where tsv files are
+    inp_dir = os.path.join(session.in_path, "inp")
+    # where tsv files should be
+    aux_dir = os.path.join(path, "auxiliary")
+    if not os.path.isdir(inp_dir):
+        raise NotADirectoryError(inp_dir)
+
+    # do not copy if we are in dry mode
+    if not dry_run:
+        os.makedirs(aux_dir, exist_ok=True)
+        # just copy file, in real life application
+        # you may parce files
+        for file in ("FCsepNBack.tsv", "VAS.tsv"):
+            file = os.path.join(inp_dir, file)
+            if not os.path.isfile(file):
+                raise FileNotFoundError(file)
+            shutil.copy2(file, aux_dir)
 
-    logs = os.path.join(session.in_path, "inp")
-    aux_d = os.path.join(path, "auxiliary")
-    if not os.path.isdir(logs):
-        raise NotADirectoryError(logs)
-
-    os.makedirs(aux_d, exist_ok=True)
-    for file in ("FCsepNBack.tsv", "VAS.tsv"):
-        file = os.path.join(logs, file)
-        if not os.path.isfile(file):
-            raise FileNotFoundError(file)
-        shutil.copy2(file, aux_d)
-
-    for file in ("FCsepNBack.json", "VAS.json"):
-        file = os.path.join(resources, file)
-        if not os.path.isfile(file):
-            raise FileNotFoundError(file)
-        shutil.copy2(file, aux_d)
-
-    return 0
+        # copiyng correspondent json files
+        for file in ("FCsepNBack.json", "VAS.json"):
+            file = os.path.join(plugin_root, file)
+            if not os.path.isfile(file):
+                raise FileNotFoundError(file)
+            shutil.copy2(file, aux_dir)
-- 
GitLab