From 82e53785eb38c3a902e31cc0f207202394bd9746 Mon Sep 17 00:00:00 2001 From: pavlemarinkovic Date: Mon, 24 Jun 2024 15:04:28 +0200 Subject: [PATCH 1/2] Binding takes itemSeparator and shellQuote GENERIC_NF_OUTPUT_DIRECTORY uses publishDir and glob pattern * Try to correct NF executor Version only if it exists Listing is added only if it doesn't already exist Add new requirement only if it doesn't already exist --- wrabbit/parser/constants.py | 15 +++++---- wrabbit/parser/nextflow.py | 45 +++++++++++++++++++++------ wrabbit/parser/utils.py | 2 +- wrabbit/specification/binding.py | 6 ++-- wrabbit/specification/requirements.py | 12 ++++++- wrabbit/version.py | 2 +- wrabbit/wrapper/wrapper.py | 1 + 7 files changed, 62 insertions(+), 21 deletions(-) diff --git a/wrabbit/parser/constants.py b/wrabbit/parser/constants.py index 02ef426..271e1b7 100755 --- a/wrabbit/parser/constants.py +++ b/wrabbit/parser/constants.py @@ -66,17 +66,19 @@ class EXTENSIONS: } GENERIC_NF_OUTPUT_DIRECTORY = { - "id": "nf_workdir", + "id": "nf_publishdir", "type": [ 'null', - 'Directory' + { + 'type': 'array', + 'items': 'File' + } ], - "label": "Work Directory", + "label": "Publish Directory", "doc": "This is a template output. " - "Please change glob to directories specified in " - "publishDir in the workflow.", + "You can modify the glob pattern to make outputs more specific.", "outputBinding": { - 'glob': "work" + 'glob': "*" } } @@ -139,6 +141,7 @@ def sample_sheet( SB_SCHEMA_DEFAULT_NAME = 'sb_nextflow_schema' README_DEFAULT_NAME = 'README.md' MINIMUM_SUPPORTED_NF_VERSION = "21.10.0" +NFCORE_OUTPUT_DIRECTORY_ID = 'outdir' # Mappings of nextflow input fields to SB input fields # nextflow_key: cwl_key mapping diff --git a/wrabbit/parser/nextflow.py b/wrabbit/parser/nextflow.py index 3d00820..1848e00 100755 --- a/wrabbit/parser/nextflow.py +++ b/wrabbit/parser/nextflow.py @@ -34,6 +34,7 @@ SAMPLE_SHEET_SWITCH, LOAD_LISTING_REQUIREMENT, SKIP_NEXTFLOW_TOWER_KEYS, + NFCORE_OUTPUT_DIRECTORY_ID, ) from wrabbit.specification.node import ( @@ -60,6 +61,11 @@ class NextflowParser: + nf_config_files = [] + nf_schema_path = None + readme_path = None + sb_samplesheet_schema = None + def __init__( self, workflow_path: str, sb_doc: Optional[str] = None, @@ -72,11 +78,10 @@ def __init__( self.workflow_path = workflow_path # Locate nextflow files in the package if possible - self.nf_config_files = get_config_files(self.workflow_path) or [] - self.nf_schema_path = get_nf_schema(self.workflow_path) - self.readme_path = get_docs_file(self.workflow_path) - self.sb_samplesheet_schema = get_sample_sheet_schema( - self.workflow_path) + self.get_config_files() + self.get_nf_schema() + self.get_docs_file() + self.get_sample_sheet_schema() self.sb_doc = sb_doc @@ -85,6 +90,19 @@ def __init__( self.executor_version = executor_version self.sb_package_id = sb_package_id + def get_config_files(self): + self.nf_config_files = get_config_files(self.workflow_path) or [] + + def get_nf_schema(self): + self.nf_schema_path = get_nf_schema(self.workflow_path) + + def get_docs_file(self): + self.readme_path = get_docs_file(self.workflow_path) + + def get_sample_sheet_schema(self): + self.sb_samplesheet_schema = get_sample_sheet_schema( + self.workflow_path) + def generate_sb_inputs(self): """ Generate SB inputs schema @@ -159,6 +177,12 @@ def generate_sb_inputs(self): required=req, )) + # Remap publishDir to string type + if temp := self.sb_wrapper.get_input(NFCORE_OUTPUT_DIRECTORY_ID): + print(f"Detected publishDir input --{temp.id_}. Remapping to " + f"string type input.") + temp.set_property('type', 'string') + # Add the generic file array input - auxiliary files self.sb_wrapper.safe_add_input(GENERIC_FILE_ARRAY_INPUT) self.sb_wrapper.safe_add_input(NF_PARAMS_FILE_INPUT) @@ -210,12 +234,13 @@ def generate_app_data(self): # Stop searching if manifest is found break - self.executor_version = self.executor_version or \ - get_executor_version(self.sb_doc) + if not self.executor_version and self.sb_doc: + self.executor_version = get_executor_version(self.sb_doc) - # Confirm if the executor version is valid. - # If it is below 22.10.1 it is not supported. - self.executor_version.correct_version() + if self.executor_version: + # Confirm if the executor version is valid. + # If it is below 22.10.1 it is not supported. + self.executor_version.correct_version() # step2: add links diff --git a/wrabbit/parser/utils.py b/wrabbit/parser/utils.py index e1f1a21..5c88958 100755 --- a/wrabbit/parser/utils.py +++ b/wrabbit/parser/utils.py @@ -226,7 +226,7 @@ def get_nf_schema(string): if os.path.exists(path): return path else: - raise Exception() + return None def get_docs_file(path): diff --git a/wrabbit/specification/binding.py b/wrabbit/specification/binding.py index ead86f8..53e4f83 100755 --- a/wrabbit/specification/binding.py +++ b/wrabbit/specification/binding.py @@ -10,8 +10,10 @@ def __init__( **kwargs ): self.prefix = prefix - self.item_separator = item_separator - self.shell_quote = shell_quote + self.item_separator = item_separator or \ + kwargs.get('itemSeparator', None) + self.shell_quote = shell_quote or \ + kwargs.get('shellQuote', None) self.glob = glob def serialize(self): diff --git a/wrabbit/specification/requirements.py b/wrabbit/specification/requirements.py index e414f37..3a636c1 100755 --- a/wrabbit/specification/requirements.py +++ b/wrabbit/specification/requirements.py @@ -37,9 +37,19 @@ def add_listing(self, obj: Union[dict, str, Listing]): if not self.listing: self.listing = list() - if obj not in self.listing: + add_new_listing = False + for ref_obj in self.listing: + if not self._compare_listings(ref_obj, obj): + add_new_listing = True + break + + if add_new_listing: self.listing.append(obj) + @staticmethod + def _compare_listings(ref, obj): + return ref.__dict__ == obj.__dict__ + def serialize(self): temp = { 'class': self.class_, diff --git a/wrabbit/version.py b/wrabbit/version.py index ae73625..d3ec452 100755 --- a/wrabbit/version.py +++ b/wrabbit/version.py @@ -1 +1 @@ -__version__ = "0.1.3" +__version__ = "0.2.0" diff --git a/wrabbit/wrapper/wrapper.py b/wrabbit/wrapper/wrapper.py index 98a23eb..1f88c7a 100755 --- a/wrabbit/wrapper/wrapper.py +++ b/wrabbit/wrapper/wrapper.py @@ -128,6 +128,7 @@ def add_requirement(self, requirement: [dict, Requirement]): for req in self.requirements: if req.class_ == requirement.class_: req.update(requirement) + break else: # add new class self.requirements.append(requirement) From d3720e91ad2025342f8e9ae3643bda75f9dbb3b0 Mon Sep 17 00:00:00 2001 From: pavlemarinkovic Date: Mon, 24 Jun 2024 16:06:29 +0200 Subject: [PATCH 2/2] Binding takes itemSeparator and shellQuote GENERIC_NF_OUTPUT_DIRECTORY uses publishDir and glob pattern * Try to correct NF executor Version only if it exists Listing is added only if it doesn't already exist Add new requirement only if it doesn't already exist --- wrabbit/parser/nextflow.py | 33 ++++++++++++--------------- wrabbit/specification/listing.py | 5 ++++ wrabbit/specification/requirements.py | 12 ++-------- 3 files changed, 21 insertions(+), 29 deletions(-) diff --git a/wrabbit/parser/nextflow.py b/wrabbit/parser/nextflow.py index 1848e00..7aa6b2f 100755 --- a/wrabbit/parser/nextflow.py +++ b/wrabbit/parser/nextflow.py @@ -61,10 +61,10 @@ class NextflowParser: - nf_config_files = [] - nf_schema_path = None - readme_path = None - sb_samplesheet_schema = None + nf_config_files: list + nf_schema_path: Optional[str] + readme_path: Optional[str] + sb_samplesheet_schema: Optional[str] def __init__( self, workflow_path: str, @@ -78,10 +78,11 @@ def __init__( self.workflow_path = workflow_path # Locate nextflow files in the package if possible - self.get_config_files() - self.get_nf_schema() - self.get_docs_file() - self.get_sample_sheet_schema() + self.init_config_files() + self.nf_schema_path = get_nf_schema(self.workflow_path) + self.readme_path = get_docs_file(self.workflow_path) + self.sb_samplesheet_schema = get_sample_sheet_schema( + self.workflow_path) self.sb_doc = sb_doc @@ -90,19 +91,13 @@ def __init__( self.executor_version = executor_version self.sb_package_id = sb_package_id - def get_config_files(self): + def init_config_files(self): + """ + Config may be initialized multiple times while working with a code + package in case a new config file is generated with nf-core lib. + """ self.nf_config_files = get_config_files(self.workflow_path) or [] - def get_nf_schema(self): - self.nf_schema_path = get_nf_schema(self.workflow_path) - - def get_docs_file(self): - self.readme_path = get_docs_file(self.workflow_path) - - def get_sample_sheet_schema(self): - self.sb_samplesheet_schema = get_sample_sheet_schema( - self.workflow_path) - def generate_sb_inputs(self): """ Generate SB inputs schema diff --git a/wrabbit/specification/listing.py b/wrabbit/specification/listing.py index 29626e9..c35cb4c 100755 --- a/wrabbit/specification/listing.py +++ b/wrabbit/specification/listing.py @@ -35,3 +35,8 @@ def deserialize(listing): elif isinstance(listing, str): return Listing(listing) return Listing(**listing) + + def __eq__(self, other): + assert isinstance(other, Listing) + + return self.__dict__ == other.__dict__ diff --git a/wrabbit/specification/requirements.py b/wrabbit/specification/requirements.py index 3a636c1..299cf0b 100755 --- a/wrabbit/specification/requirements.py +++ b/wrabbit/specification/requirements.py @@ -37,19 +37,11 @@ def add_listing(self, obj: Union[dict, str, Listing]): if not self.listing: self.listing = list() - add_new_listing = False for ref_obj in self.listing: - if not self._compare_listings(ref_obj, obj): - add_new_listing = True + if ref_obj != obj: + self.listing.append(obj) break - if add_new_listing: - self.listing.append(obj) - - @staticmethod - def _compare_listings(ref, obj): - return ref.__dict__ == obj.__dict__ - def serialize(self): temp = { 'class': self.class_,