@@ -362,7 +362,49 @@ def process_glob_to_find_datatype_folders(
362362# -----------------------------------------------------------------------------
363363
364364
365- def search_for_wildcards (
365+ def filter_names_by_datetime_range (
366+ names : List [str ],
367+ format_type : str ,
368+ start_timepoint : datetime ,
369+ end_timepoint : datetime ,
370+ ) -> List [str ]:
371+ """
372+ Filter a list of names based on a datetime range.
373+ Assumes all names contain the format_type pattern (e.g., date-*, time-*)
374+ as they were searched using this pattern.
375+
376+ Parameters
377+ ----------
378+ names : List[str]
379+ List of names to filter, all containing the datetime pattern
380+ format_type : str
381+ One of "datetime", "time", or "date"
382+ start_timepoint : datetime
383+ Start of the datetime range
384+ end_timepoint : datetime
385+ End of the datetime range
386+
387+ Returns
388+ -------
389+ List[str]
390+ Filtered list of names that fall within the datetime range
391+ """
392+ filtered_names : List [str ] = []
393+ for candidate in names :
394+ candidate_basename = candidate if isinstance (candidate , str ) else candidate .name
395+ value = get_values_from_bids_formatted_name ([candidate_basename ], format_type )[0 ]
396+ try :
397+ candidate_timepoint = datetime .strptime (
398+ value , canonical_tags .get_datetime_format (format_type )
399+ )
400+ if start_timepoint <= candidate_timepoint <= end_timepoint :
401+ filtered_names .append (candidate )
402+ except ValueError :
403+ continue
404+ return filtered_names
405+
406+
407+ def search_with_tags (
366408 cfg : Configs ,
367409 base_folder : Path ,
368410 local_or_central : str ,
@@ -400,68 +442,69 @@ def search_for_wildcards(
400442
401443 sub : optional subject to search for sessions in. If not provided,
402444 will search for subjects rather than sessions.
403-
404445 """
405446 new_all_names : List [str ] = []
406447 for name in all_names :
407- if canonical_tags .tags ("*" ) in name or "@DATETO@" in name :
408- search_str = name .replace (canonical_tags .tags ("*" ), "*" )
409- # If a date-range tag is present, extract dates and update the search string.
410- if "@DATETO@" in name :
411- m = re .search (r"(\d{8})@DATETO@(\d{8})" , name )
412- if not m :
413- raise ValueError (
414- "Invalid date range format in name: " + name
415- )
416- start_str , end_str = m .groups ()
417- try :
418- start_date = datetime .strptime (start_str , "%Y%m%d" )
419- end_date = datetime .strptime (end_str , "%Y%m%d" )
420- except ValueError as e :
421- raise ValueError ("Invalid date in date range: " + str (e ))
422- # Replace the date-range substring with "date-*"
423- search_str = re .sub (r"\d{8}@DATETO@\d{8}" , "date-*" , name )
424- # Use the helper function to perform the glob search.
425- if sub :
426- matching_names : List [str ] = search_sub_or_ses_level (
427- cfg ,
428- base_folder ,
429- local_or_central ,
430- sub ,
431- search_str = search_str ,
432- )[0 ]
433- else :
434- matching_names = search_sub_or_ses_level (
435- cfg , base_folder , local_or_central , search_str = search_str
436- )[0 ]
437- # If a date-range tag was provided, further filter the results.
438- if "@DATETO@" in name :
439- filtered_names : List [str ] = []
440- for candidate in matching_names :
441- candidate_basename = (
442- candidate
443- if isinstance (candidate , str )
444- else candidate .name
445- )
446- values_list = get_values_from_bids_formatted_name (
447- [candidate_basename ], "date"
448- )
449- if not values_list :
450- continue
451- candidate_date_str = values_list [0 ]
452- try :
453- candidate_date = datetime .strptime (
454- candidate_date_str , "%Y%m%d"
455- )
456- except ValueError :
457- continue
458- if start_date <= candidate_date <= end_date :
459- filtered_names .append (candidate )
460- matching_names = filtered_names
461- new_all_names += matching_names
448+ if not (canonical_tags .tags ("*" ) in name or
449+ canonical_tags .tags ("DATETO" ) in name or
450+ canonical_tags .tags ("TIMETO" ) in name or
451+ canonical_tags .tags ("DATETIMETO" ) in name ):
452+ new_all_names .append (name )
453+ continue
454+
455+ # Initialize search string
456+ search_str = name
457+
458+ # Handle wildcard replacement first if present
459+ if canonical_tags .tags ("*" ) in name :
460+ search_str = search_str .replace (canonical_tags .tags ("*" ), "*" )
461+
462+ # Handle datetime ranges
463+ format_type = tag = None
464+ if canonical_tags .tags ("DATETO" ) in search_str :
465+ format_type = "date"
466+ tag = canonical_tags .tags ("DATETO" )
467+ elif canonical_tags .tags ("TIMETO" ) in search_str :
468+ format_type = "time"
469+ tag = canonical_tags .tags ("TIMETO" )
470+ elif canonical_tags .tags ("DATETIMETO" ) in search_str :
471+ format_type = "datetime"
472+ tag = canonical_tags .tags ("DATETIMETO" )
473+
474+ if format_type is not None :
475+ assert tag is not None , "format and tag should be set together"
476+ search_str = validation .format_and_validate_datetime_search_str (search_str , format_type , tag )
477+
478+ # Use the helper function to perform the glob search
479+ if sub :
480+ matching_names : List [str ] = search_sub_or_ses_level (
481+ cfg ,
482+ base_folder ,
483+ local_or_central ,
484+ sub ,
485+ search_str = search_str ,
486+ )[0 ]
462487 else :
463- new_all_names += [name ]
464- # Remove duplicates in case of wildcard overlap.
488+ matching_names = search_sub_or_ses_level (
489+ cfg , base_folder , local_or_central , search_str = search_str
490+ )[0 ]
491+
492+ # Filter results by datetime range if one was present
493+ if format_type is not None and tag is not None :
494+ expected_values = validation .get_expected_num_datetime_values (format_type )
495+ full_tag_regex = fr"(\d{{{ expected_values } }}){ re .escape (tag )} (\d{{{ expected_values } }})"
496+ match = re .search (full_tag_regex , name )
497+ if match : # We know this is true because format_and_validate_datetime_search_str succeeded
498+ start_str , end_str = match .groups ()
499+ start_timepoint = datetime .strptime (start_str , canonical_tags .get_datetime_format (format_type ))
500+ end_timepoint = datetime .strptime (end_str , canonical_tags .get_datetime_format (format_type ))
501+ matching_names = filter_names_by_datetime_range (
502+ matching_names , format_type , start_timepoint , end_timepoint
503+ )
504+
505+ new_all_names .extend (matching_names )
506+
507+ # Remove duplicates in case of wildcard overlap
465508 new_all_names = list (set (new_all_names ))
466509 return new_all_names
467510
0 commit comments