1010
1111def get_data_path (filename : str ) -> Path :
1212 """Get the path to a data file included in the package.
13-
13+
1414 Parameters
1515 ----------
1616 filename : str
1717 Name of the file to get the path for. This should be relative to the data directory.
1818 For example: "sub-01/ses-baseline/anat/sub-01_ses-baseline_T1w.nii"
19-
19+
2020 Returns
2121 -------
2222 Path
2323 Path to the requested data file
24-
24+
2525 Raises
2626 ------
2727 FileNotFoundError
2828 If the requested file is not found in the package data
2929 """
3030 # Get the path to the data directory
3131 data_dir = Path (__file__ ).parent .parent / "data"
32-
32+
3333 # Construct the full path
3434 full_path = data_dir / filename
35-
35+
3636 # Check if the file exists
3737 if not full_path .exists ():
38- raise FileNotFoundError (f"Could not find data file { filename } in data directory" )
39-
38+ raise FileNotFoundError (
39+ f"Could not find data file { filename } in data directory"
40+ )
41+
4042 return full_path
4143
4244
4345def get_default_anat () -> Path :
4446 """Get the path to the default anatomical image.
45-
47+
4648 Returns
4749 -------
4850 Path
@@ -54,7 +56,7 @@ def get_default_anat() -> Path:
5456
5557def get_default_anat_data () -> nibabel .Nifti1Image :
5658 """Get the default anatomical image as a nibabel image object.
57-
59+
5860 Returns
5961 -------
6062 nibabel.Nifti1Image
@@ -65,56 +67,58 @@ def get_default_anat_data() -> nibabel.Nifti1Image:
6567
6668def extract_subject_id (input_str : str ) -> str :
6769 """Extract subject ID from various input formats.
68-
70+
6971 This function can handle:
7072 - Full paths (e.g., "/path/to/sub-123/anat/file.nii")
7173 - Subject IDs with prefix (e.g., "sub-123")
7274 - Raw subject IDs (e.g., "123")
73-
75+
7476 Parameters
7577 ----------
7678 input_str : str
7779 Input string containing a subject ID in any format
78-
80+
7981 Returns
8082 -------
8183 str
8284 Extracted subject ID without the 'sub-' prefix
83-
85+
8486 Raises
8587 ------
8688 ValueError
8789 If no valid subject ID can be extracted
8890 """
8991 # Pattern to match 'sub-XXXX' in various contexts
9092 # The pattern captures everything after 'sub-' until it hits a non-alphanumeric character or the end of the string
91- pattern = r' sub-([a-zA-Z0-9]+)(?:_|$)'
92-
93+ pattern = r" sub-([a-zA-Z0-9]+)(?:_|$)"
94+
9395 # Try to find a match
9496 match = re .search (pattern , input_str )
95-
97+
9698 if match :
9799 # Return the captured group (the subject ID without 'sub-' prefix)
98100 return match .group (1 )
99101 else :
100102 # If no 'sub-' prefix is found, check if the input is a valid subject ID
101- if re .match (r' ^[a-zA-Z0-9]+$' , input_str ):
103+ if re .match (r" ^[a-zA-Z0-9]+$" , input_str ):
102104 return input_str
103105 else :
104106 # Try a more flexible approach for paths
105- path_match = re .search (r' sub-([a-zA-Z0-9]+)' , input_str )
107+ path_match = re .search (r" sub-([a-zA-Z0-9]+)" , input_str )
106108 if path_match :
107109 return path_match .group (1 )
108110 else :
109- raise ValueError (f"Could not extract a valid subject ID from '{ input_str } '" )
111+ raise ValueError (
112+ f"Could not extract a valid subject ID from '{ input_str } '"
113+ )
110114
111115
112116def copy_default_anat_to_subject (bids_dir : Union [str , Path ], subject_id : str ) -> dict :
113117 """Copy the default anatomical image to a PET subject's folder in a BIDS dataset.
114-
118+
115119 This function extracts the subject ID from the provided string using regex,
116120 then copies the default anatomical image and its JSON metadata to the subject's folder in the BIDS dataset.
117-
121+
118122 Parameters
119123 ----------
120124 bids_dir : Union[str, Path]
@@ -124,7 +128,7 @@ def copy_default_anat_to_subject(bids_dir: Union[str, Path], subject_id: str) ->
124128 - Full path (e.g., "/path/to/sub-123/anat/file.nii")
125129 - Subject ID with prefix (e.g., "sub-123")
126130 - Raw subject ID (e.g., "123")
127-
131+
128132 Returns
129133 -------
130134 dict
@@ -135,7 +139,7 @@ def copy_default_anat_to_subject(bids_dir: Union[str, Path], subject_id: str) ->
135139 'created_dirs': List of paths to newly created directories,
136140 'created_files': List of paths to newly created files
137141 }
138-
142+
139143 Raises
140144 ------
141145 FileNotFoundError
@@ -145,71 +149,71 @@ def copy_default_anat_to_subject(bids_dir: Union[str, Path], subject_id: str) ->
145149 """
146150 # Convert bids_dir to Path if it's a string
147151 bids_dir = Path (bids_dir )
148-
152+
149153 # Check if the BIDS directory exists
150154 if not bids_dir .exists ():
151155 raise FileNotFoundError (f"BIDS directory { bids_dir } does not exist" )
152-
156+
153157 # Extract subject ID using regex
154158 try :
155159 extracted_id = extract_subject_id (subject_id )
156160 except ValueError as e :
157161 raise ValueError (f"Invalid subject ID: { e } " )
158-
162+
159163 # Create the subject directory structure
160164 subject_dir = bids_dir / f"sub-{ extracted_id } "
161-
165+
162166 # Check if the subject directory exists
163167 if not subject_dir .exists ():
164168 raise FileNotFoundError (f"Subject directory { subject_dir } does not exist" )
165-
169+
166170 anat_dir = subject_dir / "anat"
167-
171+
168172 # Create the anatomical directory if it doesn't exist
169173 created_dirs = []
170174 if not anat_dir .exists ():
171175 anat_dir .mkdir (parents = True , exist_ok = True )
172176 created_dirs .append (anat_dir )
173-
177+
174178 # Define the target file paths
175179 target_nii = anat_dir / f"sub-{ extracted_id } _T1w.nii"
176180 target_json = anat_dir / f"sub-{ extracted_id } _T1w.json"
177-
181+
178182 # Get the source file paths
179183 source_nii = get_default_anat ()
180184 source_json = get_data_path ("sub-01/ses-baseline/anat/sub-01_ses-baseline_T1w.json" )
181-
185+
182186 # Copy the files
183187 created_files = []
184188 shutil .copy2 (source_nii , target_nii )
185189 created_files .append (target_nii )
186-
190+
187191 shutil .copy2 (source_json , target_json )
188192 created_files .append (target_json )
189-
193+
190194 print (f"Copied default anatomical image to { target_nii } " )
191195 print (f"Copied default anatomical metadata to { target_json } " )
192-
196+
193197 # Return information about created files and directories
194198 return {
195- ' subject_dir' : subject_dir ,
196- ' anat_dir' : anat_dir ,
197- ' created_dirs' : created_dirs ,
198- ' created_files' : created_files
199+ " subject_dir" : subject_dir ,
200+ " anat_dir" : anat_dir ,
201+ " created_dirs" : created_dirs ,
202+ " created_files" : created_files ,
199203 }
200204
201205
202206def remove_default_anat (
203- bids_dir : Union [str , Path ],
204- subject_id : Optional [str ] = None ,
205- created_items : Optional [Dict ] = None
207+ bids_dir : Union [str , Path ],
208+ subject_id : Optional [str ] = None ,
209+ created_items : Optional [Dict ] = None ,
206210) -> None :
207211 """Remove the default anatomical image and directory created for a subject.
208-
212+
209213 This function can be used in two ways:
210214 1. With the bids_dir and subject_id to identify what to remove
211215 2. With the bids_dir and the dictionary returned by copy_default_anat_to_subject
212-
216+
213217 Parameters
214218 ----------
215219 bids_dir : Union[str, Path]
@@ -218,11 +222,11 @@ def remove_default_anat(
218222 Subject ID in any format (if created_items is not provided)
219223 created_items : Optional[Dict]
220224 Dictionary returned by copy_default_anat_to_subject (if subject_id is not provided)
221-
225+
222226 Returns
223227 -------
224228 None
225-
229+
226230 Raises
227231 ------
228232 ValueError
@@ -232,68 +236,67 @@ def remove_default_anat(
232236 """
233237 # Convert bids_dir to Path if it's a string
234238 bids_dir = Path (bids_dir )
235-
239+
236240 # Check if the BIDS directory exists
237241 if not bids_dir .exists ():
238242 raise FileNotFoundError (f"BIDS directory { bids_dir } does not exist" )
239-
243+
240244 # Determine which approach to use
241245 if subject_id is not None and created_items is not None :
242246 raise ValueError ("Cannot provide both subject_id and created_items" )
243247 elif subject_id is None and created_items is None :
244248 raise ValueError ("Must provide either subject_id or created_items" )
245-
249+
246250 if created_items is not None :
247251 # Use the dictionary returned by copy_default_anat_to_subject
248- anat_dir = created_items [' anat_dir' ]
249- created_files = created_items [' created_files' ]
250- created_dirs = created_items [' created_dirs' ]
252+ anat_dir = created_items [" anat_dir" ]
253+ created_files = created_items [" created_files" ]
254+ created_dirs = created_items [" created_dirs" ]
251255 else :
252256 # Extract subject ID using regex
253257 try :
254258 extracted_id = extract_subject_id (subject_id )
255259 except ValueError as e :
256260 raise ValueError (f"Invalid subject ID: { e } " )
257-
261+
258262 # Create the subject directory structure
259263 subject_dir = bids_dir / f"sub-{ extracted_id } "
260-
264+
261265 # Check if the subject directory exists
262266 if not subject_dir .exists ():
263267 raise FileNotFoundError (f"Subject directory { subject_dir } does not exist" )
264-
268+
265269 anat_dir = subject_dir / "anat"
266-
270+
267271 # Define the file paths
268272 target_nii = anat_dir / f"sub-{ extracted_id } _T1w.nii"
269273 target_json = anat_dir / f"sub-{ extracted_id } _T1w.json"
270-
274+
271275 # Check if the files exist
272276 created_files = []
273277 if target_nii .exists ():
274278 created_files .append (target_nii )
275279 if target_json .exists ():
276280 created_files .append (target_json )
277-
281+
278282 # Check if the directory exists and is empty
279283 created_dirs = []
280284 if anat_dir .exists ():
281285 # Only add to created_dirs if it's empty or only contains our files
282286 remaining_files = set (anat_dir .iterdir ()) - set (created_files )
283287 if not remaining_files :
284288 created_dirs .append (anat_dir )
285-
289+
286290 # Remove the files
287291 for file_path in created_files :
288292 if file_path .exists ():
289293 file_path .unlink ()
290294 print (f"Removed file: { file_path } " )
291-
295+
292296 # Remove the directories (only if they're empty)
293297 for dir_path in created_dirs :
294298 if dir_path .exists () and not any (dir_path .iterdir ()):
295299 dir_path .rmdir ()
296300 print (f"Removed directory: { dir_path } " )
297-
298- print ("Cleanup completed successfully" )
299301
302+ print ("Cleanup completed successfully" )
0 commit comments