diff --git a/extensions/chaeo/examples/batch_run_patches.py b/extensions/chaeo/examples/batch_run_patches.py
index acc6d8aa697ddd6695f699295d7b0cb45b731b38..f0edacd718c00a6bcc3b67354f5ec8139ec5296b 100644
--- a/extensions/chaeo/examples/batch_run_patches.py
+++ b/extensions/chaeo/examples/batch_run_patches.py
@@ -1,30 +1,20 @@
 from pathlib import Path
 import re
-from time import localtime, strftime
+# from time import localtime, strftime
 
 import pandas as pd
 
+from extensions.chaeo.util import autonumber_new_directory, get_matching_files
 from extensions.chaeo.workflows import export_patches_from_multichannel_zstack
+
 from model_server.accessors import InMemoryDataAccessor, write_accessor_data_to_file
 
 if __name__ == '__main__':
-    where_czi = Path(
-        'z:/rhodes/projects/proj0004-marine-photoactivation/data/exp0038/AutoMic/20230906-163415/Selection'
-    )
-
-    where_output_root = Path(
-        'c:/Users/rhodes/projects/proj0011-plankton-seg/exp0009/output'
-    )
-    yyyymmdd = strftime('%Y%m%d', localtime())
-
-    idx = 0
-    for ff in Path(where_output_root).iterdir():
-        ma = re.match(f'batch-output-{yyyymmdd}-([\d]+)', ff.name)
-        if ma:
-            idx = max(idx, int(ma.groups()[0]) + 1 )
+    where_czi = 'z:/rhodes/projects/proj0004-marine-photoactivation/data/exp0038/AutoMic/20230906-163415/Selection'
 
-    where_output = Path(
-        where_output_root / f'batch-output-{yyyymmdd}-{idx:04d}'
+    where_output = autonumber_new_directory(
+        'c:/Users/rhodes/projects/proj0011-plankton-seg/exp0009/output',
+        'batch-output'
     )
 
     csv_args = {'mode': 'w', 'header': True} # when creating file
@@ -32,25 +22,18 @@ if __name__ == '__main__':
 
     #TODO: try/catch blocks and error handling around workflow calls
     #TODO: pack JSON-serializable workflow inputs
-    for ff in where_czi.iterdir():
-
-        pattern = 'Selection--W([\d]+)--P([\d]+)-T([\d]+)'
-        ma = re.match(pattern, ff.stem)
 
-        print(ff)
-        if not ff.suffix.upper() == '.CZI':
-            continue
-        if int(ma.groups()[1]) > 10: # skip second half of set
-            continue
+    input_files = get_matching_files(where_czi, 'czi', coord_filter={'P': (0, 10)})
+    for ff in input_files:
 
         export_kwargs = {
-            'input_zstack_path': (where_czi / ff).__str__(),
+            'input_zstack_path': Path(where_czi) / ff.__str__(),
             'ilastik_project_file': px_ilp.__str__(),
             'pxmap_threshold': 0.25,
             'pixel_class': 0,
             'zmask_channel': 0,
             'patches_channel': 4,
-            'where_output': where_output.__str__(),
+            'where_output': where_output,
             'mask_type': 'boxes',
             'zmask_filters': {'area': (1e3, 1e8)},
             'zmask_expand_box_by': (128, 3),
diff --git a/extensions/chaeo/util.py b/extensions/chaeo/util.py
new file mode 100644
index 0000000000000000000000000000000000000000..402d05ddff618197ea3a70be016f265de4611560
--- /dev/null
+++ b/extensions/chaeo/util.py
@@ -0,0 +1,36 @@
+from pathlib import Path
+import re
+from time import localtime, strftime
+
+def autonumber_new_directory(where: str, prefix: str) -> str:
+    yyyymmdd = strftime('%Y%m%d', localtime())
+
+    idx = 0
+    for ff in Path(where).iterdir():
+        ma = re.match(f'{prefix}-{yyyymmdd}-([\d]+)', ff.name)
+        if ma:
+            idx = max(idx, int(ma.groups()[0]) + 1)
+
+    return (Path(where) / f'batch-output-{yyyymmdd}-{idx:04d}').__str__()
+
+def get_matching_files(where: str, ext: str, coord_filter: dict={}) -> str:
+    files = []
+
+    def is_filtered_out(ff):
+        if ff.suffix.upper() != f'.{ext}'.upper():
+            return True
+        coords = {
+            m[0]: int(m[1]) for m in re.findall('-([a-zA-Z])(\d+)', ff.name)
+        }
+        for fk in coord_filter.keys():
+            if fk in coords.keys():
+                cmin, cmax = coord_filter[fk]
+                if coords[fk] < cmin or coords[fk] > cmax:
+                    return True
+        return False
+
+    for ff in Path(where).iterdir():
+        if is_filtered_out(ff):
+            continue
+        files.append(ff.__str__())
+    return files
\ No newline at end of file