diff --git a/uit/gui_tools/submit.py b/uit/gui_tools/submit.py index 66cae46..f49872e 100644 --- a/uit/gui_tools/submit.py +++ b/uit/gui_tools/submit.py @@ -7,9 +7,10 @@ import param import panel as pn -from .file_browser import HpcFileBrowser, create_file_browser, get_js_loading_code, FileSelector +from .file_browser import HpcFileBrowser, create_file_browser, get_js_loading_code from .utils import HpcBase, HpcConfigurable from ..uit import QUEUES +from .file_browser import AsyncHpcPath from ..pbs_script import NODE_TYPES, factors, PbsScript from ..job import PbsJob @@ -74,21 +75,50 @@ class PbsScriptInputs(HpcBase): wall_time_maxes = None node_maxes = None + base_dir = param.Selector( + label="Base Directory", + doc="Base directory that the job's working directory path will be created in.\n\n" + "**Note:** by default the job's working directory is: " + f"`/{PbsJob.DEFAULT_JOB_LABEL}/./`", + ) + use_namespace = param.Boolean(label="Use Namespace Path", default=True) + namespace_path = param.String(label="Namespace Path", doc="Path created within the Base Directory") + execution_dir = param.String(label="Execution Directory", doc="Full Directory path in which job(s) will be run.") + def __init__(self, **params): super().__init__(**params) - self.workdir = FileSelector( - title="Base Directory", - show_browser=False, - help_text=( - "Base directory that the job's working directory path will be created in.\n\n" - "**Note:** by default the job's working directory is: " - f"`/{PbsJob.DEFAULT_JOB_LABEL}/./`" - ), + options = pn.widgets.Button(icon="menu-2", align="center", margin=(15, 0, 0, 0)) + options.on_click(self.toggle_exec_dir_widgets) + self.exec_dir_wg_box = pn.layout.WidgetBox( + self.param.base_dir, self.param.use_namespace, self.param.namespace_path, visible=False ) - @param.depends("uit_client", watch=True) - def set_file_browser(self): - self.workdir.file_browser = create_file_browser(self.uit_client, patterns=[]) + self.execution_dir_col = pn.Column( + pn.Row(pn.widgets.TextInput.from_param(self.param.execution_dir, disabled=True, width=500), options), + self.exec_dir_wg_box, + ) + + async def populate_base_dir_selector(self): + options = {} + if AsyncHpcPath(self.uit_client.WORKDIR, uit_client=self.uit_client).exists(): + options["WORKDIR"] = self.uit_client.WORKDIR + workdir2 = await self.uit_client.env.get_environmental_variable("WORKDIR2") + if workdir2 and AsyncHpcPath(self.uit_client.WORKDIR2, uit_client=self.uit_client).exists(): + options["WORKDIR2"] = self.uit_client.WORKDIR2 + self.param.base_dir.objects = options + self.base_dir = options["WORKDIR"] + + def default_namespace_path(self): + return f"{self.workflow_group}/{self.workflow_name}" + + @param.depends("use_namespace", "base_dir", "namespace_path", watch=True) + def update_job_dir(self): + namespace = self.namespace_path + "/" if self.use_namespace else "" + self.exec_dir_wg_box[-1] = self.param.namespace_path if self.use_namespace else None + self.execution_dir = f"{self.base_dir}/{namespace}./" + + def toggle_exec_dir_widgets(self, _): + self.exec_dir_wg_box.visible = not self.exec_dir_wg_box.visible @staticmethod def get_default(value, objects): @@ -98,14 +128,13 @@ def get_default(value, objects): async def update_hpc_connection_dependent_defaults(self): if not self.uit_client.connected: return - queues_stats = await self.await_if_async(self.uit_client.get_raw_queue_stats()) self.subproject_usage = await self.await_if_async(self.uit_client.show_usage(as_df=True)) subprojects = self.subproject_usage["Subproject"].to_list() self.param.hpc_subproject.objects = subprojects self.hpc_subproject = self.get_default(self.hpc_subproject, subprojects) - self.workdir.file_path = self.uit_client.WORKDIR.as_posix() + await self.populate_base_dir_selector() self.param.node_type.objects = list(NODE_TYPES[self.uit_client.system].keys()) self.node_type = self.get_default(self.node_type, self.param.node_type.objects) self.param.queue.objects = await self.await_if_async(self.uit_client.get_queues()) @@ -119,6 +148,7 @@ async def update_hpc_connection_dependent_defaults(self): ) self.max_wall_time = self.wall_time_maxes[self.queue] self.nodes = round(self.DEFAULT_PROCESSES_PER_JOB / self.processes_per_node) + self.namespace_path = self.default_namespace_path() @param.depends("queue", watch=True) def update_queue_depended_bounds(self): @@ -220,8 +250,7 @@ def pbs_options_view(self): ), pn.Column( self.param.hpc_subproject, - self.workdir, - self.param.node_type, + self.execution_dir_col, pn.widgets.Spinner.from_param(self.param.nodes), self.param.processes_per_node, self.param.wall_time, @@ -522,7 +551,7 @@ def job(self): script=self.pbs_script, client=self.uit_client, workspace=self.user_workspace, - base_dir=self.workdir.file_path, + base_dir=self.base_dir, ) return self._job diff --git a/uit/job.py b/uit/job.py index 02251b2..b47023a 100644 --- a/uit/job.py +++ b/uit/job.py @@ -28,9 +28,11 @@ def __init__( home_input_files=None, archive_input_files=None, base_dir=None, + namespace_path=None, description=None, metadata=None, post_processing_script=None, + use_namespace=True, ): self.script = script self.post_processing_script = post_processing_script @@ -52,6 +54,8 @@ def __init__( self._post_processing_job_id = None self._remote_workspace_id = None self._remote_workspace = None + self.use_namespace = use_namespace + self.namespace_path = namespace_path def __repr__(self): return f"<{self.__class__.__name__} name={self.name} id={self.job_id}>" @@ -116,7 +120,10 @@ def remote_workspace_suffix(self): str: Suffix """ if not self._remote_workspace: - self._remote_workspace = PurePosixPath(self.label, f"{self.name}.{self.remote_workspace_id}") + if self.use_namespace: + self._remote_workspace = PurePosixPath(self.namespace_path, f"{self.name}.{self.remote_workspace_id}") + else: + self._remote_workspace = PurePosixPath(f"{self.name}.{self.remote_workspace_id}") return self._remote_workspace @property