From 4826528f04293c30b7d0267e8dc25d3479ca1b9d Mon Sep 17 00:00:00 2001 From: nf-core-bot Date: Tue, 8 Oct 2024 12:32:06 +0000 Subject: [PATCH 01/18] Template update for nf-core/tools version 3.0.0 --- .editorconfig | 4 + .github/CONTRIBUTING.md | 10 +- .github/PULL_REQUEST_TEMPLATE.md | 2 +- .github/workflows/awsfulltest.yml | 23 +- .github/workflows/ci.yml | 17 +- .github/workflows/download_pipeline.yml | 53 ++- .github/workflows/linting.yml | 23 +- .github/workflows/linting_comment.yml | 2 +- .github/workflows/release-announcements.yml | 2 +- .../workflows/template_version_comment.yml | 43 ++ .gitpod.yml | 7 +- .nf-core.yml | 25 +- .pre-commit-config.yaml | 2 +- .prettierignore | 1 + CITATIONS.md | 4 +- README.md | 5 +- assets/schema_input.json | 2 +- conf/base.config | 34 +- conf/igenomes_ignored.config | 9 + conf/modules.config | 1 - conf/test.config | 13 +- docs/images/mqc_fastqc_adapter.png | Bin 23458 -> 0 bytes docs/images/mqc_fastqc_counts.png | Bin 33918 -> 0 bytes docs/images/mqc_fastqc_quality.png | Bin 55769 -> 0 bytes docs/output.md | 11 +- docs/usage.md | 12 +- main.nf | 10 +- modules.json | 12 +- modules/nf-core/fastqc/environment.yml | 2 - modules/nf-core/fastqc/main.nf | 5 +- modules/nf-core/fastqc/meta.yml | 57 +-- modules/nf-core/fastqc/tests/main.nf.test | 225 ++++++++--- .../nf-core/fastqc/tests/main.nf.test.snap | 370 ++++++++++++++++-- modules/nf-core/multiqc/environment.yml | 4 +- modules/nf-core/multiqc/main.nf | 14 +- modules/nf-core/multiqc/meta.yml | 78 ++-- modules/nf-core/multiqc/tests/main.nf.test | 8 + .../nf-core/multiqc/tests/main.nf.test.snap | 20 +- modules/nf-core/multiqc/tests/nextflow.config | 5 + nextflow.config | 146 ++++--- nextflow_schema.json | 85 +--- .../utils_nfcore_spatialvi_pipeline/main.nf | 56 +-- .../nf-core/utils_nextflow_pipeline/main.nf | 24 +- .../tests/nextflow.config | 2 +- .../nf-core/utils_nfcore_pipeline/main.nf | 45 ++- .../nf-core/utils_nfschema_plugin/main.nf | 46 +++ .../nf-core/utils_nfschema_plugin/meta.yml | 35 ++ .../utils_nfschema_plugin/tests/main.nf.test | 117 ++++++ .../tests/nextflow.config | 8 + .../tests/nextflow_schema.json | 8 +- .../nf-core/utils_nfvalidation_plugin/main.nf | 62 --- .../utils_nfvalidation_plugin/meta.yml | 44 --- .../tests/main.nf.test | 200 ---------- .../utils_nfvalidation_plugin/tests/tags.yml | 2 - workflows/spatialvi.nf | 23 +- 55 files changed, 1212 insertions(+), 806 deletions(-) create mode 100644 .github/workflows/template_version_comment.yml create mode 100644 conf/igenomes_ignored.config delete mode 100755 docs/images/mqc_fastqc_adapter.png delete mode 100755 docs/images/mqc_fastqc_counts.png delete mode 100755 docs/images/mqc_fastqc_quality.png create mode 100644 modules/nf-core/multiqc/tests/nextflow.config create mode 100644 subworkflows/nf-core/utils_nfschema_plugin/main.nf create mode 100644 subworkflows/nf-core/utils_nfschema_plugin/meta.yml create mode 100644 subworkflows/nf-core/utils_nfschema_plugin/tests/main.nf.test create mode 100644 subworkflows/nf-core/utils_nfschema_plugin/tests/nextflow.config rename subworkflows/nf-core/{utils_nfvalidation_plugin => utils_nfschema_plugin}/tests/nextflow_schema.json (95%) delete mode 100644 subworkflows/nf-core/utils_nfvalidation_plugin/main.nf delete mode 100644 subworkflows/nf-core/utils_nfvalidation_plugin/meta.yml delete mode 100644 subworkflows/nf-core/utils_nfvalidation_plugin/tests/main.nf.test delete mode 100644 subworkflows/nf-core/utils_nfvalidation_plugin/tests/tags.yml diff --git a/.editorconfig b/.editorconfig index 72dda28..e105881 100644 --- a/.editorconfig +++ b/.editorconfig @@ -11,6 +11,7 @@ indent_style = space [*.{md,yml,yaml,html,css,scss,js}] indent_size = 2 + # These files are edited and tested upstream in nf-core/modules [/modules/nf-core/**] charset = unset @@ -25,9 +26,12 @@ insert_final_newline = unset trim_trailing_whitespace = unset indent_style = unset + + [/assets/email*] indent_size = unset + # ignore python and markdown [*.{py,md}] indent_style = unset diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 518d733..8a7f1af 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -19,7 +19,7 @@ If you'd like to write some code for nf-core/spatialvi, the standard workflow is 1. Check that there isn't already an issue about your idea in the [nf-core/spatialvi issues](https://github.com/nf-core/spatialvi/issues) to avoid duplicating work. If there isn't one already, please create one so that others know you're working on this 2. [Fork](https://help.github.com/en/github/getting-started-with-github/fork-a-repo) the [nf-core/spatialvi repository](https://github.com/nf-core/spatialvi) to your GitHub account 3. Make the necessary changes / additions within your forked repository following [Pipeline conventions](#pipeline-contribution-conventions) -4. Use `nf-core schema build` and add any new parameters to the pipeline JSON schema (requires [nf-core tools](https://github.com/nf-core/tools) >= 1.10). +4. Use `nf-core pipelines schema build` and add any new parameters to the pipeline JSON schema (requires [nf-core tools](https://github.com/nf-core/tools) >= 1.10). 5. Submit a Pull Request against the `dev` branch and wait for the code to be reviewed and merged If you're not used to this workflow with git, you can start with some [docs from GitHub](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests) or even their [excellent `git` resources](https://try.github.io/). @@ -40,7 +40,7 @@ There are typically two types of tests that run: ### Lint tests `nf-core` has a [set of guidelines](https://nf-co.re/developers/guidelines) which all pipelines must adhere to. -To enforce these and ensure that all pipelines stay in sync, we have developed a helper tool which runs checks on the pipeline code. This is in the [nf-core/tools repository](https://github.com/nf-core/tools) and once installed can be run locally with the `nf-core lint ` command. +To enforce these and ensure that all pipelines stay in sync, we have developed a helper tool which runs checks on the pipeline code. This is in the [nf-core/tools repository](https://github.com/nf-core/tools) and once installed can be run locally with the `nf-core pipelines lint ` command. If any failures or warnings are encountered, please follow the listed URL for more documentation. @@ -75,7 +75,7 @@ If you wish to contribute a new step, please use the following coding standards: 2. Write the process block (see below). 3. Define the output channel if needed (see below). 4. Add any new parameters to `nextflow.config` with a default (see below). -5. Add any new parameters to `nextflow_schema.json` with help text (via the `nf-core schema build` tool). +5. Add any new parameters to `nextflow_schema.json` with help text (via the `nf-core pipelines schema build` tool). 6. Add sanity checks and validation for all relevant parameters. 7. Perform local tests to validate that the new code works as expected. 8. If applicable, add a new test command in `.github/workflow/ci.yml`. @@ -86,7 +86,7 @@ If you wish to contribute a new step, please use the following coding standards: Parameters should be initialised / defined with default values in `nextflow.config` under the `params` scope. -Once there, use `nf-core schema build` to add to `nextflow_schema.json`. +Once there, use `nf-core pipelines schema build` to add to `nextflow_schema.json`. ### Default processes resource requirements @@ -103,7 +103,7 @@ Please use the following naming schemes, to make it easy to understand what is g ### Nextflow version bumping -If you are using a new feature from core Nextflow, you may bump the minimum required version of nextflow in the pipeline with: `nf-core bump-version --nextflow . [min-nf-version]` +If you are using a new feature from core Nextflow, you may bump the minimum required version of nextflow in the pipeline with: `nf-core pipelines bump-version --nextflow . [min-nf-version]` ### Images and figures diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 111b958..d684424 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -17,7 +17,7 @@ Learn more about contributing: [CONTRIBUTING.md](https://github.com/nf-core/spat - [ ] If you've fixed a bug or added code that should be tested, add tests! - [ ] If you've added a new tool - have you followed the pipeline conventions in the [contribution docs](https://github.com/nf-core/spatialvi/tree/master/.github/CONTRIBUTING.md) - [ ] If necessary, also make a PR on the nf-core/spatialvi _branch_ on the [nf-core/test-datasets](https://github.com/nf-core/test-datasets) repository. -- [ ] Make sure your code lints (`nf-core lint`). +- [ ] Make sure your code lints (`nf-core pipelines lint`). - [ ] Ensure the test suite passes (`nextflow run . -profile test,docker --outdir `). - [ ] Check for unexpected warnings in debug mode (`nextflow run . -profile debug,test,docker --outdir `). - [ ] Usage Documentation in `docs/usage.md` is updated. diff --git a/.github/workflows/awsfulltest.yml b/.github/workflows/awsfulltest.yml index 652e2f3..0b454f4 100644 --- a/.github/workflows/awsfulltest.yml +++ b/.github/workflows/awsfulltest.yml @@ -1,18 +1,33 @@ name: nf-core AWS full size tests -# This workflow is triggered on published releases. +# This workflow is triggered on PRs opened against the master branch. # It can be additionally triggered manually with GitHub actions workflow dispatch button. # It runs the -profile 'test_full' on AWS batch on: - release: - types: [published] + pull_request: + branches: + - master workflow_dispatch: + pull_request_review: + types: [submitted] + jobs: run-platform: name: Run AWS full tests - if: github.repository == 'nf-core/spatialvi' + if: github.repository == 'nf-core/spatialvi' && github.event.review.state == 'approved' runs-on: ubuntu-latest steps: + - uses: octokit/request-action@v2.x + id: check_approvals + with: + route: GET /repos/${{ github.repository }}/pulls/${{ github.event.review.number }}/reviews + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - id: test_variables + run: | + JSON_RESPONSE='${{ steps.check_approvals.outputs.data }}' + CURRENT_APPROVALS_COUNT=$(echo $JSON_RESPONSE | jq -c '[.[] | select(.state | contains("APPROVED")) ] | length') + test $CURRENT_APPROVALS_COUNT -ge 2 || exit 1 # At least 2 approvals are required - name: Launch workflow via Seqera Platform uses: seqeralabs/action-tower-launch@v2 # TODO nf-core: You can customise AWS full pipeline tests as required diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a5e811f..7d85dfc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,6 +7,7 @@ on: pull_request: release: types: [published] + workflow_dispatch: env: NXF_ANSI_LOG: false @@ -24,7 +25,7 @@ jobs: strategy: matrix: NXF_VER: - - "23.04.0" + - "24.04.2" - "latest-everything" steps: - name: Check out pipeline code @@ -38,9 +39,21 @@ jobs: - name: Disk space cleanup uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be # v1.3.1 - - name: Run pipeline with test data + - name: Run pipeline with test data (docker) # TODO nf-core: You can customise CI pipeline run tests as required # For example: adding multiple test runs with different parameters # Remember that you can parallelise this by using strategy.matrix run: | nextflow run ${GITHUB_WORKSPACE} -profile test,docker --outdir ./results + + - name: Run pipeline with test data (singularity) + # TODO nf-core: You can customise CI pipeline run tests as required + run: | + nextflow run ${GITHUB_WORKSPACE} -profile test,singularity --outdir ./results + if: "${{ github.base_ref == 'master' }}" + + - name: Run pipeline with test data (conda) + # TODO nf-core: You can customise CI pipeline run tests as required + run: | + nextflow run ${GITHUB_WORKSPACE} -profile test,conda --outdir ./results + if: "${{ github.base_ref == 'master' }}" diff --git a/.github/workflows/download_pipeline.yml b/.github/workflows/download_pipeline.yml index 2d20d64..713dc3e 100644 --- a/.github/workflows/download_pipeline.yml +++ b/.github/workflows/download_pipeline.yml @@ -1,4 +1,4 @@ -name: Test successful pipeline download with 'nf-core download' +name: Test successful pipeline download with 'nf-core pipelines download' # Run the workflow when: # - dispatched manually @@ -8,7 +8,7 @@ on: workflow_dispatch: inputs: testbranch: - description: "The specific branch you wish to utilize for the test execution of nf-core download." + description: "The specific branch you wish to utilize for the test execution of nf-core pipelines download." required: true default: "dev" pull_request: @@ -39,9 +39,11 @@ jobs: with: python-version: "3.12" architecture: "x64" - - uses: eWaterCycle/setup-singularity@931d4e31109e875b13309ae1d07c70ca8fbc8537 # v7 + + - name: Setup Apptainer + uses: eWaterCycle/setup-apptainer@4bb22c52d4f63406c49e94c804632975787312b3 # v2.0.0 with: - singularity-version: 3.8.3 + apptainer-version: 1.3.4 - name: Install dependencies run: | @@ -54,33 +56,64 @@ jobs: echo "REPOTITLE_LOWERCASE=$(basename ${GITHUB_REPOSITORY,,})" >> ${GITHUB_ENV} echo "REPO_BRANCH=${{ github.event.inputs.testbranch || 'dev' }}" >> ${GITHUB_ENV} + - name: Make a cache directory for the container images + run: | + mkdir -p ./singularity_container_images + - name: Download the pipeline env: - NXF_SINGULARITY_CACHEDIR: ./ + NXF_SINGULARITY_CACHEDIR: ./singularity_container_images run: | - nf-core download ${{ env.REPO_LOWERCASE }} \ + nf-core pipelines download ${{ env.REPO_LOWERCASE }} \ --revision ${{ env.REPO_BRANCH }} \ --outdir ./${{ env.REPOTITLE_LOWERCASE }} \ --compress "none" \ --container-system 'singularity' \ - --container-library "quay.io" -l "docker.io" -l "ghcr.io" \ + --container-library "quay.io" -l "docker.io" -l "community.wave.seqera.io" \ --container-cache-utilisation 'amend' \ - --download-configuration + --download-configuration 'yes' - name: Inspect download run: tree ./${{ env.REPOTITLE_LOWERCASE }} + - name: Count the downloaded number of container images + id: count_initial + run: | + image_count=$(ls -1 ./singularity_container_images | wc -l | xargs) + echo "Initial container image count: $image_count" + echo "IMAGE_COUNT_INITIAL=$image_count" >> ${GITHUB_ENV} + - name: Run the downloaded pipeline (stub) id: stub_run_pipeline continue-on-error: true env: - NXF_SINGULARITY_CACHEDIR: ./ + NXF_SINGULARITY_CACHEDIR: ./singularity_container_images NXF_SINGULARITY_HOME_MOUNT: true run: nextflow run ./${{ env.REPOTITLE_LOWERCASE }}/$( sed 's/\W/_/g' <<< ${{ env.REPO_BRANCH }}) -stub -profile test,singularity --outdir ./results - name: Run the downloaded pipeline (stub run not supported) id: run_pipeline if: ${{ job.steps.stub_run_pipeline.status == failure() }} env: - NXF_SINGULARITY_CACHEDIR: ./ + NXF_SINGULARITY_CACHEDIR: ./singularity_container_images NXF_SINGULARITY_HOME_MOUNT: true run: nextflow run ./${{ env.REPOTITLE_LOWERCASE }}/$( sed 's/\W/_/g' <<< ${{ env.REPO_BRANCH }}) -profile test,singularity --outdir ./results + + - name: Count the downloaded number of container images + id: count_afterwards + run: | + image_count=$(ls -1 ./singularity_container_images | wc -l | xargs) + echo "Post-pipeline run container image count: $image_count" + echo "IMAGE_COUNT_AFTER=$image_count" >> ${GITHUB_ENV} + + - name: Compare container image counts + run: | + if [ "${{ env.IMAGE_COUNT_INITIAL }}" -ne "${{ env.IMAGE_COUNT_AFTER }}" ]; then + initial_count=${{ env.IMAGE_COUNT_INITIAL }} + final_count=${{ env.IMAGE_COUNT_AFTER }} + difference=$((final_count - initial_count)) + echo "$difference additional container images were \n downloaded at runtime . The pipeline has no support for offline runs!" + tree ./singularity_container_images + exit 1 + else + echo "The pipeline can be downloaded successfully!" + fi diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml index 1fcafe8..b882838 100644 --- a/.github/workflows/linting.yml +++ b/.github/workflows/linting.yml @@ -1,6 +1,6 @@ name: nf-core linting # This workflow is triggered on pushes and PRs to the repository. -# It runs the `nf-core lint` and markdown lint tests to ensure +# It runs the `nf-core pipelines lint` and markdown lint tests to ensure # that the code meets the nf-core guidelines. on: push: @@ -41,17 +41,32 @@ jobs: python-version: "3.12" architecture: "x64" + - name: read .nf-core.yml + uses: pietrobolcato/action-read-yaml@1.0.0 + id: read_yml + with: + config: ${{ github.workspace }}/.nf-core.yaml + - name: Install dependencies run: | python -m pip install --upgrade pip - pip install nf-core + pip install nf-core==${{ steps.read_yml.outputs['nf_core_version'] }} + + - name: Run nf-core pipelines lint + if: ${{ github.base_ref != 'master' }} + env: + GITHUB_COMMENTS_URL: ${{ github.event.pull_request.comments_url }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_PR_COMMIT: ${{ github.event.pull_request.head.sha }} + run: nf-core -l lint_log.txt pipelines lint --dir ${GITHUB_WORKSPACE} --markdown lint_results.md - - name: Run nf-core lint + - name: Run nf-core pipelines lint --release + if: ${{ github.base_ref == 'master' }} env: GITHUB_COMMENTS_URL: ${{ github.event.pull_request.comments_url }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_PR_COMMIT: ${{ github.event.pull_request.head.sha }} - run: nf-core -l lint_log.txt lint --dir ${GITHUB_WORKSPACE} --markdown lint_results.md + run: nf-core -l lint_log.txt pipelines lint --release --dir ${GITHUB_WORKSPACE} --markdown lint_results.md - name: Save PR number if: ${{ always() }} diff --git a/.github/workflows/linting_comment.yml b/.github/workflows/linting_comment.yml index 40acc23..42e519b 100644 --- a/.github/workflows/linting_comment.yml +++ b/.github/workflows/linting_comment.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Download lint results - uses: dawidd6/action-download-artifact@09f2f74827fd3a8607589e5ad7f9398816f540fe # v3 + uses: dawidd6/action-download-artifact@bf251b5aa9c2f7eeb574a96ee720e24f801b7c11 # v6 with: workflow: linting.yml workflow_conclusion: completed diff --git a/.github/workflows/release-announcements.yml b/.github/workflows/release-announcements.yml index 03ecfcf..c6ba35d 100644 --- a/.github/workflows/release-announcements.yml +++ b/.github/workflows/release-announcements.yml @@ -12,7 +12,7 @@ jobs: - name: get topics and convert to hashtags id: get_topics run: | - echo "topics=$(curl -s https://nf-co.re/pipelines.json | jq -r '.remote_workflows[] | select(.full_name == "${{ github.repository }}") | .topics[]' | awk '{print "#"$0}' | tr '\n' ' ')" >> $GITHUB_OUTPUT + echo "topics=$(curl -s https://nf-co.re/pipelines.json | jq -r '.remote_workflows[] | select(.full_name == "${{ github.repository }}") | .topics[]' | awk '{print "#"$0}' | tr '\n' ' ')" | sed 's/-//g' >> $GITHUB_OUTPUT - uses: rzr/fediverse-action@master with: diff --git a/.github/workflows/template_version_comment.yml b/.github/workflows/template_version_comment.yml new file mode 100644 index 0000000..9dea41f --- /dev/null +++ b/.github/workflows/template_version_comment.yml @@ -0,0 +1,43 @@ +name: nf-core template version comment +# This workflow is triggered on PRs to check if the pipeline template version matches the latest nf-core version. +# It posts a comment to the PR, even if it comes from a fork. + +on: pull_request_target + +jobs: + template_version: + runs-on: ubuntu-latest + steps: + - name: Check out pipeline code + uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4 + + - name: Read template version from .nf-core.yml + uses: pietrobolcato/action-read-yaml@1.0.0 + id: read_yml + with: + config: ${{ github.workspace }}/.nf-core.yml + + - name: Install nf-core + run: | + python -m pip install --upgrade pip + pip install nf-core==${{ steps.read_yml.outputs['nf_core_version'] }} + + - name: Check nf-core outdated + id: nf_core_outdated + run: pip list --outdated | grep nf-core + + - name: Post nf-core template version comment + uses: mshick/add-pr-comment@b8f338c590a895d50bcbfa6c5859251edc8952fc # v2 + if: | + ${{ steps.nf_core_outdated.outputs.stdout }} =~ 'nf-core' + with: + repo-token: ${{ secrets.NF_CORE_BOT_AUTH_TOKEN }} + allow-repeats: false + message: | + ## :warning: Newer version of the nf-core template is available. + + Your pipeline is using an old version of the nf-core template: ${{ steps.read_yml.outputs['nf_core_version'] }}. + Please update your pipeline to the latest version. + + For more documentation on how to update your pipeline, please see the [nf-core documentation](https://github.com/nf-core/tools?tab=readme-ov-file#sync-a-pipeline-with-the-template) and [Synchronisation documentation](https://nf-co.re/docs/contributing/sync). + # diff --git a/.gitpod.yml b/.gitpod.yml index 105a182..4611863 100644 --- a/.gitpod.yml +++ b/.gitpod.yml @@ -4,17 +4,14 @@ tasks: command: | pre-commit install --install-hooks nextflow self-update - - name: unset JAVA_TOOL_OPTIONS - command: | - unset JAVA_TOOL_OPTIONS vscode: extensions: # based on nf-core.nf-core-extensionpack - - esbenp.prettier-vscode # Markdown/CommonMark linting and style checking for Visual Studio Code + #- esbenp.prettier-vscode # Markdown/CommonMark linting and style checking for Visual Studio Code - EditorConfig.EditorConfig # override user/workspace settings with settings found in .editorconfig files - Gruntfuggly.todo-tree # Display TODO and FIXME in a tree view in the activity bar - mechatroner.rainbow-csv # Highlight columns in csv files in different colors - # - nextflow.nextflow # Nextflow syntax highlighting + - nextflow.nextflow # Nextflow syntax highlighting - oderwat.indent-rainbow # Highlight indentation level - streetsidesoftware.code-spell-checker # Spelling checker for source code - charliermarsh.ruff # Code linter Ruff diff --git a/.nf-core.yml b/.nf-core.yml index e0b85a7..4a8be1a 100644 --- a/.nf-core.yml +++ b/.nf-core.yml @@ -1,2 +1,25 @@ +bump_version: null +lint: + actions_ci: false + files_exist: + - conf/igenomes.config + files_unchanged: + - .gitattributes + - assets/nf-core-spatialvi_logo_light.png + - docs/images/nf-core-spatialvi_logo_light.png + - docs/images/nf-core-spatialvi_logo_dark.png +nf_core_version: 3.0.0 +org_path: null repository_type: pipeline -nf_core_version: "2.14.1" +template: + author: Erik Fasterius, Christophe Avenel, Sergii Domanskyi, Jeffrey Chuang, Anuj + Srivastava + description: 10X Visium Spatial Transcriptomics + force: false + is_nfcore: true + name: spatialvi + org: nf-core + outdir: . + skip_features: null + version: 1.0dev +update: null diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4dc0f1d..9e9f0e1 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -7,7 +7,7 @@ repos: - prettier@3.2.5 - repo: https://github.com/editorconfig-checker/editorconfig-checker.python - rev: "2.7.3" + rev: "3.0.3" hooks: - id: editorconfig-checker alias: ec diff --git a/.prettierignore b/.prettierignore index 437d763..610e506 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,3 +1,4 @@ + email_template.html adaptivecard.json slackreport.json diff --git a/CITATIONS.md b/CITATIONS.md index b15a7c6..0a6f4cf 100644 --- a/CITATIONS.md +++ b/CITATIONS.md @@ -12,11 +12,11 @@ - [FastQC](https://www.bioinformatics.babraham.ac.uk/projects/fastqc/) - > Andrews, S. (2010). FastQC: A Quality Control Tool for High Throughput Sequence Data [Online]. +> Andrews, S. (2010). FastQC: A Quality Control Tool for High Throughput Sequence Data [Online]. - [MultiQC](https://pubmed.ncbi.nlm.nih.gov/27312411/) - > Ewels P, Magnusson M, Lundin S, Käller M. MultiQC: summarize analysis results for multiple tools and samples in a single report. Bioinformatics. 2016 Oct 1;32(19):3047-8. doi: 10.1093/bioinformatics/btw354. Epub 2016 Jun 16. PubMed PMID: 27312411; PubMed Central PMCID: PMC5039924. +> Ewels P, Magnusson M, Lundin S, Käller M. MultiQC: summarize analysis results for multiple tools and samples in a single report. Bioinformatics. 2016 Oct 1;32(19):3047-8. doi: 10.1093/bioinformatics/btw354. Epub 2016 Jun 16. PubMed PMID: 27312411; PubMed Central PMCID: PMC5039924. ## Software packaging/containerisation tools diff --git a/README.md b/README.md index b7a0472..1ddfcdf 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ [![GitHub Actions Linting Status](https://github.com/nf-core/spatialvi/actions/workflows/linting.yml/badge.svg)](https://github.com/nf-core/spatialvi/actions/workflows/linting.yml)[![AWS CI](https://img.shields.io/badge/CI%20tests-full%20size-FF9900?labelColor=000000&logo=Amazon%20AWS)](https://nf-co.re/spatialvi/results)[![Cite with Zenodo](http://img.shields.io/badge/DOI-10.5281/zenodo.XXXXXXX-1073c8?labelColor=000000)](https://doi.org/10.5281/zenodo.XXXXXXX) [![nf-test](https://img.shields.io/badge/unit_tests-nf--test-337ab7.svg)](https://www.nf-test.com) -[![Nextflow](https://img.shields.io/badge/nextflow%20DSL2-%E2%89%A523.04.0-23aa62.svg)](https://www.nextflow.io/) +[![Nextflow](https://img.shields.io/badge/nextflow%20DSL2-%E2%89%A524.04.2-23aa62.svg)](https://www.nextflow.io/) [![run with conda](http://img.shields.io/badge/run%20with-conda-3EB049?labelColor=000000&logo=anaconda)](https://docs.conda.io/en/latest/) [![run with docker](https://img.shields.io/badge/run%20with-docker-0db7ed?labelColor=000000&logo=docker)](https://www.docker.com/) [![run with singularity](https://img.shields.io/badge/run%20with-singularity-1d355c.svg?labelColor=000000)](https://sylabs.io/docs/) @@ -67,8 +67,7 @@ nextflow run nf-core/spatialvi \ ``` > [!WARNING] -> Please provide pipeline parameters via the CLI or Nextflow `-params-file` option. Custom config files including those provided by the `-c` Nextflow option can be used to provide any configuration _**except for parameters**_; -> see [docs](https://nf-co.re/usage/configuration#custom-configuration-files). +> Please provide pipeline parameters via the CLI or Nextflow `-params-file` option. Custom config files including those provided by the `-c` Nextflow option can be used to provide any configuration _**except for parameters**_; see [docs](https://nf-co.re/docs/usage/getting_started/configuration#custom-configuration-files). For more details and further functionality, please refer to the [usage documentation](https://nf-co.re/spatialvi/usage) and the [parameter documentation](https://nf-co.re/spatialvi/parameters). diff --git a/assets/schema_input.json b/assets/schema_input.json index d3c899e..063e316 100644 --- a/assets/schema_input.json +++ b/assets/schema_input.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-07/schema", + "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "https://raw.githubusercontent.com/nf-core/spatialvi/master/assets/schema_input.json", "title": "nf-core/spatialvi pipeline - params.input schema", "description": "Schema for the file provided with params.input", diff --git a/conf/base.config b/conf/base.config index e3cd6a0..18a2a70 100644 --- a/conf/base.config +++ b/conf/base.config @@ -11,9 +11,9 @@ process { // TODO nf-core: Check the defaults for all processes - cpus = { check_max( 1 * task.attempt, 'cpus' ) } - memory = { check_max( 6.GB * task.attempt, 'memory' ) } - time = { check_max( 4.h * task.attempt, 'time' ) } + cpus = { 1 * task.attempt } + memory = { 6.GB * task.attempt } + time = { 4.h * task.attempt } errorStrategy = { task.exitStatus in ((130..145) + 104) ? 'retry' : 'finish' } maxRetries = 1 @@ -27,30 +27,30 @@ process { // TODO nf-core: Customise requirements for specific processes. // See https://www.nextflow.io/docs/latest/config.html#config-process-selectors withLabel:process_single { - cpus = { check_max( 1 , 'cpus' ) } - memory = { check_max( 6.GB * task.attempt, 'memory' ) } - time = { check_max( 4.h * task.attempt, 'time' ) } + cpus = { 1 } + memory = { 6.GB * task.attempt } + time = { 4.h * task.attempt } } withLabel:process_low { - cpus = { check_max( 2 * task.attempt, 'cpus' ) } - memory = { check_max( 12.GB * task.attempt, 'memory' ) } - time = { check_max( 4.h * task.attempt, 'time' ) } + cpus = { 2 * task.attempt } + memory = { 12.GB * task.attempt } + time = { 4.h * task.attempt } } withLabel:process_medium { - cpus = { check_max( 6 * task.attempt, 'cpus' ) } - memory = { check_max( 36.GB * task.attempt, 'memory' ) } - time = { check_max( 8.h * task.attempt, 'time' ) } + cpus = { 6 * task.attempt } + memory = { 36.GB * task.attempt } + time = { 8.h * task.attempt } } withLabel:process_high { - cpus = { check_max( 12 * task.attempt, 'cpus' ) } - memory = { check_max( 72.GB * task.attempt, 'memory' ) } - time = { check_max( 16.h * task.attempt, 'time' ) } + cpus = { 12 * task.attempt } + memory = { 72.GB * task.attempt } + time = { 16.h * task.attempt } } withLabel:process_long { - time = { check_max( 20.h * task.attempt, 'time' ) } + time = { 20.h * task.attempt } } withLabel:process_high_memory { - memory = { check_max( 200.GB * task.attempt, 'memory' ) } + memory = { 200.GB * task.attempt } } withLabel:error_ignore { errorStrategy = 'ignore' diff --git a/conf/igenomes_ignored.config b/conf/igenomes_ignored.config new file mode 100644 index 0000000..b4034d8 --- /dev/null +++ b/conf/igenomes_ignored.config @@ -0,0 +1,9 @@ +/* +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Nextflow config file for iGenomes paths +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Empty genomes dictionary to use when igenomes is ignored. +---------------------------------------------------------------------------------------- +*/ + +params.genomes = [:] diff --git a/conf/modules.config b/conf/modules.config index d203d2b..d266a38 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -21,7 +21,6 @@ process { withName: FASTQC { ext.args = '--quiet' } - withName: 'MULTIQC' { ext.args = { params.multiqc_title ? "--title \"$params.multiqc_title\"" : '' } publishDir = [ diff --git a/conf/test.config b/conf/test.config index 0549c99..85189dc 100644 --- a/conf/test.config +++ b/conf/test.config @@ -10,15 +10,18 @@ ---------------------------------------------------------------------------------------- */ +process { + resourceLimits = [ + cpus: 4, + memory: '15.GB', + time: '1.h' + ] +} + params { config_profile_name = 'Test profile' config_profile_description = 'Minimal test dataset to check pipeline function' - // Limit resources so that this can run on GitHub Actions - max_cpus = 2 - max_memory = '6.GB' - max_time = '6.h' - // Input data // TODO nf-core: Specify the paths to your test data on nf-core/test-datasets // TODO nf-core: Give any required params for the test so that command line flags are not needed diff --git a/docs/images/mqc_fastqc_adapter.png b/docs/images/mqc_fastqc_adapter.png deleted file mode 100755 index 361d0e47acfb424dea1f326590d1eb2f6dfa26b5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 23458 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 diff --git a/docs/images/mqc_fastqc_quality.png b/docs/images/mqc_fastqc_quality.png deleted file mode 100755 index a4b89bf56ab2ba88cab87841916eb680a816deae..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 55769 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�)v~C(0C)c zgd3@mu<_@wnt>uVJydQ~oz|jKOy0;^`Z?+o2D0^+hp!@j_=nH5zG^AYBuV|wimv<8 zJ-BGiO^XI}T+0%OK+mPa+&L+!)PYa5H}wL${$XzJBCc;XV=Co{g^!)F^tz?jpNo4b zH_VuCMYaCaZVyd48bC?#x#Q0K4CK%<=X&Zv)V@IQ!g5ZVK?zTp+C(vj*rq zre0*ZTR%sn9`4BUqa`iQwuwP$!iTu9y z*^Aa8nvPt{NV`}cy5l$vTGknczicBgdPa#+$B~_lxB0^l39bW-wL`u?WXo>LbCrxs zHO}TPn@o1wSYvVPGZi62B3}9ADk9<9rEQFD-?ViCJHyk~ulRlQ*z07+ zmqT0+dAd*&o$#ah@3U!@BqPvJ}Ns=MjBuIqf9PCEedGznEA@4tG^@#xdHP z5}hhW*p9vTm8p^F2zoA2iJy%YoUT99TiNM^!6xPDkXY%@^R6F7n4GGx+4V!RemOu` z=Bso5M|O}5LA6BSOdLB#UmR7s1}UL!yoSsl_4aP{66T2X(LM*|9)bk2fjUQG@;XV5 za7g2iD)Klhxr?NUp}g%l7S(du@pSRzjsod24a*3J?<_x#8}8QdV|kf7grum zMHRS^M;MRa{Q64RKHpz0W`#~YUyQ#oG(l?D10Z|E)=~C)c9e1bRQzl_KE8L*d#S4H zGq*7)2eRPeh6YhjH3bvBj1tQl|SyY`C6lvas01T(9PNZJK6 zP3wxPDqmT-KbA4>ntJkBD=r{uh>P2dKe_5iem*i@&Qi7(JIJESfjBKGU&VlMgWXOZ z+grrgAg-ko&vt-qp3qk_{Jyj{S5C8tp_aWI-lcFeqdCorB>t+{;r}X*a{YZ_D7jsx@3ZLF5~Y0 zEmA^FHl-=O@oYTk=b{3)f#6wrVMR^aAFkWt`K!X;*hkOEJ}h?qih1@jUzl5Auc6L~ zxmKdYX`}A(wIiw@Nvhre3EN-J<9T?KI85Pa#lXhN0pxf~!g)YyRJC$%aOPVO z1|N}Vm(EBijEx+5zwlamO7S~iGl_`D(3_AYNv=Tp-B zLfLb!LWW&-P|dCrm$Sp?uU4-Z9Z(L)Y`Z^8vKv;BwSQutkP{9P7Ks==4@J%CYWj*9 zM}5&B_xX$_jmo8fH#TZaygRjP#vD;JIFLu_3CL=zp!gk|koyVmeEXBMat*taN>zb& zg&Kq-YKy~J*#7QCz^h^O!Y`}mn!;bvx)sw2>M`%V$C^-PmWPOs%LdR>R9a zjk<;fPnjUHaeQF}hq2MN56#UAxS3c@3Q9#gOvfR69IJ)f)#IIsnP!H1MzFJ+M~v3H zm2atRwZuz(u=p#QW$W$iOXDKnfSyYt`5~>Wm|Mz|({I|E$#NdL=fer>#3u1y5dSj4 zhbTlcNm<$ZXDm5+&{w;^Vnmq)aShdk!HJ)q1*3!J?c7eue z4Ayl-cd=DH3Kr87G6hlUw+4yt%YStriba0x#%6h8yWB{-wpg`bEXk>vAuT`8CMCZ= z-ET)=GS~U_weHAuj!N8$QxriRCC_$2*OZ)z1s7+y0Y=tKL9QtIwdQO;E))*V`;X)q z!yVh(pIlUb7qE?K#Tiudee6%#>#9!n7viM7$pyuCMEsl%le^k_Q@40@a~s%d)S`(E zEoa4Rt!`>1A*l{oFdqaZ%8$Gp!HH!0fyIoqj-0fBJZJCd=cuTUbI%~>YWI-?Xf_iU z;p(r4yd|!ntJP(HtQYRCvJmF3CM-fcN?4UOu~xNlO#K4l9UutOL;i*TcD40HZNfNZ z48=KpV`9#O&p~l1lqXnxeu_{R(_Fy18x?Do2vyIpfsMNi==h3*DeaW9KFeGKVIEUk zFA=1Sbsa>aOw&?cN(-LAsQGLQI*QKv_J(QxZW9@`w79A$t3iTm_8RU}= zPk1~jn1_ubHVP*Y=ty%DSKZCk_LL+S4BZt3ps?hcWV7U@v&+g|tce!uuT zoaf$auXWTi2^OKA6T^5VDK+&=LRZ zh}nwN4f|Wi2H;M29qxDsS1;ds?$L2%vs&=*`}(}x?fu@t5*h?7mkz7o7{o ziz|$({9mgQP|Q^QNr%LsNmqXDY%h(Z4D5=5G#s8mXc;bGXjqNhviHGjue>Uo%4SRF z*bqwj7Nod}m)P&L4UmIEG5T06`^F6ydHyGsz7w|bSdf}FmmV{OAIoAn zvSLZ+%SiQOM*3+%Bp+W1Lg$l}=r{Uk#**4isDECH=%jX5K&c!$Byp5BG?w8J;=YkIeXoqkj znKUFjOl-m^nECRn!;La!Lg$gJIgh_m;Fm}zxFr*;hzA!C9k~v(P>w8rpF(hXh1ovr zzA%Rm`6u4?vDUSNLT~;c9KJVF;WP;$)M+Y!vNGWDe8gda@!UuX;bF}B<-Nf*2T4sj z3>#r!`)cWpK08bL@-hHE@LQROyQGIdK{mv!k;3mAV~Y*& zSx9%5c6=H`R2c<5TZom~S)T3I8*R!KE9Z zGy!Hum?_Ifj#-ah^FhR$lt)QpLd z4Z=r(dZzP@l^;2su|VZMmnmOEH~2N&6&pO_5y1FY{2%~AEy}vnB0qX?;I+BeKcB&f z|5-n=5l=bT!BIq+;RyxX6beD)7x>UAtobc61SA?P_ozwGiB-Aj_c@!Lx0)r0&$Q*; z7-Q3p>Q8fJ@t8ETi=ab%YjAt}qA~>G@Vs;N-`I%rADs}msjm0>eWY*01Gn@It7Gr) zvfk|JHY~V9eI(H5^?}anqY4?%?)Xku8F<& z>_)a|3WD-J7>6{IyHJ7Ny`sr%kPEeFA5=8sz8I;*LW|uf$ijVCB$3K8y`x{FJORg-`CT zC}*oRScJZ^5!az4e_~k*L8Kie5o|%0U=n+}6MSoXJV^q{avZhx_N7Rh6~0qzf$Y&r zdu6)*)REIY#^T(0%7wuvlqQEMvE;#rG+58^o-`ukh`jLP##HQy1~6-E4c@rB3Pqh8 zDUnBX7mjDFaBO-{#bn&eWY$}&K#}-hW>rwhHS7<%)64c=7yoZj1-pKq1+iGlPBJuV zKWWI?fcdcbKl5WJrm2fffh~(~uvkVjp*vVr(~|$L=|8=URvWRpUf6Lsh5vzbQvm?> zx`zl(i*xr!4lxhdG3~Y`Q1gGiOqdro9<4s_DQ8>s)cb318F(RE9jSx=U_oa)!&<@6 zW>xI-V$Y4~$-l&cpIC)?eD<+JdcA$LeW$*9XCE(FnjzJSg_7=*jN^W1@WeUBcjDH4 zDPL7o!srDPfz9aXRG;qPXHjo@CM^=WfXt`E4qzoma*pJ40+uSL4biBj23qPqe)@#A-O+O882J9sS zx^ICqC-ENXg873a)hiL?Yz@}dc-2eO3P(wUqi2Mlig-`}Xn^2<>c-!c)nYA2ANpSM zuX$`hTok?gLtX^Ds38~f)saMV)hGjY49J#-6JXcd)fmPuT>MU&!;gXb^H(>&Zpei{ zD6$?;nhRf>Cl)J|l?%H+@7`H_THjT#q2NZFv}4$jI?{y^AFw)t(<3NOQOC{@uK$`a zoPZm>!1K=HBz(h-CC8)qCeFF)q=Y?4W0+Y>aYM_;Ck3GXj6bx#QiT@aGiN1BTVkl{ z$_soMv^o*z|IS*ibD=5ke1x4mH+90p^=6jL+vCqdmy>bpw>AThce8)=@3y`C^n)S` z2As*5mQq-ZofZMgl3aFv4EY~!kc=DVgPk4%_|XB9(t z&pkSvEgC-Fd2cJ<#I~D^+)wy<2|Dc}KteTsyumg~<4T`RTwO73uT1x6b7?Nz2m-zv zqyOe#?uynui^nat&s)saS#K051fD3HM8_dfRsv_4@!qD$rGwLBE5@Z2j9$ta(Iy%Q zyI?(ek&`*!o}zI)2_mMe+s^6{Ncvh8eAY-1@6{vYFcn>k8*Sfm zy$cr$g*55TbyE3$Y-}MsJmS0A>(>=$`3LA|Pq1!y36T*z%Y;3sBPxQ9<3LzLbMRC2 z^lI6cc)`I^f-xhbbhyc!6GZwVIRv`9)wSdf+(mLG-yGJyMG40l%UHu-3#%X;qlpQ4 zI#_zNF=lp0{;4(>6BbnpqPK82Py0fT!H1JSM(`6+d>88_BgyPd;`e|gGv!)&v8f|h zKFe}=GlJEsk%FxPR7!jXRBNR>!wcL`rav1Gca&M6@ZFqE% z`4Mh^%VfTB>88(OnS}XjA%!~1TgzdO3p7|7|926;mpc4??7wq26+B<|^nJ2fDzywu zFo?l1EdtXHOpk5ff@z1DS-<$rG(ZFiXuFs|}Y34Kpxiz9w9v)SYh`Qlsa!LK_OFPk$W_-wQcU; zqnMAG5Q$Prs$WQkS8`znPLX==kuQ7CiAW{Rl1k9zUL&)gL2Ky%RI6%ljx`3Lym78HOG_r#NWZ`h;UmT; z8Q;NB(OjT-ypxw`C{7rz=Ah6?Ilf*d)0!r@p+-^-rj8xi z_6SQ&${Rp@207;QK;#<376gviKcGm_O;|y6$pBqF&Tj(sX+L)PBhju%zN5&)Py{q84S1 z!u8GCK6^gp(|xu;h?PPKnUh7Lmhp+RzfjWm!UtOhw9(KveIW^uIn_ z_4XfElclN`*ZUd3r=6|g_*_mCYn{^noi)emliSaY^fz<49-|%;zdlvkVbJWlK+ewK zY*{HA(P$@!lXVkSTpg#-w&~WQVm=nA@QV~tjbwOd-7zb2C?(IOw{6?D(sBB$ncUFf zOE(5xIKJ9Pt&il#NG9BsH`1^QjnQt{9LJsje&!xuc&TL(@ zAuXdsJ#S?ulhXa4ohB~W21ju2HEmn9;Ale><}Dj~ZAt1pw2jd+HpPP}W)J-w1RDseHl7A;l`H-f zBR?QsBau>#e*U!E>9Dp@ArRa{F&#eiGa?C9X0D*u+HD^SnppyBly#h5H*jF%%7=!sw59c9vD zehhfcSO<-^K!2XtS}}-6ld)lbeq<@ttMA$#^BVn6O>T$3LxpcObE-NtEn)SH3DAgsjf%Hy@L@o z>)9|}Njhf6u=~m;LtCH0meC4`1j`X@*Usz5Oj(WAi)jVKP9?vMg6!#`W_aJeyzA9E z8Et=&jhAK;rplBlx~kENNni)V)@4o#6iK~r3DI>TTeDky--t|0k4HK@%pgO9xQ%UD zyh!gX7B7xtM3{)5K!6}U%CGpooZ#bwfJBA8TNJ|w2h=#+HMy)2qAkKu)x~cv^MTR5 zgRFZprT~ARVEa$0VJl_teYh6S_m})2e(B2S7D%gA2}!UY_BEL%&Tpl&tiC2nrB;xd z>BKo49MIQG#xbHH@XVM6HDxXHxI_x8HLWh^aO2<0Q|I4KOH9SCksvdzy{{R;Q_qkt zt6QqxbuiwIc%>4LsbH_z77CuZ(N3Eh{Hjl*tq**sjUxsbL00hB%O`K$_t@x|s{n4T zNd=a$$ae5z7;Rcbu!eQO`0qOBG$j8>tyuBKRunfzdwqI*M)DkXw4BTY9#k;h5lpSc zQ`n|Bngm4zP!!TzK$%?Z-G;AmCHO7HG zJ4a(MJnx8jrjb>P`5nQ+l}d5)GCk*Icu;gi*^oOINvafMb|ZIakvKmN9Bc9!zuX@| z8c!6fcJBtgI}cj%Z*hu}cIGcMT*eEDaRt3viG8Pz`YPlFCsx%E3 ze|0qp+oBM@_a-zIsY9^~(nq26QCP#uvzBLITT-Fz1pxTVGcnL9>X6Hfuvh0pCi`ERa%Md2+UxG~gfM-;9Wc)ekf>K{tXe9Mtf!(RFbeqz0o?=Tkh6Nvrj3gQ`mk*o^N zm!-*o=#C|``9cYa3e9*JN%R@qkelPrEPd#e)szjS?u45l-g~tSiv;RefFk~@$ll69Yelw0B?`5LzC;tmCJSyx_+HqT%Gc-2 zhqa7V;q8X$f6QtH%hylOT@X$Mzo#h71A{SUK$?cZ-d!_6boCTtWx6T|zRb+Ik5lZx zC5dG%G$-g=G*YM6F_`aAlH>GIDIqE;_y7oJh498JT}+&LXR4d;+c`H(r3h&!=?z9x z4Q9TKSxmY$n+qmpaZ(L5^RA7HmY@KNAqINP#5>dVozR%cDNn*ch4az#C??EvxggEz zsSOE4zWxw3&F#htFngbgdsT{RM~3V7uK!%; zSN!T%2CcRzG~5cBOfItKldRJy+p^9QA@i?}dZ znE+cDmfM=j?ciR(FH$XL?toJf-0P#?``x(7+V%+5_T&Q}4ryu>>On>|O2>w&hEpt* z5)Q%Yc&uncx(~56ht=CiOPu^_jEY%zk8Kpx8pu5Vbwy1^yuRo6Z{#hTke{V6p)&Tv=g`ZHv@IDp| z9-YRIOoK7?Vhu_H48|kcl8_9){<@Y7i_RF`qbV6-7s>n$_Pk7Q+O8Ny@3HclM47Ac z6zq|t>*>*jzQ1Q3l^j2@k0ZK+I`N0qp{^YV!oBYzZE5 zSvR>;F(^9oMiSA@_%a>wFdl#lN12STlFn`{Qmaf}rDn#9RS6j!Q3~}X zj=UMxLXAIWT*~kt-mDJCc)Cpz=ibFBQnyK#3pFG)Am4l|0PbQn#eT`Vij|AEU5G%h z$?8@IdZ=eNwR^{eh9<;Pjkqg_&CZ`Hvor z^fGvd$l6WXOdtBDp6J#m__((+#YK7r9MVZZf^jwc^VldYv>MnCwxEHmjCA-@!jTj?aPs5l^liizJ(^&FE1FpZ{Ym2#`r~ z3$WnCaEA?+aPxO%`B{1|`gSd*Ka{eb%NZ?ZKVE^@Xr40xBKY^cL=YK*9#^7FK>)h( zQSI76fgkV{B@bpHxC!faVCy9_0+fD8)Zyl>Oz5wZTeI&x21V>$btPM->8wm90k^yf zdoyGD<+a&Jz#pF3h!1alyPUX(tHDr~S87UyD+l>$24NU?oQO9D4|DnM<<{P-5v z0EfE~)@KAjemmaKTCM0`k3tG8krF!R2_~LbrBR2%teCVPh=veVmQB9mWCw` zRBgo9P5Zjdo9INN96~`85TLimeAWEwn27-7gW?#U5e%o(cE$*1-b}L?*H}@0i!8#D z>Uo|PP&r6F`v|C&?si$#j^150fj%x~5ONvfry{1>s%V^z?BIVI6%;awoqIAAE+1r% zr%okZN!tCI+p9joS~>M{6SzZ;3?!2Dhs9X!)6EG?W`;1=K2r-_=(Wi~M!Bb|OgmT_ z`2VC)SopD@PttM9_!%^JN0ir>nt%q^UFnwBe^6%XTT+3YDSb?Ycreb%B%%D&Nya3+ z2w8xJsD7FRj?pAvgW`tTb`Y4^yWJDg1&-?3wn>%6BsC2_CNkshL&e|3s0g6 zCp}stZhun&7%~}K)l7`s*HIU=ZT@Ig^~ciyxVAo{|#log(TGcqhFz2n>YD}PfA{!SqL*%27i3L zVt~5xwo(|dpyWNbTT%Xq90l-OjX0{cQ19gm4a+43;MeNTZ=^*pQErF466HVSl3n+B>}KhjI4M{vNuAyFoXS1WABDQ=ro#C9LHsinW@c$u zat7*s0VfDf|5M;;M0)rQl0tU8yk)AY$&F5i9w5cuIvS^~N4`8Er&8j=LloSD zIB@a!n7j^ZL*-A|ES~z_uESM3XAG>{e-s_b5@Y`0H<8?2V(vtNLcG>P#L70QDc=)3S59YTUZanCyxMgJ9IkJd@Js*GAR@QbFvEkyRt*ihX00jFbI`A{T@Hi7a>$ z9dv>9Zj5Nb)QrZRk2L02K06WlI?fU!y<7-R6wIRSDQm0??g)lKHj%zN!@_9%(a0V@-q0Y8JIgQw0k zW7KL3JY)7Dk5n5?r)jU5j0mN7vF}HdGu<)aLXMCHNd@t)OBd>dOcSQhVqu3=2eTsJ zgNs889adQocnYQEJQ%-no23VQ4pIz4bPKzPwc4-DLBR#uam?%N00hJ1njr|mOjTE{ zuR*ca{PW6n35vM9iK!*t8#DOOToBZaHj4?8k)~387a3NBLhj#R<;uK?z!bpJAS{wMPPYv6QFvJ; z1pm(5kCd0#WeWoFpwEhy?MR{TpwFJvXUtWgmeSGOP~>%i;$uC8L4s7CRaGSMz)fV7 zUH@X6>SJwD$y@wy2ft<@D9oe0{#fa=1O4+V;?Bu0XBj9@M&lTPmY1jKr%$u)t-%0H z3-xW%={G`|GW$M+@#1R2?cK`Es+e7a%3W&Y1={ajI{pp38a*BZf*cLMk@lcca%YXg zlb1((z53>tdl)5ewLO~{@W(aPGbV;*m_@yq z!qTY3JAN1dwSq6%J#P}Te0+5klVk5cW$!ppnl4pN5rBxnk}NjD;mr^O8WxI(tuyk`0_N-ZINriG=?|u0V*1~khV8VY1|dGfHsb!! z+(Ui-?Et=|dkl0Y1P6cph=LaS8TfA9T!yz?PpqW;y^36HLg)!o#r+qiEHMP~Vi977 z$7(}MP96Xy$AJ4j@)5S$ z2snd)MC1dM)y=FAI%aa~((I9!l;V~J2~%)Ps1pnWdtN_h)#4y1#Z|)Fy9R6MzFoTe zsG`5SF9Og>19#F$6A!2U5?$CmJUloKIWH2K!Pd!8Gl`-1B`tWbEj% zwiRkjD6ZDTM|sd?csJIOZSX&P3A_*kqq5%5i_x!yzuk!p2uJdXg!FMp@@_6aB7IoK zTfZ~n1_C0XsCgX-MJnqGCJnx&_GY%K+A@wwo}wu?zoJ5#%SCTshjddm*NlVOA60_o!t^8= zI0W__5IW`8Nk&UmI_i37>*#cFxlw+_lofMOq0LpPidbt%JRf+;51US0iZ2wkzhXBU z{sXo$ZRM!4y-fB)6GIa>mYK;(pHg%hKn`sr{vXS;Aw-_P)O1OwGV)Fmp4(3wz9Z;JL^LazLgBqs3c>31Ete zkvJ1G`mg2RFVoXBnbHFFXWG}DO5nA2ddz$^Q8rNcLw=sroH}ESu(vXg%7D4dr20c9 zVNbh2>kz^V5OkSK&mtMk#;7y~;;>bHPfBU~h1=K)Dez%9_oT_M9oq@hXPaCI-KAEa zu{h^qo^D~8_;yJU*(bQ2%Oy5pYPXS<8wW+^w*v_EnVFo=7Mxz0CO69%AvIkDua;ml zz0U!d&tone{&(zC2X!Ary4j(iv_c8}woL+hqX_34lAb%E5GR|RK3+PiU)tc&EO!lKt<)6Q?q{01?$TSpi z38`d+Wo9~JQFS7;L2m6=S4)!eGXEzn&)k-^*? zd1y`4oT}4%G%!z%}xCXHc>M$mhmTVAT336kckoBel%Bj z)&g8&jvAf@O!Xhv1y`%@vuHDzBU2eIKJHE-d^ihaG#+dinEZ??qTvKcSlIFl81&S% zoHEM=3Op{yn%GAlOe-^MQu7mA{UvC{^itXKzvVGn(In#i#7D#%-g`5-t%^txqr;ss zRa0U@3P+4G!CJk))@m4Yv!C;=t6-d2%gT=&k-LlU|HZLBjegiyu>*aHJ!<&T@twR$ z^k4HAr3$u8`D~&vUEwT~q%_-kU^k{QgYV^l6xU@aP~?)2R7Ni$;PRB>bq>wO4x z2Q47emNCk?Js?qGe-5jolGaEsMPNIPaN$dtXL$dp|N+K@#;;e$!}L;e9} z9|)HU8%z}N04-t!fy*cV-| z&}2yI^chFepYwSOh4h{7N6VIfD{fU8et0cv8q!pPWz}4dDhN9|6I4wEbU6S->l0aK z?`%!J%XqGI<%f9I^uH^v<41c29XWsR#SV7|oO?9xCy>;&NqxDJX*3)v0PF5mQe}Es z@{;McY=s=QsWN-j8l0i~VYxwu_RW_Ls(MO$M{F8D_^*6~WTdgNv!&mSpEEAgV7HKY zTz%Wg9D9(mFuZm&NL&x$k&5rqgW!Yx@a3u(zOIv;Ue;XgsP!R%QYvY);a(757zH9- zc4Ud;32BE97bj;-a`!?>KVi0llNL>XV{9ku{Qmt2^8w^JR*d2BdNFU}#jr1+?>tXidnE0BuK=S-> z=h>P=fbRnz5T;}T#2o|*n;igrz#sHq*Bq9%ys)H0F?pyPCv1_YM@pkxZGk0jT@WbQ z5KDokY=z2KTuDMU4aqZi^4=l86&mO^S~CWqFJ#i%2anIL^fydaUH znXJV@%IYSNofgsOQP}Cg&4d09K3VJd-5y#GZ}o0}XOvHnK&sdphlZ&~#{|6}+ePr)l?$_|NKwLRKN(BdZ3 zo#DJ@U=>sU752Y!1jPp&lbVL#t1ET51sA7t1e0$u;%X|Ct*=X&mew+NwOB)Prz=`#`&@WnIu3xwe)a~C4 zL3v7x3@n3V8V#$U@_G!`_`vmnCMluP{oO7rK%lLl3x8yU+u<%d=vI7RcD(rIYmub< zT~sKdn`Pe^#RKp{qrZlIH+Iz?rGH+&5V9Psbt{^s~I1Ml@4D2Us9a; zf4SJtwo@OBo~(qNojBF^%Gy!d?!UHHei#89mXzm%#QE2`WDj{{{~$+0LOqi*%6P%0 z%3*@i?u*OGyVk3B*A@ywsLuGBl2XYGDBy!kJtwQF*UaS`^K4pW=iof1FET}khs3Pk z`NJ&y!b>98;h~${_Too$)x{x$R6!8lWcpKg1iM0@TPL@5L~j{1C5nuVnU4R5xHDw3 zqy^a<2LKeQ&$;g-_YXS^u5A2l7-&=BGi7NvGn(RPbh&U4IM@v9x)hMm*~+kBFCBdP zu4W6LX$?j_MX-4Jo@9aOZxENUak7i;55J?NPMBy`KM7T5ki?o8-nY?+u$qaWER8=g zX0`0P5AGVR99*~Hw`{`*p!!-^knJK}Mz1=QZU%3}(R)yvgcrj?|fbhq#uk$67 zMp4}MhtDq#SrBar_6ynA{zL$l`8iMX#AmJRP2+R3}^5MRaqpmbj8GW4!Z$hLkza1`zr z@k1u&zx9zVlB`!`#B2Lg5tCAMDrTA+UfcW6Nk5kMr}E;uAB)ID3+Z}V$xKiXWLCGu zb&@@Pb=!WfDCLy2e{fUTg0SW%7c@zmHGmJkn5=1dILIl&6ZLKPV0MRz{m^T^tnU0UCMJ`aMmWMX6AQLqmL;?q?P zsbsx@f@LdX-&7D>Q*qjpw6tK(m1T$qYAVZXr#d;VCrG*3N1uYBJ$*>h8d-xGYpn=o zUXj?>QLCMN@Z(K7T^8!Pfq%bg=|gHJDV*VtQ|Rre}=?E(~;cSh>N0a!&!`UV$bA_ zrNERQ=kmQr#)YKfW1eZN?^ZaROvEf+Yg$8b;+I~$(Pc$u*9{X-G#3IEkEt*`$QSVIog6J# zA`y-Qp5M6VpbaKYFu}LMRK3jUvBOu0mF2z1`>m?1rp5!TB?KT<)b`${2^}{Z=Kap0 z{@V3UP2Cu&xngy8UO?MRAL3Ui;OO2=NV3gbgfYwkP86@NxCxSNd?D*Z;Zxl1p2TPq zrfV*YYx>zPG-*J6HTk{i<}%v5b&p^5)+`-ncA=7+ncNZE0?ZkE3V~-}!vX1E{LVMpgh3KmU##d}~-$~?0L z!|)PA9W6o#giPgsU|Bd3WY?@A&mz2kBdC8gH59E4D;y?C1g*@8X)44>)LvUB+KSRrZn=Pa@>glXfFN%iKv9F#NG)hABKjwmrQf`7$ zE^WH##}=w5_T5xu{lMbWSxb-&^K6pkh!Q&d0xdri^MFOgdH#*LE+|n)iWM|pweW{VTV9CFXr9w? zT@lQL5&`5YX#i=(c#8(v!80ed^u*m4}!_GKMeCmXy@wwvgds+K#6l{NU|Do5{(O1B!Z{bv(e>!|OAEauS zFeCzQ!T5<^)IA>Yesp68z2Lp{xE_t0@12s0l`&0uW2#aSd@}jt+iIPR$@|wAI{##s zO~&Eqz$0ku7AcgPbRy%=czUPh9_h?#Y7j1-_uwi+$vayFT~X+LPFx#MV3UgN7xq*W zdRE@0<>|@hX2qG>alJKa2Lf$fQ{-%T4DfS`J5Uf9P!LYt8I`KK-+Y^67+c?upqH?A zbu+jCX>IsTy&Mr$c#Z{Qw{IN)7_C$@ll$C^JjFaM4UaBV3d+sjB%0sMUs6dF*N}-xms`V{CaT%m*h#p@O z>BQbq6`f=qyyS0ry8-B=tf6jBpPis4XrLe+l{eb)ECZnKA49`I8v$CsCnT;z#CU*a z3rJ6pN9ZOU#7HD0wcJsit~-$nq-<+5xq1!z^C_`6szx(sQ!bfJfwoLDM^!hV!6YSJ z+0L#W|7eCMNd}#2)Rrn)R4P|t<_mHSDlSf8mDcyxcR%pilbomaJVaG_erwu*dH6n; zqfkc$7&t{y139)h%fUV|pyCnKR07)+)&mzNl~E!yFB_feQ(|~4lV8CVewB`IK~pJV z&M*5ev^{b(giYFsq`_n9ZtN>{C@9!j#P?p^RxU&>uHm3yb=kO%=F>&qmOf-m(WdU_ z|GyTDdlZ_dFE9Y<2rhwQ#LPA(L4NcFlH`}C(gvI9b*L6E0yhqi4ydqdDEI}QbYJ#w z6s3BOr4oJ1EEBU=s*~`r&>xDG?ao@fK z-5cUhSAgf=s%@m1wL)&1?g>1;v`GxC45skT;j)yN7-vDMotdI z3OSDKnsivlGMbhGKdZ2B)r5|NC4od58dXW%bW&>Fm^=Eey|!iZb?s;alW-ume{ME6 z^-@gBV6DY|joezuIF0uoWhvV7FGr*jd;7XXF#8r@)E{3E0EdqiKw}A+tfszOT1xAM zI@Yp=1WjEk8mu1Q_};EU1QG6i8p@7^)KpTH<|>_KzF@VKS?)}5?*^>Muh{Dbomv}C zZ)MM%Wl3xss_PQ69Hptk8=e64H@5$<)w6K{ka$v-q*jkReP%Hpze^vX@;;S^oiF#p zP^ZC<|BZbn$a_rk_ND!%!^nzsbP&HxMfr4&>`&zRfbmN4n7}mH0brX_P`(N#XNl#< zmlf3~Eab19m+!$p{M;v`C0hYbGa_hx+LXnSpxzr-XRM%bQN=*EL!~-s>=JoHgqoiD zmVUtXU2Q0#koE<;u(ea_d7+7=)KNo`nZe3H+js%Zapby%dzMdg8Q?dPc>0LC=XW%$ zA&94IY=F+HD-W#y=xdOp2alN6y9Fl0=p-sQ1-ZEslOzb)HC zFhk+y8%GUGuIY{$8=Ly=tk*N+t09D{jR&g)Q+MN9*#U%VFjBCoYKH{i_rn4lrfa>o z|Ip`>IH&N+O+v3&tywmNYXlqo#0uK=MYXTRWm&c7fih5AWF1K^{7`h}&tQ%WMSXlH zROqnOkl9@Ep_(hq0c+Lm%78cqD5!7Hhd0}Sm(MfNEQPfILeGVu3nP>A1{j(9C!*9% ze%Y-f92R*nz*5!ps^FtUL*f%R2QFQZ?qg>85EhKo2PkKZ?fG5MUQ(OS#3l1T7ru+F zj{*hHy1JjQSmy((?D|kgxB4pGy3VpoV$y(Rb%Ou@QQXk+LK+jk1>2b~=1%HZh4Dy`vziB=x^Yls~C#>020lv-;?LpQ~-2kH;EQQ~}+TdG)vi3@3};f$5i3CQ3^ zYuR*OoV=rykE7K;8F2*>kUmk|ppqG+Wg5r&D9;dTq!bzT=#>%e^-IZIqXezVLBrT& z@UWkNe@2~93z#=99oN6=eT_z!x91M{2FA`8&61U;EHu_+{`Z+zQ}A4Ix8FtM{{Ptf z%BU*4w@*+36#)eWk$R*XrKLqWr8}j&J5&UuyG!Xt>KwYeI}aeufkSuCMxXyXGi%M4 zS!>pOdOykWu6^(O>iAtNOJpgMtw<0u=ihwTrl^KTyoGbW!|`F5VD^;|{;*Ck`6BwK z;R!>C7GoQZuIm}L!o>aW6XTd5)NV}ssjS7%Bne6|c$O3=(!|DcO2obc5h<%vtQa7IKA^Y(eaz^nI_J}jXD6Qbc0+zw*m zGAIlpF_r2+duF^JU?lZXDB#CXv2-iSNV9zV=2n^iF}4MD^%w0|x+=}D5%*+(Z+p)n zGcHG)kIj}gk@-va5Iz_UmCi7B(sM-TG9gZ}QMBu+aG7*L>S^TK`ae}ldtf4`t3`*4 zS+Go=c!Y$kP>Ok=f!pk;I~OzWHnjn_M&IKy?9^)CuV?9YyHgdXu4(;7Bd5 zQBNYajdS@nDLd2>L`LZ_uqL%P^s?e#6x`!(UOu7E#8ZB2dT(B!9;#i)q>$wuuwA^h z1As!TH~iTQ%?dE+i+}q5Ts+rXiQ4Zbt;Os7rw1K@bJs%jRGxR}QP$xyB(hl|UGzI{ z_&}Bl{<|`5m=#psfJY=E?{IQ)LLo3%Td_LJuKal7>!>LA_aF(-0WAGk`b#2n8oQuR zBXSrK%_V)B-RXe|Lo6jl_-`$PR(VcOtlCKd8NuQV~m%VsU#5A;sxAif^%f2W!v zV6na%<#KXl>0(A?!t>d|Xs6GdrDS?=5%hQbgnWqO&}rE3oN3R2{281Vn#d2EoVz@B zFNsQTDcvkO^}5C)G@p3%M-UpQ=)qV!vgOej0_~u zxVm?()qPlQu+IR^jSYtx)EOOxcHyV4N>Mx8W1m86nCC2Aq}jL3u;Zzt0>tq%$*_Zg z&GV8S1T?JU?YpbxzgXO#7f|@|2zNjV06!N&KF*F8sq|(Fg7m&tlTDpz=v;hi6_F}?!{@{|?Ly{}xL_P%Q^5Mf!3Uv<6(a-(z0BoMwi+9SaqTkg#>?mqAtcx z7Vh2pH*2+T)_C~?zp_=^DTZ1|e#lm#W1_Vlgs`z7dTFc5)y!=)yBXI-q93sE$jN)W zci(K*?77VK`%s(xh#R+Q~3K z_SwGZ*lrDT=#Mw+#TV5Lh&{A|&l%X$hAv(%Jbc;)oh`WA`CHg`HO0zn^yJ?xXia%> zY$BfiLyFS#=9dCN5Pa)_=e%*kN9L;KaGTbp9fi%{(1NmOTlM$WOpd2na~su$2FzP8YrqpiD@lmitMf1)uah)UIlDowLgx;4CIVWA`=~L--eODx>>w0 zq42Eoza~BAJ$%bJ8Q@=ev~=X5hW6KsUuq+grCk-ylG{ChyStG|2W^?vp5IkS1!|R| zJSPJ+XDyG$!`L6Bm17Q=bH6bt)CN0vhdsU=$w}W%*ORs^itINANY8Cb2CVGrJspQ` zb)d7%O^4T_1pw(B^m`ENeE5N!-7XZc0m)L83yNq5Ii!L#^uAxITrXC#pbdEI`eu*v z#E0BJaTx@Uo~e9t8hIOS_`46)_Yv|b{mzas8ou{kUhRy)ro0!yLl7r4i6TRolRV}n zz-b$y`%$$Iokcs&O|=MfK(P&vM=x10xL%c2mnubaFlTN1%ctRr)FX*W-I!^U`wo+i zI-^egAkap=9LUdqa}}h(l>NB8Yf;Z7cl&ARwr@Ayo=ud*FQ^{V<~}t`@2c&7K7)kz zyBVdYim}v8y6~A}!9RB7>w@1h#(aCtmq=hdK;2j1FUGnr_YR@HWSDx=ZKq)<6Hr6Q_OlXKN8P8$@+TzJM)aIEAUWv3 zRqdt7&kapo0e$O~MVW5fCL9lD+K$`%mK__~j;r%g3SKioa1-)p~6CIl7WCx&<1X52k`&E#vUN_LjxZ=#tYs}e7C}f@Xbwd?wN6I)TQcH2O z@5phbWfo`MPTKAqrfOkfq9=v|)5=zU=+cfCgud1f%5fmbfuHk`W((P-W)v1iwI)-# zTTw^evY{)a)4mqLo2YoA7YM3Gxm#068=i-tQ=<$RvO;o68E$ctQBJ1Sa@yiRVIdk} zL=b9xV0Un+?$XP$2Q1o(0S4>|1Npxj?(l%Ge|wek#Dct)dyLE%#oYoGJE@PoZ|C<; z@)J&;GVmBE7WbN<@i=`{Eg{7Dbq{hzio)Y-6WX=!z)WCDZV)D?Ctnk;_MI}L>ZwtX zq3*g$rM9E=EZfxURP~agWyVx(C)$<#uvSu-H&`7L~=IWbY`erWU!GmxK~32z&7iUb+4*)M{62<(fbyUL}X z;gLm}Me|4C>eTss;;XQP>xoXUeV5lBizj>0%{g1R)I0IYWtBK63}X;0EhH7hLQ8V% z&Om<@Nl(RSGmZ4NM3d2HhT)ech{7#I(Uv79d#if5Ql5nb4U;ciMlm(CS+y)@o4N&_ z{#9|!`p$5O@O?)9JeGu3iqbtzYq7Wpi&>&;f(%-8*3}2kD_Px)daZ;a znk{{2M~%;IcIhlz@B$u?f|ir$Ee}Uwu6A6X!*;bG+>FQSp%Jg5dz~>OjdfER!Hgc2 zT^048Zs#3gx&VRG(F35LS%gfHvX}iqLC+*XDfZHS&(dK__!}bD{u5%5pkn z7n#LZcQwzs7b~;B)y6MFzNeECGlF>$ce|L_o+43@7eQsrt6(qxD|?McH8|!+ zi~&PUPFv{vaG(@l1+Ui{n-B=zCyWgUsRQv~->GuKGC1xZjYvO^bI=im)K{aT(C@qA z#}k2~RC=rwBn4zh)Cy?h$VQQ>9B05SnMGgDWEh*k-}&|hnc&GufLcy76!=D+pO()y zOV6e(>{dC4K*$4dzk9CM>Y`JxWx|WBFFz^D&<{W;$)#;>9HC)^Y0^bktoQ4W>w!j6(8#7d2(>HFoYbWxPa;=9VaWbohWgh0wIqJUyA;R;LdJ;Q%B>TbjyysI8lR36tBt z*F(=XO&(Q%$)4OFQXseJpCeeXN$>+qW61gL^>!B8eBL!fr#{c7gZUD!vgLgBYtI!S zXjja|Ll6cT2_qA}pijQTowea`BG`{%3k?X@5@b$NY`xD?3ST+0FjMxUZ$JJg8^G?S zw~Ia13HUvWu(o;x88d}GgT)xtGEhbJ3XN_Og2@`3`$~T3kNiRX{E+Q^ne~<{-`lqr z{HS=iS}K7}2@P4>3@Yq8rqv9HtLpvr)HJtwVkF;*rWtefVj9t?7M#iwaZ`?h@=sv4 zwfFU}Ei5Trm~;xVn}N$)fwy;pv`aaXfTUMiW{s*NVx5xmAPT3tJHUh9NSUd%+&HY# zxTMlL&3Kp3e3wt5wzgX|WBPF24sXDiDOohs$f4-v{q{2Yiuo^+g*TFgl8lZVV-vqJ z7Tfl^6QX?fo4Z#GSaGz9l`X#EdP{n1-QLt(U$$Iw`J@aC(U!xf4@(c%m)9e7zU!zC z4}7VdAlTeSKR)(VGCPJQzMyDAKe6#Rvp^scd|8b3jk6U-jeLDjbz0~5vRKWi&9lSw=8yHd5Ypk-r=N=*>&*L`*@5vnFxto1Bx7H98)pfdGR2n=eWjXGX?eq@pEG%q4pLag@G(l6N7amC4vea^al|i&J zo8DR}R@#f7i!z1mpj9l$6W7y3u_#7*Ctk;1O@MHwe38G#PD zXK4WD6J!+7$M8do`F=p4;H%MORtoN>AL4I6m)cIUrudR*Z*#v^Lk%)SC<6O8lf z=qF5psNO-g+DoF4qNl#1s1Lt+F2)K-O6F$0n}TiVFnd0FZQuw7DND&}`x&?2VW+be zzom_~X4GoV_&^Em=ntJ`SqcO3YRfQCKr@#(V3pLi*Rls#8-&yhpP@}JOnGZ{I=Vbv zd}nWmSOJEUkv$!{Z0u}J-TA?XZU4QlmL)iRbc%RTHQM_$e?g0-YfP9o(q!~+csQI$ zK)aoBALEJpAlRWN8Ja5%5zs;@9Z@%L=!8y9IRmRQ-hL{9+*0rKv)e7a!eJVPt$%h8 zvxlwXPV%n=toc+k6kgGB)4uzZ16)oi(Els1D|9?|dNg+I;Kvyr2u66}yDMNz{W9!-8T&0< z9`tLV5LKyQC`jb%NvOiU<7S9Zx%z-+2|nS_vTw@MU-zVdrvN5Yxqn*2m`yO0H5hc< zo?Mjk8+8TMg;C2?Dz5B1Aqd_vuUx41yZq#^ROedQSyiDr%6|oXUUOqQldf`eBe+=* z1TPO#@lWWV%VIh;asl>;g0>-AZY#M92GUD^P`#CM{+3l=v?B??h9y~ zMbgEK3L|ktg{6D<(H}cSKkutKzK<>;y{_P=omYFkncFbMmzW3essXsRB-@|bErFiYvPPVZ!)vc1PQ;Jo_0&@kl0D?z9*FXtQcPj ztMzyy*Xeb2Z>yFNa}rRlp@L4rW1|zNHFNrboj@s2ULkLv-tte{ciH$CTWz48mk9vt z>3;gh*>45~RB=G?or>l4@9C)bya_rZli4?X!4%^{8G0Xra}r?vb}LqHx4`-lEfi1u z*B0crsH33Mi*5^f(#Zkxv0M=zRWJ)NKuSM`p!~TuZ)JF-ZpEN_Mx$H@R^oUJwq&PF zXqpF@7wo>n&Vy0BRkahDEeT^h_1*B*3BF1nqd!9mt0btk=9%&sqL0g78^dK&I$Un0 z)}&%VO>sHP=(L831;_M%{%hVcQo`WDr-<*=OcL+ER{NuA&u}OEo}J0LFz=b4z>`&#jB*MLq2J&h!&9@o{VO zwYu({G*vbgPE=Qxu5zJ}!VmFiJOnOx$?15~i*MoiUoSoRKq;xb{iFVkFColaGzrqN z@>(D)dGes>A7c6{*LM4&*F#VDg(nJR*}x2?IR?4DvV@+1ON zfuGxXg4k8DO-p573F@$PwK^6%qc6$Ol*>RS%d^KeDH`{ncFrpoa#ww_LfVm-dbo)! zN}KX_*Qg-eJhvCZzLrP|Y|~@X&Xq*6>Jb)Mo#-kBQwo)OzFd&Ne^R?l_YJ8F!jZ!` z7u8U~7G8(S~@urM;F z7b4B;``hMIlP^ua4Uc16d>O9n8Jv5w0y1}`4c~8jHO&SJHBd24L8k6Hn4Rr{AV|=S3HYCloaak< z`wC}VdCjdWA7_6SXq0pqgE?Y@A$+F?N4>(LU#-ufDpwli9}@v=&6tBABSl$mx6eSm zYym_5K>|URD$7U9KPr9aJq8;WH-ac_UusZI!9EqfaS+c$7YR^V5$QyFWeg$jR{B*H z4a?hwrRGJqS|j>0NanjXQn4K*Pu6f{_|1i_xjrH?!!ws9Lj9w`_=A z@pXIADP9D)JMFL(*+HgIoweJ3Hw*{pgB4)VKkK zdwNC9X6lE|b^zGsSGab(>>#KT*`tn^kqRQ~OSE#1W7Bc^u#Qo{gLZI!WnNyALdg9t z=FQ>IVr*mnYCcH#iPx>m$foh}*%2;;9_(sg*SPIRPiq)yx{(?5Y%xorkii72G zv$3bKYY4;r{q~+Yw0drlXJiJaPo;(TrJ7Pe-(pJ?vLR0#;$v0IykGro{+7<-2}dv8m)YC4 zsesa{czQQjDu9Ldmh99J%9}1_5ulTe#mTnV;5*2{f=w9Wn*A+_xGPUfk`r4GB;`aEQkpd)ZSj8EYN`#wd6z05IlD;7Z|)jhM^WA ztus>Vv$o>r%7U#>)(htR(8rRRcRmV^{mk*()>Zd;3{J*--*OC~DdMH*YW91nUu$@P zY3I@%DnXG!TGKa7Q{{)wyDpS`Z@6vP-JITVZ3N>4f7*HIjIf4zi!W0YT*=5h%tP6G zevw9YYww^pMsHrTRb!24C}pXeA&L8W{u3Av1j!`P!q8dIANx%jT=QRzea8yLL-H7O zg)YnEQE+IX6Mv1Rr)9RV=|VQvMQ)BwUXCSh{`?g`#N!jE`E{jFp(jq8Z$-5dcG%X>nL1+YPd`8n>(p}-c@!<}9T(=L#1zT=fIv`13~G>80;F0BH6%20Ep=KO z0GZ3ZQBrTNe&fA}fKA)muLqLW{dQM!iR-v7NV5DEzKtTAdi(B*e^7KV$q>Wpkf7E| zb50UPwrE`>jhn@}gT7YNGlI_}pRK~_pY0h14X1m5V~>LQq1Za8oiPYIDa-f;sd#Y zcDUVzqhptwmjsumY>2I*T{fjxgzSjoa(m+-%2-VIR*7s=SYwXYpqp_z#WxF#s#Rd< zcmwlq{S(??Ak?uDAm$*K*I~PSOeW-Zb-SpbcjKMsE~&Ebf96|>O94G0T`GR?Co%9X zoT16tY0BM7k%kE`yzlA7YUZW8;uPL99k*HO?e?$6l$-oT9@^m_*(*^F_^g*M=v=>eI2o^n9%Pr5?lmlmp>E{s5Nj~x!};_dDqpH0koFDG0kXL zOWPnD#(!R|Bc>!zdfifZ0}bhnRv_su>9P?TJUn@xx&A&>MiT@u~uqLW{da5j3+G9YU>3JeCn1OS>p0UCopmL8 z3)Va5{Yq;o;M3uCTO0t}RY&%wMoh~Sh?-)n+8XMApiyATWal=`dP8w(gb=MsFVnoT zyPj>(f0(eoiiNac<1>?3RvTWUwe8gK{6LVn$3CVkXcye|KCU}O{9@BW9FhXOr@k92 z$DPX>kV3QT=cdV|v-k;`e6-VCJzeysOfh3f5$LtUOm+$KsZ4Lu_Fgr*(a(bkX&MW& z3X`J>3-`@I8^j(6nA*G)9+5S!viDxTQ!GibBAY}ZA^OYq_C2zqW>#B`MNA`9hJs>6 zU#L0`aR$>~az_kgNyiXVAFZ8m=*&88qt1<*S&_>P2MZ-82E|DJjZ|l5+vKpI>~DZ=Kxi@a-b-h5%ME5J4XTS`&6 zZoq&RFO}Z-dwWjt-9z>F7N3>6E$oEZazGU>9TTV+`7({1d45!fbtSnpsc-`1EC1JqGzR>|7byEk!PP2vt36DJ<{bj?GRJu-Ds4qfdx1-m^^NoE`-XN2CT6~CW{)68e>}wpg-DpXx=y;3)#Prr zT?F!FlC3wq&qTT@3`8Rb*LA=^E4-!hi~CT z-&zk1$K0(dGS9I03{T=eGr=1MEJS;SNgMh)qtDWPFfIo|U5w&fjHgyMTYI*0Nyn<)KQ&tm=LitCT53i%K7fgfu<3Wf@sP2)f1t* zMJYz^w2-9yd&E#<*)YPk4EL-j=I2 zp{YK3I)Bny-&{u7csL1VgBG)wR{T;j>y`KvU}i=5tm*Iwk>8Vs|k+7eXO0ndvY&uPPR?yvQV4#3s%v-inRcYoC_suE5G3pt*+;hn$H zUP&!JAzC@W8O-vFiXzLSiHW3@U7<~Gdgub%`9&4qzrIwxBv2PSJ4#?u0{uE{apj@^ zwyKYp7pg^U6s;-fMC;QXaLcvNuN{V!VA$VW)3C7H&`%$o-Qa4SnWgNZG4^B#^g0ut zjn39cPK=@ctIinZ5ArI+us~YqRc}Z!Az|An>^FQ%xd;7#SBo)ivT$l~WqmCManNy& zX!1q)K2z9gBHGiqbT7K^UU)55pY62%CMtnMS~}=~&pi<2&`+t-D*n-#X1^L0nkQw! zb=}{k;epXO=~*xa0J<2L;R#e!Vf_5JeritDJ6o3mvOmV@qkm+B$RL*Y(Z+oG&ktt0 z!_{P!Yjgjmtqh!X+v1vsVJO?@%x~+zt_O8)!%dXRBz58{{hr&O1_%#~T7aO2s(yX8a?l*)v6m#lqT zDX6HNHn|CZ(<7;KDvZ5H5jTh#YJi3sGuS)bd?jf66en(W8*X(PcwqNqP^(eFCnh*6 zTPHBZ-E|Qrpidq*m@tD~HB2F8`%H3BJbFCsI-{NhaRA*g6YSdgN)|x-^{*HH5P+?C zXp^t?t{mAd&k{X0TNMs_H#56kT>DZ#d#!^qWye=gyiIiR@haS)Jc=Ys#TFSR^5OQGeh)Gwp3p0MdYBY7OnJZB0jKGQeSC zNcN<0+8LknO^1iTe#OM*nFr4bb`@uxjKvZm|JCkK%VZ7$6i>!k;5rTAu5d?%tWw6g zt=b*h-Jd>Ijf09>^zqdp15Zd-73lirKx>XCbE{klcSS4ZxEBN8*+EP7Xz5`_o~eRT z)AET}A0FWCGV}k10K~FZJ_Q_g$1yj0=ygBu&-E{Ra{O+|K_d|j^yd7TjDFJYZ+ZGBG0$k9r!7sDI7{D8-G?mk-p+JcU(&G z!QapOtm(dwXu}N}8*Y{FzXUM-rn)=fsJwB2=TzUyXh3n%mz(fN+kMD+E(Qn=vw@_b zXUSDXb-Ch|af_yA;SXyiT;Uchm29$HX|4?HE?iDGljz24%o1`JV+~l9myD4}yx+nd z3^ zuvtE%$N_pOfkL z=U^?Ts`-NT6!z?2f>=qXit4W0OMHwt*u>A-_zk#3%QUpP9B zBT#hpp_x_2jrPJ%Ivy?Vj&@(IL-Bd{tf1qKqMf7lFrp{%Jwb`WtE+t|Ig?=_Ia$M_v!=(6YVI{W z?lmyvMz!}3U(ZU12zQTf2GZc!o@_f~#$m^Qs6{*?l}_b&u{r5$SpyXz%DuVOtz1u%iCx0XpHy*s>u=Yz`Y6ztlGP zP#8gf893Kf%1AwWn}P%>vHCu zf@Snh=Wv6Gv{AYLHTxA6XNW|G2x z!x&&kMEPoT@6`rN#ph?aBoag)jEutJ!t;w(!SOHfcwJSjB!YlIEXNbE`;bA0>S0?w zmkKe;k~(&RCoiGD&g>b>y(^pHzu03^`gwVRM(iSMDcq&>pS!aOSh?_U^TZM)bYX_9 z`gI(lzb)6N*|GVE!V2F$a&T6yCrUlRE!W2jPl_MF2r(QCGZ@6m2$wA;Z}@KiG||L5 z%-EXa@g2MvZ5HJiZdOs%&h-UJylPb|zsK({o#+u7W(qbx|D=>b9xu$p;Wal;s)DK1 zi;ir~>SVR`rtMQ8_t*}^^4_Er)l$#wv?)5-up0B+2|^fO+AEt1Xy?qV<@T1X=w{zz z!G|K`@y($20XwMgiMTG{06`lW;-NzRlTDCNpm0 zYznetu>CM{(X4iP63P%pvt??2qFrEsXCB6xzDvohwz_BMMV@mMw+LGa&U5})TF}quF=FDk_9~}1H!*++63B)oqR6uKBMi^jtx;&0q5a!%L z)9^DTb;1vsL&x<&$PVTpN%3d5SJEldB#gCP80E0I$Lq3$t1l%fxT~ZboJi5zGZUeG|2~}-vVCAX*hvN3qS~h zMehJS4r3iR-s>y6={U6H#IM{Nr`onn?#G4`FVHx@ib%H?`4M6CT8L&(tUjK*zC9s^ zwL9Uwu6>!$@Z$YnKjs^P`2g;4vWiSmTX*Efw`#Mx=T;xLd#G(+eVQ)`dwpR`U1scG zw(e)=^Qjr@s>FmuLGt0WG$?y~_#a_58QE>5?L~HYMVAn#ql2w9xm=2gi0BT6MQ|yI zgEfP3OaJw>a0~Xs9(?euGxeL>h57pS4#)LVWd6DhtC?7aX_j;;joJpwIz}gf5`+;> z#v?nL4Iu}1VYv+PFA(Z(l)#gp+mdqM$bJZa{2}YQfjOR&ju{}8v_6cVtk+#RUx zmRN|<8#@_jD9!>gkYu-1!;2iXH^TJ)AW=cFD%=0_=v)A4&~UBK=7x*KzTxWD`<96@ zli-t<++b7ad?)edwFZ{6HJd224P7Ke6VDVK38^B%b87=}>u!J2pT-!Vm7eR~$y?8V z_`9Z)I2dn48VUM2G>0K(#3V10vBUt*Bdqq1B{I_I-u_AB1y?5c_CW{t@nBqE1gzfD ze0LeE^VaQRSDFJER#(hs3AZY~kAy@&IX8Z}cb~xfP{r!fd1034;B=DrxTtuRo#V7G zjn95x7Axhl{`TbD`-%yV^44PK+RUCCsZ@zrT#+WE;bNsttbk0i&TFH)(9t3QK6?)d zNyT_)V}E)wO!J~!<5-qYl7r1*!PR|ccJ+n`PWd^hz4F8oPJJdnfu!98X-05cRc5OB&^lXja+EC#W7c^H>wi%$U2Lz zfGaZBsW6t2p|r&a2}u_N4sUdBExCckdLM^Duadl9F;zUS>PtI6TDm>oufDzF=f9jA z@xAtDc0O{6KFUF>@+~x*i6rP!>Rm{)AZS)g@z^hr*Z}WrE^!Je+VbAd>%U!sT3{Z%lE!-mbJ#Mc^u55O4I@4XN(QPDEuWK0M`aec5DA4mo z$*M35&fy{omtLyG4rY@Rd1iWTd^X4$DG^)I$k@xZ<;yjFBoCC78yy1+T7-n_86kmYk+H5-72Z}ir-B<=&(2iZeqiNL;rD)B-+blaxpsISMKVzDcrX(p0r{mq0s9yb;o}a5Mf_L1wG4rdzcyi#FUt{Vlsj=)l?Y4FH=DHDf zP;%Ryy+Eve8zg(|wY;U}3^|T$WaW0Qb28ne!t1%c)P$e%U#2WvUOAt7?(5wCZn?c^ zEVr&>xgDN9GD6~jZHAIx>~%KYQmv<+abt;!YI~hWiF#iL6n8IqyPcOe8{baru2Ftr zk9>%PRF-Gno4w<{v*T%_I|pqjy;)EDetXP!AmDskKL=fy7@yO+UGiY%U#K&@zVba+ zFkTBKPP^`Hjl*nkg8x23M4YbipHT-|ms@E~W{31AA!`;$g^-(tQm9YFQSjG6Iin?2 z%38!ok&sj~HjmF0NCs78+0aP(mG}$257cVR^NOVjYMtk2N7Jsh<`cFWwhEY%krK-| z?mJkPacaxZtujhUMZfz)LTco^nxWoroJr3)yz3w%;pxR8TeZ8rr-(iZHaB0UrnsK} z(D`plC4O()8zIZ$h(-^!voco&S#RvxOkN$xeCiHTm+H(&VidL3Amg3Xg}sX0TXnfR zlYFtaGcA)lR-z>?MH~_NjcK2M5gj(e90RG4y-K$Hvjz%^*3fxtUnY{iG_}_r(-o!b zUv5Gcu2+j^ttB~-p^?EMHJD*0AQAx&!@c%%qqMl{<;rs$aM?NQ-0&|r z^yG-|#-`>TOoEvs(quYV2xGbcO!o$ok1^^S(=JtMFYI!>*s-4A7L=b%9A{sC*66Ox zW|-@DL_$J}h0j!!o-U$I+_pp|-3*r#q+PPfq1(jt0Sp>z@JdL(?s)=kM?&I)qbhbY zsEo$oI^O;M%tof*sgWPG(8yy3o`h7DP;`+jB)4`^su^%c&`3>>na817dn>v%55O;* zAk{hAYTt;`T*c(VtOD>qNF4RQ$pRvWKg2k=Qsl1y34~D5uTSj#CsNe0LX)^6~hn zT=`cFp75@pEvn27)RKMTcgrvQhs+-PZZ)uUZe}|)=6`VEXYMy5$dAzdJCNd7sGqZC3$#y8`^$&>> zX274XAfxfY6wHQgOk7}rA^PRHOC4YzKlQ+8#C-z5)t@nYy<%Y5naWm{vZZHI>g3Qe z>k5bTdXt?40?j11`ipsUI5Rj;AW0fJXTJ`)9Epjk9Eqt6hm27MEw93+gbKb&7P|dV zO`fTbhiJmtCw09VE}GH)y=XpY9lCHkUfTUiLPL3@BC?H6q4pHlKQT)qQbTx>2tw|u zftiT>3Ou0d>ntkj1*%m({tw9**xttKvX9+|R-f^M8zU{)=1NeEviRM%`i$A*vJjiu z+cOg2_t=t1H9u;(-OfHWy}2|XqVfGy`d@BaI z{-KzM;&=KC>1kvI3i#(A@;_$@h~4oV(&z9yMnXb*E&hk71tTGMzrK>RQ)@v5_Dg`ufZviPSX%1&>B?v&`<+Pgu47RqDZjZR`I_<_;2tLBUS2mlH#ZK3hD8pBMcE7? zE{0~O^GhGg!Gvj6^}u3o3-OWINo~ovJ7G6tQL~=Py<5wqr8Yeys}YI+g8;c#tgeXb zUFwko4WGSlKzfNpy*97Qo4+@=pKTIYXcDL?D^sp1^Vtl{k`}7^?@>F3bN>xf-KNc6W!Fa|*OeI{8D1d27rki`TN*e*RIUS}^Wt z>*C43`W0|&crRQ2;N$}5fnJSZtY*Hmv*>YZ@rpOi^jnSH&?Ez`Nsk&Cqqc2qsEq7n z9W}3cU6SF1Ca)LM)`4HFv`n%^;A|FMpj!&tG!93%W<9r6V%3+f#Et-k-DAJlx8=uG z;>9QCP1%malZ{T+e>qcmG*+aJxzgR*Hdn1C3s^hClLQcP$w;BT}X=w$Mm+Z%xTLvOmRww&?h!p7Y38yLZ8p60diT$X}+62y(V7n-P9fWSb zuNGAtMPY1Y1hqh@?Y4Et4>rUHmAvAxK4SaF-e`R*&4b!1nD?5w#xnY)1J3l`h3sIPwc+dzEWS7j zpCpA>hxfXjg9Mfc7U}J{vYc{iRlRkB0q2_D+u4_$JU)TN%|?PV*9Qh0T#pb?;_6x| zxR(%w@ZAY~Erj>_l+(5>%k2Wzw;o5_a2x8t`|VE7WmL9^*`5iRvdYn)h6SkKkrTb@ zC{e<}2X`uYajZXf%>awV6L8@F&K42Oc64^kl584>&(<+&kxEXSUNrR=A8%F2h*)Ya zL@^?(bWS35g%-Qj6W?;W9c>hA)g~r^ryx}+7dZ&e2>K~vJrBAp*cbG=GyWQ?OYyo`5ss3_VGD*ZV_mbtXwQTA6Jy zd#YnjpXy=ivEqzLKi5xNKz!y^ARGx%H3^Q-h8J#r*$?pTP@Q1iFOJy1Ki*-d!D8z} zu`XPAJvPKjY+b+6y*{us z4ptt$GOq2iidT{HUNXtFdy@^SK&SQgV*;W;ra`rP7vG99sA=_2eL5c|o@(-t1)X9{%$!Bf5wnAB<&)?;)41Iew<|Ie(j}@j>7L}M2>34Yp7#VrO%BV9;4+se zC*-d>V?i1`S5fWcR+T1?QslWOHougZmSvWeD5_m)mJlXd-A=>|o{Em=1!5f%&^0(| z)={ecFlCkmi#Rr5=-FmuEfI(v0*~W;Be!E+Ut*dVDye-ak;j?f!D0SDZ;<^^LV8pW zNIV_Hl>lG9Qk2mMEB?sC_8C6sNTYm0GtC}y6;_`h@2RC4v)A(F4 zPW?Se;W38>;0=uSn}ZFL!x9Y#?Zd&wNyU#L1Qh%gP}dQu;N!TUB1yM0-5Q6D+5Qe1 z%yrtV6VBi#-%DO*@MgdtJ}mnQoGZ@C+ISC+g4j;cppHxfp$uJHNAFU6VvEU%g|G~`=rPM9as(*y&Vi++ENO&a$J#4ne8d41GsHj$DnvW2UN78N5gd-+ue zbL^3Y^v#JpEUIKDP3&eT-Ly=1aaXUjl&EtFRZJc1tN2K1u2#mnoRw%@>9Ag-)=0^! z+W~N>65{9(14=pB8giZ^)5VrmWE_IW0=A3Gbs^c^#Vt`j+iVVz|Ijzq+H9vi(@cX{ ztCpS}yyeiexEf={&oHFP*s$ULJ^k^Kl!tq)<`fd@4%-P50%>_(L#KNl-HA0 z+K)U(%AGBC1tD&nBE}b)okXFDO{ao;`FI4k%v$`*My6GlKFvp~?*_?E$7T9yZvnei zcFPwG+Q@TzzTKup;19^gjeZf9?8zV1OQhs}<(rEu>1m#b8PvGM82ipddp2j($s}<= za&t*%5sNl4yZqID&r&dZ$kIRPlY!uZM4V!V=RAOXBMDv+Yi_)pKZBX}SJpVxY z2tL|0A5|)uTqY3>Bc7`?SFy)&P|RXYjE>b*-u)r>HuHR;{w-!%X?srG^VwQI(?l6{kK>ZP3$Q+O^AzCBPCPjUZzLBo znE2u`)HHD*UmCZw7kyzQ*6Z02Ys%P(mD4$gf%NFJ?q2O$1WJiaC|+;>p852;j61iM zlkLT-Iy~^NZ~IxfM*pu*@c-Gp70?~OpVh5i_Hmkni;GXq(xT2RW~4!)<{?s{G;p;4 z(a1*&%#e&O=6BDP?&wtCztL$ptpP$Y?~5R#R;`oo;>|&B6AIGAoeLlS-nTR$yHrq- zM$7&*90iEg<);`iBO50B0<#gZ2#hRw+Ht=|j%Znx649H4#TEw|k0%e1VAOZd>3!Vl zejvB4`bl%()kofs#Vby?7+ermibluP_O1SSq|Y)@z{58e{e&3&N|C}p(@DbMq^m|q zr%1!*rF=@oA!+@~gIsRp-0*#=noE}H&nt;7RJvpCJmu{C^EuyDA`RTMlO;U@Sx&xz zB_9Y0YaN3V^==&$s(GSm0g;w_s6MDwlHhxk?rGzv~s}vT<7f6k#!$Pyr zN@9W*!bAxCi3kc~J7>dQ@tYjR?~|?3WkJ4E0WUGX)4>Y)bLE|{YM=t*$mzMfrltuFev!U8<`6GHijVw!)&De8So2^o7;`?4a>x1fhe|5@$d?j?;mO z+|(~{x8RSL$wDewZ$|2DD|z_bSftW43ntQgQ7Mp-%)bGeR>fi5vKWcaGcgsPA1L{*R_Z=pk5kU7ucPZ%>U!a{-r#U1D<447=)Na`FF~eFg%5S|*TatjGp@5B*BEU9R7%jwSX9z3V@IDVlbo(R76 zyC787atv<4HhaNH#YoC#_sodKJtXshyG4=NeQ2+5mHYH~UDdSa4Z9qn+1fMHggBux z&!4p0^5;KyG1kpj&u)SggqX~p7pBOBDZofDcI!9gq%0%HjHdhgeLiIj3mxXJnw08W zeb7V9`oF48Y?RqTrdz!pH?q`4(q-7ppWNCH%McCQnW-$OeuVUSO9kY~IDfG!Re#<5 zqMw1f_kuLVU@~AaAi^BW9qDtZSr**|AixJoFX?vpAervHm3h&^3`oB^?tJNcz5Fb( zn6@>Cn9<%fd{|L>w+|9iyYPe@eGpX#*UuC99Objq6NG-bPg zb=>|e%QL1(JTo?C4}-(3v|N*s*83bU`NuDj+Q%o^?< zncUo8ASQ_u0kymrgVYxoJ!9Xz6Bb^9t(SE8pJudq-Hr zd)39HpZH#qG+Nt}d7HqNeHeVO*svOZ!MDRQf`*9}zVD7tC4b-5 z_TrzMiiB-$uVoOX!cH@)n``I2ZW?b5=6-(|9`WZqJ#nxc%e9NBQvOavW;pF$ILz&U=hg#^G!(p`jrmEV7o+YyB(~ zLIp*<)@QL+jLhLYI0}u5p*yCiKFkxmIFcbL?0e#|y;&1%AxpAe8?sQp`nY6#PUF&O zpiPwjYNxy5l0+@>M3d!Dv=?^d^nBza8NQGGL5%1B*hcZV`7b0aukwwq0Er}f<#pt=s&-;&I!&RFpNhjn=13e}f^lf1lE%(44X zb1U%a%egOgr+NQsTe5Cd!kcfqC)X)0x9fUW|Ky_Er=lN^XUfL!o>g79(p~@AV&=?R~j!`T6hP`EI3K;1p0={86)cK~BzX=kN3X zf8?K(wPoXyS8o@W$5vFox|;I$(pzi0s`OQXOUiElVXy!Acx4*r?Z$TYbN>GWtNM@K zJIlPYRkyg-+HUWTOwXxzj%?fcDqiMhz>ljx949-=-i-Kh_1KBUKX&esw4a``^RJ>* zXwhtT%ei{n#FzEH|C;yZ>+$!u_x#*+`=L8{b9SH^9&27u3G_Gxqxe`L2UJtdxghk z&-wzDFvLvW{chK5u3{n6GSKKy!P&C6w^IFpbD0bcp^A{{2lcLh_DXj@ybtYvc^;(2 M)78&qol`;+0Fu7JivR!s diff --git a/docs/output.md b/docs/output.md index 115da0e..d293978 100644 --- a/docs/output.md +++ b/docs/output.md @@ -14,6 +14,7 @@ The pipeline is built using [Nextflow](https://www.nextflow.io/) and processes d - [FastQC](#fastqc) - Raw read QC - [MultiQC](#multiqc) - Aggregate report describing results and QC from the whole pipeline + - [Pipeline information](#pipeline-information) - Report metrics generated during the workflow execution ### FastQC @@ -29,16 +30,6 @@ The pipeline is built using [Nextflow](https://www.nextflow.io/) and processes d [FastQC](http://www.bioinformatics.babraham.ac.uk/projects/fastqc/) gives general quality metrics about your sequenced reads. It provides information about the quality score distribution across your reads, per base sequence content (%A/T/G/C), adapter contamination and overrepresented sequences. For further reading and documentation see the [FastQC help pages](http://www.bioinformatics.babraham.ac.uk/projects/fastqc/Help/). -![MultiQC - FastQC sequence counts plot](images/mqc_fastqc_counts.png) - -![MultiQC - FastQC mean quality scores plot](images/mqc_fastqc_quality.png) - -![MultiQC - FastQC adapter content plot](images/mqc_fastqc_adapter.png) - -:::note -The FastQC plots displayed in the MultiQC report shows _untrimmed_ reads. They may contain adapter sequence and potentially regions with low quality. -::: - ### MultiQC
diff --git a/docs/usage.md b/docs/usage.md index fced1c9..3e98181 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -85,9 +85,9 @@ The above pipeline run specified with a params file in yaml format: nextflow run nf-core/spatialvi -profile docker -params-file params.yaml ``` -with `params.yaml` containing: +with: -```yaml +```yaml title="params.yaml" input: './samplesheet.csv' outdir: './results/' genome: 'GRCh37' @@ -199,14 +199,6 @@ See the main [Nextflow documentation](https://www.nextflow.io/docs/latest/config If you have any questions or issues please send us a message on [Slack](https://nf-co.re/join/slack) on the [`#configs` channel](https://nfcore.slack.com/channels/configs). -## Azure Resource Requests - -To be used with the `azurebatch` profile by specifying the `-profile azurebatch`. -We recommend providing a compute `params.vm_type` of `Standard_D16_v3` VMs by default but these options can be changed if required. - -Note that the choice of VM size depends on your quota and the overall workload during the analysis. -For a thorough list, please refer the [Azure Sizes for virtual machines in Azure](https://docs.microsoft.com/en-us/azure/virtual-machines/sizes). - ## Running in the background Nextflow handles job submissions and supervises the running jobs. The Nextflow process must run until the pipeline is finished. diff --git a/main.nf b/main.nf index 02fad16..a571159 100644 --- a/main.nf +++ b/main.nf @@ -9,8 +9,6 @@ ---------------------------------------------------------------------------------------- */ -nextflow.enable.dsl = 2 - /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ IMPORT FUNCTIONS / MODULES / SUBWORKFLOWS / WORKFLOWS @@ -20,7 +18,6 @@ nextflow.enable.dsl = 2 include { SPATIALVI } from './workflows/spatialvi' include { PIPELINE_INITIALISATION } from './subworkflows/local/utils_nfcore_spatialvi_pipeline' include { PIPELINE_COMPLETION } from './subworkflows/local/utils_nfcore_spatialvi_pipeline' - include { getGenomeAttribute } from './subworkflows/local/utils_nfcore_spatialvi_pipeline' /* @@ -56,10 +53,8 @@ workflow NFCORE_SPATIALVI { SPATIALVI ( samplesheet ) - emit: multiqc_report = SPATIALVI.out.multiqc_report // channel: /path/to/multiqc_report.html - } /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -70,27 +65,24 @@ workflow NFCORE_SPATIALVI { workflow { main: - // // SUBWORKFLOW: Run initialisation tasks // PIPELINE_INITIALISATION ( params.version, - params.help, params.validate_params, params.monochrome_logs, args, params.outdir, params.input ) - + // // WORKFLOW: Run main workflow // NFCORE_SPATIALVI ( PIPELINE_INITIALISATION.out.samplesheet ) - // // SUBWORKFLOW: Run completion tasks // diff --git a/modules.json b/modules.json index 5940c72..e4928fd 100644 --- a/modules.json +++ b/modules.json @@ -7,12 +7,12 @@ "nf-core": { "fastqc": { "branch": "master", - "git_sha": "285a50500f9e02578d90b3ce6382ea3c30216acd", + "git_sha": "666652151335353eef2fcd58880bcef5bc2928e1", "installed_by": ["modules"] }, "multiqc": { "branch": "master", - "git_sha": "b7ebe95761cd389603f9cc0e0dc384c0f663815a", + "git_sha": "666652151335353eef2fcd58880bcef5bc2928e1", "installed_by": ["modules"] } } @@ -21,17 +21,17 @@ "nf-core": { "utils_nextflow_pipeline": { "branch": "master", - "git_sha": "5caf7640a9ef1d18d765d55339be751bb0969dfa", + "git_sha": "d20fb2a9cc3e2835e9d067d1046a63252eb17352", "installed_by": ["subworkflows"] }, "utils_nfcore_pipeline": { "branch": "master", - "git_sha": "92de218a329bfc9a9033116eb5f65fd270e72ba3", + "git_sha": "2fdce49d30c0254f76bc0f13c55c17455c1251ab", "installed_by": ["subworkflows"] }, - "utils_nfvalidation_plugin": { + "utils_nfschema_plugin": { "branch": "master", - "git_sha": "5caf7640a9ef1d18d765d55339be751bb0969dfa", + "git_sha": "bbd5a41f4535a8defafe6080e00ea74c45f4f96c", "installed_by": ["subworkflows"] } } diff --git a/modules/nf-core/fastqc/environment.yml b/modules/nf-core/fastqc/environment.yml index 1787b38..691d4c7 100644 --- a/modules/nf-core/fastqc/environment.yml +++ b/modules/nf-core/fastqc/environment.yml @@ -1,7 +1,5 @@ -name: fastqc channels: - conda-forge - bioconda - - defaults dependencies: - bioconda::fastqc=0.12.1 diff --git a/modules/nf-core/fastqc/main.nf b/modules/nf-core/fastqc/main.nf index d79f1c8..d8989f4 100644 --- a/modules/nf-core/fastqc/main.nf +++ b/modules/nf-core/fastqc/main.nf @@ -26,7 +26,10 @@ process FASTQC { def rename_to = old_new_pairs*.join(' ').join(' ') def renamed_files = old_new_pairs.collect{ old_name, new_name -> new_name }.join(' ') - def memory_in_mb = MemoryUnit.of("${task.memory}").toUnit('MB') + // The total amount of allocated RAM by FastQC is equal to the number of threads defined (--threads) time the amount of RAM defined (--memory) + // https://github.com/s-andrews/FastQC/blob/1faeea0412093224d7f6a07f777fad60a5650795/fastqc#L211-L222 + // Dividing the task.memory by task.cpu allows to stick to requested amount of RAM in the label + def memory_in_mb = MemoryUnit.of("${task.memory}").toUnit('MB') / task.cpus // FastQC memory value allowed range (100 - 10000) def fastqc_memory = memory_in_mb > 10000 ? 10000 : (memory_in_mb < 100 ? 100 : memory_in_mb) diff --git a/modules/nf-core/fastqc/meta.yml b/modules/nf-core/fastqc/meta.yml index ee5507e..4827da7 100644 --- a/modules/nf-core/fastqc/meta.yml +++ b/modules/nf-core/fastqc/meta.yml @@ -16,35 +16,44 @@ tools: homepage: https://www.bioinformatics.babraham.ac.uk/projects/fastqc/ documentation: https://www.bioinformatics.babraham.ac.uk/projects/fastqc/Help/ licence: ["GPL-2.0-only"] + identifier: biotools:fastqc input: - - meta: - type: map - description: | - Groovy Map containing sample information - e.g. [ id:'test', single_end:false ] - - reads: - type: file - description: | - List of input FastQ files of size 1 and 2 for single-end and paired-end data, - respectively. + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - reads: + type: file + description: | + List of input FastQ files of size 1 and 2 for single-end and paired-end data, + respectively. output: - - meta: - type: map - description: | - Groovy Map containing sample information - e.g. [ id:'test', single_end:false ] - html: - type: file - description: FastQC report - pattern: "*_{fastqc.html}" + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - "*.html": + type: file + description: FastQC report + pattern: "*_{fastqc.html}" - zip: - type: file - description: FastQC report archive - pattern: "*_{fastqc.zip}" + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - "*.zip": + type: file + description: FastQC report archive + pattern: "*_{fastqc.zip}" - versions: - type: file - description: File containing software versions - pattern: "versions.yml" + - versions.yml: + type: file + description: File containing software versions + pattern: "versions.yml" authors: - "@drpatelh" - "@grst" diff --git a/modules/nf-core/fastqc/tests/main.nf.test b/modules/nf-core/fastqc/tests/main.nf.test index 70edae4..e9d79a0 100644 --- a/modules/nf-core/fastqc/tests/main.nf.test +++ b/modules/nf-core/fastqc/tests/main.nf.test @@ -23,17 +23,14 @@ nextflow_process { then { assertAll ( - { assert process.success }, - - // NOTE The report contains the date inside it, which means that the md5sum is stable per day, but not longer than that. So you can't md5sum it. - // looks like this:
Mon 2 Oct 2023
test.gz
- // https://github.com/nf-core/modules/pull/3903#issuecomment-1743620039 - - { assert process.out.html[0][1] ==~ ".*/test_fastqc.html" }, - { assert process.out.zip[0][1] ==~ ".*/test_fastqc.zip" }, - { assert path(process.out.html[0][1]).text.contains("File typeConventional base calls") }, - - { assert snapshot(process.out.versions).match("fastqc_versions_single") } + { assert process.success }, + // NOTE The report contains the date inside it, which means that the md5sum is stable per day, but not longer than that. So you can't md5sum it. + // looks like this:
Mon 2 Oct 2023
test.gz
+ // https://github.com/nf-core/modules/pull/3903#issuecomment-1743620039 + { assert process.out.html[0][1] ==~ ".*/test_fastqc.html" }, + { assert process.out.zip[0][1] ==~ ".*/test_fastqc.zip" }, + { assert path(process.out.html[0][1]).text.contains("File typeConventional base calls") }, + { assert snapshot(process.out.versions).match() } ) } } @@ -54,16 +51,14 @@ nextflow_process { then { assertAll ( - { assert process.success }, - - { assert process.out.html[0][1][0] ==~ ".*/test_1_fastqc.html" }, - { assert process.out.html[0][1][1] ==~ ".*/test_2_fastqc.html" }, - { assert process.out.zip[0][1][0] ==~ ".*/test_1_fastqc.zip" }, - { assert process.out.zip[0][1][1] ==~ ".*/test_2_fastqc.zip" }, - { assert path(process.out.html[0][1][0]).text.contains("File typeConventional base calls") }, - { assert path(process.out.html[0][1][1]).text.contains("File typeConventional base calls") }, - - { assert snapshot(process.out.versions).match("fastqc_versions_paired") } + { assert process.success }, + { assert process.out.html[0][1][0] ==~ ".*/test_1_fastqc.html" }, + { assert process.out.html[0][1][1] ==~ ".*/test_2_fastqc.html" }, + { assert process.out.zip[0][1][0] ==~ ".*/test_1_fastqc.zip" }, + { assert process.out.zip[0][1][1] ==~ ".*/test_2_fastqc.zip" }, + { assert path(process.out.html[0][1][0]).text.contains("File typeConventional base calls") }, + { assert path(process.out.html[0][1][1]).text.contains("File typeConventional base calls") }, + { assert snapshot(process.out.versions).match() } ) } } @@ -83,13 +78,11 @@ nextflow_process { then { assertAll ( - { assert process.success }, - - { assert process.out.html[0][1] ==~ ".*/test_fastqc.html" }, - { assert process.out.zip[0][1] ==~ ".*/test_fastqc.zip" }, - { assert path(process.out.html[0][1]).text.contains("File typeConventional base calls") }, - - { assert snapshot(process.out.versions).match("fastqc_versions_interleaved") } + { assert process.success }, + { assert process.out.html[0][1] ==~ ".*/test_fastqc.html" }, + { assert process.out.zip[0][1] ==~ ".*/test_fastqc.zip" }, + { assert path(process.out.html[0][1]).text.contains("File typeConventional base calls") }, + { assert snapshot(process.out.versions).match() } ) } } @@ -109,13 +102,11 @@ nextflow_process { then { assertAll ( - { assert process.success }, - - { assert process.out.html[0][1] ==~ ".*/test_fastqc.html" }, - { assert process.out.zip[0][1] ==~ ".*/test_fastqc.zip" }, - { assert path(process.out.html[0][1]).text.contains("File typeConventional base calls") }, - - { assert snapshot(process.out.versions).match("fastqc_versions_bam") } + { assert process.success }, + { assert process.out.html[0][1] ==~ ".*/test_fastqc.html" }, + { assert process.out.zip[0][1] ==~ ".*/test_fastqc.zip" }, + { assert path(process.out.html[0][1]).text.contains("File typeConventional base calls") }, + { assert snapshot(process.out.versions).match() } ) } } @@ -138,22 +129,20 @@ nextflow_process { then { assertAll ( - { assert process.success }, - - { assert process.out.html[0][1][0] ==~ ".*/test_1_fastqc.html" }, - { assert process.out.html[0][1][1] ==~ ".*/test_2_fastqc.html" }, - { assert process.out.html[0][1][2] ==~ ".*/test_3_fastqc.html" }, - { assert process.out.html[0][1][3] ==~ ".*/test_4_fastqc.html" }, - { assert process.out.zip[0][1][0] ==~ ".*/test_1_fastqc.zip" }, - { assert process.out.zip[0][1][1] ==~ ".*/test_2_fastqc.zip" }, - { assert process.out.zip[0][1][2] ==~ ".*/test_3_fastqc.zip" }, - { assert process.out.zip[0][1][3] ==~ ".*/test_4_fastqc.zip" }, - { assert path(process.out.html[0][1][0]).text.contains("File typeConventional base calls") }, - { assert path(process.out.html[0][1][1]).text.contains("File typeConventional base calls") }, - { assert path(process.out.html[0][1][2]).text.contains("File typeConventional base calls") }, - { assert path(process.out.html[0][1][3]).text.contains("File typeConventional base calls") }, - - { assert snapshot(process.out.versions).match("fastqc_versions_multiple") } + { assert process.success }, + { assert process.out.html[0][1][0] ==~ ".*/test_1_fastqc.html" }, + { assert process.out.html[0][1][1] ==~ ".*/test_2_fastqc.html" }, + { assert process.out.html[0][1][2] ==~ ".*/test_3_fastqc.html" }, + { assert process.out.html[0][1][3] ==~ ".*/test_4_fastqc.html" }, + { assert process.out.zip[0][1][0] ==~ ".*/test_1_fastqc.zip" }, + { assert process.out.zip[0][1][1] ==~ ".*/test_2_fastqc.zip" }, + { assert process.out.zip[0][1][2] ==~ ".*/test_3_fastqc.zip" }, + { assert process.out.zip[0][1][3] ==~ ".*/test_4_fastqc.zip" }, + { assert path(process.out.html[0][1][0]).text.contains("File typeConventional base calls") }, + { assert path(process.out.html[0][1][1]).text.contains("File typeConventional base calls") }, + { assert path(process.out.html[0][1][2]).text.contains("File typeConventional base calls") }, + { assert path(process.out.html[0][1][3]).text.contains("File typeConventional base calls") }, + { assert snapshot(process.out.versions).match() } ) } } @@ -173,21 +162,18 @@ nextflow_process { then { assertAll ( - { assert process.success }, - - { assert process.out.html[0][1] ==~ ".*/mysample_fastqc.html" }, - { assert process.out.zip[0][1] ==~ ".*/mysample_fastqc.zip" }, - { assert path(process.out.html[0][1]).text.contains("File typeConventional base calls") }, - - { assert snapshot(process.out.versions).match("fastqc_versions_custom_prefix") } + { assert process.success }, + { assert process.out.html[0][1] ==~ ".*/mysample_fastqc.html" }, + { assert process.out.zip[0][1] ==~ ".*/mysample_fastqc.zip" }, + { assert path(process.out.html[0][1]).text.contains("File typeConventional base calls") }, + { assert snapshot(process.out.versions).match() } ) } } test("sarscov2 single-end [fastq] - stub") { - options "-stub" - + options "-stub" when { process { """ @@ -201,12 +187,123 @@ nextflow_process { then { assertAll ( - { assert process.success }, - { assert snapshot(process.out.html.collect { file(it[1]).getName() } + - process.out.zip.collect { file(it[1]).getName() } + - process.out.versions ).match("fastqc_stub") } + { assert process.success }, + { assert snapshot(process.out).match() } ) } } + test("sarscov2 paired-end [fastq] - stub") { + + options "-stub" + when { + process { + """ + input[0] = Channel.of([ + [id: 'test', single_end: false], // meta map + [ file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test_1.fastq.gz', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test_2.fastq.gz', checkIfExists: true) ] + ]) + """ + } + } + + then { + assertAll ( + { assert process.success }, + { assert snapshot(process.out).match() } + ) + } + } + + test("sarscov2 interleaved [fastq] - stub") { + + options "-stub" + when { + process { + """ + input[0] = Channel.of([ + [id: 'test', single_end: false], // meta map + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test_interleaved.fastq.gz', checkIfExists: true) + ]) + """ + } + } + + then { + assertAll ( + { assert process.success }, + { assert snapshot(process.out).match() } + ) + } + } + + test("sarscov2 paired-end [bam] - stub") { + + options "-stub" + when { + process { + """ + input[0] = Channel.of([ + [id: 'test', single_end: false], // meta map + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/bam/test.paired_end.sorted.bam', checkIfExists: true) + ]) + """ + } + } + + then { + assertAll ( + { assert process.success }, + { assert snapshot(process.out).match() } + ) + } + } + + test("sarscov2 multiple [fastq] - stub") { + + options "-stub" + when { + process { + """ + input[0] = Channel.of([ + [id: 'test', single_end: false], // meta map + [ file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test_1.fastq.gz', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test_2.fastq.gz', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test2_1.fastq.gz', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test2_2.fastq.gz', checkIfExists: true) ] + ]) + """ + } + } + + then { + assertAll ( + { assert process.success }, + { assert snapshot(process.out).match() } + ) + } + } + + test("sarscov2 custom_prefix - stub") { + + options "-stub" + when { + process { + """ + input[0] = Channel.of([ + [ id:'mysample', single_end:true ], // meta map + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test_1.fastq.gz', checkIfExists: true) + ]) + """ + } + } + + then { + assertAll ( + { assert process.success }, + { assert snapshot(process.out).match() } + ) + } + } } diff --git a/modules/nf-core/fastqc/tests/main.nf.test.snap b/modules/nf-core/fastqc/tests/main.nf.test.snap index 86f7c31..d5db309 100644 --- a/modules/nf-core/fastqc/tests/main.nf.test.snap +++ b/modules/nf-core/fastqc/tests/main.nf.test.snap @@ -1,88 +1,392 @@ { - "fastqc_versions_interleaved": { + "sarscov2 custom_prefix": { "content": [ [ "versions.yml:md5,e1cc25ca8af856014824abd842e93978" ] ], "meta": { - "nf-test": "0.8.4", - "nextflow": "23.10.1" + "nf-test": "0.9.0", + "nextflow": "24.04.3" }, - "timestamp": "2024-01-31T17:40:07.293713" + "timestamp": "2024-07-22T11:02:16.374038" }, - "fastqc_stub": { + "sarscov2 single-end [fastq] - stub": { "content": [ - [ - "test.html", - "test.zip", - "versions.yml:md5,e1cc25ca8af856014824abd842e93978" - ] + { + "0": [ + [ + { + "id": "test", + "single_end": true + }, + "test.html:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "1": [ + [ + { + "id": "test", + "single_end": true + }, + "test.zip:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "2": [ + "versions.yml:md5,e1cc25ca8af856014824abd842e93978" + ], + "html": [ + [ + { + "id": "test", + "single_end": true + }, + "test.html:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "versions": [ + "versions.yml:md5,e1cc25ca8af856014824abd842e93978" + ], + "zip": [ + [ + { + "id": "test", + "single_end": true + }, + "test.zip:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ] + } + ], + "meta": { + "nf-test": "0.9.0", + "nextflow": "24.04.3" + }, + "timestamp": "2024-07-22T11:02:24.993809" + }, + "sarscov2 custom_prefix - stub": { + "content": [ + { + "0": [ + [ + { + "id": "mysample", + "single_end": true + }, + "mysample.html:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "1": [ + [ + { + "id": "mysample", + "single_end": true + }, + "mysample.zip:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "2": [ + "versions.yml:md5,e1cc25ca8af856014824abd842e93978" + ], + "html": [ + [ + { + "id": "mysample", + "single_end": true + }, + "mysample.html:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "versions": [ + "versions.yml:md5,e1cc25ca8af856014824abd842e93978" + ], + "zip": [ + [ + { + "id": "mysample", + "single_end": true + }, + "mysample.zip:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ] + } ], "meta": { - "nf-test": "0.8.4", - "nextflow": "23.10.1" + "nf-test": "0.9.0", + "nextflow": "24.04.3" }, - "timestamp": "2024-01-31T17:31:01.425198" + "timestamp": "2024-07-22T11:03:10.93942" }, - "fastqc_versions_multiple": { + "sarscov2 interleaved [fastq]": { "content": [ [ "versions.yml:md5,e1cc25ca8af856014824abd842e93978" ] ], "meta": { - "nf-test": "0.8.4", - "nextflow": "23.10.1" + "nf-test": "0.9.0", + "nextflow": "24.04.3" }, - "timestamp": "2024-01-31T17:40:55.797907" + "timestamp": "2024-07-22T11:01:42.355718" }, - "fastqc_versions_bam": { + "sarscov2 paired-end [bam]": { "content": [ [ "versions.yml:md5,e1cc25ca8af856014824abd842e93978" ] ], "meta": { - "nf-test": "0.8.4", - "nextflow": "23.10.1" + "nf-test": "0.9.0", + "nextflow": "24.04.3" }, - "timestamp": "2024-01-31T17:40:26.795862" + "timestamp": "2024-07-22T11:01:53.276274" }, - "fastqc_versions_single": { + "sarscov2 multiple [fastq]": { "content": [ [ "versions.yml:md5,e1cc25ca8af856014824abd842e93978" ] ], "meta": { - "nf-test": "0.8.4", - "nextflow": "23.10.1" + "nf-test": "0.9.0", + "nextflow": "24.04.3" }, - "timestamp": "2024-01-31T17:39:27.043675" + "timestamp": "2024-07-22T11:02:05.527626" }, - "fastqc_versions_paired": { + "sarscov2 paired-end [fastq]": { "content": [ [ "versions.yml:md5,e1cc25ca8af856014824abd842e93978" ] ], "meta": { - "nf-test": "0.8.4", - "nextflow": "23.10.1" + "nf-test": "0.9.0", + "nextflow": "24.04.3" + }, + "timestamp": "2024-07-22T11:01:31.188871" + }, + "sarscov2 paired-end [fastq] - stub": { + "content": [ + { + "0": [ + [ + { + "id": "test", + "single_end": false + }, + "test.html:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "1": [ + [ + { + "id": "test", + "single_end": false + }, + "test.zip:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "2": [ + "versions.yml:md5,e1cc25ca8af856014824abd842e93978" + ], + "html": [ + [ + { + "id": "test", + "single_end": false + }, + "test.html:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "versions": [ + "versions.yml:md5,e1cc25ca8af856014824abd842e93978" + ], + "zip": [ + [ + { + "id": "test", + "single_end": false + }, + "test.zip:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ] + } + ], + "meta": { + "nf-test": "0.9.0", + "nextflow": "24.04.3" + }, + "timestamp": "2024-07-22T11:02:34.273566" + }, + "sarscov2 multiple [fastq] - stub": { + "content": [ + { + "0": [ + [ + { + "id": "test", + "single_end": false + }, + "test.html:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "1": [ + [ + { + "id": "test", + "single_end": false + }, + "test.zip:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "2": [ + "versions.yml:md5,e1cc25ca8af856014824abd842e93978" + ], + "html": [ + [ + { + "id": "test", + "single_end": false + }, + "test.html:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "versions": [ + "versions.yml:md5,e1cc25ca8af856014824abd842e93978" + ], + "zip": [ + [ + { + "id": "test", + "single_end": false + }, + "test.zip:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ] + } + ], + "meta": { + "nf-test": "0.9.0", + "nextflow": "24.04.3" }, - "timestamp": "2024-01-31T17:39:47.584191" + "timestamp": "2024-07-22T11:03:02.304411" }, - "fastqc_versions_custom_prefix": { + "sarscov2 single-end [fastq]": { "content": [ [ "versions.yml:md5,e1cc25ca8af856014824abd842e93978" ] ], "meta": { - "nf-test": "0.8.4", - "nextflow": "23.10.1" + "nf-test": "0.9.0", + "nextflow": "24.04.3" + }, + "timestamp": "2024-07-22T11:01:19.095607" + }, + "sarscov2 interleaved [fastq] - stub": { + "content": [ + { + "0": [ + [ + { + "id": "test", + "single_end": false + }, + "test.html:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "1": [ + [ + { + "id": "test", + "single_end": false + }, + "test.zip:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "2": [ + "versions.yml:md5,e1cc25ca8af856014824abd842e93978" + ], + "html": [ + [ + { + "id": "test", + "single_end": false + }, + "test.html:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "versions": [ + "versions.yml:md5,e1cc25ca8af856014824abd842e93978" + ], + "zip": [ + [ + { + "id": "test", + "single_end": false + }, + "test.zip:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ] + } + ], + "meta": { + "nf-test": "0.9.0", + "nextflow": "24.04.3" + }, + "timestamp": "2024-07-22T11:02:44.640184" + }, + "sarscov2 paired-end [bam] - stub": { + "content": [ + { + "0": [ + [ + { + "id": "test", + "single_end": false + }, + "test.html:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "1": [ + [ + { + "id": "test", + "single_end": false + }, + "test.zip:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "2": [ + "versions.yml:md5,e1cc25ca8af856014824abd842e93978" + ], + "html": [ + [ + { + "id": "test", + "single_end": false + }, + "test.html:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "versions": [ + "versions.yml:md5,e1cc25ca8af856014824abd842e93978" + ], + "zip": [ + [ + { + "id": "test", + "single_end": false + }, + "test.zip:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ] + } + ], + "meta": { + "nf-test": "0.9.0", + "nextflow": "24.04.3" }, - "timestamp": "2024-01-31T17:41:14.576531" + "timestamp": "2024-07-22T11:02:53.550742" } } \ No newline at end of file diff --git a/modules/nf-core/multiqc/environment.yml b/modules/nf-core/multiqc/environment.yml index ca39fb6..f1cd99b 100644 --- a/modules/nf-core/multiqc/environment.yml +++ b/modules/nf-core/multiqc/environment.yml @@ -1,7 +1,5 @@ -name: multiqc channels: - conda-forge - bioconda - - defaults dependencies: - - bioconda::multiqc=1.21 + - bioconda::multiqc=1.24.1 diff --git a/modules/nf-core/multiqc/main.nf b/modules/nf-core/multiqc/main.nf index 47ac352..b9ccebd 100644 --- a/modules/nf-core/multiqc/main.nf +++ b/modules/nf-core/multiqc/main.nf @@ -3,14 +3,16 @@ process MULTIQC { conda "${moduleDir}/environment.yml" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/multiqc:1.21--pyhdfd78af_0' : - 'biocontainers/multiqc:1.21--pyhdfd78af_0' }" + 'https://depot.galaxyproject.org/singularity/multiqc:1.25--pyhdfd78af_0' : + 'biocontainers/multiqc:1.25--pyhdfd78af_0' }" input: path multiqc_files, stageAs: "?/*" path(multiqc_config) path(extra_multiqc_config) path(multiqc_logo) + path(replace_names) + path(sample_names) output: path "*multiqc_report.html", emit: report @@ -23,16 +25,22 @@ process MULTIQC { script: def args = task.ext.args ?: '' + def prefix = task.ext.prefix ? "--filename ${task.ext.prefix}.html" : '' def config = multiqc_config ? "--config $multiqc_config" : '' def extra_config = extra_multiqc_config ? "--config $extra_multiqc_config" : '' - def logo = multiqc_logo ? /--cl-config 'custom_logo: "${multiqc_logo}"'/ : '' + def logo = multiqc_logo ? "--cl-config 'custom_logo: \"${multiqc_logo}\"'" : '' + def replace = replace_names ? "--replace-names ${replace_names}" : '' + def samples = sample_names ? "--sample-names ${sample_names}" : '' """ multiqc \\ --force \\ $args \\ $config \\ + $prefix \\ $extra_config \\ $logo \\ + $replace \\ + $samples \\ . cat <<-END_VERSIONS > versions.yml diff --git a/modules/nf-core/multiqc/meta.yml b/modules/nf-core/multiqc/meta.yml index 45a9bc3..b16c187 100644 --- a/modules/nf-core/multiqc/meta.yml +++ b/modules/nf-core/multiqc/meta.yml @@ -1,5 +1,6 @@ name: multiqc -description: Aggregate results from bioinformatics analyses across many samples into a single report +description: Aggregate results from bioinformatics analyses across many samples into + a single report keywords: - QC - bioinformatics tools @@ -12,40 +13,59 @@ tools: homepage: https://multiqc.info/ documentation: https://multiqc.info/docs/ licence: ["GPL-3.0-or-later"] + identifier: biotools:multiqc input: - - multiqc_files: - type: file - description: | - List of reports / files recognised by MultiQC, for example the html and zip output of FastQC - - multiqc_config: - type: file - description: Optional config yml for MultiQC - pattern: "*.{yml,yaml}" - - extra_multiqc_config: - type: file - description: Second optional config yml for MultiQC. Will override common sections in multiqc_config. - pattern: "*.{yml,yaml}" - - multiqc_logo: - type: file - description: Optional logo file for MultiQC - pattern: "*.{png}" + - - multiqc_files: + type: file + description: | + List of reports / files recognised by MultiQC, for example the html and zip output of FastQC + - - multiqc_config: + type: file + description: Optional config yml for MultiQC + pattern: "*.{yml,yaml}" + - - extra_multiqc_config: + type: file + description: Second optional config yml for MultiQC. Will override common sections + in multiqc_config. + pattern: "*.{yml,yaml}" + - - multiqc_logo: + type: file + description: Optional logo file for MultiQC + pattern: "*.{png}" + - - replace_names: + type: file + description: | + Optional two-column sample renaming file. First column a set of + patterns, second column a set of corresponding replacements. Passed via + MultiQC's `--replace-names` option. + pattern: "*.{tsv}" + - - sample_names: + type: file + description: | + Optional TSV file with headers, passed to the MultiQC --sample_names + argument. + pattern: "*.{tsv}" output: - report: - type: file - description: MultiQC report file - pattern: "multiqc_report.html" + - "*multiqc_report.html": + type: file + description: MultiQC report file + pattern: "multiqc_report.html" - data: - type: directory - description: MultiQC data dir - pattern: "multiqc_data" + - "*_data": + type: directory + description: MultiQC data dir + pattern: "multiqc_data" - plots: - type: file - description: Plots created by MultiQC - pattern: "*_data" + - "*_plots": + type: file + description: Plots created by MultiQC + pattern: "*_data" - versions: - type: file - description: File containing software versions - pattern: "versions.yml" + - versions.yml: + type: file + description: File containing software versions + pattern: "versions.yml" authors: - "@abhi18av" - "@bunop" diff --git a/modules/nf-core/multiqc/tests/main.nf.test b/modules/nf-core/multiqc/tests/main.nf.test index f1c4242..33316a7 100644 --- a/modules/nf-core/multiqc/tests/main.nf.test +++ b/modules/nf-core/multiqc/tests/main.nf.test @@ -8,6 +8,8 @@ nextflow_process { tag "modules_nfcore" tag "multiqc" + config "./nextflow.config" + test("sarscov2 single-end [fastqc]") { when { @@ -17,6 +19,8 @@ nextflow_process { input[1] = [] input[2] = [] input[3] = [] + input[4] = [] + input[5] = [] """ } } @@ -41,6 +45,8 @@ nextflow_process { input[1] = Channel.of(file("https://github.com/nf-core/tools/raw/dev/nf_core/pipeline-template/assets/multiqc_config.yml", checkIfExists: true)) input[2] = [] input[3] = [] + input[4] = [] + input[5] = [] """ } } @@ -66,6 +72,8 @@ nextflow_process { input[1] = [] input[2] = [] input[3] = [] + input[4] = [] + input[5] = [] """ } } diff --git a/modules/nf-core/multiqc/tests/main.nf.test.snap b/modules/nf-core/multiqc/tests/main.nf.test.snap index bfebd80..b779e46 100644 --- a/modules/nf-core/multiqc/tests/main.nf.test.snap +++ b/modules/nf-core/multiqc/tests/main.nf.test.snap @@ -2,14 +2,14 @@ "multiqc_versions_single": { "content": [ [ - "versions.yml:md5,21f35ee29416b9b3073c28733efe4b7d" + "versions.yml:md5,8c8724363a5efe0c6f43ab34faa57efd" ] ], "meta": { "nf-test": "0.8.4", - "nextflow": "23.10.1" + "nextflow": "24.04.2" }, - "timestamp": "2024-02-29T08:48:55.657331" + "timestamp": "2024-07-10T12:41:34.562023" }, "multiqc_stub": { "content": [ @@ -17,25 +17,25 @@ "multiqc_report.html", "multiqc_data", "multiqc_plots", - "versions.yml:md5,21f35ee29416b9b3073c28733efe4b7d" + "versions.yml:md5,8c8724363a5efe0c6f43ab34faa57efd" ] ], "meta": { "nf-test": "0.8.4", - "nextflow": "23.10.1" + "nextflow": "24.04.2" }, - "timestamp": "2024-02-29T08:49:49.071937" + "timestamp": "2024-07-10T11:27:11.933869532" }, "multiqc_versions_config": { "content": [ [ - "versions.yml:md5,21f35ee29416b9b3073c28733efe4b7d" + "versions.yml:md5,8c8724363a5efe0c6f43ab34faa57efd" ] ], "meta": { "nf-test": "0.8.4", - "nextflow": "23.10.1" + "nextflow": "24.04.2" }, - "timestamp": "2024-02-29T08:49:25.457567" + "timestamp": "2024-07-10T11:26:56.709849369" } -} \ No newline at end of file +} diff --git a/modules/nf-core/multiqc/tests/nextflow.config b/modules/nf-core/multiqc/tests/nextflow.config new file mode 100644 index 0000000..c537a6a --- /dev/null +++ b/modules/nf-core/multiqc/tests/nextflow.config @@ -0,0 +1,5 @@ +process { + withName: 'MULTIQC' { + ext.prefix = null + } +} diff --git a/nextflow.config b/nextflow.config index dfece07..90de03a 100644 --- a/nextflow.config +++ b/nextflow.config @@ -16,7 +16,6 @@ params { genome = null igenomes_base = 's3://ngi-igenomes/igenomes/' igenomes_ignore = false - // MultiQC options multiqc_config = null multiqc_title = null @@ -33,48 +32,26 @@ params { monochrome_logs = false hook_url = null help = false + help_full = false + show_hidden = false version = false pipelines_testdata_base_path = 'https://raw.githubusercontent.com/nf-core/test-datasets/' - // Config options config_profile_name = null config_profile_description = null + custom_config_version = 'master' custom_config_base = "https://raw.githubusercontent.com/nf-core/configs/${params.custom_config_version}" config_profile_contact = null config_profile_url = null - - // Max resource options - // Defaults only, expecting to be overwritten - max_memory = '128.GB' - max_cpus = 16 - max_time = '240.h' - // Schema validation default options - validationFailUnrecognisedParams = false - validationLenientMode = false - validationSchemaIgnoreParams = 'genomes,igenomes_base' - validationShowHiddenParams = false - validate_params = true - + validate_params = true + } // Load base.config by default for all pipelines includeConfig 'conf/base.config' -// Load nf-core custom profiles from different Institutions -try { - includeConfig "${params.custom_config_base}/nfcore_custom.config" -} catch (Exception e) { - System.err.println("WARNING: Could not load nf-core/config profiles: ${params.custom_config_base}/nfcore_custom.config") -} - -// Load nf-core/spatialvi custom profiles from different institutions. -try { - includeConfig "${params.custom_config_base}/pipeline/spatialvi.config" -} catch (Exception e) { - System.err.println("WARNING: Could not load nf-core/config/spatialvi profiles: ${params.custom_config_base}/pipeline/spatialvi.config") -} profiles { debug { dumpHashes = true @@ -89,7 +66,7 @@ profiles { podman.enabled = false shifter.enabled = false charliecloud.enabled = false - conda.channels = ['conda-forge', 'bioconda', 'defaults'] + conda.channels = ['conda-forge', 'bioconda'] apptainer.enabled = false } mamba { @@ -178,25 +155,23 @@ profiles { test_full { includeConfig 'conf/test_full.config' } } -// Set default registry for Apptainer, Docker, Podman and Singularity independent of -profile -// Will not be used unless Apptainer / Docker / Podman / Singularity are enabled -// Set to your registry if you have a mirror of containers -apptainer.registry = 'quay.io' -docker.registry = 'quay.io' -podman.registry = 'quay.io' -singularity.registry = 'quay.io' +// Load nf-core custom profiles from different Institutions +includeConfig !System.getenv('NXF_OFFLINE') && params.custom_config_base ? "${params.custom_config_base}/nfcore_custom.config" : "/dev/null" -// Nextflow plugins -plugins { - id 'nf-validation@1.1.3' // Validation of pipeline parameters and creation of an input channel from a sample sheet -} +// Load nf-core/spatialvi custom profiles from different institutions. +// TODO nf-core: Optionally, you can add a pipeline-specific nf-core config at https://github.com/nf-core/configs +// includeConfig !System.getenv('NXF_OFFLINE') && params.custom_config_base ? "${params.custom_config_base}/pipeline/spatialvi.config" : "/dev/null" +// Set default registry for Apptainer, Docker, Podman, Charliecloud and Singularity independent of -profile +// Will not be used unless Apptainer / Docker / Podman / Charliecloud / Singularity are enabled +// Set to your registry if you have a mirror of containers +apptainer.registry = 'quay.io' +docker.registry = 'quay.io' +podman.registry = 'quay.io' +singularity.registry = 'quay.io' +charliecloud.registry = 'quay.io' // Load igenomes.config if required -if (!params.igenomes_ignore) { - includeConfig 'conf/igenomes.config' -} else { - params.genomes = [:] -} +includeConfig !params.igenomes_ignore ? 'conf/igenomes.config' : 'conf/igenomes_ignored.config' // Export these variables to prevent local Python/R libraries from conflicting with those in the container // The JULIA depot path has been adjusted to a fixed path `/usr/local/share/julia` that needs to be used for packages in the container. // See https://apeltzer.github.io/post/03-julia-lang-nextflow/ for details on that. Once we have a common agreement on where to keep Julia packages, this is adjustable. @@ -208,8 +183,15 @@ env { JULIA_DEPOT_PATH = "/usr/local/share/julia" } -// Capture exit codes from upstream processes when piping -process.shell = ['/bin/bash', '-euo', 'pipefail'] +// Set bash options +process.shell = """\ +bash + +set -e # Exit if a tool returns a non-zero status/exit code +set -u # Treat unset variables and parameters as an error +set -o pipefail # Returns the status of the last command to exit with a non-zero status or zero if all successfully execute +set -C # No clobber - prevent output redirection from overwriting files. +""" // Disable process selector warnings by default. Use debug profile to enable warnings. nextflow.enable.configProcessNamesValidation = false @@ -238,43 +220,47 @@ manifest { homePage = 'https://github.com/nf-core/spatialvi' description = """10X Visium Spatial Transcriptomics""" mainScript = 'main.nf' - nextflowVersion = '!>=23.04.0' + nextflowVersion = '!>=24.04.2' version = '1.0dev' doi = '' } -// Load modules.config for DSL2 module specific options -includeConfig 'conf/modules.config' +// Nextflow plugins +plugins { + id 'nf-schema@2.1.1' // Validation of pipeline parameters and creation of an input channel from a sample sheet +} + +validation { + defaultIgnoreParams = ["genomes"] + help { + enabled = true + command = "nextflow run $manifest.name -profile --input samplesheet.csv --outdir " + fullParameter = "help_full" + showHiddenParameter = "show_hidden" + beforeText = """ +-\033[2m----------------------------------------------------\033[0m- + \033[0;32m,--.\033[0;30m/\033[0;32m,-.\033[0m +\033[0;34m ___ __ __ __ ___ \033[0;32m/,-._.--~\'\033[0m +\033[0;34m |\\ | |__ __ / ` / \\ |__) |__ \033[0;33m} {\033[0m +\033[0;34m | \\| | \\__, \\__/ | \\ |___ \033[0;32m\\`-._,-`-,\033[0m + \033[0;32m`._,._,\'\033[0m +\033[0;35m ${manifest.name} ${manifest.version}\033[0m +-\033[2m----------------------------------------------------\033[0m- +""" + afterText = """${manifest.doi ? "* The pipeline\n" : ""}${manifest.doi.tokenize(",").collect { " https://doi.org/${it.trim().replace('https://doi.org/','')}"}.join("\n")}${manifest.doi ? "\n" : ""} +* The nf-core framework + https://doi.org/10.1038/s41587-020-0439-x -// Function to ensure that resource requirements don't go beyond -// a maximum limit -def check_max(obj, type) { - if (type == 'memory') { - try { - if (obj.compareTo(params.max_memory as nextflow.util.MemoryUnit) == 1) - return params.max_memory as nextflow.util.MemoryUnit - else - return obj - } catch (all) { - println " ### ERROR ### Max memory '${params.max_memory}' is not valid! Using default value: $obj" - return obj - } - } else if (type == 'time') { - try { - if (obj.compareTo(params.max_time as nextflow.util.Duration) == 1) - return params.max_time as nextflow.util.Duration - else - return obj - } catch (all) { - println " ### ERROR ### Max time '${params.max_time}' is not valid! Using default value: $obj" - return obj - } - } else if (type == 'cpus') { - try { - return Math.min( obj, params.max_cpus as int ) - } catch (all) { - println " ### ERROR ### Max cpus '${params.max_cpus}' is not valid! Using default value: $obj" - return obj - } +* Software dependencies + https://github.com/${manifest.name}/blob/master/CITATIONS.md +""" + } + summary { + beforeText = validation.help.beforeText + afterText = validation.help.afterText } } + +// Load modules.config for DSL2 module specific options +includeConfig 'conf/modules.config' + diff --git a/nextflow_schema.json b/nextflow_schema.json index 8673db7..ed99c44 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -1,10 +1,10 @@ { - "$schema": "http://json-schema.org/draft-07/schema", + "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "https://raw.githubusercontent.com/nf-core/spatialvi/master/nextflow_schema.json", "title": "nf-core/spatialvi pipeline parameters", "description": "10X Visium Spatial Transcriptomics", "type": "object", - "definitions": { + "$defs": { "input_output_options": { "title": "Input/output options", "type": "object", @@ -71,6 +71,14 @@ "fa_icon": "fas fa-ban", "hidden": true, "help_text": "Do not load `igenomes.config` when running the pipeline. You may choose this option if you observe clashes between custom parameters and those supplied in `igenomes.config`." + }, + "igenomes_base": { + "type": "string", + "format": "directory-path", + "description": "The base path to the igenomes reference files", + "fa_icon": "fas fa-ban", + "hidden": true, + "default": "s3://ngi-igenomes/igenomes/" } } }, @@ -122,41 +130,6 @@ } } }, - "max_job_request_options": { - "title": "Max job request options", - "type": "object", - "fa_icon": "fab fa-acquisitions-incorporated", - "description": "Set the top limit for requested resources for any single job.", - "help_text": "If you are running on a smaller system, a pipeline step requesting more resources than are available may cause the Nextflow to stop the run with an error. These options allow you to cap the maximum resources requested by any single job so that the pipeline will run on your system.\n\nNote that you can not _increase_ the resources requested by any job using these options. For that you will need your own configuration file. See [the nf-core website](https://nf-co.re/usage/configuration) for details.", - "properties": { - "max_cpus": { - "type": "integer", - "description": "Maximum number of CPUs that can be requested for any single job.", - "default": 16, - "fa_icon": "fas fa-microchip", - "hidden": true, - "help_text": "Use to set an upper-limit for the CPU requirement for each process. Should be an integer e.g. `--max_cpus 1`" - }, - "max_memory": { - "type": "string", - "description": "Maximum amount of memory that can be requested for any single job.", - "default": "128.GB", - "fa_icon": "fas fa-memory", - "pattern": "^\\d+(\\.\\d+)?\\.?\\s*(K|M|G|T)?B$", - "hidden": true, - "help_text": "Use to set an upper-limit for the memory requirement for each process. Should be a string in the format integer-unit e.g. `--max_memory '8.GB'`" - }, - "max_time": { - "type": "string", - "description": "Maximum amount of time that can be requested for any single job.", - "default": "240.h", - "fa_icon": "far fa-clock", - "pattern": "^(\\d+\\.?\\s*(s|m|h|d|day)\\s*)+$", - "hidden": true, - "help_text": "Use to set an upper-limit for the time requirement for each process. Should be a string in the format integer-unit e.g. `--max_time '2.h'`" - } - } - }, "generic_options": { "title": "Generic options", "type": "object", @@ -164,12 +137,6 @@ "description": "Less common options for the pipeline, typically set in a config file.", "help_text": "These options are common to all nf-core pipelines and allow you to customise some of the core preferences for how the pipeline runs.\n\nTypically these options would be set in a Nextflow config file loaded for all pipeline runs, such as `~/.nextflow/config`.", "properties": { - "help": { - "type": "boolean", - "description": "Display help text.", - "fa_icon": "fas fa-question-circle", - "hidden": true - }, "version": { "type": "boolean", "description": "Display version and exit.", @@ -245,27 +212,6 @@ "fa_icon": "fas fa-check-square", "hidden": true }, - "validationShowHiddenParams": { - "type": "boolean", - "fa_icon": "far fa-eye-slash", - "description": "Show all params when using `--help`", - "hidden": true, - "help_text": "By default, parameters set as _hidden_ in the schema are not shown on the command line when a user runs with `--help`. Specifying this option will tell the pipeline to show all parameters." - }, - "validationFailUnrecognisedParams": { - "type": "boolean", - "fa_icon": "far fa-check-circle", - "description": "Validation of parameters fails when an unrecognised parameter is found.", - "hidden": true, - "help_text": "By default, when an unrecognised parameter is found, it returns a warinig." - }, - "validationLenientMode": { - "type": "boolean", - "fa_icon": "far fa-check-circle", - "description": "Validation of parameters in lenient more.", - "hidden": true, - "help_text": "Allows string values that are parseable as numbers or booleans. For further information see [JSONSchema docs](https://github.com/everit-org/json-schema#lenient-mode)." - }, "pipelines_testdata_base_path": { "type": "string", "fa_icon": "far fa-check-circle", @@ -278,19 +224,16 @@ }, "allOf": [ { - "$ref": "#/definitions/input_output_options" - }, - { - "$ref": "#/definitions/reference_genome_options" + "$ref": "#/$defs/input_output_options" }, { - "$ref": "#/definitions/institutional_config_options" + "$ref": "#/$defs/reference_genome_options" }, { - "$ref": "#/definitions/max_job_request_options" + "$ref": "#/$defs/institutional_config_options" }, { - "$ref": "#/definitions/generic_options" + "$ref": "#/$defs/generic_options" } ] } diff --git a/subworkflows/local/utils_nfcore_spatialvi_pipeline/main.nf b/subworkflows/local/utils_nfcore_spatialvi_pipeline/main.nf index 2c67711..6a59d26 100644 --- a/subworkflows/local/utils_nfcore_spatialvi_pipeline/main.nf +++ b/subworkflows/local/utils_nfcore_spatialvi_pipeline/main.nf @@ -8,17 +8,14 @@ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ -include { UTILS_NFVALIDATION_PLUGIN } from '../../nf-core/utils_nfvalidation_plugin' -include { paramsSummaryMap } from 'plugin/nf-validation' -include { fromSamplesheet } from 'plugin/nf-validation' -include { UTILS_NEXTFLOW_PIPELINE } from '../../nf-core/utils_nextflow_pipeline' +include { UTILS_NFSCHEMA_PLUGIN } from '../../nf-core/utils_nfschema_plugin' +include { paramsSummaryMap } from 'plugin/nf-schema' +include { samplesheetToList } from 'plugin/nf-schema' include { completionEmail } from '../../nf-core/utils_nfcore_pipeline' include { completionSummary } from '../../nf-core/utils_nfcore_pipeline' -include { dashedLine } from '../../nf-core/utils_nfcore_pipeline' -include { nfCoreLogo } from '../../nf-core/utils_nfcore_pipeline' include { imNotification } from '../../nf-core/utils_nfcore_pipeline' include { UTILS_NFCORE_PIPELINE } from '../../nf-core/utils_nfcore_pipeline' -include { workflowCitation } from '../../nf-core/utils_nfcore_pipeline' +include { UTILS_NEXTFLOW_PIPELINE } from '../../nf-core/utils_nextflow_pipeline' /* ======================================================================================== @@ -30,7 +27,6 @@ workflow PIPELINE_INITIALISATION { take: version // boolean: Display version and exit - help // boolean: Display help text validate_params // boolean: Boolean whether to validate parameters against the schema at runtime monochrome_logs // boolean: Do not use coloured log outputs nextflow_cli_args // array: List of positional nextflow CLI args @@ -51,20 +47,16 @@ workflow PIPELINE_INITIALISATION { workflow.profile.tokenize(',').intersect(['conda', 'mamba']).size() >= 1 ) + // // Validate parameters and generate parameter summary to stdout // - pre_help_text = nfCoreLogo(monochrome_logs) - post_help_text = '\n' + workflowCitation() + '\n' + dashedLine(monochrome_logs) - def String workflow_command = "nextflow run ${workflow.manifest.name} -profile --input samplesheet.csv --outdir " - UTILS_NFVALIDATION_PLUGIN ( - help, - workflow_command, - pre_help_text, - post_help_text, + UTILS_NFSCHEMA_PLUGIN ( + workflow, validate_params, - "nextflow_schema.json" + null ) + // // Check config provided to the pipeline @@ -80,8 +72,9 @@ workflow PIPELINE_INITIALISATION { // // Create channel from input file provided through params.input // + Channel - .fromSamplesheet("input") + .fromList(samplesheetToList(params.input, "${projectDir}/assets/schema_input.json")) .map { meta, fastq_1, fastq_2 -> if (!fastq_2) { @@ -91,8 +84,8 @@ workflow PIPELINE_INITIALISATION { } } .groupTuple() - .map { - validateInputSamplesheet(it) + .map { samplesheet -> + validateInputSamplesheet(samplesheet) } .map { meta, fastqs -> @@ -117,13 +110,13 @@ workflow PIPELINE_COMPLETION { email // string: email address email_on_fail // string: email address sent on pipeline failure plaintext_email // boolean: Send plain-text email instead of HTML + outdir // path: Path to output directory where results will be published monochrome_logs // boolean: Disable ANSI colour codes in log output hook_url // string: hook URL for notifications multiqc_report // string: Path to MultiQC report main: - summary_params = paramsSummaryMap(workflow, parameters_schema: "nextflow_schema.json") // @@ -131,11 +124,18 @@ workflow PIPELINE_COMPLETION { // workflow.onComplete { if (email || email_on_fail) { - completionEmail(summary_params, email, email_on_fail, plaintext_email, outdir, monochrome_logs, multiqc_report.toList()) + completionEmail( + summary_params, + email, + email_on_fail, + plaintext_email, + outdir, + monochrome_logs, + multiqc_report.toList() + ) } completionSummary(monochrome_logs) - if (hook_url) { imNotification(summary_params, hook_url) } @@ -165,7 +165,7 @@ def validateInputSamplesheet(input) { def (metas, fastqs) = input[1..2] // Check that multiple runs of the same sample are of the same datatype i.e. single-end / paired-end - def endedness_ok = metas.collect{ it.single_end }.unique().size == 1 + def endedness_ok = metas.collect{ meta -> meta.single_end }.unique().size == 1 if (!endedness_ok) { error("Please check input samplesheet -> Multiple runs of a sample must be of the same datatype i.e. single-end or paired-end: ${metas[0].id}") } @@ -197,7 +197,6 @@ def genomeExistsError() { error(error_string) } } - // // Generate methods description for MultiQC // @@ -239,8 +238,10 @@ def methodsDescriptionText(mqc_methods_yaml) { // Removing `https://doi.org/` to handle pipelines using DOIs vs DOI resolvers // Removing ` ` since the manifest.doi is a string and not a proper list def temp_doi_ref = "" - String[] manifest_doi = meta.manifest_map.doi.tokenize(",") - for (String doi_ref: manifest_doi) temp_doi_ref += "(doi:
${doi_ref.replace("https://doi.org/", "").replace(" ", "")}), " + def manifest_doi = meta.manifest_map.doi.tokenize(",") + manifest_doi.each { doi_ref -> + temp_doi_ref += "(doi: ${doi_ref.replace("https://doi.org/", "").replace(" ", "")}), " + } meta["doi_text"] = temp_doi_ref.substring(0, temp_doi_ref.length() - 2) } else meta["doi_text"] = "" meta["nodoi_text"] = meta.manifest_map.doi ? "" : "
  • If available, make sure to update the text to include the Zenodo DOI of version of the pipeline used.
  • " @@ -261,3 +262,4 @@ def methodsDescriptionText(mqc_methods_yaml) { return description_html.toString() } + diff --git a/subworkflows/nf-core/utils_nextflow_pipeline/main.nf b/subworkflows/nf-core/utils_nextflow_pipeline/main.nf index ac31f28..28e32b2 100644 --- a/subworkflows/nf-core/utils_nextflow_pipeline/main.nf +++ b/subworkflows/nf-core/utils_nextflow_pipeline/main.nf @@ -2,10 +2,6 @@ // Subworkflow with functionality that may be useful for any Nextflow pipeline // -import org.yaml.snakeyaml.Yaml -import groovy.json.JsonOutput -import nextflow.extension.FilesEx - /* ======================================================================================== SUBWORKFLOW DEFINITION @@ -58,7 +54,7 @@ workflow UTILS_NEXTFLOW_PIPELINE { // Generate version string // def getWorkflowVersion() { - String version_string = "" + def version_string = "" as String if (workflow.manifest.version) { def prefix_v = workflow.manifest.version[0] != 'v' ? 'v' : '' version_string += "${prefix_v}${workflow.manifest.version}" @@ -79,10 +75,10 @@ def dumpParametersToJSON(outdir) { def timestamp = new java.util.Date().format( 'yyyy-MM-dd_HH-mm-ss') def filename = "params_${timestamp}.json" def temp_pf = new File(workflow.launchDir.toString(), ".${filename}") - def jsonStr = JsonOutput.toJson(params) - temp_pf.text = JsonOutput.prettyPrint(jsonStr) + def jsonStr = groovy.json.JsonOutput.toJson(params) + temp_pf.text = groovy.json.JsonOutput.prettyPrint(jsonStr) - FilesEx.copyTo(temp_pf.toPath(), "${outdir}/pipeline_info/params_${timestamp}.json") + nextflow.extension.FilesEx.copyTo(temp_pf.toPath(), "${outdir}/pipeline_info/params_${timestamp}.json") temp_pf.delete() } @@ -90,7 +86,7 @@ def dumpParametersToJSON(outdir) { // When running with -profile conda, warn if channels have not been set-up appropriately // def checkCondaChannels() { - Yaml parser = new Yaml() + def parser = new org.yaml.snakeyaml.Yaml() def channels = [] try { def config = parser.load("conda config --show channels".execute().text) @@ -102,14 +98,16 @@ def checkCondaChannels() { // Check that all channels are present // This channel list is ordered by required channel priority. - def required_channels_in_order = ['conda-forge', 'bioconda', 'defaults'] + def required_channels_in_order = ['conda-forge', 'bioconda'] def channels_missing = ((required_channels_in_order as Set) - (channels as Set)) as Boolean // Check that they are in the right order def channel_priority_violation = false - def n = required_channels_in_order.size() - for (int i = 0; i < n - 1; i++) { - channel_priority_violation |= !(channels.indexOf(required_channels_in_order[i]) < channels.indexOf(required_channels_in_order[i+1])) + + required_channels_in_order.eachWithIndex { channel, index -> + if (index < required_channels_in_order.size() - 1) { + channel_priority_violation |= !(channels.indexOf(channel) < channels.indexOf(required_channels_in_order[index+1])) + } } if (channels_missing | channel_priority_violation) { diff --git a/subworkflows/nf-core/utils_nextflow_pipeline/tests/nextflow.config b/subworkflows/nf-core/utils_nextflow_pipeline/tests/nextflow.config index d0a926b..a09572e 100644 --- a/subworkflows/nf-core/utils_nextflow_pipeline/tests/nextflow.config +++ b/subworkflows/nf-core/utils_nextflow_pipeline/tests/nextflow.config @@ -3,7 +3,7 @@ manifest { author = """nf-core""" homePage = 'https://127.0.0.1' description = """Dummy pipeline""" - nextflowVersion = '!>=23.04.0' + nextflowVersion = '!>=23.04.0' version = '9.9.9' doi = 'https://doi.org/10.5281/zenodo.5070524' } diff --git a/subworkflows/nf-core/utils_nfcore_pipeline/main.nf b/subworkflows/nf-core/utils_nfcore_pipeline/main.nf index 14558c3..cbd8495 100644 --- a/subworkflows/nf-core/utils_nfcore_pipeline/main.nf +++ b/subworkflows/nf-core/utils_nfcore_pipeline/main.nf @@ -2,9 +2,6 @@ // Subworkflow with utility functions specific to the nf-core pipeline template // -import org.yaml.snakeyaml.Yaml -import nextflow.extension.FilesEx - /* ======================================================================================== SUBWORKFLOW DEFINITION @@ -34,7 +31,7 @@ workflow UTILS_NFCORE_PIPELINE { // Warn if a -profile or Nextflow config has not been provided to run the pipeline // def checkConfigProvided() { - valid_config = true + def valid_config = true as Boolean if (workflow.profile == 'standard' && workflow.configFiles.size() <= 1) { log.warn "[$workflow.manifest.name] You are attempting to run the pipeline without any custom configuration!\n\n" + "This will be dependent on your local compute environment but can be achieved via one or more of the following:\n" + @@ -66,11 +63,13 @@ def checkProfileProvided(nextflow_cli_args) { // def workflowCitation() { def temp_doi_ref = "" - String[] manifest_doi = workflow.manifest.doi.tokenize(",") + def manifest_doi = workflow.manifest.doi.tokenize(",") // Using a loop to handle multiple DOIs // Removing `https://doi.org/` to handle pipelines using DOIs vs DOI resolvers // Removing ` ` since the manifest.doi is a string and not a proper list - for (String doi_ref: manifest_doi) temp_doi_ref += " https://doi.org/${doi_ref.replace('https://doi.org/', '').replace(' ', '')}\n" + manifest_doi.each { doi_ref -> + temp_doi_ref += " https://doi.org/${doi_ref.replace('https://doi.org/', '').replace(' ', '')}\n" + } return "If you use ${workflow.manifest.name} for your analysis please cite:\n\n" + "* The pipeline\n" + temp_doi_ref + "\n" + @@ -84,7 +83,7 @@ def workflowCitation() { // Generate workflow version string // def getWorkflowVersion() { - String version_string = "" + def version_string = "" as String if (workflow.manifest.version) { def prefix_v = workflow.manifest.version[0] != 'v' ? 'v' : '' version_string += "${prefix_v}${workflow.manifest.version}" @@ -102,8 +101,8 @@ def getWorkflowVersion() { // Get software versions for pipeline // def processVersionsFromYAML(yaml_file) { - Yaml yaml = new Yaml() - versions = yaml.load(yaml_file).collectEntries { k, v -> [ k.tokenize(':')[-1], v ] } + def yaml = new org.yaml.snakeyaml.Yaml() + def versions = yaml.load(yaml_file).collectEntries { k, v -> [ k.tokenize(':')[-1], v ] } return yaml.dumpAsMap(versions).trim() } @@ -124,7 +123,7 @@ def workflowVersionToYAML() { def softwareVersionsToYAML(ch_versions) { return ch_versions .unique() - .map { processVersionsFromYAML(it) } + .map { version -> processVersionsFromYAML(version) } .unique() .mix(Channel.of(workflowVersionToYAML())) } @@ -134,19 +133,19 @@ def softwareVersionsToYAML(ch_versions) { // def paramsSummaryMultiqc(summary_params) { def summary_section = '' - for (group in summary_params.keySet()) { + summary_params.keySet().each { group -> def group_params = summary_params.get(group) // This gets the parameters of that particular group if (group_params) { summary_section += "

    $group

    \n" summary_section += "
    \n" - for (param in group_params.keySet()) { + group_params.keySet().sort().each { param -> summary_section += "
    $param
    ${group_params.get(param) ?: 'N/A'}
    \n" } summary_section += "
    \n" } } - String yaml_file_text = "id: '${workflow.manifest.name.replace('/','-')}-summary'\n" + def yaml_file_text = "id: '${workflow.manifest.name.replace('/','-')}-summary'\n" as String yaml_file_text += "description: ' - this information is collected when the pipeline is started.'\n" yaml_file_text += "section_name: '${workflow.manifest.name} Workflow Summary'\n" yaml_file_text += "section_href: 'https://github.com/${workflow.manifest.name}'\n" @@ -161,7 +160,7 @@ def paramsSummaryMultiqc(summary_params) { // nf-core logo // def nfCoreLogo(monochrome_logs=true) { - Map colors = logColours(monochrome_logs) + def colors = logColours(monochrome_logs) as Map String.format( """\n ${dashedLine(monochrome_logs)} @@ -180,7 +179,7 @@ def nfCoreLogo(monochrome_logs=true) { // Return dashed line // def dashedLine(monochrome_logs=true) { - Map colors = logColours(monochrome_logs) + def colors = logColours(monochrome_logs) as Map return "-${colors.dim}----------------------------------------------------${colors.reset}-" } @@ -188,7 +187,7 @@ def dashedLine(monochrome_logs=true) { // ANSII colours used for terminal logging // def logColours(monochrome_logs=true) { - Map colorcodes = [:] + def colorcodes = [:] as Map // Reset / Meta colorcodes['reset'] = monochrome_logs ? '' : "\033[0m" @@ -287,7 +286,7 @@ def completionEmail(summary_params, email, email_on_fail, plaintext_email, outdi } def summary = [:] - for (group in summary_params.keySet()) { + summary_params.keySet().sort().each { group -> summary << summary_params[group] } @@ -344,10 +343,10 @@ def completionEmail(summary_params, email, email_on_fail, plaintext_email, outdi def sendmail_html = sendmail_template.toString() // Send the HTML e-mail - Map colors = logColours(monochrome_logs) + def colors = logColours(monochrome_logs) as Map if (email_address) { try { - if (plaintext_email) { throw GroovyException('Send plaintext e-mail, not HTML') } + if (plaintext_email) { throw new org.codehaus.groovy.GroovyException('Send plaintext e-mail, not HTML') } // Try to send HTML e-mail using sendmail def sendmail_tf = new File(workflow.launchDir.toString(), ".sendmail_tmp.html") sendmail_tf.withWriter { w -> w << sendmail_html } @@ -364,13 +363,13 @@ def completionEmail(summary_params, email, email_on_fail, plaintext_email, outdi // Write summary e-mail HTML to a file def output_hf = new File(workflow.launchDir.toString(), ".pipeline_report.html") output_hf.withWriter { w -> w << email_html } - FilesEx.copyTo(output_hf.toPath(), "${outdir}/pipeline_info/pipeline_report.html"); + nextflow.extension.FilesEx.copyTo(output_hf.toPath(), "${outdir}/pipeline_info/pipeline_report.html"); output_hf.delete() // Write summary e-mail TXT to a file def output_tf = new File(workflow.launchDir.toString(), ".pipeline_report.txt") output_tf.withWriter { w -> w << email_txt } - FilesEx.copyTo(output_tf.toPath(), "${outdir}/pipeline_info/pipeline_report.txt"); + nextflow.extension.FilesEx.copyTo(output_tf.toPath(), "${outdir}/pipeline_info/pipeline_report.txt"); output_tf.delete() } @@ -378,7 +377,7 @@ def completionEmail(summary_params, email, email_on_fail, plaintext_email, outdi // Print pipeline summary on completion // def completionSummary(monochrome_logs=true) { - Map colors = logColours(monochrome_logs) + def colors = logColours(monochrome_logs) as Map if (workflow.success) { if (workflow.stats.ignoredCount == 0) { log.info "-${colors.purple}[$workflow.manifest.name]${colors.green} Pipeline completed successfully${colors.reset}-" @@ -395,7 +394,7 @@ def completionSummary(monochrome_logs=true) { // def imNotification(summary_params, hook_url) { def summary = [:] - for (group in summary_params.keySet()) { + summary_params.keySet().sort().each { group -> summary << summary_params[group] } diff --git a/subworkflows/nf-core/utils_nfschema_plugin/main.nf b/subworkflows/nf-core/utils_nfschema_plugin/main.nf new file mode 100644 index 0000000..4994303 --- /dev/null +++ b/subworkflows/nf-core/utils_nfschema_plugin/main.nf @@ -0,0 +1,46 @@ +// +// Subworkflow that uses the nf-schema plugin to validate parameters and render the parameter summary +// + +include { paramsSummaryLog } from 'plugin/nf-schema' +include { validateParameters } from 'plugin/nf-schema' + +workflow UTILS_NFSCHEMA_PLUGIN { + + take: + input_workflow // workflow: the workflow object used by nf-schema to get metadata from the workflow + validate_params // boolean: validate the parameters + parameters_schema // string: path to the parameters JSON schema. + // this has to be the same as the schema given to `validation.parametersSchema` + // when this input is empty it will automatically use the configured schema or + // "${projectDir}/nextflow_schema.json" as default. This input should not be empty + // for meta pipelines + + main: + + // + // Print parameter summary to stdout. This will display the parameters + // that differ from the default given in the JSON schema + // + if(parameters_schema) { + log.info paramsSummaryLog(input_workflow, parameters_schema:parameters_schema) + } else { + log.info paramsSummaryLog(input_workflow) + } + + // + // Validate the parameters using nextflow_schema.json or the schema + // given via the validation.parametersSchema configuration option + // + if(validate_params) { + if(parameters_schema) { + validateParameters(parameters_schema:parameters_schema) + } else { + validateParameters() + } + } + + emit: + dummy_emit = true +} + diff --git a/subworkflows/nf-core/utils_nfschema_plugin/meta.yml b/subworkflows/nf-core/utils_nfschema_plugin/meta.yml new file mode 100644 index 0000000..f7d9f02 --- /dev/null +++ b/subworkflows/nf-core/utils_nfschema_plugin/meta.yml @@ -0,0 +1,35 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/nf-core/modules/master/subworkflows/yaml-schema.json +name: "utils_nfschema_plugin" +description: Run nf-schema to validate parameters and create a summary of changed parameters +keywords: + - validation + - JSON schema + - plugin + - parameters + - summary +components: [] +input: + - input_workflow: + type: object + description: | + The workflow object of the used pipeline. + This object contains meta data used to create the params summary log + - validate_params: + type: boolean + description: Validate the parameters and error if invalid. + - parameters_schema: + type: string + description: | + Path to the parameters JSON schema. + This has to be the same as the schema given to the `validation.parametersSchema` config + option. When this input is empty it will automatically use the configured schema or + "${projectDir}/nextflow_schema.json" as default. The schema should not be given in this way + for meta pipelines. +output: + - dummy_emit: + type: boolean + description: Dummy emit to make nf-core subworkflows lint happy +authors: + - "@nvnieuwk" +maintainers: + - "@nvnieuwk" diff --git a/subworkflows/nf-core/utils_nfschema_plugin/tests/main.nf.test b/subworkflows/nf-core/utils_nfschema_plugin/tests/main.nf.test new file mode 100644 index 0000000..842dc43 --- /dev/null +++ b/subworkflows/nf-core/utils_nfschema_plugin/tests/main.nf.test @@ -0,0 +1,117 @@ +nextflow_workflow { + + name "Test Subworkflow UTILS_NFSCHEMA_PLUGIN" + script "../main.nf" + workflow "UTILS_NFSCHEMA_PLUGIN" + + tag "subworkflows" + tag "subworkflows_nfcore" + tag "subworkflows/utils_nfschema_plugin" + tag "plugin/nf-schema" + + config "./nextflow.config" + + test("Should run nothing") { + + when { + + params { + test_data = '' + } + + workflow { + """ + validate_params = false + input[0] = workflow + input[1] = validate_params + input[2] = "" + """ + } + } + + then { + assertAll( + { assert workflow.success } + ) + } + } + + test("Should validate params") { + + when { + + params { + test_data = '' + outdir = 1 + } + + workflow { + """ + validate_params = true + input[0] = workflow + input[1] = validate_params + input[2] = "" + """ + } + } + + then { + assertAll( + { assert workflow.failed }, + { assert workflow.stdout.any { it.contains('ERROR ~ Validation of pipeline parameters failed!') } } + ) + } + } + + test("Should run nothing - custom schema") { + + when { + + params { + test_data = '' + } + + workflow { + """ + validate_params = false + input[0] = workflow + input[1] = validate_params + input[2] = "${projectDir}/subworkflows/nf-core/utils_nfschema_plugin/tests/nextflow_schema.json" + """ + } + } + + then { + assertAll( + { assert workflow.success } + ) + } + } + + test("Should validate params - custom schema") { + + when { + + params { + test_data = '' + outdir = 1 + } + + workflow { + """ + validate_params = true + input[0] = workflow + input[1] = validate_params + input[2] = "${projectDir}/subworkflows/nf-core/utils_nfschema_plugin/tests/nextflow_schema.json" + """ + } + } + + then { + assertAll( + { assert workflow.failed }, + { assert workflow.stdout.any { it.contains('ERROR ~ Validation of pipeline parameters failed!') } } + ) + } + } +} diff --git a/subworkflows/nf-core/utils_nfschema_plugin/tests/nextflow.config b/subworkflows/nf-core/utils_nfschema_plugin/tests/nextflow.config new file mode 100644 index 0000000..0907ac5 --- /dev/null +++ b/subworkflows/nf-core/utils_nfschema_plugin/tests/nextflow.config @@ -0,0 +1,8 @@ +plugins { + id "nf-schema@2.1.0" +} + +validation { + parametersSchema = "${projectDir}/subworkflows/nf-core/utils_nfschema_plugin/tests/nextflow_schema.json" + monochromeLogs = true +} \ No newline at end of file diff --git a/subworkflows/nf-core/utils_nfvalidation_plugin/tests/nextflow_schema.json b/subworkflows/nf-core/utils_nfschema_plugin/tests/nextflow_schema.json similarity index 95% rename from subworkflows/nf-core/utils_nfvalidation_plugin/tests/nextflow_schema.json rename to subworkflows/nf-core/utils_nfschema_plugin/tests/nextflow_schema.json index 7626c1c..331e0d2 100644 --- a/subworkflows/nf-core/utils_nfvalidation_plugin/tests/nextflow_schema.json +++ b/subworkflows/nf-core/utils_nfschema_plugin/tests/nextflow_schema.json @@ -1,10 +1,10 @@ { - "$schema": "http://json-schema.org/draft-07/schema", + "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "https://raw.githubusercontent.com/./master/nextflow_schema.json", "title": ". pipeline parameters", "description": "", "type": "object", - "definitions": { + "$defs": { "input_output_options": { "title": "Input/output options", "type": "object", @@ -87,10 +87,10 @@ }, "allOf": [ { - "$ref": "#/definitions/input_output_options" + "$ref": "#/$defs/input_output_options" }, { - "$ref": "#/definitions/generic_options" + "$ref": "#/$defs/generic_options" } ] } diff --git a/subworkflows/nf-core/utils_nfvalidation_plugin/main.nf b/subworkflows/nf-core/utils_nfvalidation_plugin/main.nf deleted file mode 100644 index 2585b65..0000000 --- a/subworkflows/nf-core/utils_nfvalidation_plugin/main.nf +++ /dev/null @@ -1,62 +0,0 @@ -// -// Subworkflow that uses the nf-validation plugin to render help text and parameter summary -// - -/* -======================================================================================== - IMPORT NF-VALIDATION PLUGIN -======================================================================================== -*/ - -include { paramsHelp } from 'plugin/nf-validation' -include { paramsSummaryLog } from 'plugin/nf-validation' -include { validateParameters } from 'plugin/nf-validation' - -/* -======================================================================================== - SUBWORKFLOW DEFINITION -======================================================================================== -*/ - -workflow UTILS_NFVALIDATION_PLUGIN { - - take: - print_help // boolean: print help - workflow_command // string: default commmand used to run pipeline - pre_help_text // string: string to be printed before help text and summary log - post_help_text // string: string to be printed after help text and summary log - validate_params // boolean: validate parameters - schema_filename // path: JSON schema file, null to use default value - - main: - - log.debug "Using schema file: ${schema_filename}" - - // Default values for strings - pre_help_text = pre_help_text ?: '' - post_help_text = post_help_text ?: '' - workflow_command = workflow_command ?: '' - - // - // Print help message if needed - // - if (print_help) { - log.info pre_help_text + paramsHelp(workflow_command, parameters_schema: schema_filename) + post_help_text - System.exit(0) - } - - // - // Print parameter summary to stdout - // - log.info pre_help_text + paramsSummaryLog(workflow, parameters_schema: schema_filename) + post_help_text - - // - // Validate parameters relative to the parameter JSON schema - // - if (validate_params){ - validateParameters(parameters_schema: schema_filename) - } - - emit: - dummy_emit = true -} diff --git a/subworkflows/nf-core/utils_nfvalidation_plugin/meta.yml b/subworkflows/nf-core/utils_nfvalidation_plugin/meta.yml deleted file mode 100644 index 3d4a6b0..0000000 --- a/subworkflows/nf-core/utils_nfvalidation_plugin/meta.yml +++ /dev/null @@ -1,44 +0,0 @@ -# yaml-language-server: $schema=https://raw.githubusercontent.com/nf-core/modules/master/subworkflows/yaml-schema.json -name: "UTILS_NFVALIDATION_PLUGIN" -description: Use nf-validation to initiate and validate a pipeline -keywords: - - utility - - pipeline - - initialise - - validation -components: [] -input: - - print_help: - type: boolean - description: | - Print help message and exit - - workflow_command: - type: string - description: | - The command to run the workflow e.g. "nextflow run main.nf" - - pre_help_text: - type: string - description: | - Text to print before the help message - - post_help_text: - type: string - description: | - Text to print after the help message - - validate_params: - type: boolean - description: | - Validate the parameters and error if invalid. - - schema_filename: - type: string - description: | - The filename of the schema to validate against. -output: - - dummy_emit: - type: boolean - description: | - Dummy emit to make nf-core subworkflows lint happy -authors: - - "@adamrtalbot" -maintainers: - - "@adamrtalbot" - - "@maxulysse" diff --git a/subworkflows/nf-core/utils_nfvalidation_plugin/tests/main.nf.test b/subworkflows/nf-core/utils_nfvalidation_plugin/tests/main.nf.test deleted file mode 100644 index 5784a33..0000000 --- a/subworkflows/nf-core/utils_nfvalidation_plugin/tests/main.nf.test +++ /dev/null @@ -1,200 +0,0 @@ -nextflow_workflow { - - name "Test Workflow UTILS_NFVALIDATION_PLUGIN" - script "../main.nf" - workflow "UTILS_NFVALIDATION_PLUGIN" - tag "subworkflows" - tag "subworkflows_nfcore" - tag "plugin/nf-validation" - tag "'plugin/nf-validation'" - tag "utils_nfvalidation_plugin" - tag "subworkflows/utils_nfvalidation_plugin" - - test("Should run nothing") { - - when { - - params { - monochrome_logs = true - test_data = '' - } - - workflow { - """ - help = false - workflow_command = null - pre_help_text = null - post_help_text = null - validate_params = false - schema_filename = "$moduleTestDir/nextflow_schema.json" - - input[0] = help - input[1] = workflow_command - input[2] = pre_help_text - input[3] = post_help_text - input[4] = validate_params - input[5] = schema_filename - """ - } - } - - then { - assertAll( - { assert workflow.success } - ) - } - } - - test("Should run help") { - - - when { - - params { - monochrome_logs = true - test_data = '' - } - workflow { - """ - help = true - workflow_command = null - pre_help_text = null - post_help_text = null - validate_params = false - schema_filename = "$moduleTestDir/nextflow_schema.json" - - input[0] = help - input[1] = workflow_command - input[2] = pre_help_text - input[3] = post_help_text - input[4] = validate_params - input[5] = schema_filename - """ - } - } - - then { - assertAll( - { assert workflow.success }, - { assert workflow.exitStatus == 0 }, - { assert workflow.stdout.any { it.contains('Input/output options') } }, - { assert workflow.stdout.any { it.contains('--outdir') } } - ) - } - } - - test("Should run help with command") { - - when { - - params { - monochrome_logs = true - test_data = '' - } - workflow { - """ - help = true - workflow_command = "nextflow run noorg/doesntexist" - pre_help_text = null - post_help_text = null - validate_params = false - schema_filename = "$moduleTestDir/nextflow_schema.json" - - input[0] = help - input[1] = workflow_command - input[2] = pre_help_text - input[3] = post_help_text - input[4] = validate_params - input[5] = schema_filename - """ - } - } - - then { - assertAll( - { assert workflow.success }, - { assert workflow.exitStatus == 0 }, - { assert workflow.stdout.any { it.contains('nextflow run noorg/doesntexist') } }, - { assert workflow.stdout.any { it.contains('Input/output options') } }, - { assert workflow.stdout.any { it.contains('--outdir') } } - ) - } - } - - test("Should run help with extra text") { - - - when { - - params { - monochrome_logs = true - test_data = '' - } - workflow { - """ - help = true - workflow_command = "nextflow run noorg/doesntexist" - pre_help_text = "pre-help-text" - post_help_text = "post-help-text" - validate_params = false - schema_filename = "$moduleTestDir/nextflow_schema.json" - - input[0] = help - input[1] = workflow_command - input[2] = pre_help_text - input[3] = post_help_text - input[4] = validate_params - input[5] = schema_filename - """ - } - } - - then { - assertAll( - { assert workflow.success }, - { assert workflow.exitStatus == 0 }, - { assert workflow.stdout.any { it.contains('pre-help-text') } }, - { assert workflow.stdout.any { it.contains('nextflow run noorg/doesntexist') } }, - { assert workflow.stdout.any { it.contains('Input/output options') } }, - { assert workflow.stdout.any { it.contains('--outdir') } }, - { assert workflow.stdout.any { it.contains('post-help-text') } } - ) - } - } - - test("Should validate params") { - - when { - - params { - monochrome_logs = true - test_data = '' - outdir = 1 - } - workflow { - """ - help = false - workflow_command = null - pre_help_text = null - post_help_text = null - validate_params = true - schema_filename = "$moduleTestDir/nextflow_schema.json" - - input[0] = help - input[1] = workflow_command - input[2] = pre_help_text - input[3] = post_help_text - input[4] = validate_params - input[5] = schema_filename - """ - } - } - - then { - assertAll( - { assert workflow.failed }, - { assert workflow.stdout.any { it.contains('ERROR ~ ERROR: Validation of pipeline parameters failed!') } } - ) - } - } -} diff --git a/subworkflows/nf-core/utils_nfvalidation_plugin/tests/tags.yml b/subworkflows/nf-core/utils_nfvalidation_plugin/tests/tags.yml deleted file mode 100644 index 60b1cff..0000000 --- a/subworkflows/nf-core/utils_nfvalidation_plugin/tests/tags.yml +++ /dev/null @@ -1,2 +0,0 @@ -subworkflows/utils_nfvalidation_plugin: - - subworkflows/nf-core/utils_nfvalidation_plugin/** diff --git a/workflows/spatialvi.nf b/workflows/spatialvi.nf index 6b2543b..3f26926 100644 --- a/workflows/spatialvi.nf +++ b/workflows/spatialvi.nf @@ -3,10 +3,9 @@ IMPORT MODULES / SUBWORKFLOWS / FUNCTIONS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ - include { FASTQC } from '../modules/nf-core/fastqc/main' include { MULTIQC } from '../modules/nf-core/multiqc/main' -include { paramsSummaryMap } from 'plugin/nf-validation' +include { paramsSummaryMap } from 'plugin/nf-schema' include { paramsSummaryMultiqc } from '../subworkflows/nf-core/utils_nfcore_pipeline' include { softwareVersionsToYAML } from '../subworkflows/nf-core/utils_nfcore_pipeline' include { methodsDescriptionText } from '../subworkflows/local/utils_nfcore_spatialvi_pipeline' @@ -21,12 +20,10 @@ workflow SPATIALVI { take: ch_samplesheet // channel: samplesheet read in from --input - main: ch_versions = Channel.empty() ch_multiqc_files = Channel.empty() - // // MODULE: Run FastQC // @@ -42,11 +39,12 @@ workflow SPATIALVI { softwareVersionsToYAML(ch_versions) .collectFile( storeDir: "${params.outdir}/pipeline_info", - name: 'nf_core_pipeline_software_mqc_versions.yml', + name: 'nf_core_' + 'pipeline_software_' + 'mqc_' + 'versions.yml', sort: true, newLine: true ).set { ch_collated_versions } + // // MODULE: MultiQC // @@ -59,18 +57,19 @@ workflow SPATIALVI { Channel.fromPath(params.multiqc_logo, checkIfExists: true) : Channel.empty() + summary_params = paramsSummaryMap( workflow, parameters_schema: "nextflow_schema.json") ch_workflow_summary = Channel.value(paramsSummaryMultiqc(summary_params)) - + ch_multiqc_files = ch_multiqc_files.mix( + ch_workflow_summary.collectFile(name: 'workflow_summary_mqc.yaml')) + ch_multiqc_custom_methods_description = params.multiqc_methods_description ? file(params.multiqc_methods_description, checkIfExists: true) : file("$projectDir/assets/methods_description_template.yml", checkIfExists: true) ch_methods_description = Channel.value( methodsDescriptionText(ch_multiqc_custom_methods_description)) - ch_multiqc_files = ch_multiqc_files.mix( - ch_workflow_summary.collectFile(name: 'workflow_summary_mqc.yaml')) ch_multiqc_files = ch_multiqc_files.mix(ch_collated_versions) ch_multiqc_files = ch_multiqc_files.mix( ch_methods_description.collectFile( @@ -83,12 +82,14 @@ workflow SPATIALVI { ch_multiqc_files.collect(), ch_multiqc_config.toList(), ch_multiqc_custom_config.toList(), - ch_multiqc_logo.toList() + ch_multiqc_logo.toList(), + [], + [] ) - emit: - multiqc_report = MULTIQC.out.report.toList() // channel: /path/to/multiqc_report.html + emit:multiqc_report = MULTIQC.out.report.toList() // channel: /path/to/multiqc_report.html versions = ch_versions // channel: [ path(versions.yml) ] + } /* From b78e03c8612b68eee951df348137f9d2db03fff8 Mon Sep 17 00:00:00 2001 From: nf-core-bot Date: Wed, 9 Oct 2024 11:07:51 +0000 Subject: [PATCH 02/18] Template update for nf-core/tools version 3.0.1 --- .editorconfig | 4 - .github/CONTRIBUTING.md | 2 +- .github/workflows/awsfulltest.yml | 6 +- .github/workflows/linting.yml | 4 +- .nf-core.yml | 2 +- .prettierignore | 1 - docs/output.md | 1 - modules.json | 6 +- modules/nf-core/multiqc/environment.yml | 2 +- modules/nf-core/multiqc/main.nf | 4 +- .../nf-core/multiqc/tests/main.nf.test.snap | 26 +- nextflow.config | 8 +- .../utils_nfcore_spatialvi_pipeline/main.nf | 12 +- .../nf-core/utils_nextflow_pipeline/main.nf | 46 ++- .../nf-core/utils_nfcore_pipeline/main.nf | 279 ++++++++++-------- 15 files changed, 209 insertions(+), 194 deletions(-) diff --git a/.editorconfig b/.editorconfig index e105881..72dda28 100644 --- a/.editorconfig +++ b/.editorconfig @@ -11,7 +11,6 @@ indent_style = space [*.{md,yml,yaml,html,css,scss,js}] indent_size = 2 - # These files are edited and tested upstream in nf-core/modules [/modules/nf-core/**] charset = unset @@ -26,12 +25,9 @@ insert_final_newline = unset trim_trailing_whitespace = unset indent_style = unset - - [/assets/email*] indent_size = unset - # ignore python and markdown [*.{py,md}] indent_style = unset diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 8a7f1af..301e13e 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -90,7 +90,7 @@ Once there, use `nf-core pipelines schema build` to add to `nextflow_schema.json ### Default processes resource requirements -Sensible defaults for process resource requirements (CPUs / memory / time) for a process should be defined in `conf/base.config`. These should generally be specified generic with `withLabel:` selectors so they can be shared across multiple processes/steps of the pipeline. A nf-core standard set of labels that should be followed where possible can be seen in the [nf-core pipeline template](https://github.com/nf-core/tools/blob/master/nf_core/pipeline-template/conf/base.config), which has the default process as a single core-process, and then different levels of multi-core configurations for increasingly large memory requirements defined with standardised labels. +Sensible defaults for process resource requirements (CPUs / memory / time) for a process should be defined in `conf/base.config`. These should generally be specified generic with `withLabel:` selectors so they can be shared across multiple processes/steps of the pipeline. A nf-core standard set of labels that should be followed where possible can be seen in the [nf-core pipeline template](https://github.com/nf-core/tools/blob/main/nf_core/pipeline-template/conf/base.config), which has the default process as a single core-process, and then different levels of multi-core configurations for increasingly large memory requirements defined with standardised labels. The process resources can be passed on to the tool dynamically within the process with the `${task.cpus}` and `${task.memory}` variables in the `script:` block. diff --git a/.github/workflows/awsfulltest.yml b/.github/workflows/awsfulltest.yml index 0b454f4..5f14fd5 100644 --- a/.github/workflows/awsfulltest.yml +++ b/.github/workflows/awsfulltest.yml @@ -14,16 +14,18 @@ on: jobs: run-platform: name: Run AWS full tests - if: github.repository == 'nf-core/spatialvi' && github.event.review.state == 'approved' + # run only if the PR is approved by at least 2 reviewers and against the master branch or manually triggered + if: github.repository == 'nf-core/spatialvi' && github.event.review.state == 'approved' && github.event.pull_request.base.ref == 'master' || github.event_name == 'workflow_dispatch' runs-on: ubuntu-latest steps: - uses: octokit/request-action@v2.x id: check_approvals with: - route: GET /repos/${{ github.repository }}/pulls/${{ github.event.review.number }}/reviews + route: GET /repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/reviews env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - id: test_variables + if: github.event_name != 'workflow_dispatch' run: | JSON_RESPONSE='${{ steps.check_approvals.outputs.data }}' CURRENT_APPROVALS_COUNT=$(echo $JSON_RESPONSE | jq -c '[.[] | select(.state | contains("APPROVED")) ] | length') diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml index b882838..a502573 100644 --- a/.github/workflows/linting.yml +++ b/.github/workflows/linting.yml @@ -42,10 +42,10 @@ jobs: architecture: "x64" - name: read .nf-core.yml - uses: pietrobolcato/action-read-yaml@1.0.0 + uses: pietrobolcato/action-read-yaml@1.1.0 id: read_yml with: - config: ${{ github.workspace }}/.nf-core.yaml + config: ${{ github.workspace }}/.nf-core.yml - name: Install dependencies run: | diff --git a/.nf-core.yml b/.nf-core.yml index 4a8be1a..ade8590 100644 --- a/.nf-core.yml +++ b/.nf-core.yml @@ -8,7 +8,7 @@ lint: - assets/nf-core-spatialvi_logo_light.png - docs/images/nf-core-spatialvi_logo_light.png - docs/images/nf-core-spatialvi_logo_dark.png -nf_core_version: 3.0.0 +nf_core_version: 3.0.1 org_path: null repository_type: pipeline template: diff --git a/.prettierignore b/.prettierignore index 610e506..437d763 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,4 +1,3 @@ - email_template.html adaptivecard.json slackreport.json diff --git a/docs/output.md b/docs/output.md index d293978..e456810 100644 --- a/docs/output.md +++ b/docs/output.md @@ -14,7 +14,6 @@ The pipeline is built using [Nextflow](https://www.nextflow.io/) and processes d - [FastQC](#fastqc) - Raw read QC - [MultiQC](#multiqc) - Aggregate report describing results and QC from the whole pipeline - - [Pipeline information](#pipeline-information) - Report metrics generated during the workflow execution ### FastQC diff --git a/modules.json b/modules.json index e4928fd..d0a89b1 100644 --- a/modules.json +++ b/modules.json @@ -12,7 +12,7 @@ }, "multiqc": { "branch": "master", - "git_sha": "666652151335353eef2fcd58880bcef5bc2928e1", + "git_sha": "b8d36829fa84b6e404364abff787e8b07f6d058c", "installed_by": ["modules"] } } @@ -21,12 +21,12 @@ "nf-core": { "utils_nextflow_pipeline": { "branch": "master", - "git_sha": "d20fb2a9cc3e2835e9d067d1046a63252eb17352", + "git_sha": "9d05360da397692321d377b6102d2fb22507c6ef", "installed_by": ["subworkflows"] }, "utils_nfcore_pipeline": { "branch": "master", - "git_sha": "2fdce49d30c0254f76bc0f13c55c17455c1251ab", + "git_sha": "772684d9d66f37b650c8ba5146ac1ee3ecba2acb", "installed_by": ["subworkflows"] }, "utils_nfschema_plugin": { diff --git a/modules/nf-core/multiqc/environment.yml b/modules/nf-core/multiqc/environment.yml index f1cd99b..6f5b867 100644 --- a/modules/nf-core/multiqc/environment.yml +++ b/modules/nf-core/multiqc/environment.yml @@ -2,4 +2,4 @@ channels: - conda-forge - bioconda dependencies: - - bioconda::multiqc=1.24.1 + - bioconda::multiqc=1.25.1 diff --git a/modules/nf-core/multiqc/main.nf b/modules/nf-core/multiqc/main.nf index b9ccebd..9724d2f 100644 --- a/modules/nf-core/multiqc/main.nf +++ b/modules/nf-core/multiqc/main.nf @@ -3,8 +3,8 @@ process MULTIQC { conda "${moduleDir}/environment.yml" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/multiqc:1.25--pyhdfd78af_0' : - 'biocontainers/multiqc:1.25--pyhdfd78af_0' }" + 'https://depot.galaxyproject.org/singularity/multiqc:1.25.1--pyhdfd78af_0' : + 'biocontainers/multiqc:1.25.1--pyhdfd78af_0' }" input: path multiqc_files, stageAs: "?/*" diff --git a/modules/nf-core/multiqc/tests/main.nf.test.snap b/modules/nf-core/multiqc/tests/main.nf.test.snap index b779e46..2fcbb5f 100644 --- a/modules/nf-core/multiqc/tests/main.nf.test.snap +++ b/modules/nf-core/multiqc/tests/main.nf.test.snap @@ -2,14 +2,14 @@ "multiqc_versions_single": { "content": [ [ - "versions.yml:md5,8c8724363a5efe0c6f43ab34faa57efd" + "versions.yml:md5,41f391dcedce7f93ca188f3a3ffa0916" ] ], "meta": { - "nf-test": "0.8.4", - "nextflow": "24.04.2" + "nf-test": "0.9.0", + "nextflow": "24.04.4" }, - "timestamp": "2024-07-10T12:41:34.562023" + "timestamp": "2024-10-02T17:51:46.317523" }, "multiqc_stub": { "content": [ @@ -17,25 +17,25 @@ "multiqc_report.html", "multiqc_data", "multiqc_plots", - "versions.yml:md5,8c8724363a5efe0c6f43ab34faa57efd" + "versions.yml:md5,41f391dcedce7f93ca188f3a3ffa0916" ] ], "meta": { - "nf-test": "0.8.4", - "nextflow": "24.04.2" + "nf-test": "0.9.0", + "nextflow": "24.04.4" }, - "timestamp": "2024-07-10T11:27:11.933869532" + "timestamp": "2024-10-02T17:52:20.680978" }, "multiqc_versions_config": { "content": [ [ - "versions.yml:md5,8c8724363a5efe0c6f43ab34faa57efd" + "versions.yml:md5,41f391dcedce7f93ca188f3a3ffa0916" ] ], "meta": { - "nf-test": "0.8.4", - "nextflow": "24.04.2" + "nf-test": "0.9.0", + "nextflow": "24.04.4" }, - "timestamp": "2024-07-10T11:26:56.709849369" + "timestamp": "2024-10-02T17:52:09.185842" } -} +} \ No newline at end of file diff --git a/nextflow.config b/nextflow.config index 90de03a..b9fc66d 100644 --- a/nextflow.config +++ b/nextflow.config @@ -12,10 +12,12 @@ params { // TODO nf-core: Specify your pipeline's command line flags // Input options input = null + // References genome = null igenomes_base = 's3://ngi-igenomes/igenomes/' igenomes_ignore = false + // MultiQC options multiqc_config = null multiqc_title = null @@ -36,6 +38,7 @@ params { show_hidden = false version = false pipelines_testdata_base_path = 'https://raw.githubusercontent.com/nf-core/test-datasets/' + // Config options config_profile_name = null config_profile_description = null @@ -44,9 +47,9 @@ params { custom_config_base = "https://raw.githubusercontent.com/nf-core/configs/${params.custom_config_version}" config_profile_contact = null config_profile_url = null + // Schema validation default options validate_params = true - } // Load base.config by default for all pipelines @@ -161,6 +164,7 @@ includeConfig !System.getenv('NXF_OFFLINE') && params.custom_config_base ? "${pa // Load nf-core/spatialvi custom profiles from different institutions. // TODO nf-core: Optionally, you can add a pipeline-specific nf-core config at https://github.com/nf-core/configs // includeConfig !System.getenv('NXF_OFFLINE') && params.custom_config_base ? "${params.custom_config_base}/pipeline/spatialvi.config" : "/dev/null" + // Set default registry for Apptainer, Docker, Podman, Charliecloud and Singularity independent of -profile // Will not be used unless Apptainer / Docker / Podman / Charliecloud / Singularity are enabled // Set to your registry if you have a mirror of containers @@ -172,6 +176,7 @@ charliecloud.registry = 'quay.io' // Load igenomes.config if required includeConfig !params.igenomes_ignore ? 'conf/igenomes.config' : 'conf/igenomes_ignored.config' + // Export these variables to prevent local Python/R libraries from conflicting with those in the container // The JULIA depot path has been adjusted to a fixed path `/usr/local/share/julia` that needs to be used for packages in the container. // See https://apeltzer.github.io/post/03-julia-lang-nextflow/ for details on that. Once we have a common agreement on where to keep Julia packages, this is adjustable. @@ -263,4 +268,3 @@ validation { // Load modules.config for DSL2 module specific options includeConfig 'conf/modules.config' - diff --git a/subworkflows/local/utils_nfcore_spatialvi_pipeline/main.nf b/subworkflows/local/utils_nfcore_spatialvi_pipeline/main.nf index 6a59d26..9cf8934 100644 --- a/subworkflows/local/utils_nfcore_spatialvi_pipeline/main.nf +++ b/subworkflows/local/utils_nfcore_spatialvi_pipeline/main.nf @@ -18,9 +18,9 @@ include { UTILS_NFCORE_PIPELINE } from '../../nf-core/utils_nfcore_pipeline' include { UTILS_NEXTFLOW_PIPELINE } from '../../nf-core/utils_nextflow_pipeline' /* -======================================================================================== +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ SUBWORKFLOW TO INITIALISE PIPELINE -======================================================================================== +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ workflow PIPELINE_INITIALISATION { @@ -99,9 +99,9 @@ workflow PIPELINE_INITIALISATION { } /* -======================================================================================== +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ SUBWORKFLOW FOR PIPELINE COMPLETION -======================================================================================== +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ workflow PIPELINE_COMPLETION { @@ -147,9 +147,9 @@ workflow PIPELINE_COMPLETION { } /* -======================================================================================== +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ FUNCTIONS -======================================================================================== +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ // // Check and validate pipeline parameters diff --git a/subworkflows/nf-core/utils_nextflow_pipeline/main.nf b/subworkflows/nf-core/utils_nextflow_pipeline/main.nf index 28e32b2..2b0dc67 100644 --- a/subworkflows/nf-core/utils_nextflow_pipeline/main.nf +++ b/subworkflows/nf-core/utils_nextflow_pipeline/main.nf @@ -3,13 +3,12 @@ // /* -======================================================================================== +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ SUBWORKFLOW DEFINITION -======================================================================================== +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ workflow UTILS_NEXTFLOW_PIPELINE { - take: print_version // boolean: print version dump_parameters // boolean: dump parameters @@ -22,7 +21,7 @@ workflow UTILS_NEXTFLOW_PIPELINE { // Print workflow version and exit on --version // if (print_version) { - log.info "${workflow.manifest.name} ${getWorkflowVersion()}" + log.info("${workflow.manifest.name} ${getWorkflowVersion()}") System.exit(0) } @@ -45,9 +44,9 @@ workflow UTILS_NEXTFLOW_PIPELINE { } /* -======================================================================================== +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ FUNCTIONS -======================================================================================== +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ // @@ -72,11 +71,11 @@ def getWorkflowVersion() { // Dump pipeline parameters to a JSON file // def dumpParametersToJSON(outdir) { - def timestamp = new java.util.Date().format( 'yyyy-MM-dd_HH-mm-ss') - def filename = "params_${timestamp}.json" - def temp_pf = new File(workflow.launchDir.toString(), ".${filename}") - def jsonStr = groovy.json.JsonOutput.toJson(params) - temp_pf.text = groovy.json.JsonOutput.prettyPrint(jsonStr) + def timestamp = new java.util.Date().format('yyyy-MM-dd_HH-mm-ss') + def filename = "params_${timestamp}.json" + def temp_pf = new File(workflow.launchDir.toString(), ".${filename}") + def jsonStr = groovy.json.JsonOutput.toJson(params) + temp_pf.text = groovy.json.JsonOutput.prettyPrint(jsonStr) nextflow.extension.FilesEx.copyTo(temp_pf.toPath(), "${outdir}/pipeline_info/params_${timestamp}.json") temp_pf.delete() @@ -91,9 +90,14 @@ def checkCondaChannels() { try { def config = parser.load("conda config --show channels".execute().text) channels = config.channels - } catch(NullPointerException | IOException e) { - log.warn "Could not verify conda channel configuration." - return + } + catch (NullPointerException e) { + log.warn("Could not verify conda channel configuration.") + return null + } + catch (IOException e) { + log.warn("Could not verify conda channel configuration.") + return null } // Check that all channels are present @@ -106,19 +110,13 @@ def checkCondaChannels() { required_channels_in_order.eachWithIndex { channel, index -> if (index < required_channels_in_order.size() - 1) { - channel_priority_violation |= !(channels.indexOf(channel) < channels.indexOf(required_channels_in_order[index+1])) + channel_priority_violation |= !(channels.indexOf(channel) < channels.indexOf(required_channels_in_order[index + 1])) } } if (channels_missing | channel_priority_violation) { - log.warn "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" + - " There is a problem with your Conda configuration!\n\n" + - " You will need to set-up the conda-forge and bioconda channels correctly.\n" + - " Please refer to https://bioconda.github.io/\n" + - " The observed channel order is \n" + - " ${channels}\n" + - " but the following channel order is required:\n" + - " ${required_channels_in_order}\n" + - "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" + log.warn( + "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" + " There is a problem with your Conda configuration!\n\n" + " You will need to set-up the conda-forge and bioconda channels correctly.\n" + " Please refer to https://bioconda.github.io/\n" + " The observed channel order is \n" + " ${channels}\n" + " but the following channel order is required:\n" + " ${required_channels_in_order}\n" + "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" + ) } } diff --git a/subworkflows/nf-core/utils_nfcore_pipeline/main.nf b/subworkflows/nf-core/utils_nfcore_pipeline/main.nf index cbd8495..b78273c 100644 --- a/subworkflows/nf-core/utils_nfcore_pipeline/main.nf +++ b/subworkflows/nf-core/utils_nfcore_pipeline/main.nf @@ -3,13 +3,12 @@ // /* -======================================================================================== +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ SUBWORKFLOW DEFINITION -======================================================================================== +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ workflow UTILS_NFCORE_PIPELINE { - take: nextflow_cli_args @@ -22,9 +21,9 @@ workflow UTILS_NFCORE_PIPELINE { } /* -======================================================================================== +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ FUNCTIONS -======================================================================================== +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ // @@ -33,12 +32,9 @@ workflow UTILS_NFCORE_PIPELINE { def checkConfigProvided() { def valid_config = true as Boolean if (workflow.profile == 'standard' && workflow.configFiles.size() <= 1) { - log.warn "[$workflow.manifest.name] You are attempting to run the pipeline without any custom configuration!\n\n" + - "This will be dependent on your local compute environment but can be achieved via one or more of the following:\n" + - " (1) Using an existing pipeline profile e.g. `-profile docker` or `-profile singularity`\n" + - " (2) Using an existing nf-core/configs for your Institution e.g. `-profile crick` or `-profile uppmax`\n" + - " (3) Using your own local custom config e.g. `-c /path/to/your/custom.config`\n\n" + - "Please refer to the quick start section and usage docs for the pipeline.\n " + log.warn( + "[${workflow.manifest.name}] You are attempting to run the pipeline without any custom configuration!\n\n" + "This will be dependent on your local compute environment but can be achieved via one or more of the following:\n" + " (1) Using an existing pipeline profile e.g. `-profile docker` or `-profile singularity`\n" + " (2) Using an existing nf-core/configs for your Institution e.g. `-profile crick` or `-profile uppmax`\n" + " (3) Using your own local custom config e.g. `-c /path/to/your/custom.config`\n\n" + "Please refer to the quick start section and usage docs for the pipeline.\n " + ) valid_config = false } return valid_config @@ -49,12 +45,14 @@ def checkConfigProvided() { // def checkProfileProvided(nextflow_cli_args) { if (workflow.profile.endsWith(',')) { - error "The `-profile` option cannot end with a trailing comma, please remove it and re-run the pipeline!\n" + - "HINT: A common mistake is to provide multiple values separated by spaces e.g. `-profile test, docker`.\n" + error( + "The `-profile` option cannot end with a trailing comma, please remove it and re-run the pipeline!\n" + "HINT: A common mistake is to provide multiple values separated by spaces e.g. `-profile test, docker`.\n" + ) } if (nextflow_cli_args[0]) { - log.warn "nf-core pipelines do not accept positional arguments. The positional argument `${nextflow_cli_args[0]}` has been detected.\n" + - "HINT: A common mistake is to provide multiple values separated by spaces e.g. `-profile test, docker`.\n" + log.warn( + "nf-core pipelines do not accept positional arguments. The positional argument `${nextflow_cli_args[0]}` has been detected.\n" + "HINT: A common mistake is to provide multiple values separated by spaces e.g. `-profile test, docker`.\n" + ) } } @@ -70,13 +68,7 @@ def workflowCitation() { manifest_doi.each { doi_ref -> temp_doi_ref += " https://doi.org/${doi_ref.replace('https://doi.org/', '').replace(' ', '')}\n" } - return "If you use ${workflow.manifest.name} for your analysis please cite:\n\n" + - "* The pipeline\n" + - temp_doi_ref + "\n" + - "* The nf-core framework\n" + - " https://doi.org/10.1038/s41587-020-0439-x\n\n" + - "* Software dependencies\n" + - " https://github.com/${workflow.manifest.name}/blob/master/CITATIONS.md" + return "If you use ${workflow.manifest.name} for your analysis please cite:\n\n" + "* The pipeline\n" + temp_doi_ref + "\n" + "* The nf-core framework\n" + " https://doi.org/10.1038/s41587-020-0439-x\n\n" + "* Software dependencies\n" + " https://github.com/${workflow.manifest.name}/blob/master/CITATIONS.md" } // @@ -102,7 +94,7 @@ def getWorkflowVersion() { // def processVersionsFromYAML(yaml_file) { def yaml = new org.yaml.snakeyaml.Yaml() - def versions = yaml.load(yaml_file).collectEntries { k, v -> [ k.tokenize(':')[-1], v ] } + def versions = yaml.load(yaml_file).collectEntries { k, v -> [k.tokenize(':')[-1], v] } return yaml.dumpAsMap(versions).trim() } @@ -112,8 +104,8 @@ def processVersionsFromYAML(yaml_file) { def workflowVersionToYAML() { return """ Workflow: - $workflow.manifest.name: ${getWorkflowVersion()} - Nextflow: $workflow.nextflow.version + ${workflow.manifest.name}: ${getWorkflowVersion()} + Nextflow: ${workflow.nextflow.version} """.stripIndent().trim() } @@ -121,11 +113,7 @@ def workflowVersionToYAML() { // Get channel of software versions used in pipeline in YAML format // def softwareVersionsToYAML(ch_versions) { - return ch_versions - .unique() - .map { version -> processVersionsFromYAML(version) } - .unique() - .mix(Channel.of(workflowVersionToYAML())) + return ch_versions.unique().map { version -> processVersionsFromYAML(version) }.unique().mix(Channel.of(workflowVersionToYAML())) } // @@ -133,25 +121,31 @@ def softwareVersionsToYAML(ch_versions) { // def paramsSummaryMultiqc(summary_params) { def summary_section = '' - summary_params.keySet().each { group -> - def group_params = summary_params.get(group) // This gets the parameters of that particular group - if (group_params) { - summary_section += "

    $group

    \n" - summary_section += "
    \n" - group_params.keySet().sort().each { param -> - summary_section += "
    $param
    ${group_params.get(param) ?: 'N/A'}
    \n" + summary_params + .keySet() + .each { group -> + def group_params = summary_params.get(group) + // This gets the parameters of that particular group + if (group_params) { + summary_section += "

    ${group}

    \n" + summary_section += "
    \n" + group_params + .keySet() + .sort() + .each { param -> + summary_section += "
    ${param}
    ${group_params.get(param) ?: 'N/A'}
    \n" + } + summary_section += "
    \n" } - summary_section += "
    \n" } - } - def yaml_file_text = "id: '${workflow.manifest.name.replace('/','-')}-summary'\n" as String - yaml_file_text += "description: ' - this information is collected when the pipeline is started.'\n" - yaml_file_text += "section_name: '${workflow.manifest.name} Workflow Summary'\n" - yaml_file_text += "section_href: 'https://github.com/${workflow.manifest.name}'\n" - yaml_file_text += "plot_type: 'html'\n" - yaml_file_text += "data: |\n" - yaml_file_text += "${summary_section}" + def yaml_file_text = "id: '${workflow.manifest.name.replace('/', '-')}-summary'\n" as String + yaml_file_text += "description: ' - this information is collected when the pipeline is started.'\n" + yaml_file_text += "section_name: '${workflow.manifest.name} Workflow Summary'\n" + yaml_file_text += "section_href: 'https://github.com/${workflow.manifest.name}'\n" + yaml_file_text += "plot_type: 'html'\n" + yaml_file_text += "data: |\n" + yaml_file_text += "${summary_section}" return yaml_file_text } @@ -199,54 +193,54 @@ def logColours(monochrome_logs=true) { colorcodes['hidden'] = monochrome_logs ? '' : "\033[8m" // Regular Colors - colorcodes['black'] = monochrome_logs ? '' : "\033[0;30m" - colorcodes['red'] = monochrome_logs ? '' : "\033[0;31m" - colorcodes['green'] = monochrome_logs ? '' : "\033[0;32m" - colorcodes['yellow'] = monochrome_logs ? '' : "\033[0;33m" - colorcodes['blue'] = monochrome_logs ? '' : "\033[0;34m" - colorcodes['purple'] = monochrome_logs ? '' : "\033[0;35m" - colorcodes['cyan'] = monochrome_logs ? '' : "\033[0;36m" - colorcodes['white'] = monochrome_logs ? '' : "\033[0;37m" + colorcodes['black'] = monochrome_logs ? '' : "\033[0;30m" + colorcodes['red'] = monochrome_logs ? '' : "\033[0;31m" + colorcodes['green'] = monochrome_logs ? '' : "\033[0;32m" + colorcodes['yellow'] = monochrome_logs ? '' : "\033[0;33m" + colorcodes['blue'] = monochrome_logs ? '' : "\033[0;34m" + colorcodes['purple'] = monochrome_logs ? '' : "\033[0;35m" + colorcodes['cyan'] = monochrome_logs ? '' : "\033[0;36m" + colorcodes['white'] = monochrome_logs ? '' : "\033[0;37m" // Bold - colorcodes['bblack'] = monochrome_logs ? '' : "\033[1;30m" - colorcodes['bred'] = monochrome_logs ? '' : "\033[1;31m" - colorcodes['bgreen'] = monochrome_logs ? '' : "\033[1;32m" - colorcodes['byellow'] = monochrome_logs ? '' : "\033[1;33m" - colorcodes['bblue'] = monochrome_logs ? '' : "\033[1;34m" - colorcodes['bpurple'] = monochrome_logs ? '' : "\033[1;35m" - colorcodes['bcyan'] = monochrome_logs ? '' : "\033[1;36m" - colorcodes['bwhite'] = monochrome_logs ? '' : "\033[1;37m" + colorcodes['bblack'] = monochrome_logs ? '' : "\033[1;30m" + colorcodes['bred'] = monochrome_logs ? '' : "\033[1;31m" + colorcodes['bgreen'] = monochrome_logs ? '' : "\033[1;32m" + colorcodes['byellow'] = monochrome_logs ? '' : "\033[1;33m" + colorcodes['bblue'] = monochrome_logs ? '' : "\033[1;34m" + colorcodes['bpurple'] = monochrome_logs ? '' : "\033[1;35m" + colorcodes['bcyan'] = monochrome_logs ? '' : "\033[1;36m" + colorcodes['bwhite'] = monochrome_logs ? '' : "\033[1;37m" // Underline - colorcodes['ublack'] = monochrome_logs ? '' : "\033[4;30m" - colorcodes['ured'] = monochrome_logs ? '' : "\033[4;31m" - colorcodes['ugreen'] = monochrome_logs ? '' : "\033[4;32m" - colorcodes['uyellow'] = monochrome_logs ? '' : "\033[4;33m" - colorcodes['ublue'] = monochrome_logs ? '' : "\033[4;34m" - colorcodes['upurple'] = monochrome_logs ? '' : "\033[4;35m" - colorcodes['ucyan'] = monochrome_logs ? '' : "\033[4;36m" - colorcodes['uwhite'] = monochrome_logs ? '' : "\033[4;37m" + colorcodes['ublack'] = monochrome_logs ? '' : "\033[4;30m" + colorcodes['ured'] = monochrome_logs ? '' : "\033[4;31m" + colorcodes['ugreen'] = monochrome_logs ? '' : "\033[4;32m" + colorcodes['uyellow'] = monochrome_logs ? '' : "\033[4;33m" + colorcodes['ublue'] = monochrome_logs ? '' : "\033[4;34m" + colorcodes['upurple'] = monochrome_logs ? '' : "\033[4;35m" + colorcodes['ucyan'] = monochrome_logs ? '' : "\033[4;36m" + colorcodes['uwhite'] = monochrome_logs ? '' : "\033[4;37m" // High Intensity - colorcodes['iblack'] = monochrome_logs ? '' : "\033[0;90m" - colorcodes['ired'] = monochrome_logs ? '' : "\033[0;91m" - colorcodes['igreen'] = monochrome_logs ? '' : "\033[0;92m" - colorcodes['iyellow'] = monochrome_logs ? '' : "\033[0;93m" - colorcodes['iblue'] = monochrome_logs ? '' : "\033[0;94m" - colorcodes['ipurple'] = monochrome_logs ? '' : "\033[0;95m" - colorcodes['icyan'] = monochrome_logs ? '' : "\033[0;96m" - colorcodes['iwhite'] = monochrome_logs ? '' : "\033[0;97m" + colorcodes['iblack'] = monochrome_logs ? '' : "\033[0;90m" + colorcodes['ired'] = monochrome_logs ? '' : "\033[0;91m" + colorcodes['igreen'] = monochrome_logs ? '' : "\033[0;92m" + colorcodes['iyellow'] = monochrome_logs ? '' : "\033[0;93m" + colorcodes['iblue'] = monochrome_logs ? '' : "\033[0;94m" + colorcodes['ipurple'] = monochrome_logs ? '' : "\033[0;95m" + colorcodes['icyan'] = monochrome_logs ? '' : "\033[0;96m" + colorcodes['iwhite'] = monochrome_logs ? '' : "\033[0;97m" // Bold High Intensity - colorcodes['biblack'] = monochrome_logs ? '' : "\033[1;90m" - colorcodes['bired'] = monochrome_logs ? '' : "\033[1;91m" - colorcodes['bigreen'] = monochrome_logs ? '' : "\033[1;92m" - colorcodes['biyellow'] = monochrome_logs ? '' : "\033[1;93m" - colorcodes['biblue'] = monochrome_logs ? '' : "\033[1;94m" - colorcodes['bipurple'] = monochrome_logs ? '' : "\033[1;95m" - colorcodes['bicyan'] = monochrome_logs ? '' : "\033[1;96m" - colorcodes['biwhite'] = monochrome_logs ? '' : "\033[1;97m" + colorcodes['biblack'] = monochrome_logs ? '' : "\033[1;90m" + colorcodes['bired'] = monochrome_logs ? '' : "\033[1;91m" + colorcodes['bigreen'] = monochrome_logs ? '' : "\033[1;92m" + colorcodes['biyellow'] = monochrome_logs ? '' : "\033[1;93m" + colorcodes['biblue'] = monochrome_logs ? '' : "\033[1;94m" + colorcodes['bipurple'] = monochrome_logs ? '' : "\033[1;95m" + colorcodes['bicyan'] = monochrome_logs ? '' : "\033[1;96m" + colorcodes['biwhite'] = monochrome_logs ? '' : "\033[1;97m" return colorcodes } @@ -261,14 +255,15 @@ def attachMultiqcReport(multiqc_report) { mqc_report = multiqc_report.getVal() if (mqc_report.getClass() == ArrayList && mqc_report.size() >= 1) { if (mqc_report.size() > 1) { - log.warn "[$workflow.manifest.name] Found multiple reports from process 'MULTIQC', will use only one" + log.warn("[${workflow.manifest.name}] Found multiple reports from process 'MULTIQC', will use only one") } mqc_report = mqc_report[0] } } - } catch (all) { + } + catch (Exception all) { if (multiqc_report) { - log.warn "[$workflow.manifest.name] Could not attach MultiQC report to summary email" + log.warn("[${workflow.manifest.name}] Could not attach MultiQC report to summary email") } } return mqc_report @@ -280,26 +275,35 @@ def attachMultiqcReport(multiqc_report) { def completionEmail(summary_params, email, email_on_fail, plaintext_email, outdir, monochrome_logs=true, multiqc_report=null) { // Set up the e-mail variables - def subject = "[$workflow.manifest.name] Successful: $workflow.runName" + def subject = "[${workflow.manifest.name}] Successful: ${workflow.runName}" if (!workflow.success) { - subject = "[$workflow.manifest.name] FAILED: $workflow.runName" + subject = "[${workflow.manifest.name}] FAILED: ${workflow.runName}" } def summary = [:] - summary_params.keySet().sort().each { group -> - summary << summary_params[group] - } + summary_params + .keySet() + .sort() + .each { group -> + summary << summary_params[group] + } def misc_fields = [:] misc_fields['Date Started'] = workflow.start misc_fields['Date Completed'] = workflow.complete misc_fields['Pipeline script file path'] = workflow.scriptFile misc_fields['Pipeline script hash ID'] = workflow.scriptId - if (workflow.repository) misc_fields['Pipeline repository Git URL'] = workflow.repository - if (workflow.commitId) misc_fields['Pipeline repository Git Commit'] = workflow.commitId - if (workflow.revision) misc_fields['Pipeline Git branch/tag'] = workflow.revision - misc_fields['Nextflow Version'] = workflow.nextflow.version - misc_fields['Nextflow Build'] = workflow.nextflow.build + if (workflow.repository) { + misc_fields['Pipeline repository Git URL'] = workflow.repository + } + if (workflow.commitId) { + misc_fields['Pipeline repository Git Commit'] = workflow.commitId + } + if (workflow.revision) { + misc_fields['Pipeline Git branch/tag'] = workflow.revision + } + misc_fields['Nextflow Version'] = workflow.nextflow.version + misc_fields['Nextflow Build'] = workflow.nextflow.build misc_fields['Nextflow Compile Timestamp'] = workflow.nextflow.timestamp def email_fields = [:] @@ -337,7 +341,7 @@ def completionEmail(summary_params, email, email_on_fail, plaintext_email, outdi // Render the sendmail template def max_multiqc_email_size = (params.containsKey('max_multiqc_email_size') ? params.max_multiqc_email_size : 0) as nextflow.util.MemoryUnit - def smail_fields = [ email: email_address, subject: subject, email_txt: email_txt, email_html: email_html, projectDir: "${workflow.projectDir}", mqcFile: mqc_report, mqcMaxSize: max_multiqc_email_size.toBytes() ] + def smail_fields = [email: email_address, subject: subject, email_txt: email_txt, email_html: email_html, projectDir: "${workflow.projectDir}", mqcFile: mqc_report, mqcMaxSize: max_multiqc_email_size.toBytes()] def sf = new File("${workflow.projectDir}/assets/sendmail_template.txt") def sendmail_template = engine.createTemplate(sf).make(smail_fields) def sendmail_html = sendmail_template.toString() @@ -346,30 +350,32 @@ def completionEmail(summary_params, email, email_on_fail, plaintext_email, outdi def colors = logColours(monochrome_logs) as Map if (email_address) { try { - if (plaintext_email) { throw new org.codehaus.groovy.GroovyException('Send plaintext e-mail, not HTML') } + if (plaintext_email) { +new org.codehaus.groovy.GroovyException('Send plaintext e-mail, not HTML') } // Try to send HTML e-mail using sendmail def sendmail_tf = new File(workflow.launchDir.toString(), ".sendmail_tmp.html") sendmail_tf.withWriter { w -> w << sendmail_html } - [ 'sendmail', '-t' ].execute() << sendmail_html - log.info "-${colors.purple}[$workflow.manifest.name]${colors.green} Sent summary e-mail to $email_address (sendmail)-" - } catch (all) { + ['sendmail', '-t'].execute() << sendmail_html + log.info("-${colors.purple}[${workflow.manifest.name}]${colors.green} Sent summary e-mail to ${email_address} (sendmail)-") + } + catch (Exception all) { // Catch failures and try with plaintext - def mail_cmd = [ 'mail', '-s', subject, '--content-type=text/html', email_address ] + def mail_cmd = ['mail', '-s', subject, '--content-type=text/html', email_address] mail_cmd.execute() << email_html - log.info "-${colors.purple}[$workflow.manifest.name]${colors.green} Sent summary e-mail to $email_address (mail)-" + log.info("-${colors.purple}[${workflow.manifest.name}]${colors.green} Sent summary e-mail to ${email_address} (mail)-") } } // Write summary e-mail HTML to a file def output_hf = new File(workflow.launchDir.toString(), ".pipeline_report.html") output_hf.withWriter { w -> w << email_html } - nextflow.extension.FilesEx.copyTo(output_hf.toPath(), "${outdir}/pipeline_info/pipeline_report.html"); + nextflow.extension.FilesEx.copyTo(output_hf.toPath(), "${outdir}/pipeline_info/pipeline_report.html") output_hf.delete() // Write summary e-mail TXT to a file def output_tf = new File(workflow.launchDir.toString(), ".pipeline_report.txt") output_tf.withWriter { w -> w << email_txt } - nextflow.extension.FilesEx.copyTo(output_tf.toPath(), "${outdir}/pipeline_info/pipeline_report.txt"); + nextflow.extension.FilesEx.copyTo(output_tf.toPath(), "${outdir}/pipeline_info/pipeline_report.txt") output_tf.delete() } @@ -380,12 +386,14 @@ def completionSummary(monochrome_logs=true) { def colors = logColours(monochrome_logs) as Map if (workflow.success) { if (workflow.stats.ignoredCount == 0) { - log.info "-${colors.purple}[$workflow.manifest.name]${colors.green} Pipeline completed successfully${colors.reset}-" - } else { - log.info "-${colors.purple}[$workflow.manifest.name]${colors.yellow} Pipeline completed successfully, but with errored process(es) ${colors.reset}-" + log.info("-${colors.purple}[${workflow.manifest.name}]${colors.green} Pipeline completed successfully${colors.reset}-") + } + else { + log.info("-${colors.purple}[${workflow.manifest.name}]${colors.yellow} Pipeline completed successfully, but with errored process(es) ${colors.reset}-") } - } else { - log.info "-${colors.purple}[$workflow.manifest.name]${colors.red} Pipeline completed with errors${colors.reset}-" + } + else { + log.info("-${colors.purple}[${workflow.manifest.name}]${colors.red} Pipeline completed with errors${colors.reset}-") } } @@ -394,21 +402,30 @@ def completionSummary(monochrome_logs=true) { // def imNotification(summary_params, hook_url) { def summary = [:] - summary_params.keySet().sort().each { group -> - summary << summary_params[group] - } + summary_params + .keySet() + .sort() + .each { group -> + summary << summary_params[group] + } def misc_fields = [:] - misc_fields['start'] = workflow.start - misc_fields['complete'] = workflow.complete - misc_fields['scriptfile'] = workflow.scriptFile - misc_fields['scriptid'] = workflow.scriptId - if (workflow.repository) misc_fields['repository'] = workflow.repository - if (workflow.commitId) misc_fields['commitid'] = workflow.commitId - if (workflow.revision) misc_fields['revision'] = workflow.revision - misc_fields['nxf_version'] = workflow.nextflow.version - misc_fields['nxf_build'] = workflow.nextflow.build - misc_fields['nxf_timestamp'] = workflow.nextflow.timestamp + misc_fields['start'] = workflow.start + misc_fields['complete'] = workflow.complete + misc_fields['scriptfile'] = workflow.scriptFile + misc_fields['scriptid'] = workflow.scriptId + if (workflow.repository) { + misc_fields['repository'] = workflow.repository + } + if (workflow.commitId) { + misc_fields['commitid'] = workflow.commitId + } + if (workflow.revision) { + misc_fields['revision'] = workflow.revision + } + misc_fields['nxf_version'] = workflow.nextflow.version + misc_fields['nxf_build'] = workflow.nextflow.build + misc_fields['nxf_timestamp'] = workflow.nextflow.timestamp def msg_fields = [:] msg_fields['version'] = getWorkflowVersion() @@ -433,13 +450,13 @@ def imNotification(summary_params, hook_url) { def json_message = json_template.toString() // POST - def post = new URL(hook_url).openConnection(); + def post = new URL(hook_url).openConnection() post.setRequestMethod("POST") post.setDoOutput(true) post.setRequestProperty("Content-Type", "application/json") - post.getOutputStream().write(json_message.getBytes("UTF-8")); - def postRC = post.getResponseCode(); - if (! postRC.equals(200)) { - log.warn(post.getErrorStream().getText()); + post.getOutputStream().write(json_message.getBytes("UTF-8")) + def postRC = post.getResponseCode() + if (!postRC.equals(200)) { + log.warn(post.getErrorStream().getText()) } } From 91b73cc18a3c11ea1ea5cfacad9f1efb0100e2ad Mon Sep 17 00:00:00 2001 From: nf-core-bot Date: Fri, 11 Oct 2024 12:35:10 +0000 Subject: [PATCH 03/18] Template update for nf-core/tools version 3.0.2 --- .github/workflows/ci.yml | 60 +++++++++++++------ .../workflows/template_version_comment.yml | 21 ++++--- .gitignore | 1 + .nf-core.yml | 2 +- main.nf | 2 +- modules.json | 6 +- modules/nf-core/multiqc/main.nf | 2 +- nextflow.config | 4 +- .../utils_nfcore_spatialvi_pipeline/main.nf | 4 +- .../nf-core/utils_nextflow_pipeline/main.nf | 30 +++++----- .../nf-core/utils_nfcore_pipeline/main.nf | 10 ++-- workflows/spatialvi.nf | 2 - 12 files changed, 86 insertions(+), 58 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7d85dfc..505bec1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,6 +11,8 @@ on: env: NXF_ANSI_LOG: false + NXF_SINGULARITY_CACHEDIR: ${{ github.workspace }}/.singularity + NXF_SINGULARITY_LIBRARYDIR: ${{ github.workspace }}/.singularity concurrency: group: "${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}" @@ -18,7 +20,7 @@ concurrency: jobs: test: - name: Run pipeline with test data + name: "Run pipeline with test data (${{ matrix.NXF_VER }} | ${{ matrix.test_name }} | ${{ matrix.profile }})" # Only run on push if this is the nf-core dev branch (merged PRs) if: "${{ github.event_name != 'push' || (github.event_name == 'push' && github.repository == 'nf-core/spatialvi') }}" runs-on: ubuntu-latest @@ -27,33 +29,57 @@ jobs: NXF_VER: - "24.04.2" - "latest-everything" + profile: + - "conda" + - "docker" + - "singularity" + test_name: + - "test" + isMaster: + - ${{ github.base_ref == 'master' }} + # Exclude conda and singularity on dev + exclude: + - isMaster: false + profile: "conda" + - isMaster: false + profile: "singularity" steps: - name: Check out pipeline code uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4 - - name: Install Nextflow + - name: Set up Nextflow uses: nf-core/setup-nextflow@v2 with: version: "${{ matrix.NXF_VER }}" - - name: Disk space cleanup - uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be # v1.3.1 + - name: Set up Apptainer + if: matrix.profile == 'singularity' + uses: eWaterCycle/setup-apptainer@main - - name: Run pipeline with test data (docker) - # TODO nf-core: You can customise CI pipeline run tests as required - # For example: adding multiple test runs with different parameters - # Remember that you can parallelise this by using strategy.matrix + - name: Set up Singularity + if: matrix.profile == 'singularity' run: | - nextflow run ${GITHUB_WORKSPACE} -profile test,docker --outdir ./results + mkdir -p $NXF_SINGULARITY_CACHEDIR + mkdir -p $NXF_SINGULARITY_LIBRARYDIR + + - name: Set up Miniconda + if: matrix.profile == 'conda' + uses: conda-incubator/setup-miniconda@a4260408e20b96e80095f42ff7f1a15b27dd94ca # v3 + with: + miniconda-version: "latest" + auto-update-conda: true + conda-solver: libmamba + channels: conda-forge,bioconda - - name: Run pipeline with test data (singularity) - # TODO nf-core: You can customise CI pipeline run tests as required + - name: Set up Conda + if: matrix.profile == 'conda' run: | - nextflow run ${GITHUB_WORKSPACE} -profile test,singularity --outdir ./results - if: "${{ github.base_ref == 'master' }}" + echo $(realpath $CONDA)/condabin >> $GITHUB_PATH + echo $(realpath python) >> $GITHUB_PATH + + - name: Clean up Disk space + uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be # v1.3.1 - - name: Run pipeline with test data (conda) - # TODO nf-core: You can customise CI pipeline run tests as required + - name: "Run pipeline with test data ${{ matrix.NXF_VER }} | ${{ matrix.test_name }} | ${{ matrix.profile }}" run: | - nextflow run ${GITHUB_WORKSPACE} -profile test,conda --outdir ./results - if: "${{ github.base_ref == 'master' }}" + nextflow run ${GITHUB_WORKSPACE} -profile ${{ matrix.test_name }},${{ matrix.profile }} --outdir ./results diff --git a/.github/workflows/template_version_comment.yml b/.github/workflows/template_version_comment.yml index 9dea41f..e8aafe4 100644 --- a/.github/workflows/template_version_comment.yml +++ b/.github/workflows/template_version_comment.yml @@ -10,9 +10,11 @@ jobs: steps: - name: Check out pipeline code uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4 + with: + ref: ${{ github.event.pull_request.head.sha }} - name: Read template version from .nf-core.yml - uses: pietrobolcato/action-read-yaml@1.0.0 + uses: nichmor/minimal-read-yaml@v0.0.2 id: read_yml with: config: ${{ github.workspace }}/.nf-core.yml @@ -24,20 +26,21 @@ jobs: - name: Check nf-core outdated id: nf_core_outdated - run: pip list --outdated | grep nf-core + run: echo "OUTPUT=$(pip list --outdated | grep nf-core)" >> ${GITHUB_ENV} - name: Post nf-core template version comment uses: mshick/add-pr-comment@b8f338c590a895d50bcbfa6c5859251edc8952fc # v2 if: | - ${{ steps.nf_core_outdated.outputs.stdout }} =~ 'nf-core' + contains(env.OUTPUT, 'nf-core') with: repo-token: ${{ secrets.NF_CORE_BOT_AUTH_TOKEN }} allow-repeats: false message: | - ## :warning: Newer version of the nf-core template is available. - - Your pipeline is using an old version of the nf-core template: ${{ steps.read_yml.outputs['nf_core_version'] }}. - Please update your pipeline to the latest version. - - For more documentation on how to update your pipeline, please see the [nf-core documentation](https://github.com/nf-core/tools?tab=readme-ov-file#sync-a-pipeline-with-the-template) and [Synchronisation documentation](https://nf-co.re/docs/contributing/sync). + > [!WARNING] + > Newer version of the nf-core template is available. + > + > Your pipeline is using an old version of the nf-core template: ${{ steps.read_yml.outputs['nf_core_version'] }}. + > Please update your pipeline to the latest version. + > + > For more documentation on how to update your pipeline, please see the [nf-core documentation](https://github.com/nf-core/tools?tab=readme-ov-file#sync-a-pipeline-with-the-template) and [Synchronisation documentation](https://nf-co.re/docs/contributing/sync). # diff --git a/.gitignore b/.gitignore index 5124c9a..a42ce01 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ results/ testing/ testing* *.pyc +null/ diff --git a/.nf-core.yml b/.nf-core.yml index ade8590..5a2c198 100644 --- a/.nf-core.yml +++ b/.nf-core.yml @@ -8,7 +8,7 @@ lint: - assets/nf-core-spatialvi_logo_light.png - docs/images/nf-core-spatialvi_logo_light.png - docs/images/nf-core-spatialvi_logo_dark.png -nf_core_version: 3.0.1 +nf_core_version: 3.0.2 org_path: null repository_type: pipeline template: diff --git a/main.nf b/main.nf index a571159..5288a5b 100644 --- a/main.nf +++ b/main.nf @@ -76,7 +76,7 @@ workflow { params.outdir, params.input ) - + // // WORKFLOW: Run main workflow // diff --git a/modules.json b/modules.json index d0a89b1..beb3903 100644 --- a/modules.json +++ b/modules.json @@ -12,7 +12,7 @@ }, "multiqc": { "branch": "master", - "git_sha": "b8d36829fa84b6e404364abff787e8b07f6d058c", + "git_sha": "cf17ca47590cc578dfb47db1c2a44ef86f89976d", "installed_by": ["modules"] } } @@ -21,12 +21,12 @@ "nf-core": { "utils_nextflow_pipeline": { "branch": "master", - "git_sha": "9d05360da397692321d377b6102d2fb22507c6ef", + "git_sha": "3aa0aec1d52d492fe241919f0c6100ebf0074082", "installed_by": ["subworkflows"] }, "utils_nfcore_pipeline": { "branch": "master", - "git_sha": "772684d9d66f37b650c8ba5146ac1ee3ecba2acb", + "git_sha": "1b6b9a3338d011367137808b49b923515080e3ba", "installed_by": ["subworkflows"] }, "utils_nfschema_plugin": { diff --git a/modules/nf-core/multiqc/main.nf b/modules/nf-core/multiqc/main.nf index 9724d2f..cc0643e 100644 --- a/modules/nf-core/multiqc/main.nf +++ b/modules/nf-core/multiqc/main.nf @@ -52,7 +52,7 @@ process MULTIQC { stub: """ mkdir multiqc_data - touch multiqc_plots + mkdir multiqc_plots touch multiqc_report.html cat <<-END_VERSIONS > versions.yml diff --git a/nextflow.config b/nextflow.config index b9fc66d..a23a06f 100644 --- a/nextflow.config +++ b/nextflow.config @@ -254,10 +254,10 @@ validation { """ afterText = """${manifest.doi ? "* The pipeline\n" : ""}${manifest.doi.tokenize(",").collect { " https://doi.org/${it.trim().replace('https://doi.org/','')}"}.join("\n")}${manifest.doi ? "\n" : ""} * The nf-core framework - https://doi.org/10.1038/s41587-020-0439-x + https://doi.org/10.1038/s41587-020-0439-x * Software dependencies - https://github.com/${manifest.name}/blob/master/CITATIONS.md + https://github.com/${manifest.name}/blob/master/CITATIONS.md """ } summary { diff --git a/subworkflows/local/utils_nfcore_spatialvi_pipeline/main.nf b/subworkflows/local/utils_nfcore_spatialvi_pipeline/main.nf index 9cf8934..1947e0c 100644 --- a/subworkflows/local/utils_nfcore_spatialvi_pipeline/main.nf +++ b/subworkflows/local/utils_nfcore_spatialvi_pipeline/main.nf @@ -47,7 +47,6 @@ workflow PIPELINE_INITIALISATION { workflow.profile.tokenize(',').intersect(['conda', 'mamba']).size() >= 1 ) - // // Validate parameters and generate parameter summary to stdout // @@ -56,7 +55,6 @@ workflow PIPELINE_INITIALISATION { validate_params, null ) - // // Check config provided to the pipeline @@ -64,6 +62,7 @@ workflow PIPELINE_INITIALISATION { UTILS_NFCORE_PIPELINE ( nextflow_cli_args ) + // // Custom validation for pipeline parameters // @@ -110,7 +109,6 @@ workflow PIPELINE_COMPLETION { email // string: email address email_on_fail // string: email address sent on pipeline failure plaintext_email // boolean: Send plain-text email instead of HTML - outdir // path: Path to output directory where results will be published monochrome_logs // boolean: Disable ANSI colour codes in log output hook_url // string: hook URL for notifications diff --git a/subworkflows/nf-core/utils_nextflow_pipeline/main.nf b/subworkflows/nf-core/utils_nextflow_pipeline/main.nf index 2b0dc67..0fcbf7b 100644 --- a/subworkflows/nf-core/utils_nextflow_pipeline/main.nf +++ b/subworkflows/nf-core/utils_nextflow_pipeline/main.nf @@ -3,9 +3,9 @@ // /* -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ SUBWORKFLOW DEFINITION -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ workflow UTILS_NEXTFLOW_PIPELINE { @@ -44,9 +44,9 @@ workflow UTILS_NEXTFLOW_PIPELINE { } /* -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ FUNCTIONS -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ // @@ -106,17 +106,19 @@ def checkCondaChannels() { def channels_missing = ((required_channels_in_order as Set) - (channels as Set)) as Boolean // Check that they are in the right order - def channel_priority_violation = false - - required_channels_in_order.eachWithIndex { channel, index -> - if (index < required_channels_in_order.size() - 1) { - channel_priority_violation |= !(channels.indexOf(channel) < channels.indexOf(required_channels_in_order[index + 1])) - } - } + def channel_priority_violation = required_channels_in_order != channels.findAll { ch -> ch in required_channels_in_order } if (channels_missing | channel_priority_violation) { - log.warn( - "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" + " There is a problem with your Conda configuration!\n\n" + " You will need to set-up the conda-forge and bioconda channels correctly.\n" + " Please refer to https://bioconda.github.io/\n" + " The observed channel order is \n" + " ${channels}\n" + " but the following channel order is required:\n" + " ${required_channels_in_order}\n" + "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" - ) + log.warn """\ + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + There is a problem with your Conda configuration! + You will need to set-up the conda-forge and bioconda channels correctly. + Please refer to https://bioconda.github.io/ + The observed channel order is + ${channels} + but the following channel order is required: + ${required_channels_in_order} + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" + """.stripIndent(true) } } diff --git a/subworkflows/nf-core/utils_nfcore_pipeline/main.nf b/subworkflows/nf-core/utils_nfcore_pipeline/main.nf index b78273c..5cb7baf 100644 --- a/subworkflows/nf-core/utils_nfcore_pipeline/main.nf +++ b/subworkflows/nf-core/utils_nfcore_pipeline/main.nf @@ -3,9 +3,9 @@ // /* -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ SUBWORKFLOW DEFINITION -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ workflow UTILS_NFCORE_PIPELINE { @@ -21,9 +21,9 @@ workflow UTILS_NFCORE_PIPELINE { } /* -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ FUNCTIONS -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ // @@ -62,7 +62,7 @@ def checkProfileProvided(nextflow_cli_args) { def workflowCitation() { def temp_doi_ref = "" def manifest_doi = workflow.manifest.doi.tokenize(",") - // Using a loop to handle multiple DOIs + // Handling multiple DOIs // Removing `https://doi.org/` to handle pipelines using DOIs vs DOI resolvers // Removing ` ` since the manifest.doi is a string and not a proper list manifest_doi.each { doi_ref -> diff --git a/workflows/spatialvi.nf b/workflows/spatialvi.nf index 3f26926..b56e88e 100644 --- a/workflows/spatialvi.nf +++ b/workflows/spatialvi.nf @@ -57,13 +57,11 @@ workflow SPATIALVI { Channel.fromPath(params.multiqc_logo, checkIfExists: true) : Channel.empty() - summary_params = paramsSummaryMap( workflow, parameters_schema: "nextflow_schema.json") ch_workflow_summary = Channel.value(paramsSummaryMultiqc(summary_params)) ch_multiqc_files = ch_multiqc_files.mix( ch_workflow_summary.collectFile(name: 'workflow_summary_mqc.yaml')) - ch_multiqc_custom_methods_description = params.multiqc_methods_description ? file(params.multiqc_methods_description, checkIfExists: true) : file("$projectDir/assets/methods_description_template.yml", checkIfExists: true) From 40a7fc8f72127717a546e75adce8dbf7325215df Mon Sep 17 00:00:00 2001 From: Erik Fasterius Date: Tue, 22 Oct 2024 13:22:25 +0200 Subject: [PATCH 04/18] Template update for nf-core/tools version 3.0.2 --- assets/nf-core-spatialvi_logo_light.png | Bin 75825 -> 75828 bytes docs/images/nf-core-spatialvi_logo_dark.png | Bin 29006 -> 28955 bytes docs/images/nf-core-spatialvi_logo_light.png | Bin 24702 -> 24680 bytes 3 files changed, 0 insertions(+), 0 deletions(-) diff --git a/assets/nf-core-spatialvi_logo_light.png b/assets/nf-core-spatialvi_logo_light.png index 6a3e713011b5c9a2d332d85928ad6ed0abda1419..c3bb5a55e2ae7475fc1cbda82ad68887e490e47c 100644 GIT binary patch delta 24984 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 Tx8@%p-a@l?`6_Sg{_p+``6KmB delta 24981 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 diff --git a/docs/images/nf-core-spatialvi_logo_dark.png b/docs/images/nf-core-spatialvi_logo_dark.png index 095b5eb3672f08a5ff1f6437c1ef98709a3ba58c..7bdd03d3dfc131cd6f427afced45650645ff18f8 100644 GIT binary patch literal 28955 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 by%U#vZ;l1kU)}_l9zskmU4vH|I^O#q{+$Er delta 28390 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 diff --git a/docs/images/nf-core-spatialvi_logo_light.png b/docs/images/nf-core-spatialvi_logo_light.png index 2467c33aeed2706b6eba9b88d01586f0eda203cd..533c426a9c2b4b269b31289cde9ca10060cfa967 100644 GIT binary patch literal 24680 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 T5{&@=K>)&_ERA2CagF{j5Q_Wp literal 24702 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 From 93e5c793c8820a507c750de5bba8d08fd44eec83 Mon Sep 17 00:00:00 2001 From: Erik Fasterius Date: Tue, 22 Oct 2024 13:23:58 +0200 Subject: [PATCH 05/18] Skip igenomes --- .nf-core.yml | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/.nf-core.yml b/.nf-core.yml index d8c63cb..d5a560c 100644 --- a/.nf-core.yml +++ b/.nf-core.yml @@ -1,7 +1,6 @@ -repository_type: pipeline -nf_core_version: "2.14.1" +bump_version: null lint: - actions_ci: False + actions_ci: false files_exist: - conf/igenomes.config files_unchanged: @@ -9,3 +8,19 @@ lint: - assets/nf-core-spatialvi_logo_light.png - docs/images/nf-core-spatialvi_logo_light.png - docs/images/nf-core-spatialvi_logo_dark.png +nf_core_version: 3.0.2 +org_path: null +repository_type: pipeline +template: + author: Erik Fasterius, Christophe Avenel, Sergii Domanskyi, Jeffrey Chuang, Anuj + Srivastava + description: 10X Visium Spatial Transcriptomics + force: false + is_nfcore: true + name: spatialvi + org: nf-core + outdir: . + skip_features: + - igenomes + version: 1.0dev +update: null From 63a18a9ef29b5feb73cf55a5a94845f50f4a7adc Mon Sep 17 00:00:00 2001 From: Erik Fasterius Date: Tue, 22 Oct 2024 14:39:24 +0200 Subject: [PATCH 06/18] Do not lint .gitignore --- .gitignore | 1 + .nf-core.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index a42ce01..23b0c7d 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ testing/ testing* *.pyc null/ +.nf-test* diff --git a/.nf-core.yml b/.nf-core.yml index d5a560c..a46ed43 100644 --- a/.nf-core.yml +++ b/.nf-core.yml @@ -5,6 +5,7 @@ lint: - conf/igenomes.config files_unchanged: - .gitattributes + - .gitignore - assets/nf-core-spatialvi_logo_light.png - docs/images/nf-core-spatialvi_logo_light.png - docs/images/nf-core-spatialvi_logo_dark.png From ba196b2e7a9123ee9ff7340593c2260d54697503 Mon Sep 17 00:00:00 2001 From: Erik Fasterius Date: Tue, 22 Oct 2024 14:43:45 +0200 Subject: [PATCH 07/18] Update modules --- modules.json | 8 +- modules/nf-core/multiqc/main.nf | 4 +- modules/nf-core/multiqc/multiqc.diff | 17 ++- .../nf-core/multiqc/tests/main.nf.test.snap | 26 ++-- .../nf-core/quartonotebook/environment.yml | 3 - modules/nf-core/quartonotebook/meta.yml | 124 +++++++++------- .../quartonotebook/quartonotebook.diff | 10 ++ modules/nf-core/spaceranger/count/meta.yml | 132 +++++++++--------- modules/nf-core/untar/environment.yml | 2 - modules/nf-core/untar/meta.yml | 43 +++--- 10 files changed, 205 insertions(+), 164 deletions(-) diff --git a/modules.json b/modules.json index ae16e94..6d76717 100644 --- a/modules.json +++ b/modules.json @@ -12,24 +12,24 @@ }, "multiqc": { "branch": "master", - "git_sha": "19ca321db5d8bd48923262c2eca6422359633491", + "git_sha": "cf17ca47590cc578dfb47db1c2a44ef86f89976d", "installed_by": ["modules"], "patch": "modules/nf-core/multiqc/multiqc.diff" }, "quartonotebook": { "branch": "master", - "git_sha": "04e1b2340f6cf9cd7b9b06fb7d86da4531714519", + "git_sha": "666652151335353eef2fcd58880bcef5bc2928e1", "installed_by": ["modules"], "patch": "modules/nf-core/quartonotebook/quartonotebook.diff" }, "spaceranger/count": { "branch": "master", - "git_sha": "238b3efbbd113529d19ec866eba545911bc27682", + "git_sha": "666652151335353eef2fcd58880bcef5bc2928e1", "installed_by": ["modules"] }, "untar": { "branch": "master", - "git_sha": "4e5f4687318f24ba944a13609d3ea6ebd890737d", + "git_sha": "666652151335353eef2fcd58880bcef5bc2928e1", "installed_by": ["modules"] } } diff --git a/modules/nf-core/multiqc/main.nf b/modules/nf-core/multiqc/main.nf index 17d6b53..161ccbc 100644 --- a/modules/nf-core/multiqc/main.nf +++ b/modules/nf-core/multiqc/main.nf @@ -1,10 +1,10 @@ process MULTIQC { - label 'process_medium' + label 'process_single' conda "${moduleDir}/environment.yml" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? 'https://depot.galaxyproject.org/singularity/multiqc:1.25.1--pyhdfd78af_0' : - 'biocontainers/multiqc:1.25.1--pyhdfd78af_0' }" + 'docker.io/multiqc/multiqc:v1.24.1' }" input: path multiqc_files, stageAs: "?/*" diff --git a/modules/nf-core/multiqc/multiqc.diff b/modules/nf-core/multiqc/multiqc.diff index a698258..f41f4aa 100644 --- a/modules/nf-core/multiqc/multiqc.diff +++ b/modules/nf-core/multiqc/multiqc.diff @@ -1,18 +1,21 @@ Changes in module 'nf-core/multiqc' +'modules/nf-core/multiqc/environment.yml' is unchanged +'modules/nf-core/multiqc/meta.yml' is unchanged +Changes in 'multiqc/main.nf': --- modules/nf-core/multiqc/main.nf +++ modules/nf-core/multiqc/main.nf -@@ -1,10 +1,10 @@ - process MULTIQC { -- label 'process_single' -+ label 'process_medium' - +@@ -4,7 +4,7 @@ conda "${moduleDir}/environment.yml" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/multiqc:1.24.1--pyhdfd78af_0' : -- 'biocontainers/multiqc:1.24.1--pyhdfd78af_0' }" + 'https://depot.galaxyproject.org/singularity/multiqc:1.25.1--pyhdfd78af_0' : +- 'biocontainers/multiqc:1.25.1--pyhdfd78af_0' }" + 'docker.io/multiqc/multiqc:v1.24.1' }" input: path multiqc_files, stageAs: "?/*" +'modules/nf-core/multiqc/tests/main.nf.test.snap' is unchanged +'modules/nf-core/multiqc/tests/tags.yml' is unchanged +'modules/nf-core/multiqc/tests/nextflow.config' is unchanged +'modules/nf-core/multiqc/tests/main.nf.test' is unchanged ************************************************************ diff --git a/modules/nf-core/multiqc/tests/main.nf.test.snap b/modules/nf-core/multiqc/tests/main.nf.test.snap index 83fa080..2fcbb5f 100644 --- a/modules/nf-core/multiqc/tests/main.nf.test.snap +++ b/modules/nf-core/multiqc/tests/main.nf.test.snap @@ -2,14 +2,14 @@ "multiqc_versions_single": { "content": [ [ - "versions.yml:md5,6eb13f3b11bbcbfc98ad3166420ff760" + "versions.yml:md5,41f391dcedce7f93ca188f3a3ffa0916" ] ], "meta": { - "nf-test": "0.8.4", - "nextflow": "24.04.2" + "nf-test": "0.9.0", + "nextflow": "24.04.4" }, - "timestamp": "2024-07-10T12:41:34.562023" + "timestamp": "2024-10-02T17:51:46.317523" }, "multiqc_stub": { "content": [ @@ -17,25 +17,25 @@ "multiqc_report.html", "multiqc_data", "multiqc_plots", - "versions.yml:md5,6eb13f3b11bbcbfc98ad3166420ff760" + "versions.yml:md5,41f391dcedce7f93ca188f3a3ffa0916" ] ], "meta": { - "nf-test": "0.8.4", - "nextflow": "24.04.2" + "nf-test": "0.9.0", + "nextflow": "24.04.4" }, - "timestamp": "2024-07-10T11:27:11.933869532" + "timestamp": "2024-10-02T17:52:20.680978" }, "multiqc_versions_config": { "content": [ [ - "versions.yml:md5,6eb13f3b11bbcbfc98ad3166420ff760" + "versions.yml:md5,41f391dcedce7f93ca188f3a3ffa0916" ] ], "meta": { - "nf-test": "0.8.4", - "nextflow": "24.04.2" + "nf-test": "0.9.0", + "nextflow": "24.04.4" }, - "timestamp": "2024-07-10T11:26:56.709849369" + "timestamp": "2024-10-02T17:52:09.185842" } -} +} \ No newline at end of file diff --git a/modules/nf-core/quartonotebook/environment.yml b/modules/nf-core/quartonotebook/environment.yml index 1084ec0..3a48c78 100644 --- a/modules/nf-core/quartonotebook/environment.yml +++ b/modules/nf-core/quartonotebook/environment.yml @@ -1,9 +1,6 @@ -name: quartonotebook - channels: - conda-forge - bioconda - - defaults dependencies: - conda-forge::jupyter=1.0.0 diff --git a/modules/nf-core/quartonotebook/meta.yml b/modules/nf-core/quartonotebook/meta.yml index 5d95e8b..e3c3c1c 100644 --- a/modules/nf-core/quartonotebook/meta.yml +++ b/modules/nf-core/quartonotebook/meta.yml @@ -13,70 +13,96 @@ tools: documentation: https://quarto.org/docs/reference/ tool_dev_url: https://github.com/quarto-dev/quarto-cli licence: ["MIT"] + identifier: "" - papermill: description: Parameterize, execute, and analyze notebooks homepage: https://github.com/nteract/papermill documentation: http://papermill.readthedocs.io/en/latest/ tool_dev_url: https://github.com/nteract/papermill licence: ["BSD 3-clause"] + identifier: "" input: - - meta: - type: map - description: | - Groovy Map containing sample information - e.g. `[ id:'sample1', single_end:false ]`. - - notebook: - type: file - description: The Quarto notebook to be rendered. - pattern: "*.{qmd}" - - parameters: - type: map - description: | - Groovy map with notebook parameters which will be passed to Quarto to - generate parametrized reports. - - input_files: - type: file - description: One or multiple files serving as input data for the notebook. - pattern: "*" - - extensions: - type: file - description: | - A quarto `_extensions` directory with custom template(s) to be - available for rendering. - pattern: "*" - + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'sample1', single_end:false ]`. + - notebook: + type: file + description: The Quarto notebook to be rendered. + pattern: "*.{qmd}" + - - parameters: + type: map + description: | + Groovy map with notebook parameters which will be passed to Quarto to + generate parametrized reports. + - - input_files: + type: file + description: One or multiple files serving as input data for the notebook. + pattern: "*" + - - extensions: + type: file + description: | + A quarto `_extensions` directory with custom template(s) to be + available for rendering. + pattern: "*" output: - - meta: - type: map - description: | - Groovy Map containing sample information - e.g. `[ id:'sample1', single_end:false ]`. - html: - type: file - description: HTML report generated by Quarto. - pattern: "*.html" + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'sample1', single_end:false ]`. + - "*.html": + type: file + description: HTML report generated by Quarto. + pattern: "*.html" - notebook: - type: file - description: The original, un-rendered notebook. - pattern: "*.[qmd,ipynb,rmd]" + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'sample1', single_end:false ]`. + - ${notebook}: + type: file + description: The original, un-rendered notebook. + pattern: "*.[qmd,ipynb,rmd]" - artifacts: - type: file - description: Artifacts generated during report rendering. - pattern: "*" + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'sample1', single_end:false ]`. + - artifacts/*: + type: file + description: Artifacts generated during report rendering. + pattern: "*" - params_yaml: - type: file - description: Parameters used during report rendering. - pattern: "*" + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'sample1', single_end:false ]`. + - params.yml: + type: file + description: Parameters used during report rendering. + pattern: "*" - extensions: - type: file - description: Quarto extensions used during report rendering. - pattern: "*" + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'sample1', single_end:false ]`. + - _extensions: + type: file + description: Quarto extensions used during report rendering. + pattern: "*" - versions: - type: file - description: File containing software versions. - pattern: "versions.yml" - + - versions.yml: + type: file + description: File containing software versions. + pattern: "versions.yml" authors: - "@fasterius" maintainers: diff --git a/modules/nf-core/quartonotebook/quartonotebook.diff b/modules/nf-core/quartonotebook/quartonotebook.diff index 61538bd..74d3e08 100644 --- a/modules/nf-core/quartonotebook/quartonotebook.diff +++ b/modules/nf-core/quartonotebook/quartonotebook.diff @@ -1,4 +1,9 @@ Changes in module 'nf-core/quartonotebook' +'modules/nf-core/quartonotebook/parametrize.nf' is unchanged +'modules/nf-core/quartonotebook/environment.yml' is unchanged +'modules/nf-core/quartonotebook/Dockerfile' is unchanged +'modules/nf-core/quartonotebook/meta.yml' is unchanged +Changes in 'quartonotebook/main.nf': --- modules/nf-core/quartonotebook/main.nf +++ modules/nf-core/quartonotebook/main.nf @@ -4,11 +4,7 @@ @@ -15,4 +20,9 @@ Changes in module 'nf-core/quartonotebook' input: tuple val(meta), path(notebook) +'modules/nf-core/quartonotebook/tests/main.nf.test.snap' is unchanged +'modules/nf-core/quartonotebook/tests/no-parametrization.config' is unchanged +'modules/nf-core/quartonotebook/tests/tags.yml' is unchanged +'modules/nf-core/quartonotebook/tests/main.nf.test' is unchanged +'modules/nf-core/quartonotebook/tests/with-parametrization.config' is unchanged ************************************************************ diff --git a/modules/nf-core/spaceranger/count/meta.yml b/modules/nf-core/spaceranger/count/meta.yml index 167ac8c..e1ffcef 100644 --- a/modules/nf-core/spaceranger/count/meta.yml +++ b/modules/nf-core/spaceranger/count/meta.yml @@ -1,7 +1,7 @@ ---- # yaml-language-server: $schema=https://raw.githubusercontent.com/nf-core/modules/master/modules/yaml-schema.json name: "spaceranger_count" -description: Module to use the 10x Space Ranger pipeline to process 10x spatial transcriptomics data +description: Module to use the 10x Space Ranger pipeline to process 10x spatial transcriptomics + data keywords: - align - count @@ -22,73 +22,77 @@ tools: tool_dev_url: "https://support.10xgenomics.com/spatial-gene-expression/software/pipelines/latest/what-is-space-ranger" licence: - "10x Genomics EULA" + identifier: "" input: - - meta: - type: map - description: | - Groovy Map containing sample information - e.g. [ id:'test', slide:'10L13-020', area: 'B1'] + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', slide:'10L13-020', area: 'B1'] - `id`, `slide` and `area` are mandatory information! - - reads: - type: file - description: | - List of input FastQ files of size 1 and 2 for single-end and paired-end data, - respectively. - pattern: "${Sample_Name}_S1_L00${Lane_Number}_${I1,I2,R1,R2}_001.fastq.gz" - - image: - type: file - description: Brightfield tissue H&E image in JPEG or TIFF format. - pattern: "*.{tif,tiff,jpg,jpeg}" - - cytaimage: - type: file - description: | - CytAssist instrument captured eosin stained Brightfield tissue image with fiducial - frame in TIFF format. The size of this image is set at 3k in both dimensions and this image should - not be modified any way before passing it as input to either Space Ranger or Loupe Browser. - pattern: "*.{tif,tiff}" - - darkimage: - type: file - description: | - Optional for dark background fluorescence microscope image input. Multi-channel, dark-background fluorescence - image as either a single, multi-layer TIFF file or as multiple TIFF or JPEG files. - pattern: "*.{tif,tiff,jpg,jpeg}" - - colorizedimage: - type: file - description: | - Required for color composite fluorescence microscope image input. - A color composite of one or more fluorescence image channels saved as a single-page, - single-file color TIFF or JPEG. - pattern: "*.{tif,tiff,jpg,jpeg}" - - alignment: - type: file - description: OPTIONAL - Path to manual image alignment. - pattern: "*.json" - - slidefile: - type: file - description: OPTIONAL - Path to slide specifications. - pattern: "*.json" - - reference: - type: directory - description: Folder containing all the reference indices needed by Space Ranger - - probeset: - type: file - description: OPTIONAL - Probe set specification. - pattern: "*.csv" + `id`, `slide` and `area` are mandatory information! + - reads: + type: file + description: | + List of input FastQ files of size 1 and 2 for single-end and paired-end data, + respectively. + pattern: "${Sample_Name}_S1_L00${Lane_Number}_${I1,I2,R1,R2}_001.fastq.gz" + - image: + type: file + description: Brightfield tissue H&E image in JPEG or TIFF format. + pattern: "*.{tif,tiff,jpg,jpeg}" + - cytaimage: + type: file + description: | + CytAssist instrument captured eosin stained Brightfield tissue image with fiducial + frame in TIFF format. The size of this image is set at 3k in both dimensions and this image should + not be modified any way before passing it as input to either Space Ranger or Loupe Browser. + pattern: "*.{tif,tiff}" + - darkimage: + type: file + description: | + Optional for dark background fluorescence microscope image input. Multi-channel, dark-background fluorescence + image as either a single, multi-layer TIFF file or as multiple TIFF or JPEG files. + pattern: "*.{tif,tiff,jpg,jpeg}" + - colorizedimage: + type: file + description: | + Required for color composite fluorescence microscope image input. + A color composite of one or more fluorescence image channels saved as a single-page, + single-file color TIFF or JPEG. + pattern: "*.{tif,tiff,jpg,jpeg}" + - alignment: + type: file + description: OPTIONAL - Path to manual image alignment. + pattern: "*.json" + - slidefile: + type: file + description: OPTIONAL - Path to slide specifications. + pattern: "*.json" + - - reference: + type: directory + description: Folder containing all the reference indices needed by Space Ranger + - - probeset: + type: file + description: OPTIONAL - Probe set specification. + pattern: "*.csv" output: - - meta: - type: map - description: | - Groovy Map containing sample information - e.g. [ id:'test', single_end:false ] - outs: - type: file - description: Files containing the outputs of Space Ranger, see official 10X Genomics documentation for a complete list - pattern: "outs/*" + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - outs/**: + type: file + description: Files containing the outputs of Space Ranger, see official 10X + Genomics documentation for a complete list + pattern: "outs/*" - versions: - type: file - description: File containing software versions - pattern: "versions.yml" + - versions.yml: + type: file + description: File containing software versions + pattern: "versions.yml" authors: - "@grst" maintainers: diff --git a/modules/nf-core/untar/environment.yml b/modules/nf-core/untar/environment.yml index 4f49824..c779485 100644 --- a/modules/nf-core/untar/environment.yml +++ b/modules/nf-core/untar/environment.yml @@ -1,8 +1,6 @@ -name: untar channels: - conda-forge - bioconda - - defaults dependencies: - conda-forge::grep=3.11 - conda-forge::sed=4.8 diff --git a/modules/nf-core/untar/meta.yml b/modules/nf-core/untar/meta.yml index a9a2110..290346b 100644 --- a/modules/nf-core/untar/meta.yml +++ b/modules/nf-core/untar/meta.yml @@ -10,30 +10,33 @@ tools: Extract tar.gz files. documentation: https://www.gnu.org/software/tar/manual/ licence: ["GPL-3.0-or-later"] + identifier: "" input: - - meta: - type: map - description: | - Groovy Map containing sample information - e.g. [ id:'test', single_end:false ] - - archive: - type: file - description: File to be untar - pattern: "*.{tar}.{gz}" + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - archive: + type: file + description: File to be untar + pattern: "*.{tar}.{gz}" output: - - meta: - type: map - description: | - Groovy Map containing sample information - e.g. [ id:'test', single_end:false ] - untar: - type: directory - description: Directory containing contents of archive - pattern: "*/" + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - $prefix: + type: directory + description: Directory containing contents of archive + pattern: "*/" - versions: - type: file - description: File containing software versions - pattern: "versions.yml" + - versions.yml: + type: file + description: File containing software versions + pattern: "versions.yml" authors: - "@joseespinosa" - "@drpatelh" From afe0e7398a6caaa3c2f9d3cbb604656ebc0ef8e0 Mon Sep 17 00:00:00 2001 From: Erik Fasterius Date: Tue, 22 Oct 2024 15:12:09 +0200 Subject: [PATCH 08/18] Remove unused genomes-related code --- conf/igenomes_ignored.config | 9 ----- .../utils_nfcore_spatialvi_pipeline/main.nf | 35 ------------------- 2 files changed, 44 deletions(-) delete mode 100644 conf/igenomes_ignored.config diff --git a/conf/igenomes_ignored.config b/conf/igenomes_ignored.config deleted file mode 100644 index b4034d8..0000000 --- a/conf/igenomes_ignored.config +++ /dev/null @@ -1,9 +0,0 @@ -/* -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - Nextflow config file for iGenomes paths -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - Empty genomes dictionary to use when igenomes is ignored. ----------------------------------------------------------------------------------------- -*/ - -params.genomes = [:] diff --git a/subworkflows/local/utils_nfcore_spatialvi_pipeline/main.nf b/subworkflows/local/utils_nfcore_spatialvi_pipeline/main.nf index b84e4f9..8caad93 100644 --- a/subworkflows/local/utils_nfcore_spatialvi_pipeline/main.nf +++ b/subworkflows/local/utils_nfcore_spatialvi_pipeline/main.nf @@ -63,11 +63,6 @@ workflow PIPELINE_INITIALISATION { nextflow_cli_args ) - // - // Custom validation for pipeline parameters - // - validateInputParameters() - // // Create channel from input file provided through params.input // @@ -148,12 +143,6 @@ workflow PIPELINE_COMPLETION { FUNCTIONS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ -// -// Check and validate pipeline parameters -// -def validateInputParameters() { - genomeExistsError() -} // // Validate channels from input samplesheet @@ -169,31 +158,7 @@ def validateInputSamplesheet(input) { return [ metas[0], fastqs ] } -// -// Get attribute from genome config file e.g. fasta -// -def getGenomeAttribute(attribute) { - if (params.genomes && params.genome && params.genomes.containsKey(params.genome)) { - if (params.genomes[ params.genome ].containsKey(attribute)) { - return params.genomes[ params.genome ][ attribute ] - } - } - return null -} -// -// Exit pipeline if incorrect --genome key provided -// -def genomeExistsError() { - if (params.genomes && params.genome && !params.genomes.containsKey(params.genome)) { - def error_string = "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" + - " Genome '${params.genome}' not found in any config files provided to the pipeline.\n" + - " Currently, the available genome keys are:\n" + - " ${params.genomes.keySet().join(", ")}\n" + - "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" - error(error_string) - } -} // // Generate methods description for MultiQC // From 2321e615b0a536d0000070259894e3896ad46931 Mon Sep 17 00:00:00 2001 From: Erik Fasterius Date: Wed, 23 Oct 2024 09:18:51 +0200 Subject: [PATCH 09/18] Do not validate input in pipeline initialisation --- main.nf | 1 - .../utils_nfcore_spatialvi_pipeline/main.nf | 40 ------------------- workflows/spatialvi.nf | 7 +++- 3 files changed, 5 insertions(+), 43 deletions(-) diff --git a/main.nf b/main.nf index c946553..ed59ac9 100644 --- a/main.nf +++ b/main.nf @@ -62,7 +62,6 @@ workflow { params.monochrome_logs, args, params.outdir, - params.input ) // diff --git a/subworkflows/local/utils_nfcore_spatialvi_pipeline/main.nf b/subworkflows/local/utils_nfcore_spatialvi_pipeline/main.nf index 8caad93..c72036a 100644 --- a/subworkflows/local/utils_nfcore_spatialvi_pipeline/main.nf +++ b/subworkflows/local/utils_nfcore_spatialvi_pipeline/main.nf @@ -31,7 +31,6 @@ workflow PIPELINE_INITIALISATION { monochrome_logs // boolean: Do not use coloured log outputs nextflow_cli_args // array: List of positional nextflow CLI args outdir // string: The output directory where the results will be saved - input // string: Path to input samplesheet main: @@ -63,30 +62,6 @@ workflow PIPELINE_INITIALISATION { nextflow_cli_args ) - // - // Create channel from input file provided through params.input - // - - Channel - .fromList(samplesheetToList(params.input, "${projectDir}/assets/schema_input.json")) - .map { - meta, fastq_1, fastq_2 -> - if (!fastq_2) { - return [ meta.id, meta + [ single_end:true ], [ fastq_1 ] ] - } else { - return [ meta.id, meta + [ single_end:false ], [ fastq_1, fastq_2 ] ] - } - } - .groupTuple() - .map { samplesheet -> - validateInputSamplesheet(samplesheet) - } - .map { - meta, fastqs -> - return [ meta, fastqs.flatten() ] - } - .set { ch_samplesheet } - emit: versions = ch_versions } @@ -144,21 +119,6 @@ workflow PIPELINE_COMPLETION { ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ -// -// Validate channels from input samplesheet -// -def validateInputSamplesheet(input) { - def (metas, fastqs) = input[1..2] - - // Check that multiple runs of the same sample are of the same datatype i.e. single-end / paired-end - def endedness_ok = metas.collect{ meta -> meta.single_end }.unique().size == 1 - if (!endedness_ok) { - error("Please check input samplesheet -> Multiple runs of a sample must be of the same datatype i.e. single-end or paired-end: ${metas[0].id}") - } - - return [ metas[0], fastqs ] -} - // // Generate methods description for MultiQC // diff --git a/workflows/spatialvi.nf b/workflows/spatialvi.nf index b17b689..f7262c6 100644 --- a/workflows/spatialvi.nf +++ b/workflows/spatialvi.nf @@ -11,6 +11,7 @@ include { INPUT_CHECK } from '../subworkflows/local/input_check' include { SPACERANGER } from '../subworkflows/local/spaceranger' include { DOWNSTREAM } from '../subworkflows/local/downstream' include { paramsSummaryMultiqc } from '../subworkflows/nf-core/utils_nfcore_pipeline' +include { paramsSummaryMap } from 'plugin/nf-schema' include { softwareVersionsToYAML } from '../subworkflows/nf-core/utils_nfcore_pipeline' include { methodsDescriptionText } from '../subworkflows/local/utils_nfcore_spatialvi_pipeline' @@ -23,16 +24,18 @@ include { methodsDescriptionText } from '../subworkflows/local/utils_nfcore_spat workflow SPATIALVI { take: - ch_samplesheet // channel: samplesheet read in from --input + samplesheet // file: samplesheet read in from --input + main: ch_versions = Channel.empty() ch_multiqc_files = Channel.empty() + // // SUBWORKFLOW: Read and validate samplesheet // INPUT_CHECK ( - ch_samplesheet + samplesheet ) ch_versions = ch_versions.mix(INPUT_CHECK.out.versions) From ccf00845884c4d6ee438c43ab0f61f28f44de458 Mon Sep 17 00:00:00 2001 From: Erik Fasterius Date: Wed, 23 Oct 2024 10:19:17 +0200 Subject: [PATCH 10/18] Fix missing dollar sign in GHA --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 63e1d05..88a718a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -76,7 +76,7 @@ jobs: uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be # v1.3.1 - name: Run nf-test - run: nf-test test --verbose --profile "{{ matrix.profile }}" --tap=test.tap ${{ matrix.nf_test_files }} + run: nf-test test --verbose --profile "${{ matrix.profile }}" --tap=test.tap ${{ matrix.nf_test_files }} - name: Output log on failure if: failure() From d9ec30f390c1856fe6422bce09ca0998f26426e1 Mon Sep 17 00:00:00 2001 From: Erik Fasterius Date: Wed, 23 Oct 2024 13:14:16 +0200 Subject: [PATCH 11/18] Fix GHA job names --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 88a718a..8922708 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,7 +25,7 @@ concurrency: jobs: test: - name: "Run pipeline with test data (${{ matrix.NXF_VER }} | ${{ matrix.test_name }} | ${{ matrix.profile }})" + name: "Run pipeline with test data (${{ matrix.nf_test_files }} | ${{ matrix.NXF_VER }} | ${{ matrix.profile }})" # Only run on push if this is the nf-core dev branch (merged PRs) if: "${{ github.event_name != 'push' || (github.event_name == 'push' && github.repository == 'nf-core/spatialvi') }}" runs-on: ubuntu-latest From 5b7b81a738aa574741db752c18faae6c197a34cc Mon Sep 17 00:00:00 2001 From: Erik Fasterius Date: Wed, 23 Oct 2024 13:14:31 +0200 Subject: [PATCH 12/18] Remove remaining usage of old `check_max()` --- conf/base.config | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/conf/base.config b/conf/base.config index 26c7180..1f07601 100644 --- a/conf/base.config +++ b/conf/base.config @@ -46,9 +46,9 @@ process { memory = { 200.GB * task.attempt } } withLabel:process_spaceranger { - cpus = { check_max( 8 * task.attempt, 'cpus' ) } - memory = { check_max( 64.GB * task.attempt, 'memory' ) } - time = { check_max( 20.h * task.attempt, 'time' ) } + cpus = { 8 * task.attempt } + memory = { 64.GB * task.attempt } + time = { 20.h * task.attempt } } withLabel:error_ignore { errorStrategy = 'ignore' From 09b6fc7bd6913b7447352639df2a32c084600e13 Mon Sep 17 00:00:00 2001 From: Erik Fasterius Date: Wed, 23 Oct 2024 13:18:01 +0200 Subject: [PATCH 13/18] Fix missing `test` profile from nf-test default --- .github/workflows/ci.yml | 2 +- nf-test.config | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8922708..22c8a80 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -76,7 +76,7 @@ jobs: uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be # v1.3.1 - name: Run nf-test - run: nf-test test --verbose --profile "${{ matrix.profile }}" --tap=test.tap ${{ matrix.nf_test_files }} + run: nf-test test --verbose --profile "+${{ matrix.profile }}" --tap=test.tap ${{ matrix.nf_test_files }} - name: Output log on failure if: failure() diff --git a/nf-test.config b/nf-test.config index b57ac7d..c381fb7 100644 --- a/nf-test.config +++ b/nf-test.config @@ -12,5 +12,5 @@ config { configFile "nextflow.config" // test profile is overriden via nf-test CLI - profile "" + profile "test" } From 29a3db79cfc317a0eaa4ece2d4faf1c158309355 Mon Sep 17 00:00:00 2001 From: Erik Fasterius Date: Thu, 24 Oct 2024 13:21:55 +0200 Subject: [PATCH 14/18] Add patch for `spaceranger/count` --- conf/modules.config | 2 +- modules.json | 3 +- modules/nf-core/spaceranger/count/meta.yml | 103 +++++++------- .../nf-core/spaceranger/count/modules.json | 5 + .../spaceranger/count/spaceranger-count.diff | 132 ++++++++++++++++++ .../spaceranger/count/tests/nextflow.config | 2 +- 6 files changed, 192 insertions(+), 55 deletions(-) create mode 100644 modules/nf-core/spaceranger/count/modules.json create mode 100644 modules/nf-core/spaceranger/count/spaceranger-count.diff diff --git a/conf/modules.config b/conf/modules.config index 2d1265d..9cb5c54 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -47,7 +47,7 @@ process { } withName: SPACERANGER_COUNT { - ext.args = '--create-bam false' + ext.args = '--create-bam="false"' publishDir = [ path: { "${params.outdir}/${meta.id}/spaceranger" }, mode: params.publish_dir_mode, diff --git a/modules.json b/modules.json index 6d76717..4031514 100644 --- a/modules.json +++ b/modules.json @@ -25,7 +25,8 @@ "spaceranger/count": { "branch": "master", "git_sha": "666652151335353eef2fcd58880bcef5bc2928e1", - "installed_by": ["modules"] + "installed_by": ["modules"], + "patch": "modules/nf-core/spaceranger/count/spaceranger-count.diff" }, "untar": { "branch": "master", diff --git a/modules/nf-core/spaceranger/count/meta.yml b/modules/nf-core/spaceranger/count/meta.yml index e1ffcef..17626e1 100644 --- a/modules/nf-core/spaceranger/count/meta.yml +++ b/modules/nf-core/spaceranger/count/meta.yml @@ -24,58 +24,57 @@ tools: - "10x Genomics EULA" identifier: "" input: - - - meta: - type: map - description: | - Groovy Map containing sample information - e.g. [ id:'test', slide:'10L13-020', area: 'B1'] - - `id`, `slide` and `area` are mandatory information! - - reads: - type: file - description: | - List of input FastQ files of size 1 and 2 for single-end and paired-end data, - respectively. - pattern: "${Sample_Name}_S1_L00${Lane_Number}_${I1,I2,R1,R2}_001.fastq.gz" - - image: - type: file - description: Brightfield tissue H&E image in JPEG or TIFF format. - pattern: "*.{tif,tiff,jpg,jpeg}" - - cytaimage: - type: file - description: | - CytAssist instrument captured eosin stained Brightfield tissue image with fiducial - frame in TIFF format. The size of this image is set at 3k in both dimensions and this image should - not be modified any way before passing it as input to either Space Ranger or Loupe Browser. - pattern: "*.{tif,tiff}" - - darkimage: - type: file - description: | - Optional for dark background fluorescence microscope image input. Multi-channel, dark-background fluorescence - image as either a single, multi-layer TIFF file or as multiple TIFF or JPEG files. - pattern: "*.{tif,tiff,jpg,jpeg}" - - colorizedimage: - type: file - description: | - Required for color composite fluorescence microscope image input. - A color composite of one or more fluorescence image channels saved as a single-page, - single-file color TIFF or JPEG. - pattern: "*.{tif,tiff,jpg,jpeg}" - - alignment: - type: file - description: OPTIONAL - Path to manual image alignment. - pattern: "*.json" - - slidefile: - type: file - description: OPTIONAL - Path to slide specifications. - pattern: "*.json" - - - reference: - type: directory - description: Folder containing all the reference indices needed by Space Ranger - - - probeset: - type: file - description: OPTIONAL - Probe set specification. - pattern: "*.csv" + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', slide:'10L13-020', area: 'B1'] + `id`, `slide` and `area` are mandatory information! + - reads: + type: file + description: | + List of input FastQ files of size 1 and 2 for single-end and paired-end data, + respectively. + pattern: "${Sample_Name}_S1_L00${Lane_Number}_${I1,I2,R1,R2}_001.fastq.gz" + - image: + type: file + description: Brightfield tissue H&E image in JPEG or TIFF format. + pattern: "*.{tif,tiff,jpg,jpeg}" + - cytaimage: + type: file + description: | + CytAssist instrument captured eosin stained Brightfield tissue image with fiducial + frame in TIFF format. The size of this image is set at 3k in both dimensions and this image should + not be modified any way before passing it as input to either Space Ranger or Loupe Browser. + pattern: "*.{tif,tiff}" + - darkimage: + type: file + description: | + Optional for dark background fluorescence microscope image input. Multi-channel, dark-background fluorescence + image as either a single, multi-layer TIFF file or as multiple TIFF or JPEG files. + pattern: "*.{tif,tiff,jpg,jpeg}" + - colorizedimage: + type: file + description: | + Required for color composite fluorescence microscope image input. + A color composite of one or more fluorescence image channels saved as a single-page, + single-file color TIFF or JPEG. + pattern: "*.{tif,tiff,jpg,jpeg}" + - alignment: + type: file + description: OPTIONAL - Path to manual image alignment. + pattern: "*.json" + - slidefile: + type: file + description: OPTIONAL - Path to slide specifications. + pattern: "*.json" + - reference: + type: directory + description: Folder containing all the reference indices needed by Space Ranger + - probeset: + type: file + description: OPTIONAL - Probe set specification. + pattern: "*.csv" output: - outs: - meta: diff --git a/modules/nf-core/spaceranger/count/modules.json b/modules/nf-core/spaceranger/count/modules.json new file mode 100644 index 0000000..02a6334 --- /dev/null +++ b/modules/nf-core/spaceranger/count/modules.json @@ -0,0 +1,5 @@ +{ + "name": "", + "homePage": "", + "repos": {} +} diff --git a/modules/nf-core/spaceranger/count/spaceranger-count.diff b/modules/nf-core/spaceranger/count/spaceranger-count.diff new file mode 100644 index 0000000..141c0e5 --- /dev/null +++ b/modules/nf-core/spaceranger/count/spaceranger-count.diff @@ -0,0 +1,132 @@ +Changes in module 'nf-core/spaceranger/count' +Changes in 'spaceranger/count/meta.yml': +--- modules/nf-core/spaceranger/count/meta.yml ++++ modules/nf-core/spaceranger/count/meta.yml +@@ -24,58 +24,57 @@ + - "10x Genomics EULA" + identifier: "" + input: +- - - meta: +- type: map +- description: | +- Groovy Map containing sample information +- e.g. [ id:'test', slide:'10L13-020', area: 'B1'] +- +- `id`, `slide` and `area` are mandatory information! +- - reads: +- type: file +- description: | +- List of input FastQ files of size 1 and 2 for single-end and paired-end data, +- respectively. +- pattern: "${Sample_Name}_S1_L00${Lane_Number}_${I1,I2,R1,R2}_001.fastq.gz" +- - image: +- type: file +- description: Brightfield tissue H&E image in JPEG or TIFF format. +- pattern: "*.{tif,tiff,jpg,jpeg}" +- - cytaimage: +- type: file +- description: | +- CytAssist instrument captured eosin stained Brightfield tissue image with fiducial +- frame in TIFF format. The size of this image is set at 3k in both dimensions and this image should +- not be modified any way before passing it as input to either Space Ranger or Loupe Browser. +- pattern: "*.{tif,tiff}" +- - darkimage: +- type: file +- description: | +- Optional for dark background fluorescence microscope image input. Multi-channel, dark-background fluorescence +- image as either a single, multi-layer TIFF file or as multiple TIFF or JPEG files. +- pattern: "*.{tif,tiff,jpg,jpeg}" +- - colorizedimage: +- type: file +- description: | +- Required for color composite fluorescence microscope image input. +- A color composite of one or more fluorescence image channels saved as a single-page, +- single-file color TIFF or JPEG. +- pattern: "*.{tif,tiff,jpg,jpeg}" +- - alignment: +- type: file +- description: OPTIONAL - Path to manual image alignment. +- pattern: "*.json" +- - slidefile: +- type: file +- description: OPTIONAL - Path to slide specifications. +- pattern: "*.json" +- - - reference: +- type: directory +- description: Folder containing all the reference indices needed by Space Ranger +- - - probeset: +- type: file +- description: OPTIONAL - Probe set specification. +- pattern: "*.csv" ++ - meta: ++ type: map ++ description: | ++ Groovy Map containing sample information ++ e.g. [ id:'test', slide:'10L13-020', area: 'B1'] ++ `id`, `slide` and `area` are mandatory information! ++ - reads: ++ type: file ++ description: | ++ List of input FastQ files of size 1 and 2 for single-end and paired-end data, ++ respectively. ++ pattern: "${Sample_Name}_S1_L00${Lane_Number}_${I1,I2,R1,R2}_001.fastq.gz" ++ - image: ++ type: file ++ description: Brightfield tissue H&E image in JPEG or TIFF format. ++ pattern: "*.{tif,tiff,jpg,jpeg}" ++ - cytaimage: ++ type: file ++ description: | ++ CytAssist instrument captured eosin stained Brightfield tissue image with fiducial ++ frame in TIFF format. The size of this image is set at 3k in both dimensions and this image should ++ not be modified any way before passing it as input to either Space Ranger or Loupe Browser. ++ pattern: "*.{tif,tiff}" ++ - darkimage: ++ type: file ++ description: | ++ Optional for dark background fluorescence microscope image input. Multi-channel, dark-background fluorescence ++ image as either a single, multi-layer TIFF file or as multiple TIFF or JPEG files. ++ pattern: "*.{tif,tiff,jpg,jpeg}" ++ - colorizedimage: ++ type: file ++ description: | ++ Required for color composite fluorescence microscope image input. ++ A color composite of one or more fluorescence image channels saved as a single-page, ++ single-file color TIFF or JPEG. ++ pattern: "*.{tif,tiff,jpg,jpeg}" ++ - alignment: ++ type: file ++ description: OPTIONAL - Path to manual image alignment. ++ pattern: "*.json" ++ - slidefile: ++ type: file ++ description: OPTIONAL - Path to slide specifications. ++ pattern: "*.json" ++ - reference: ++ type: directory ++ description: Folder containing all the reference indices needed by Space Ranger ++ - probeset: ++ type: file ++ description: OPTIONAL - Probe set specification. ++ pattern: "*.csv" + output: + - outs: + - meta: + +'modules/nf-core/spaceranger/count/modules.json' was created +'modules/nf-core/spaceranger/count/main.nf' is unchanged +'modules/nf-core/spaceranger/count/tests/main.nf.test.snap' is unchanged +'modules/nf-core/spaceranger/count/tests/tags.yml' is unchanged +Changes in 'spaceranger/count/tests/nextflow.config': +--- modules/nf-core/spaceranger/count/tests/nextflow.config ++++ modules/nf-core/spaceranger/count/tests/nextflow.config +@@ -1,5 +1,5 @@ + process { + withName: SPACERANGER_COUNT { +- ext.args = '--create-bam false' ++ ext.args = '--create-bam="false"' + } + } + +'modules/nf-core/spaceranger/count/tests/main.nf.test' is unchanged +************************************************************ diff --git a/modules/nf-core/spaceranger/count/tests/nextflow.config b/modules/nf-core/spaceranger/count/tests/nextflow.config index fe9d61a..e4b3571 100644 --- a/modules/nf-core/spaceranger/count/tests/nextflow.config +++ b/modules/nf-core/spaceranger/count/tests/nextflow.config @@ -1,5 +1,5 @@ process { withName: SPACERANGER_COUNT { - ext.args = '--create-bam false' + ext.args = '--create-bam="false"' } } From f3a218efa77f21c913e41884af3d44ff16825ebd Mon Sep 17 00:00:00 2001 From: Erik Fasterius Date: Fri, 25 Oct 2024 12:44:53 +0200 Subject: [PATCH 15/18] Pin scipy version in Conda environment --- env/environment.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/env/environment.yml b/env/environment.yml index c376fb2..ef9221c 100644 --- a/env/environment.yml +++ b/env/environment.yml @@ -13,6 +13,7 @@ dependencies: - imagecodecs=2024.1.1 - pip: - scanpy==1.10.0 + - scipy==1.12.0 - squidpy==1.4.1 - spatialdata==0.1.2 - spatialdata-io==0.1.2 From 99f2fb5527a964b254b40692f9e95466e2a040f8 Mon Sep 17 00:00:00 2001 From: Erik Fasterius Date: Fri, 25 Oct 2024 14:25:47 +0200 Subject: [PATCH 16/18] Fix test profile resource limits The `spaceranger/count` module stalled during GHA CI tests due to some issue with the new template resource specifications for the test profile. The new template specifies 15 GB of RAM, but the GHA runner only had 13.6 available, which somehow lead to a stall of the task until the time ran out. Setting a slightly lower memory requirement fixes this. --- conf/test.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/test.config b/conf/test.config index 3596a95..eed0d19 100644 --- a/conf/test.config +++ b/conf/test.config @@ -13,7 +13,7 @@ process { resourceLimits = [ cpus: 4, - memory: '15.GB', + memory: '12.GB', time: '1.h' ] } From 1d3f3a5fcfe16a579063901aa6ffa729af596a07 Mon Sep 17 00:00:00 2001 From: Erik Fasterius Date: Fri, 25 Oct 2024 15:32:45 +0200 Subject: [PATCH 17/18] Remove obsolete snapshot --- tests/pipeline/test_spaceranger_ffpe_v1.nf.test.snap | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/tests/pipeline/test_spaceranger_ffpe_v1.nf.test.snap b/tests/pipeline/test_spaceranger_ffpe_v1.nf.test.snap index 7b91971..c08cf37 100644 --- a/tests/pipeline/test_spaceranger_ffpe_v1.nf.test.snap +++ b/tests/pipeline/test_spaceranger_ffpe_v1.nf.test.snap @@ -1,14 +1,4 @@ { - "software_versions": { - "content": [ - "{CUSTOM_DUMPSOFTWAREVERSIONS={python=3.11.7, yaml=5.4.1}, FASTQC={fastqc=0.12.1}, SPACERANGER_COUNT={spaceranger=2.1.0}, SPACERANGER_UNTAR_REFERENCE={untar=1.34}, CLUSTERING={quarto=1.3.302, scanpy=1.9.3}, QUALITY_CONTROLS={quarto=1.3.302, scanpy=1.9.3}, READ_DATA={scanpy=1.7.2}, SPATIAL_DE={SpatialDE=1.1.3, leidenalg=0.9.1, quarto=1.3.302, scanpy=1.9.3}, Workflow={nf-core/spatialvi=1.0dev}}" - ], - "meta": { - "nf-test": "0.8.4", - "nextflow": "23.10.1" - }, - "timestamp": "2024-01-15T13:44:40.789425" - }, "nf_core_pipeline_software_mqc_versions.yml": { "content": [ "{CLUSTERING={quarto=1.3.450, papermill=null}, FASTQC={fastqc=0.12.1}, QUALITY_CONTROLS={quarto=1.3.450, papermill=null}, READ_DATA={spatialdata_io=0.1.2}, SPACERANGER_COUNT={spaceranger=3.0.0}, SPACERANGER_UNTAR_REFERENCE={untar=1.34}, SPATIALLY_VARIABLE_GENES={quarto=1.3.450, papermill=null}, UNTAR_SPACERANGER_INPUT={untar=1.34}, Workflow={nf-core/spatialvi=v1.0dev}}" From af8d1b0d05d59ea745cb8cd1f843c4f320bc2211 Mon Sep 17 00:00:00 2001 From: Erik Fasterius Date: Fri, 25 Oct 2024 15:34:22 +0200 Subject: [PATCH 18/18] Skip linting of `conf/igenomes_ignored.config` --- .nf-core.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.nf-core.yml b/.nf-core.yml index a46ed43..264c943 100644 --- a/.nf-core.yml +++ b/.nf-core.yml @@ -3,6 +3,7 @@ lint: actions_ci: false files_exist: - conf/igenomes.config + - conf/igenomes_ignored.config files_unchanged: - .gitattributes - .gitignore