99# (at your option) any later version.
1010
1111import pathlib
12+ import shutil
1213
1314from logging import Logger
1415from typing import List , Optional , Union
1718from ...common import Workflow
1819from ...common .task import Task
1920
21+ this_dir = pathlib .Path (__file__ ).resolve ().parent
22+
2023
2124class NextflowTranslator (Translator ):
2225 """
@@ -27,26 +30,14 @@ class NextflowTranslator(Translator):
2730 :param logger: The logger where to log information/warning or errors (optional).
2831 :type logger: Logger
2932 """
30-
3133 def __init__ (self ,
3234 workflow : Union [Workflow , pathlib .Path ],
3335 logger : Optional [Logger ] = None ) -> None :
3436 """Create an object of the translator."""
3537 super ().__init__ (workflow , logger )
3638
3739 self .script = ""
38-
39- self ._usage_string = """
40- Usage: nextflow run workflow.nf --pwd /path/to/directory [--simulate] [--help]
41-
42- Required parameters:
43- --pwd Working directory (where the workflow.nf file is located)
44-
45- Optional parameters:
46- --help Show this message and exit.
47- --simulate Use a "sleep 1" for all tasks instead of the WfBench benchmark.
48- """
49-
40+ self .out_files = set ()
5041
5142 def translate (self , output_folder : pathlib .Path ) -> None :
5243 """
@@ -55,25 +46,29 @@ def translate(self, output_folder: pathlib.Path) -> None:
5546 :param output_folder: The path to the folder in which the workflow benchmark will be generated.
5647 :type output_folder: pathlib.Path
5748 """
58-
5949 # Create the output folder
60- output_folder .mkdir (parents = True )
50+ self .output_folder = output_folder
51+ self .output_folder .mkdir (parents = True )
6152
6253 # Create benchmark files
6354 self ._copy_binary_files (output_folder )
6455 self ._generate_input_files (output_folder )
56+
57+ if self .workflow .workflow_id :
58+ shutil .copy (this_dir .joinpath ("templates/flowcept_agent.py" ), output_folder .joinpath ("bin" ))
6559
6660 # Create a topological order of the tasks
6761 sorted_tasks = self ._get_tasks_in_topological_order ()
6862 # print([t.task_id for t in sorted_tasks])
6963
7064 # Create the bash script for each task
7165 for task in sorted_tasks :
72- self ._create_task_script (output_folder , task )
66+ self ._create_task_script (task )
7367
7468 # Create the Nextflow workflow script and file
7569 self ._create_workflow_script (sorted_tasks )
76- self ._write_output_file (self .script , output_folder .joinpath ("workflow.nf" ))
70+ run_workflow_code = self ._merge_codelines ("templates/nextflow/workflow.nf" , self .script )
71+ self ._write_output_file (run_workflow_code , output_folder .joinpath ("workflow.nf" ))
7772
7873 # Create the README file
7974 self ._write_readme_file (output_folder )
@@ -86,10 +81,10 @@ def _create_workflow_script(self, tasks: list[Task]):
8681
8782 :param tasks: The (sorted) list of tasks.
8883 :type tasks: list[Task]
89- """
90-
91- # Output the code for command-line argument processing
92- self .script += self ._generate_arg_parsing_code ()
84+ """
85+ # Add Flowcept code if enabled
86+ if self . workflow . workflow_id :
87+ self .script += self ._generate_flowcept_code ()
9388
9489 # Output the code for each task
9590 for task in tasks :
@@ -100,60 +95,29 @@ def _create_workflow_script(self, tasks: list[Task]):
10095
10196 return
10297
103- def _generate_arg_parsing_code (self ):
98+ def _generate_flowcept_code (self ) -> str :
10499 """
105- Generate the code to parse command-line argument.
106100
107101 :return: The code.
108102 :rtype: str
109103 """
110-
111- code = r'''
112- params.simulate = false
113- params.pwd = null
114- params.help = null
115- pwd = null
116-
117- def printUsage(error_msg, exit_code) {
118-
119- def usage_string = """
120- '''
121- code += self ._usage_string
122-
123- code += r'''
124- """
125- if (error_msg) {
126- def RED = '\u001B[31m'
127- def RESET = '\u001B[0m'
128- System.err.println "${RED}Error: ${RESET}" + error_msg
129- }
130- System.err.println usage_string
131- exit exit_code
132- }
133-
134- def validateParams() {
135- if (params.help) {
136- printUsage(msg = "", exit_code=0)
137- }
138- if (params.pwd == null) {
139- printUsage(msg = "Missing required parameter: --pwd", exit_code=1)
140- }
141- pwd = file(params.pwd).toAbsolutePath().toString()
142- if (!file(pwd).exists()) {
143- printUsage(msg = "Directory not found: ${pwd}", exit_code=1)
144- }
145- }
146-
147- // Call validation at the start
148- validateParams()
149-
150- '''
151- return code
104+ out_files = ", " .join (f"\" { item } \" " for item in self .out_files )
105+ return "process flowcept(){\n " \
106+ " input:\n " \
107+ " output:\n " \
108+ " script:\n " \
109+ " \" \" \" \n " \
110+ " ${pwd}/bin/flowcept_agent.py " \
111+ f"{ self .workflow .name } { self .workflow .workflow_id } '[{ out_files } ]' \n " \
112+ " \" \" \" \n " \
113+ "}\n \n "
152114
153115 def _get_tasks_in_topological_order (self ) -> List [Task ]:
154116 """
155117 Sort the workflow tasks in topological order.
156118
119+ :param output_folder: The path to the output folder.
120+ :type output_folder: pathlib.Path
157121 :return: A sorted list of tasks.
158122 :rtype: List[Task]
159123 """
@@ -168,21 +132,20 @@ def _get_tasks_in_topological_order(self) -> List[Task]:
168132 if not all_children :
169133 break
170134 for potential_task in all_children :
135+ num_children = len (self .task_children [potential_task .task_id ])
136+ if not num_children :
137+ self .out_files .add (f"{ self .output_folder .absolute ()} /{ potential_task .output_files [0 ]} " )
171138 if all (parent in sorted_tasks for parent in self ._find_parents (potential_task .task_id )):
172139 tasks_in_current_level .append (potential_task )
173140 levels [current_level ] = tasks_in_current_level
174141 sorted_tasks += tasks_in_current_level
175142 current_level += 1
176143 return sorted_tasks
177144
178-
179- @staticmethod
180- def _create_task_script (output_folder : pathlib .Path , task : Task ):
145+ def _create_task_script (self , task : Task ):
181146 """
182147 Generate the bash script for invoking a task.
183148
184- :param output_folder: The path to the output folder.
185- :type output_folder: pathlib.Path
186149 :param task: The task.
187150 :type task: Task
188151 :return: The code.
@@ -194,16 +157,16 @@ def _create_task_script(output_folder: pathlib.Path, task: Task):
194157 # Generate input spec
195158 input_spec = "'\\ ["
196159 for f in task .input_files :
197- input_spec += f"\" { output_folder .resolve ()} /data/{ f .file_id } \" ,"
160+ input_spec += f"\" { self . output_folder .resolve ()} /data/{ f .file_id } \" ,"
198161 input_spec = input_spec [:- 1 ] + "\\ ]'"
199162
200163 # Generate output spec
201164 output_spec = "'\\ {"
202165 for f in task .output_files :
203- output_spec += f"\" { output_folder .resolve ()} /data/{ f .file_id } \" :{ str (f .size )} ,"
166+ output_spec += f"\" { self . output_folder .resolve ()} /data/{ f .file_id } \" :{ str (f .size )} ,"
204167 output_spec = output_spec [:- 1 ] + "\\ }'"
205168
206- code += f"{ output_folder .resolve ()} /bin/{ task .program } "
169+ code += f"{ self . output_folder .resolve ()} /bin/{ task .program } "
207170
208171 for a in task .args :
209172 if "--output-files" in a :
@@ -214,7 +177,7 @@ def _create_task_script(output_folder: pathlib.Path, task: Task):
214177 code += f"{ a } "
215178 code += "\n "
216179
217- script_file_path = output_folder .joinpath (f"bin/script_{ task .task_id } .sh" )
180+ script_file_path = self . output_folder .joinpath (f"bin/script_{ task .task_id } .sh" )
218181 with open (script_file_path , "w" ) as out :
219182 out .write (code )
220183
@@ -296,6 +259,8 @@ def _generate_workflow_code(self, sorted_tasks: List[Task]) -> str:
296259
297260 # Generate workflow function
298261 code += "workflow {\n "
262+ if self .workflow .workflow_id :
263+ code += "\t flowcept()\n "
299264 code += "\t results = bootstrap()\n "
300265 for task in sorted_tasks :
301266 function_name = task .task_id .replace ("." , "_" )
@@ -353,6 +318,3 @@ def _write_readme_file(self, output_folder: pathlib.Path) -> None:
353318 out .write (f"Run the workflow in directory { str (output_folder )} using the following command:\n " )
354319
355320 out .write (f"\t nextflow run ./workflow.nf --pwd `pwd`\n " )
356- out .write ("\n " )
357- out .write (self ._usage_string )
358-
0 commit comments