55
66from prompt_toolkit .shortcuts import CompleteStyle
77from prompt_toolkit .validation import Validator , ValidationError
8- from questionary import Choice
98
109from cirro .api .models .process import Process
1110from cirro .api .models .project import Project
1211from cirro .cli .interactive .common_args import ask_project
13- from cirro .cli .interactive .utils import ask
12+ from cirro .cli .interactive .utils import ask , prompt_wrapper
1413from cirro .cli .models import UploadArguments
15- from cirro .file_utils import get_directory_stats , get_files_in_directory
14+ from cirro .file_utils import get_files_in_directory , get_files_stats
15+
16+
17+ def ask_data_directory (input_value : str ) -> str :
18+ directory_prompt = {
19+ 'type' : 'path' ,
20+ 'name' : 'data_directory' ,
21+ 'message' : 'Enter the full path of the data directory' ,
22+ 'validate' : DataDirectoryValidator ,
23+ 'default' : input_value or '' ,
24+ 'complete_style' : CompleteStyle .READLINE_LIKE ,
25+ 'only_directories' : True
26+ }
27+
28+ answers = prompt_wrapper (directory_prompt )
29+ return answers ['data_directory' ]
30+
31+
32+ def ask_name (input_value : str ) -> str :
33+ name_prompt = {
34+ 'type' : 'input' ,
35+ 'name' : 'name' ,
36+ 'message' : 'What is the name of this dataset?' ,
37+ 'validate' : lambda val : len (val .strip ()) > 0 or 'Please enter a name' ,
38+ 'default' : input_value or ''
39+ }
40+
41+ answers = prompt_wrapper (name_prompt )
42+ return answers ['name' ]
43+
44+
45+ def ask_description (input_value : str ) -> str :
46+ description_prompt = {
47+ 'type' : 'input' ,
48+ 'name' : 'description' ,
49+ 'message' : 'Enter a description of the dataset (optional)' ,
50+ 'default' : input_value or ''
51+ }
52+
53+ answers = prompt_wrapper (description_prompt )
54+ return answers ['description' ]
1655
1756
1857class DataDirectoryValidator (Validator ):
@@ -25,20 +64,22 @@ def validate(self, document):
2564 )
2665
2766
28- def confirm_data_directory (directory : str , files : List [str ]):
29- stats = get_directory_stats (directory , files )
30- is_accepted = ask (
31- 'confirm' ,
32- f'Please confirm that you wish to upload { stats ["numberOfFiles" ]} files ({ stats ["sizeFriendly" ]} )' ,
33- default = True
34- )
67+ def confirm_data_files (data_directory : str , files : List [str ]):
68+ stats = get_files_stats ([
69+ Path (data_directory ) / file
70+ for file in files
71+ ])
3572
36- if not is_accepted :
73+ if not ask (
74+ "confirm" ,
75+ f'Please confirm that you wish to upload { stats ["numberOfFiles" ]} files ({ stats ["sizeFriendly" ]} )'
76+ ):
3777 sys .exit (1 )
3878
3979
4080def ask_process (processes : List [Process ], input_value : str ) -> str :
4181 process_names = [process .name for process in processes ]
82+ process_names .sort ()
4283 return ask (
4384 'select' ,
4485 'What type of files?' ,
@@ -47,61 +88,114 @@ def ask_process(processes: List[Process], input_value: str) -> str:
4788 )
4889
4990
50- def gather_upload_arguments (input_params : UploadArguments , projects : List [Project ], processes : List [Process ]):
51- input_params ['project' ] = ask_project (projects , input_params .get ('project' ))
91+ def ask_include_hidden () -> bool :
92+ return prompt_wrapper ({
93+ 'type' : 'confirm' ,
94+ 'message' : "Include hidden files (expert: e.g. Zarr)" ,
95+ 'name' : 'include_hidden' ,
96+ 'default' : False
97+ })['include_hidden' ]
98+
99+
100+ def ask_files_in_directory (data_directory ) -> List [str ]:
101+
102+ # Ask whether hidden files should be included
103+ include_hidden = ask_include_hidden ()
52104
53- input_params ['data_directory' ] = ask (
54- 'path' ,
55- 'Enter the full path of the data directory' ,
56- required = True ,
57- validate = DataDirectoryValidator ,
58- default = input_params .get ('data_directory' ) or '' ,
59- complete_style = CompleteStyle .READLINE_LIKE ,
60- only_directories = True
105+ # Get the list of all files in the directory
106+ # (relative to the data_directory)
107+ files = get_files_in_directory (
108+ data_directory ,
109+ include_hidden = include_hidden
61110 )
62111
63- upload_method = ask (
112+ choices = [
113+ "Upload all files" ,
114+ "Select files from a list" ,
115+ "Select files with a naming pattern (glob)"
116+ ]
117+
118+ choice = ask (
64119 'select' ,
65- 'What files would you like to upload?' ,
66- choices = [
67- Choice ('Upload all files in directory' , 'all' ),
68- Choice ('Choose files from a list' , 'select' ),
69- Choice ('Specify a glob pattern' , 'glob' ),
70- ]
120+ 'Which files would you like to upload from this dataset?' ,
121+ choices = choices
71122 )
72- input_params ['files' ] = get_files_in_directory (input_params ['data_directory' ])
73- if upload_method == 'select' :
74- input_params ['files' ] = ask (
75- 'checkbox' ,
76- 'Select the files you wish to upload' ,
77- choices = input_params ['files' ]
123+
124+ if choice == choices [0 ]:
125+ return files
126+ elif choice == choices [1 ]:
127+ return ask_dataset_files_list (files )
128+ else :
129+ return ask_dataset_files_glob (files )
130+
131+
132+ def ask_dataset_files_list (files : List [str ]) -> List [str ]:
133+ return prompt_wrapper ({
134+ 'type' : 'checkbox' ,
135+ 'name' : 'files' ,
136+ 'message' : 'Select the files to upload' ,
137+ 'choices' : files
138+ })['files' ]
139+
140+
141+ def ask_dataset_files_glob (files : List [str ]) -> List [str ]:
142+
143+ selected_files = ask_dataset_files_glob_single (files )
144+ confirmed = ask (
145+ "confirm" ,
146+ f'Number of files selected: { len (selected_files ):} / { len (files ):,} '
147+ )
148+ while not confirmed :
149+ selected_files = ask_dataset_files_glob_single (files )
150+ confirmed = ask (
151+ "confirm" ,
152+ f'Number of files selected: { len (selected_files ):} / { len (files ):,} '
78153 )
79- elif upload_method == 'glob' :
80- matching_files = None
81- while not matching_files :
82- glob_pattern = ask ('text' , 'Glob pattern:' )
83- matching_files = [f for f in input_params ['files' ] if fnmatch (f , glob_pattern )]
84- if len (matching_files ) == 0 :
85- print ('Glob pattern does not match any files, please specify another' )
86154
87- input_params ['files' ] = matching_files
155+ if len (selected_files ) == 0 :
156+ raise RuntimeWarning ("No files selected" )
157+
158+ return selected_files
159+
160+
161+ def ask_dataset_files_glob_single (files : List [str ]) -> List [str ]:
162+
163+ print ("All Files:" )
164+ for file in files :
165+ print (f" - { file } " )
166+
167+ answers = prompt_wrapper ({
168+ 'type' : 'text' ,
169+ 'name' : 'glob' ,
170+ 'message' : 'Select files by naming pattern (using the * wildcard)' ,
171+ 'default' : '*'
172+ })
173+
174+ selected_files = [
175+ file
176+ for file in files
177+ if fnmatch (file , answers ['glob' ])
178+ ]
179+
180+ print ("Selected Files:" )
181+ for file in selected_files :
182+ print (f" - { file } " )
88183
89- confirm_data_directory (input_params ['data_directory' ], input_params ['files' ])
184+ return selected_files
185+
186+
187+ def gather_upload_arguments (input_params : UploadArguments , projects : List [Project ], processes : List [Process ]):
188+ input_params ['project' ] = ask_project (projects , input_params .get ('project' ))
189+
190+ input_params ['data_directory' ] = ask_data_directory (input_params .get ('data_directory' ))
191+ files = ask_files_in_directory (input_params ['data_directory' ])
192+
193+ confirm_data_files (input_params ['data_directory' ], files )
90194
91195 input_params ['process' ] = ask_process (processes , input_params .get ('process' ))
92196
93197 data_directory_name = Path (input_params ['data_directory' ]).name
94198 default_name = input_params .get ('name' ) or data_directory_name
95- input_params ['name' ] = ask (
96- 'text' ,
97- 'What is the name of this dataset?' ,
98- default = default_name ,
99- validate = lambda val : len (val .strip ()) > 0 or 'Please enter a name'
100- )
101- input_params ['description' ] = ask (
102- 'text' ,
103- 'Enter a description of the dataset (optional)' ,
104- default = input_params .get ('description' ) or ''
105- )
106-
107- return input_params
199+ input_params ['name' ] = ask_name (default_name )
200+ input_params ['description' ] = ask_description (input_params .get ('description' ))
201+ return input_params , files
0 commit comments