labdata 0.0.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,561 @@
1
+ from ..utils import *
2
+ import traceback
3
+
4
+ def load_analysis_object(analysis):
5
+ if not analysis in prefs['compute']['analysis'].keys():
6
+ print(f'\n\nCould not find [{analysis}] analysis.\n\t The analysis are {list(prefs["compute"]["analysis"].keys())}\n\n')
7
+ raise ValueError('Add the analysis to the "compute" section of the preference file {analysis_name:analysis_object}.\n\n')
8
+
9
+ import labdata
10
+ return eval(prefs['compute']['analysis'][analysis])
11
+
12
+ def handle_compute(job_id):
13
+ from ..schema import ComputeTask
14
+ jobinfo = pd.DataFrame((ComputeTask() & dict(job_id = job_id)).fetch())
15
+ if not len(jobinfo):
16
+ print(f'No task with id: {job_id}')
17
+ jobinfo = jobinfo.iloc[0]
18
+ if jobinfo.task_waiting == 0:
19
+ print(f'Task {job_id} is running on {jobinfo.task_host}')
20
+ obj = load_analysis_object(jobinfo.task_name)(jobinfo.job_id)
21
+ return obj
22
+
23
+ def parse_analysis(analysis, job_id = None,
24
+ subject = None,
25
+ session = None,
26
+ secondary_args = [],
27
+ parameter_number = None,
28
+ full_command = None,
29
+ launch_singularity = False,
30
+ force_submit = False,
31
+ **kwargs):
32
+
33
+ obj = load_analysis_object(analysis)(job_id)
34
+ obj.secondary_parse(secondary_args, parameter_number)
35
+ if obj.job_id is None:
36
+ # then we have to create jobs and assign
37
+ from ..schema import ComputeTask
38
+ # first check if there is a task that has already been submitted with the exact same command.
39
+ # this has a caveat: if the order of the arguments is switched, it wont work..
40
+ if not full_command is None:
41
+ task_command = str(full_command)
42
+ if len(full_command) >500:
43
+ task_command = task_command[:499]
44
+ submittedjobs = (ComputeTask() & dict(task_cmd = task_command))
45
+ if len(submittedjobs) and not force_submit:
46
+ print(f'{tcolor["r"]("A similar task command is already submitted:")} \n {submittedjobs}')
47
+ return [], None
48
+ if not subject is None or not session is None:
49
+ datasets = obj.find_datasets(subject_name = subject, session_name = session)
50
+ else:
51
+ datasets = None
52
+ job_ids = obj.place_tasks_in_queue(datasets,task_cmd = full_command, force_submit = force_submit)
53
+ # now we have the job ids, need to figure out how to launch the jobs
54
+ return job_ids, obj # returns the job ids and the task
55
+
56
+ def check_archived(jobid, check_local=False):
57
+ from ..schema import ComputeTask,File
58
+ # check if the files are archived before launching remote jobs
59
+ files = File() & (ComputeTask.AssignedFiles() & f'job_id = {jobid}')
60
+ if not len(files):
61
+ return False # there are no files to fetch
62
+ if check_local:
63
+ localfiles, _ = files.check_if_files_local()
64
+ if len(self) == len(localfiles):
65
+ return False # all files are local
66
+ files_archived = files.check_if_files_archived(restore = False, suppress_error = True)
67
+ if files_archived:
68
+ response = None
69
+ while response not in ['y','yes','n','no']:
70
+ response = input(f'Files are archived for {jobid}, do you want to unarchive?' + " [Y/N]: ")
71
+ response = response.lower()
72
+ if response in ['y','yes']:
73
+ files_archived = files.check_if_files_archived(restore = True, suppress_error = True)
74
+ return files_archived
75
+
76
+ def run_analysis(target, jobids, compute_obj):
77
+ '''
78
+ Launches a set if analysis on a specific target
79
+ '''
80
+
81
+ from .singularity import run_on_apptainer
82
+ container_file = (Path(prefs['compute']['containers']['local'])/compute_obj.container).with_suffix('.sif')
83
+ def _get_cmds(jobids,
84
+ container_file = container_file,
85
+ bind = [],
86
+ bind_from_prefs = True):
87
+ cmds = []
88
+ from shutil import which
89
+ for j in jobids:
90
+ if container_file.exists() and which('apptainer'):
91
+ cmds.append(run_on_apptainer(container_file,
92
+ command = f'labdata2 task {j}',
93
+ cuda = compute_obj.cuda,
94
+ bind = bind,
95
+ bind_from_prefs = bind_from_prefs,
96
+ dry_run = True))
97
+ else:
98
+ cmds.append(f'labdata2 task {j}')
99
+ return cmds
100
+ task_host = prefs['hostname']
101
+ from ..schema import ComputeTask
102
+ if target == 'slurm': # run with slurm
103
+ from .schedulers import slurm_exists, slurm_submit
104
+ if slurm_exists():
105
+ for jid,cmd in zip(jobids,_get_cmds(jobids)):
106
+ begin = None
107
+ files_archived = check_archived(jid,check_local = True)
108
+ if files_archived:
109
+ begin = "now+5hour"
110
+ print('Delaying job for 5 hours (retrieve archive). ')
111
+ ComputeTask.update1(dict(job_id = j,task_status = 'WAITING (ARCHIVE)'))
112
+
113
+ if container_file.exists():
114
+ cmd += f' | apptainer exec {container_file} labdata2 logpipe {jid}'
115
+ else:
116
+ cmd += f' | labdata2 logpipe {jid}'
117
+ slurmjob = slurm_submit(compute_obj.name,
118
+ cmd,
119
+ begin = begin,
120
+ ntasks = 1,
121
+ ncpuspertask = DEFAULT_N_JOBS, # change later
122
+ gpus = 1 if compute_obj.cuda else None)
123
+ print(f'Submitted {tcolor["g"](compute_obj.name)} {tcolor["y"](jid)} to slurm [{tcolor["y"](slurmjob)}]')
124
+ ComputeTask.update1(dict(job_id = jid,
125
+ task_host = task_host + f'@{slurmjob}',
126
+ task_target = target))
127
+ else:
128
+ print(f'{tcolor["r"]("Could not find SLURM: did not submit compute tasks:")}')
129
+ print('\t\n'.join(cmds))
130
+ elif target == 'local': # run locally without scheduler
131
+ for cmd in _get_cmds(jobids,compute_obj.cuda):
132
+ os.system(cmd)
133
+ elif 'ec2' in target: # launch dedicated instance on AWS
134
+ # TODO: delayed begin not used here.
135
+ from .ec2 import ec2_cmd_for_launch,ec2_create_instance,ec2_connect
136
+ session,ec2 = ec2_connect()
137
+ for jid, cmd in zip(jobids,_get_cmds(
138
+ jobids,
139
+ compute_obj.cuda,
140
+ container_file = Path('idontexist'))):
141
+ cmd = ec2_cmd_for_launch(compute_obj.container,
142
+ cmd,
143
+ singularity_cuda = compute_obj.cuda,
144
+ append_log = jid)
145
+ # check if the target contains the words small or large
146
+
147
+ instance_type = target.replace('ec2-','')
148
+ if instance_type in compute_obj.ec2.keys():
149
+ instance_opts = compute_obj.ec2[instance_type]
150
+ else:
151
+ # using small instance
152
+ instance_opts = compute_obj.ec2['small']
153
+ ins = ec2_create_instance(ec2, user_data = cmd,
154
+ **instance_opts)
155
+ print(f'Submitted job to {tcolor["r"](ins["id"])} on an ec2 {instance_opts}')
156
+ task_host = ins["id"]
157
+ ComputeTask.update1(dict(job_id = jid,
158
+ task_host = task_host,
159
+ task_target = target))
160
+ else:
161
+ # check if there are remote services to launch
162
+ if 'remotes' in prefs['compute'].keys():
163
+ names = prefs['compute']['remotes'].keys()
164
+ targetname = str(target)
165
+ if target in names:
166
+ target = prefs['compute']['remotes'][target]
167
+ else:
168
+ raise(ValueError(f'Could not find target [{target}]'))
169
+ from .schedulers import ssh_connect,slurm_schedule_remote
170
+ container_file = f"$LABDATA_PATH/containers/{compute_obj.container}.sif"
171
+ with ssh_connect(target['address'],target['user'],target['permission_key']) as conn:
172
+ for j in jobids:
173
+ begin = None
174
+ files_archived = check_archived(j)
175
+ if files_archived:
176
+ begin = "now+5hour"
177
+ print('Delaying job for 5 hours (retrieve archive). ')
178
+ ComputeTask.update1(dict(job_id = j,task_status = 'WAITING (ARCHIVE)'))
179
+ # needs to have LABDATA_PATH defined in the remote
180
+ cmd = run_on_apptainer(container_file,
181
+ command = f'labdata2 task {j}',
182
+ cuda = compute_obj.cuda,
183
+ dry_run = True)
184
+ # generate slurm cmd and launch
185
+ cmd += f' | apptainer exec {container_file} labdata2 logpipe {j}'
186
+ opts = dict()
187
+ nt = str(targetname)
188
+ if compute_obj.name in target['analysis_options']:
189
+ opts = target['analysis_options'][compute_obj.name]
190
+ nt = f'{targetname}@{opts["queue"]}'
191
+ slurmjob = slurm_schedule_remote(cmd,
192
+ conn = conn,
193
+ begin = begin,
194
+ jobname = compute_obj.name+f'_{j}',
195
+ pre_cmds = target['pre_cmds'],
196
+ **opts)
197
+ if not slurmjob is None:
198
+ print(f'Submitted {tcolor["r"](compute_obj.name)} job {tcolor["y"](j)} to {tcolor["y"](nt)}[{tcolor["y"](slurmjob)}]')
199
+ ComputeTask.update1(dict(job_id = j,
200
+ task_target = nt))
201
+
202
+ # this class will execute compute jobs, it should be independent from the CLI but work with it.
203
+ class BaseCompute():
204
+ name = None
205
+ container = 'labdata-base'
206
+ cuda = False
207
+ ec2 = dict(small = dict(instance_type = 'g4dn.2xlarge'), # 8 cpus, 32 GB mem, 200 GB nvme, 1 gpu
208
+ large = dict(instance_type = 'g6.4xlarge',
209
+ availability_zone = 'us-west-2b')) # 16 cpus, 64 GB mem, 600 GB nvme, 1 gpu
210
+
211
+ def __init__(self,job_id, allow_s3 = None):
212
+ '''
213
+ Executes a computation on a dataset, that can be remote or local
214
+ Uses a singularity/apptainer image if possible
215
+ '''
216
+ self.file_filters = ['.'] # selects all files...
217
+ self.parameters = dict()
218
+
219
+ self.job_id = job_id
220
+ if not self.job_id is None:
221
+ self._check_if_taken()
222
+
223
+ self.paths = None
224
+ self.local_path = Path(prefs['local_paths'][0])
225
+ self.scratch_path = Path(prefs['scratch_path'])
226
+ self.assigned_files = None
227
+ self.dataset_key = None
228
+ self.is_container = False
229
+ if allow_s3 is None:
230
+ self.allow_s3 = prefs['allow_s3_download']
231
+ if 'LABDATA_CONTAINER' in os.environ.keys():
232
+ # then it is running inside a container
233
+ self.is_container = True
234
+ #self.is_ec2 = False # then files should be taken from s3
235
+
236
+ def _init_job(self): # to run in the init function
237
+ if not self.job_id is None:
238
+ from ..schema import ComputeTask,dj
239
+ with dj.conn().transaction:
240
+ self.jobquery = (ComputeTask() & dict(job_id = self.job_id))
241
+ job_status = self.jobquery.fetch(as_dict = True)
242
+ if len(job_status):
243
+ if not job_status[0]['task_waiting']:
244
+ if 'SLURM_RESTART_COUNT' in os.environ.keys():
245
+ # then the job is running on slurm.. its a putative restart, try to run it..
246
+ self.set_job_status(job_status = 'WORKING',
247
+ job_waiting = 0)
248
+ else:
249
+ print(f"Compute task [{self.job_id}] is already taken.")
250
+ print(job_status, flush = True)
251
+ return # exit.
252
+ else:
253
+ self.set_job_status(job_status = 'WORKING',
254
+ job_waiting = 0)
255
+
256
+ def cleanup_function(job_id = self.job_id):
257
+ # if it quits then register as canceled and put as waiting
258
+ from ..schema import ComputeTask
259
+ status = (ComputeTask() & dict(job_id = job_id)).fetch(as_dict = True)[0]
260
+ if status['task_status'] in ['WORKING']:
261
+ ComputeTask.update1(dict(job_id = job_id,
262
+ task_status = 'CANCELLED',
263
+ task_waiting = 1,
264
+ task_endtime = datetime.now()))
265
+
266
+ self.cleanup_function = cleanup_function
267
+ self.register_safe_exit()
268
+ par = json.loads(job_status[0]['task_parameters'])
269
+ for k in par.keys():
270
+ self.parameters[k] = par[k]
271
+ self.assigned_files = pd.DataFrame((ComputeTask.AssignedFiles() & dict(job_id = self.job_id)).fetch())
272
+ self.dataset_key = dict(subject_name = job_status[0]['subject_name'],
273
+ session_name = job_status[0]['session_name'],
274
+ dataset_name = job_status[0]['dataset_name'])
275
+ else:
276
+ # that should just be a problem to fix
277
+ raise ValueError(f'job_id {self.job_id} does not exist.')
278
+
279
+ def register_safe_exit(self):
280
+ import safe_exit
281
+ safe_exit.register(self.cleanup_function)
282
+
283
+ def unregister_safe_exit(self):
284
+ import safe_exit
285
+ safe_exit.unregister(self.cleanup_function)
286
+
287
+ def get_files(self, dset, allowed_extensions=[]):
288
+ '''
289
+ Gets the paths and downloads from S3 if needed.
290
+ '''
291
+ if type(dset) is list:
292
+ # then it is a list of dicts, convert to DataFrame
293
+ dset = pd.DataFrame(dset)
294
+ print(dset)
295
+ files = dset.file_path.values
296
+ storage = dset.storage.values
297
+ localpath = Path(prefs['local_paths'][0])
298
+ self.files_existed = True
299
+ localfiles = [find_local_filepath(f,
300
+ allowed_extensions = allowed_extensions) for f in files]
301
+ localfiles = np.unique(list(filter(lambda x: not x is None,localfiles)))
302
+ if not len(localfiles):
303
+ # then you can try downloading the files
304
+ if self.allow_s3: # get the files from s3
305
+ from ..s3 import copy_from_s3
306
+ for s in np.unique(storage):
307
+ # so it can work with multiple storages
308
+ srcfiles = [f for f in files[storage == s]]
309
+ dstfiles = [localpath/f for f in srcfiles]
310
+ print(f'Downloading {len(srcfiles)} files from S3 [{s}].')
311
+ copy_from_s3(srcfiles,dstfiles,storage_name = s)
312
+ localfiles = np.unique([find_local_filepath(
313
+ f,
314
+ allowed_extensions = allowed_extensions) for f in files])
315
+ if len(localfiles):
316
+ self.files_existed = False # delete the files in the end if they were not local.
317
+ else:
318
+ print(files, localpath)
319
+ raise(ValueError('Files not found locally, set allow_s3 in the preferences to download.'))
320
+ return localfiles
321
+
322
+
323
+ def place_tasks_in_queue(self,datasets,task_cmd = None, force_submit = False):
324
+ ''' This will put the tasks in the queue for each dataset.
325
+ If the task and parameters are the same it will return the job_id instead.
326
+ '''
327
+ from ..schema import ComputeTask, Dataset,dj
328
+ job_ids = []
329
+ if datasets is None:
330
+ datasets = [None]
331
+
332
+ for dataset in datasets:
333
+ if not dataset is None: # then there are no associated files.
334
+ files = pd.DataFrame((Dataset.DataFiles() & dataset).fetch())
335
+ idx = []
336
+ for f in self.file_filters:
337
+ idx += list(filter(lambda x: not x is None,[i if f in s else None for i,s in enumerate(
338
+ files.file_path.values)]))
339
+ if len(idx) == 0:
340
+ raise ValueError(f'Could not find valid Dataset.DataFiles for {dataset}')
341
+ files = files.iloc[idx]
342
+ key = dict(dataset,task_name = self.name)
343
+ exists = ComputeTask() & key
344
+ if len(exists):
345
+ d = pd.DataFrame(exists.fetch())
346
+ idx = np.where(np.array(d.task_parameters.values) == json.dumps(self.parameters))[0]
347
+ if len(idx):
348
+ job_id = d.iloc[idx].job_id.values[0]
349
+ print(f'There is a task to analyse dataset {key} with the same parameters. [{job_id}]')
350
+ if force_submit:
351
+ print('Deleting the previous job because force_submit is set.')
352
+ (ComputeTask() & f'job_id = {job_id}').delete(safemode = False) # deleting a previous job because of force_submit
353
+ else:
354
+ continue
355
+ else:
356
+ key = dict(task_name = self.name)
357
+ files = None
358
+
359
+ with dj.conn().transaction:
360
+ job_id = ComputeTask().fetch('job_id')
361
+ if len(job_id):
362
+ job_id = np.max(job_id) + 1
363
+ else:
364
+ job_id = 1
365
+ if not task_cmd is None:
366
+ if len(task_cmd) >500:
367
+ task_cmd = task_cmd[:499]
368
+ ComputeTask().insert1(dict(key,
369
+ job_id = job_id,
370
+ task_waiting = 1,
371
+ task_status = "WAITING",
372
+ task_target = None,
373
+ task_host = None,
374
+ task_cmd = task_cmd,
375
+ task_parameters = json.dumps(self.parameters),
376
+ task_log = None))
377
+ if not files is None:
378
+ ComputeTask.AssignedFiles().insert([dict(job_id = job_id,
379
+ storage = f.storage,
380
+ file_path = f.file_path)
381
+ for i,f in files.iterrows()])
382
+ job_ids.append(job_id)
383
+ return job_ids
384
+
385
+ def find_datasets(self,subject_name = None, session_name = None, dataset_name = None):
386
+ '''
387
+ Find datasets to analyze, this function will search in the proper tables if datasets are available.
388
+ Has to be implemented per Compute class since it varies.
389
+ '''
390
+ raise NotImplementedError('The find_datasets method has to be implemented.')
391
+
392
+ def secondary_parse(self,secondary_arguments,parameter_number = None):
393
+ self._secondary_parse(secondary_arguments,parameter_number)
394
+
395
+ def _secondary_parse(self,secondary_arguments):
396
+ return
397
+
398
+ def _check_if_taken(self):
399
+ if not self.job_id is None:
400
+ from ..schema import ComputeTask, File, dj
401
+ self.jobquery = (ComputeTask() & dict(job_id = self.job_id))
402
+ job_status = self.jobquery.fetch(as_dict = True)
403
+ if len(job_status):
404
+ if job_status[0]['task_waiting']:
405
+ return
406
+ else:
407
+ print(job_status, flush = True)
408
+ raise ValueError(f'job_id {self.job_id} is already taken.')
409
+ return # exit.
410
+ else:
411
+ raise ValueError(f'job_id {self.job_id} does not exist.')
412
+ # get the paths?
413
+ #self.src_paths = pd.DataFrame((ComputeTask.AssignedFiles() &
414
+ # dict(job_id = self.job_id)).fetch())
415
+ #if not len(self.src_paths):
416
+ # self.set_job_status(job_status = 'FAILED',
417
+ # job_log = f'Could not find files for {self.job_id} in ComputeTask.AssignedFiles.')
418
+ # raise ValueError(f'Could not find files for {self.job_id} in ComputeTask.AssignedFiles.')
419
+ else:
420
+ raise ValueError(f'Compute: job_id not specified.')
421
+
422
+ def compute(self):
423
+ '''This calls the compute function.
424
+ If "use_s3" is true it will download the files from s3 when needed.'''
425
+ from ..schema import ComputeTask
426
+ try:
427
+ if not self.job_id is None:
428
+ dd = dict(job_id = self.job_id,
429
+ task_starttime = datetime.now())
430
+ ComputeTask().update1(dd)
431
+ self._compute() # can use the src_paths
432
+ except Exception as err:
433
+ # log the error
434
+ print(f'There was an error processing job {self.job_id}.')
435
+ err = str(traceback.format_exc()) + "ERROR" +str(err)
436
+ print(err)
437
+
438
+ if len(err) > 1999: # then get only the last part of the error.
439
+ err = err[-1900:]
440
+ self.set_job_status(job_status = 'FAILED',
441
+ job_log = f'{err}')
442
+ return
443
+ self._post_compute() # so the rules can insert tables and all.
444
+ # get the job from the DB if the status is not failed, mark completed (remember to clean the log)
445
+ self.jobquery = (ComputeTask() & dict(job_id = self.job_id))
446
+ job_status = self.jobquery.fetch(as_dict = True)
447
+ if not job_status[0]['task_status'] in ['FAILED']:
448
+ self.set_job_status(job_status = 'COMPLETED')
449
+ if not self.job_id is None:
450
+ dd = dict(job_id = self.job_id,
451
+ task_endtime = datetime.now())
452
+ ComputeTask().update1(dd)
453
+
454
+ def set_job_status(self, job_status = None, job_log = None,job_waiting = 0):
455
+ from ..schema import ComputeTask
456
+ if not self.job_id is None:
457
+ dd = dict(job_id = self.job_id,
458
+ task_waiting = job_waiting,
459
+ task_host = prefs['hostname']) # so we know where it failed.)
460
+ if not job_status is None:
461
+ dd['task_status'] = job_status
462
+ if not job_log is None:
463
+ dd['task_log'] = job_log
464
+ ComputeTask.update1(dd)
465
+ if not job_status is None:
466
+ if not 'WORK' in job_status: # display the message
467
+ print(f'Check job_id {self.job_id} : {job_status}')
468
+
469
+ def _post_compute(self):
470
+ '''
471
+ Inserts the data to the database
472
+ '''
473
+ return
474
+
475
+ def _compute(self):
476
+ '''
477
+ Runs the compute job on a scratch folder.
478
+ '''
479
+ return
480
+
481
+ class PopulateCompute(BaseCompute):
482
+ container = 'labdata-base'
483
+ cuda = False
484
+ name = 'populate'
485
+ url = 'http://github.com/jcouto/labdata'
486
+ def __init__(self,job_id, allow_s3 = None, delete_results = False, **kwargs):
487
+ super(PopulateCompute,self).__init__(job_id, allow_s3 = allow_s3)
488
+ self.file_filters = None
489
+ # default parameters
490
+ self.parameters = dict(imports = 'labdata.schema',
491
+ table = 'UnitMetrics',
492
+ processes = 10)
493
+
494
+ self._init_job() # gets the parameters
495
+
496
+ def _secondary_parse(self,arguments,parameter_number):
497
+ '''
498
+ Handles parsing the command line interface
499
+ '''
500
+ import argparse
501
+ parser = argparse.ArgumentParser(
502
+ description = 'Populate arbitrary tables',
503
+ usage = 'populate -- -t <TABLE> -i <IMPORTS>')
504
+
505
+ parser.add_argument('table',action='store',type = str,
506
+ help = 'Table to populate')
507
+ parser.add_argument('-s','--stop-on-errors',action='store_true',default= False,
508
+ help = 'Stop on errors (negates suppress_errors)')
509
+ parser.add_argument('-r','--restrictions',action='store',default= '',
510
+ help = 'Restrictions to the populate table (dict(X = "x")) or completed_today (to run for sessions that were completed less than 24h ago)')
511
+ parser.add_argument('-i','--imports',action='store',default= 'labdata.schema',type = str,
512
+ help = 'import modules to load the table')
513
+ parser.add_argument('-p','--processes',
514
+ action='store', default=1, type = int,
515
+ help = "Required imports.")
516
+ parser.add_argument
517
+
518
+ args = parser.parse_args(arguments[1:])
519
+ self.parameters = dict(table = args.table,
520
+ imports = args.imports,
521
+ processes = args.processes,
522
+ suppress_errors = not args.stop_on_errors,
523
+ restrictions = args.restrictions)
524
+ # try the import and check if the default container exists.
525
+ if not self.parameters["imports"] in ['none','']:
526
+ to_import = self.parameters["table"]
527
+ if '.' in to_import: # to import plugins
528
+ to_import = to_import.split('.')[0]
529
+ exec(f'from {self.parameters["imports"]} import {to_import}')
530
+ table = eval(f'{self.parameters["table"]}')
531
+ if hasattr(table,'default_container'):
532
+ self.container = table.default_container
533
+ print(self.parameters)
534
+
535
+ def find_datasets(self):
536
+ return
537
+
538
+ def _compute(self):
539
+ # import
540
+ if not self.parameters["imports"] in ['none','']:
541
+ to_import = self.parameters["table"]
542
+ if '.' in to_import: # to import plugins
543
+ to_import = to_import.split('.')[0]
544
+ exec(f'from {self.parameters["imports"]} import {to_import}')
545
+ processes = 1
546
+ # check nprocesses
547
+ if 'processes' in self.parameters.keys():
548
+ processes = int(self.parameters['processes'])
549
+ # submit populate
550
+ suppress_errors = self.parameters['suppress_errors']
551
+ if self.parameters['restrictions'] == '':
552
+ exec(f'{self.parameters["table"]}.populate(suppress_errors={suppress_errors}, processes = {processes}, display_progress = True)')
553
+ else:
554
+ if self.parameters['restrictions'] == 'completed_today': # this will look for uploads that happened less than 24h ago
555
+ from ..schema import Session, UploadJob
556
+ restrictions = (Session() & (UploadJob() & 'job_status = "COMPLETED"') & 'session_datetime > DATE_SUB(CURDATE(), INTERVAL 24 HOUR)').proj().fetch(as_dict = True)
557
+ else:
558
+ restrictions = eval(self.parameters['restrictions'])
559
+ exec(f'{self.parameters["table"]}.populate({restrictions}, suppress_errors={suppress_errors}, processes = {processes}, display_progress = True)')
560
+
561
+