diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index b290e09..97c8c97 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,20 +1,20 @@ { "name": "nfcore", - "image": "nfcore/gitpod:latest", - "remoteUser": "gitpod", - "runArgs": ["--privileged"], + "image": "nfcore/devcontainer:latest", - // Configure tool-specific properties. - "customizations": { - // Configure properties specific to VS Code. - "vscode": { - // Set *default* container specific settings.json values on container create. - "settings": { - "python.defaultInterpreterPath": "/opt/conda/bin/python" - }, + "remoteUser": "root", + "privileged": true, - // Add the IDs of extensions you want installed when the container is created. - "extensions": ["ms-python.python", "ms-python.vscode-pylance", "nf-core.nf-core-extensionpack"] - } + "remoteEnv": { + // Workspace path on the host for mounting with docker-outside-of-docker + "LOCAL_WORKSPACE_FOLDER": "${localWorkspaceFolder}" + }, + + "onCreateCommand": "./.devcontainer/setup.sh", + + "hostRequirements": { + "cpus": 4, + "memory": "16gb", + "storage": "32gb" } } diff --git a/.devcontainer/setup.sh b/.devcontainer/setup.sh new file mode 100755 index 0000000..7548feb --- /dev/null +++ b/.devcontainer/setup.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +# Customise the terminal command prompt +echo "export PROMPT_DIRTRIM=2" >> $HOME/.bashrc +echo "export PS1='\[\e[3;36m\]\w ->\[\e[0m\\] '" >> $HOME/.bashrc +export PROMPT_DIRTRIM=2 +export PS1='\[\e[3;36m\]\w ->\[\e[0m\\] ' + +# Update Nextflow +nextflow self-update + +# Update welcome message +echo "Welcome to the nf-core/coproid devcontainer!" > /usr/local/etc/vscode-dev-containers/first-run-notice.txt diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index f9fee26..0000000 --- a/.editorconfig +++ /dev/null @@ -1,43 +0,0 @@ -root = true - -[*] -charset = utf-8 -end_of_line = lf -insert_final_newline = true -trim_trailing_whitespace = true -indent_size = 4 -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 -end_of_line = unset -insert_final_newline = unset -trim_trailing_whitespace = unset -indent_style = unset -[/subworkflows/nf-core/**] -charset = unset -end_of_line = unset -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 - -# ignore quarto markdown report -[*.{py,md,qmd}] -indent_style = unset -end_of_line = unset -insert_final_newline = unset - -# ignore ro-crate metadata files -[**/ro-crate-metadata.json] -insert_final_newline = unset diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 11aa270..1b3c3de 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -78,7 +78,7 @@ If you wish to contribute a new step, please use the following coding standards: 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`. +8. If applicable, add a new test in the `tests` directory. 9. Update MultiQC config `assets/multiqc_config.yml` so relevant suffixes, file name clean up and module plots are in the appropriate order. If applicable, add a [MultiQC](https://https://multiqc.info/) module. 10. Add a description of the output files and if relevant any appropriate images from the MultiQC report to `docs/output.md`. diff --git a/.github/actions/get-shards/action.yml b/.github/actions/get-shards/action.yml new file mode 100644 index 0000000..3408527 --- /dev/null +++ b/.github/actions/get-shards/action.yml @@ -0,0 +1,69 @@ +name: "Get number of shards" +description: "Get the number of nf-test shards for the current CI job" +inputs: + max_shards: + description: "Maximum number of shards allowed" + required: true + paths: + description: "Component paths to test" + required: false + tags: + description: "Tags to pass as argument for nf-test --tag parameter" + required: false +outputs: + shard: + description: "Array of shard numbers" + value: ${{ steps.shards.outputs.shard }} + total_shards: + description: "Total number of shards" + value: ${{ steps.shards.outputs.total_shards }} +runs: + using: "composite" + steps: + - name: Install nf-test + uses: nf-core/setup-nf-test@v1 + with: + version: ${{ env.NFT_VER }} + - name: Get number of shards + id: shards + shell: bash + run: | + # Run nf-test with dynamic parameter + nftest_output=$(nf-test test \ + --profile +docker \ + $(if [ -n "${{ inputs.tags }}" ]; then echo "--tag ${{ inputs.tags }}"; fi) \ + --dry-run \ + --ci \ + --changed-since HEAD^) || { + echo "nf-test command failed with exit code $?" + echo "Full output: $nftest_output" + exit 1 + } + echo "nf-test dry-run output: $nftest_output" + + # Default values for shard and total_shards + shard="[]" + total_shards=0 + + # Check if there are related tests + if echo "$nftest_output" | grep -q 'No tests to execute'; then + echo "No related tests found." + else + # Extract the number of related tests + number_of_shards=$(echo "$nftest_output" | sed -n 's|.*Executed \([0-9]*\) tests.*|\1|p') + if [[ -n "$number_of_shards" && "$number_of_shards" -gt 0 ]]; then + shards_to_run=$(( $number_of_shards < ${{ inputs.max_shards }} ? $number_of_shards : ${{ inputs.max_shards }} )) + shard=$(seq 1 "$shards_to_run" | jq -R . | jq -c -s .) + total_shards="$shards_to_run" + else + echo "Unexpected output format. Falling back to default values." + fi + fi + + # Write to GitHub Actions outputs + echo "shard=$shard" >> $GITHUB_OUTPUT + echo "total_shards=$total_shards" >> $GITHUB_OUTPUT + + # Debugging output + echo "Final shard array: $shard" + echo "Total number of shards: $total_shards" diff --git a/.github/actions/nf-test/action.yml b/.github/actions/nf-test/action.yml new file mode 100644 index 0000000..3b9724c --- /dev/null +++ b/.github/actions/nf-test/action.yml @@ -0,0 +1,111 @@ +name: "nf-test Action" +description: "Runs nf-test with common setup steps" +inputs: + profile: + description: "Profile to use" + required: true + shard: + description: "Shard number for this CI job" + required: true + total_shards: + description: "Total number of test shards(NOT the total number of matrix jobs)" + required: true + paths: + description: "Test paths" + required: true + tags: + description: "Tags to pass as argument for nf-test --tag parameter" + required: false +runs: + using: "composite" + steps: + - name: Setup Nextflow + uses: nf-core/setup-nextflow@v2 + with: + version: "${{ env.NXF_VERSION }}" + + - name: Set up Python + uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6 + with: + python-version: "3.14" + + - name: Install nf-test + uses: nf-core/setup-nf-test@v1 + with: + version: "${{ env.NFT_VER }}" + install-pdiff: true + + - name: Setup apptainer + if: contains(inputs.profile, 'singularity') + uses: eWaterCycle/setup-apptainer@main + + - name: Set up Singularity + if: contains(inputs.profile, 'singularity') + shell: bash + run: | + mkdir -p $NXF_SINGULARITY_CACHEDIR + mkdir -p $NXF_SINGULARITY_LIBRARYDIR + + - name: Conda setup + if: contains(inputs.profile, 'conda') + uses: conda-incubator/setup-miniconda@505e6394dae86d6a5c7fbb6e3fb8938e3e863830 # v3 + with: + auto-update-conda: true + conda-solver: libmamba + channels: conda-forge + channel-priority: strict + conda-remove-defaults: true + + - name: Run nf-test + shell: bash + env: + NFT_WORKDIR: ${{ env.NFT_WORKDIR }} + run: | + nf-test test \ + --profile=+${{ inputs.profile }} \ + $(if [ -n "${{ inputs.tags }}" ]; then echo "--tag ${{ inputs.tags }}"; fi) \ + --ci \ + --changed-since HEAD^ \ + --verbose \ + --tap=test.tap \ + --shard ${{ inputs.shard }}/${{ inputs.total_shards }} + + # Save the absolute path of the test.tap file to the output + echo "tap_file_path=$(realpath test.tap)" >> $GITHUB_OUTPUT + + - name: Generate test summary + if: always() + shell: bash + run: | + # Add header if it doesn't exist (using a token file to track this) + if [ ! -f ".summary_header" ]; then + echo "# 🚀 nf-test results" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "| Status | Test Name | Profile | Shard |" >> $GITHUB_STEP_SUMMARY + echo "|:------:|-----------|---------|-------|" >> $GITHUB_STEP_SUMMARY + touch .summary_header + fi + + if [ -f test.tap ]; then + while IFS= read -r line; do + if [[ $line =~ ^ok ]]; then + test_name="${line#ok }" + # Remove the test number from the beginning + test_name="${test_name#* }" + echo "| ✅ | ${test_name} | ${{ inputs.profile }} | ${{ inputs.shard }}/${{ inputs.total_shards }} |" >> $GITHUB_STEP_SUMMARY + elif [[ $line =~ ^not\ ok ]]; then + test_name="${line#not ok }" + # Remove the test number from the beginning + test_name="${test_name#* }" + echo "| ❌ | ${test_name} | ${{ inputs.profile }} | ${{ inputs.shard }}/${{ inputs.total_shards }} |" >> $GITHUB_STEP_SUMMARY + fi + done < test.tap + else + echo "| ⚠️ | No test results found | ${{ inputs.profile }} | ${{ inputs.shard }}/${{ inputs.total_shards }} |" >> $GITHUB_STEP_SUMMARY + fi + + - name: Clean up + if: always() + shell: bash + run: | + sudo rm -rf /home/ubuntu/tests/ diff --git a/.github/workflows/awsfulltest.yml b/.github/workflows/awsfulltest.yml index 7995311..1a62c60 100644 --- a/.github/workflows/awsfulltest.yml +++ b/.github/workflows/awsfulltest.yml @@ -4,13 +4,11 @@ name: nf-core AWS full size tests # It runs the -profile 'test_full' on AWS batch on: - pull_request: - branches: - - main - - master workflow_dispatch: pull_request_review: types: [submitted] + release: + types: [published] jobs: run-platform: @@ -46,21 +44,21 @@ jobs: - name: Launch workflow via Seqera Platform uses: seqeralabs/action-tower-launch@v2 with: - workspace_id: ${{ secrets.TOWER_WORKSPACE_ID }} + workspace_id: ${{ vars.TOWER_WORKSPACE_ID }} access_token: ${{ secrets.TOWER_ACCESS_TOKEN }} - compute_env: ${{ secrets.TOWER_COMPUTE_ENV }} - revision: ${{ github.sha }} - workdir: s3://${{ secrets.AWS_S3_BUCKET }}/work/coproid/work-${{ github.sha }} + compute_env: ${{ vars.TOWER_COMPUTE_ENV }} + revision: ${{ steps.revision.outputs.revision }} + workdir: s3://${{ vars.AWS_S3_BUCKET }}/work/coproid/work-${{ steps.revision.outputs.revision }} parameters: | { "hook_url": "${{ secrets.MEGATESTS_ALERTS_SLACK_HOOK_URL }}", - "outdir": "s3://${{ secrets.AWS_S3_BUCKET }}/coproid/results-${{ github.sha }}" + "outdir": "s3://${{ vars.AWS_S3_BUCKET }}/coproid/results-${{ steps.revision.outputs.revision }}" } profiles: test_full - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5 with: name: Seqera Platform debug log file path: | - seqera_platform_action_*.log - seqera_platform_action_*.json + tower_action_*.log + tower_action_*.json diff --git a/.github/workflows/awstest.yml b/.github/workflows/awstest.yml index 2f00cc5..b0f77bd 100644 --- a/.github/workflows/awstest.yml +++ b/.github/workflows/awstest.yml @@ -14,20 +14,20 @@ jobs: - name: Launch workflow via Seqera Platform uses: seqeralabs/action-tower-launch@v2 with: - workspace_id: ${{ secrets.TOWER_WORKSPACE_ID }} + workspace_id: ${{ vars.TOWER_WORKSPACE_ID }} access_token: ${{ secrets.TOWER_ACCESS_TOKEN }} - compute_env: ${{ secrets.TOWER_COMPUTE_ENV }} + compute_env: ${{ vars.TOWER_COMPUTE_ENV }} revision: ${{ github.sha }} - workdir: s3://${{ secrets.AWS_S3_BUCKET }}/work/coproid/work-${{ github.sha }} + workdir: s3://${{ vars.AWS_S3_BUCKET }}/work/coproid/work-${{ github.sha }} parameters: | { - "outdir": "s3://${{ secrets.AWS_S3_BUCKET }}/coproid/results-test-${{ github.sha }}" + "outdir": "s3://${{ vars.AWS_S3_BUCKET }}/coproid/results-test-${{ github.sha }}" } profiles: test - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5 with: name: Seqera Platform debug log file path: | - seqera_platform_action_*.log - seqera_platform_action_*.json + tower_action_*.log + tower_action_*.json diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index 5bd5781..0000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,85 +0,0 @@ -name: nf-core CI -# This workflow runs the pipeline with the minimal test dataset to check that it completes without any syntax errors -on: - push: - branches: - - dev - pull_request: - release: - types: [published] - workflow_dispatch: - -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 }}" - cancel-in-progress: true - -jobs: - test: - 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/coproid') }}" - runs-on: ubuntu-latest - strategy: - matrix: - NXF_VER: - - "24.10.0" - profile: - - "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@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 - with: - fetch-depth: 0 - - - name: Set up Nextflow - uses: nf-core/setup-nextflow@v2 - with: - version: "${{ matrix.NXF_VER }}" - - - name: Set up Apptainer - if: matrix.profile == 'singularity' - uses: eWaterCycle/setup-apptainer@main - - - name: Set up Singularity - if: matrix.profile == 'singularity' - run: | - 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: Set up Conda - if: matrix.profile == 'conda' - run: | - 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 ${{ matrix.NXF_VER }} | ${{ matrix.test_name }} | ${{ matrix.profile }}" - run: | - nextflow run ${GITHUB_WORKSPACE} -profile ${{ matrix.test_name }},${{ matrix.profile }} --outdir ./results diff --git a/.github/workflows/clean-up.yml b/.github/workflows/clean-up.yml index 0b6b1f2..6adb0ff 100644 --- a/.github/workflows/clean-up.yml +++ b/.github/workflows/clean-up.yml @@ -10,7 +10,7 @@ jobs: issues: write pull-requests: write steps: - - uses: actions/stale@28ca1036281a5e5922ead5184a1bbf96e5fc984e # v9 + - uses: actions/stale@5f858e3efba33a5ca4407a664cc011ad407f2008 # v10 with: stale-issue-message: "This issue has been tagged as awaiting-changes or awaiting-feedback by an nf-core contributor. Remove stale label or add a comment otherwise this issue will be closed in 20 days." stale-pr-message: "This PR has been tagged as awaiting-changes or awaiting-feedback by an nf-core contributor. Remove stale label or add a comment if it is still useful." diff --git a/.github/workflows/download_pipeline.yml b/.github/workflows/download_pipeline.yml index ab06316..32232b0 100644 --- a/.github/workflows/download_pipeline.yml +++ b/.github/workflows/download_pipeline.yml @@ -12,14 +12,6 @@ on: required: true default: "dev" pull_request: - types: - - opened - - edited - - synchronize - branches: - - main - - master - pull_request_target: branches: - main - master @@ -48,13 +40,15 @@ jobs: steps: - name: Install 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 - - uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5 + - uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6 with: - python-version: "3.12" + python-version: "3.14" architecture: "x64" - name: Setup Apptainer @@ -65,7 +59,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install git+https://github.com/nf-core/tools.git@dev + pip install git+https://github.com/nf-core/tools.git - name: Make a cache directory for the container images run: | @@ -120,6 +114,7 @@ jobs: echo "IMAGE_COUNT_AFTER=$image_count" >> "$GITHUB_OUTPUT" - name: Compare container image counts + id: count_comparison run: | if [ "${{ steps.count_initial.outputs.IMAGE_COUNT_INITIAL }}" -ne "${{ steps.count_afterwards.outputs.IMAGE_COUNT_AFTER }}" ]; then initial_count=${{ steps.count_initial.outputs.IMAGE_COUNT_INITIAL }} @@ -132,3 +127,10 @@ jobs: else echo "The pipeline can be downloaded successfully!" fi + + - name: Upload Nextflow logfile for debugging purposes + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5 + with: + name: nextflow_logfile.txt + path: .nextflow.log* + include-hidden-files: true diff --git a/.github/workflows/fix-linting.yml b/.github/workflows/fix_linting.yml similarity index 80% rename from .github/workflows/fix-linting.yml rename to .github/workflows/fix_linting.yml index 4ea9855..50d4c5a 100644 --- a/.github/workflows/fix-linting.yml +++ b/.github/workflows/fix_linting.yml @@ -13,13 +13,13 @@ jobs: runs-on: ubuntu-latest steps: # Use the @nf-core-bot token to check out so we can push later - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 with: token: ${{ secrets.nf_core_bot_auth_token }} # indication that the linting is being fixed - name: React on comment - uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4 + uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9 # v5 with: comment-id: ${{ github.event.comment.id }} reactions: eyes @@ -32,9 +32,9 @@ jobs: GITHUB_TOKEN: ${{ secrets.nf_core_bot_auth_token }} # Install and run pre-commit - - uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5 + - uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6 with: - python-version: "3.12" + python-version: "3.14" - name: Install pre-commit run: pip install pre-commit @@ -47,7 +47,7 @@ jobs: # indication that the linting has finished - name: react if linting finished succesfully if: steps.pre-commit.outcome == 'success' - uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4 + uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9 # v5 with: comment-id: ${{ github.event.comment.id }} reactions: "+1" @@ -67,21 +67,21 @@ jobs: - name: react if linting errors were fixed id: react-if-fixed if: steps.commit-and-push.outcome == 'success' - uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4 + uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9 # v5 with: comment-id: ${{ github.event.comment.id }} reactions: hooray - name: react if linting errors were not fixed if: steps.commit-and-push.outcome == 'failure' - uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4 + uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9 # v5 with: comment-id: ${{ github.event.comment.id }} reactions: confused - name: react if linting errors were not fixed if: steps.commit-and-push.outcome == 'failure' - uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4 + uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9 # v5 with: issue-number: ${{ github.event.issue.number }} body: | diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml index dbd52d5..7a527a3 100644 --- a/.github/workflows/linting.yml +++ b/.github/workflows/linting.yml @@ -3,9 +3,6 @@ name: nf-core linting # It runs the `nf-core pipelines lint` and markdown lint tests to ensure # that the code meets the nf-core guidelines. on: - push: - branches: - - dev pull_request: release: types: [published] @@ -14,12 +11,12 @@ jobs: pre-commit: runs-on: ubuntu-latest steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 - - name: Set up Python 3.12 - uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5 + - name: Set up Python 3.14 + uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6 with: - python-version: "3.12" + python-version: "3.14" - name: Install pre-commit run: pip install pre-commit @@ -31,18 +28,18 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out pipeline code - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 - name: Install Nextflow uses: nf-core/setup-nextflow@v2 - - uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5 + - uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6 with: - python-version: "3.12" + python-version: "3.14" architecture: "x64" - name: read .nf-core.yml - uses: pietrobolcato/action-read-yaml@1.1.0 + uses: pietrobolcato/action-read-yaml@9f13718d61111b69f30ab4ac683e67a56d254e1d # 1.1.0 id: read_yml with: config: ${{ github.workspace }}/.nf-core.yml @@ -74,7 +71,7 @@ jobs: - name: Upload linting log file artifact if: ${{ always() }} - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5 with: name: linting-logs path: | diff --git a/.github/workflows/linting_comment.yml b/.github/workflows/linting_comment.yml index 95b6b6a..e6e9bc2 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@20319c5641d495c8a52e688b7dc5fada6c3a9fbc # v8 + uses: dawidd6/action-download-artifact@ac66b43f0e6a346234dd65d4d0c8fbb31cb316e5 # v11 with: workflow: linting.yml workflow_conclusion: completed @@ -21,7 +21,7 @@ jobs: run: echo "pr_number=$(cat linting-logs/PR_number.txt)" >> $GITHUB_OUTPUT - name: Post PR comment - uses: marocchino/sticky-pull-request-comment@331f8f5b4215f0445d3c07b4967662a32a2d3e31 # v2 + uses: marocchino/sticky-pull-request-comment@773744901bac0e8cbb5a0dc842800d45e9b2b405 # v2 with: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} number: ${{ steps.pr_number.outputs.pr_number }} diff --git a/.github/workflows/nf-test.yml b/.github/workflows/nf-test.yml new file mode 100644 index 0000000..c98d76e --- /dev/null +++ b/.github/workflows/nf-test.yml @@ -0,0 +1,144 @@ +name: Run nf-test +on: + pull_request: + paths-ignore: + - "docs/**" + - "**/meta.yml" + - "**/*.md" + - "**/*.png" + - "**/*.svg" + release: + types: [published] + workflow_dispatch: + +# Cancel if a newer run is started +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + NFT_VER: "0.9.3" + NFT_WORKDIR: "~" + NXF_ANSI_LOG: false + NXF_SINGULARITY_CACHEDIR: ${{ github.workspace }}/.singularity + NXF_SINGULARITY_LIBRARYDIR: ${{ github.workspace }}/.singularity + +jobs: + nf-test-changes: + name: nf-test-changes + runs-on: # use self-hosted runners + - runs-on=${{ github.run_id }}-nf-test-changes + - runner=4cpu-linux-x64 + outputs: + shard: ${{ steps.set-shards.outputs.shard }} + total_shards: ${{ steps.set-shards.outputs.total_shards }} + steps: + - name: Clean Workspace # Purge the workspace in case it's running on a self-hosted runner + run: | + ls -la ./ + rm -rf ./* || true + rm -rf ./.??* || true + ls -la ./ + - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 + with: + fetch-depth: 0 + + - name: get number of shards + id: set-shards + uses: ./.github/actions/get-shards + env: + NFT_VER: ${{ env.NFT_VER }} + with: + max_shards: 7 + + - name: debug + run: | + echo ${{ steps.set-shards.outputs.shard }} + echo ${{ steps.set-shards.outputs.total_shards }} + + nf-test: + name: "${{ matrix.profile }} | ${{ matrix.NXF_VER }} | ${{ matrix.shard }}/${{ needs.nf-test-changes.outputs.total_shards }}" + needs: [nf-test-changes] + if: ${{ needs.nf-test-changes.outputs.total_shards != '0' }} + runs-on: # use self-hosted runners + - runs-on=${{ github.run_id }}-nf-test + - runner=4cpu-linux-x64 + strategy: + fail-fast: false + matrix: + shard: ${{ fromJson(needs.nf-test-changes.outputs.shard) }} + profile: [conda, docker, singularity] + isMain: + - ${{ github.base_ref == 'master' || github.base_ref == 'main' }} + # Exclude conda and singularity on dev + exclude: + - isMain: false + profile: "conda" + - isMain: false + profile: "singularity" + NXF_VER: + - "25.04.0" + - "latest-everything" + env: + NXF_ANSI_LOG: false + TOTAL_SHARDS: ${{ needs.nf-test-changes.outputs.total_shards }} + + steps: + - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 + with: + fetch-depth: 0 + + - name: Run nf-test + id: run_nf_test + uses: ./.github/actions/nf-test + continue-on-error: ${{ matrix.NXF_VER == 'latest-everything' }} + env: + NFT_WORKDIR: ${{ env.NFT_WORKDIR }} + NXF_VERSION: ${{ matrix.NXF_VER }} + with: + profile: ${{ matrix.profile }} + shard: ${{ matrix.shard }} + total_shards: ${{ env.TOTAL_SHARDS }} + + - name: Report test status + if: ${{ always() }} + run: | + if [[ "${{ steps.run_nf_test.outcome }}" == "failure" ]]; then + echo "::error::Test with ${{ matrix.NXF_VER }} failed" + # Add to workflow summary + echo "## ❌ Test failed: ${{ matrix.profile }} | ${{ matrix.NXF_VER }} | Shard ${{ matrix.shard }}/${{ env.TOTAL_SHARDS }}" >> $GITHUB_STEP_SUMMARY + if [[ "${{ matrix.NXF_VER }}" == "latest-everything" ]]; then + echo "::warning::Test with latest-everything failed but will not cause workflow failure. Please check if the error is expected or if it needs fixing." + fi + if [[ "${{ matrix.NXF_VER }}" != "latest-everything" ]]; then + exit 1 + fi + fi + + confirm-pass: + needs: [nf-test] + if: always() + runs-on: # use self-hosted runners + - runs-on=${{ github.run_id }}-confirm-pass + - runner=2cpu-linux-x64 + steps: + - name: One or more tests failed (excluding latest-everything) + if: ${{ contains(needs.*.result, 'failure') }} + run: exit 1 + + - name: One or more tests cancelled + if: ${{ contains(needs.*.result, 'cancelled') }} + run: exit 1 + + - name: All tests ok + if: ${{ contains(needs.*.result, 'success') }} + run: exit 0 + + - name: debug-print + if: always() + run: | + echo "::group::DEBUG: `needs` Contents" + echo "DEBUG: toJSON(needs) = ${{ toJSON(needs) }}" + echo "DEBUG: toJSON(needs.*.result) = ${{ toJSON(needs.*.result) }}" + echo "::endgroup::" diff --git a/.github/workflows/release-announcements.yml b/.github/workflows/release-announcements.yml index 76a9e67..431d3d4 100644 --- a/.github/workflows/release-announcements.yml +++ b/.github/workflows/release-announcements.yml @@ -14,6 +14,10 @@ jobs: 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' ' ')" | sed 's/-//g' >> $GITHUB_OUTPUT + - name: get description + id: get_description + run: | + echo "description=$(curl -s https://nf-co.re/pipelines.json | jq -r '.remote_workflows[] | select(.full_name == "${{ github.repository }}") | .description')" >> $GITHUB_OUTPUT - uses: rzr/fediverse-action@master with: access-token: ${{ secrets.MASTODON_ACCESS_TOKEN }} @@ -22,7 +26,7 @@ jobs: # https://docs.github.com/en/developers/webhooks-and-events/webhooks/webhook-events-and-payloads#release message: | Pipeline release! ${{ github.repository }} v${{ github.event.release.tag_name }} - ${{ github.event.release.name }}! - + ${{ steps.get_description.outputs.description }} Please see the changelog: ${{ github.event.release.html_url }} ${{ steps.get_topics.outputs.topics }} #nfcore #openscience #nextflow #bioinformatics @@ -30,7 +34,7 @@ jobs: bsky-post: runs-on: ubuntu-latest steps: - - uses: zentered/bluesky-post-action@80dbe0a7697de18c15ad22f4619919ceb5ccf597 # v0.1.0 + - uses: zentered/bluesky-post-action@6461056ea355ea43b977e149f7bf76aaa572e5e8 # v0.3.0 with: post: | Pipeline release! ${{ github.repository }} v${{ github.event.release.tag_name }} - ${{ github.event.release.name }}! diff --git a/.github/workflows/template-version-comment.yml b/.github/workflows/template-version-comment.yml new file mode 100644 index 0000000..e8560fc --- /dev/null +++ b/.github/workflows/template-version-comment.yml @@ -0,0 +1,46 @@ +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@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 + with: + ref: ${{ github.event.pull_request.head.sha }} + + - name: Read template version from .nf-core.yml + uses: nichmor/minimal-read-yaml@1f7205277e25e156e1f63815781db80a6d490b8f # v0.0.2 + 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: 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: | + 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). + # diff --git a/.gitignore b/.gitignore index a0e1d09..d65563e 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,5 @@ testing* *.pyc null/ .qodo +.nf-test +.nf-test.log diff --git a/.gitpod.yml b/.gitpod.yml deleted file mode 100644 index 83599f6..0000000 --- a/.gitpod.yml +++ /dev/null @@ -1,10 +0,0 @@ -image: nfcore/gitpod:latest -tasks: - - name: Update Nextflow and setup pre-commit - command: | - pre-commit install --install-hooks - nextflow self-update - -vscode: - extensions: - - nf-core.nf-core-extensionpack # https://github.com/nf-core/vscode-extensionpack diff --git a/.nf-core.yml b/.nf-core.yml index 835dc66..363b723 100644 --- a/.nf-core.yml +++ b/.nf-core.yml @@ -2,7 +2,7 @@ lint: files_unchanged: - docs/images/nf-core-coproid_logo_light.png - docs/images/nf-core-coproid_logo_dark.png -nf_core_version: 3.2.0 +nf_core_version: 3.5.2 repository_type: pipeline template: author: Maxime Borry & Meriam Van Os @@ -13,4 +13,4 @@ template: org: nf-core outdir: . skip_features: [] - version: 2.0.0 + version: 2.0.1 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1dec865..d06777a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -4,10 +4,24 @@ repos: hooks: - id: prettier additional_dependencies: - - prettier@3.2.5 - - - repo: https://github.com/editorconfig-checker/editorconfig-checker.python - rev: "3.1.2" + - prettier@3.6.2 + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v6.0.0 hooks: - - id: editorconfig-checker - alias: ec + - id: trailing-whitespace + args: [--markdown-linebreak-ext=md] + exclude: | + (?x)^( + .*ro-crate-metadata.json$| + modules/nf-core/.*| + subworkflows/nf-core/.*| + .*\.snap$ + )$ + - id: end-of-file-fixer + exclude: | + (?x)^( + .*ro-crate-metadata.json$| + modules/nf-core/.*| + subworkflows/nf-core/.*| + .*\.snap$ + )$ diff --git a/.prettierignore b/.prettierignore index edd29f0..dd749d4 100644 --- a/.prettierignore +++ b/.prettierignore @@ -10,4 +10,7 @@ testing/ testing* *.pyc bin/ +.nf-test/ ro-crate-metadata.json +modules/nf-core/ +subworkflows/nf-core/ diff --git a/.prettierrc.yml b/.prettierrc.yml index c81f9a7..07dbd8b 100644 --- a/.prettierrc.yml +++ b/.prettierrc.yml @@ -1 +1,6 @@ printWidth: 120 +tabWidth: 4 +overrides: + - files: "*.{md,yml,yaml,html,css,scss,js,cff}" + options: + tabWidth: 2 diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ee48cc..128c2fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,20 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## v2.0.1 - 06/02/2026 + +### `Changed` + +- Fixed the Nextflow version badge to ≤24.10.5 +- Added some extra information to the README about running the pipeline offline and on HPC with container restrictions +- Updated nf-core/tools to the latest version (by @jfy133) + +### `Dependencies` + +| Tool | Old Version | New Version | +| ------------- | ----------- | ----------- | +| nf-core/tools | 3.2.0 | 3.5.2 | + ## v2.0.0 - 22/05/2025 nf-core/coproid v2.0 is based on [nf-core](https://nf-co.re/) DSL2 template. @@ -29,3 +43,46 @@ This release is a complete rewrite of the original nf-core/coproid pipeline, ori ### `Dependencies` ### `Deprecated` + +## v1.1.1 + +### Fixed + +- Fix [#37](https://github.com/nf-core/coproid/issues/37) with conda environment and docker container update + +## v1.1.0 + +- Update mapped basepair count to be quicker and include it in report [#14](https://github.com/nf-core/coproid/pull/14) +- Remove outdated scripts [#14](https://github.com/nf-core/coproid/pull/14) +- Update logo to match font [#13](https://github.com/nf-core/coproid/pull/13) +- Update Sourcepredict to version 0.4 and reflect new parameters in coproID [#19](https://github.com/nf-core/coproid/pull/19) [e4afca7](https://github.com/nf-core/coproid/commit/e4afca7059c00ebbc753dd02d4aed3f3a1b3b7b8) [ + c2d4164](https://github.com/nf-core/coproid/pull/20/commits/c2d4164bf068ed4fc92d529470b0a3af3a69113a) +- Changed bedtools bamtofastq to samtools fastq [e4afca7](https://github.com/nf-core/coproid/commit/e4afca7059c00ebbc753dd02d4aed3f3a1b3b7b8) +- Fixed column names in report (`PC*` to `DIM*`) [e4afca7](https://github.com/nf-core/coproid/commit/63a6bc6998c240b77791916c243d538b2268b5d5) +- Update README to inlude Zenodo badge, Quick start, contributor section, and tools references. [9874ae8](https://github.com/nf-core/coproid/commit/9874ae87c88842d75c29088672aa81023408d4e7) [e85988b](https://github.com/nf-core/coproid/commit/e85988b883539aa51461e749bc14ec6563f62fc8) +- Update documentation [bedfdde](https://github.com/nf-core/coproid/commit/bedfddec8500adac8e0cb9cc8e0df2dc6a784f15) +- Update Nextflow minimum version to 19.04.0 [44999fd](https://github.com/nf-core/coproid/commit/44999fd4d38b21d53f970621dbf3587c044da8d1) +- Update travis for more recent nextflow requirements [1e3e454](https://github.com/nf-core/coproid/commit/1e3e454e72f1bc8eb43aaa1e5165981cb77a56dc) +- Adapt coproID to nf-core tools 1.8 release [#21](https://github.com/nf-core/coproid/pull/21) +- Add social preview image [#22](https://github.com/nf-core/coproid/pull/22) +- Fix Kraken2 segmentation error [#26](https://github.com/nf-core/coproid/pull/26) +- Update to nf-core tools 1.9 release, and doc for new version of sphinx [#27](https://github.com/nf-core/coproid/pull/27) + +## v1.0.0 - 2019-04-26 + +Initial release of nf-core/coproid, created with the [nf-core](http://nf-co.re/) template. +Adapting [CoproID](https://github.com/maxibor/coproID/tree/dev) to nf-core template + +### Improvements over [coproID version 0.6](https://github.com/maxibor/coproID/releases/tag/v0.6.0) + +- Support for 3 organism comparison +- Adding [sourcepredict](https://github.com/maxibor/sourcepredict) +- Updating reports to have interactive plotting +- Updated to use Kraken2 instead of Kraken1 +- Adding docker + +### Adaptions to port to _nf-core_ + +- Major redefinition of the channels creation to adapt to iGenomes and profiles +- Added and adapted all the nf-core boilerplate code for support of configs and containers +- Improved documentation diff --git a/CITATIONS.md b/CITATIONS.md index d95778b..7aa82b4 100644 --- a/CITATIONS.md +++ b/CITATIONS.md @@ -26,7 +26,7 @@ - [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]. - [Kraken2](https://doi.org/10.1186/s13059-019-1891-0) @@ -34,7 +34,7 @@ - [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. - [PyDamage](https://doi.org/10.7717/peerj.11845) diff --git a/README.md b/README.md index af6d429..8234cf3 100644 --- a/README.md +++ b/README.md @@ -5,17 +5,19 @@ -[![GitHub Actions CI Status](https://github.com/nf-core/coproid/actions/workflows/ci.yml/badge.svg)](https://github.com/nf-core/coproid/actions/workflows/ci.yml) -[![GitHub Actions Linting Status](https://github.com/nf-core/coproid/actions/workflows/linting.yml/badge.svg)](https://github.com/nf-core/coproid/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/coproid/results)[![Cite with Zenodo](http://img.shields.io/badge/DOI-10.5281/zenodo.7292889-1073c8?labelColor=000000)](https://doi.org/10.5281/zenodo.7292889) +[![Open in GitHub Codespaces](https://img.shields.io/badge/Open_In_GitHub_Codespaces-black?labelColor=grey&logo=github)](https://github.com/codespaces/new/nf-core/coproid) +[![GitHub Actions CI Status](https://github.com/nf-core/coproid/actions/workflows/nf-test.yml/badge.svg)](https://github.com/nf-core/coproid/actions/workflows/nf-test.yml) +[![GitHub Actions Linting Status](https://github.com/nf-core/coproid/actions/workflows/linting.yml/badge.svg)](https://github.com/nf-core/coproid/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/coproid/results)[![Cite with Zenodo](http://img.shields.io/badge/DOI-10.5281/zenodo.2653756-1073c8?labelColor=000000)](https://doi.org/10.5281/zenodo.2653756) [![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%A524.10.0-23aa62.svg)](https://www.nextflow.io/) +[![Nextflow](https://img.shields.io/badge/version-%E2%89%A525.04.0-green?style=flat&logo=nextflow&logoColor=white&color=%230DC09D&link=https%3A%2F%2Fnextflow.io)](https://www.nextflow.io/) +[![nf-core template version](https://img.shields.io/badge/nf--core_template-3.5.1-green?style=flat&logo=nfcore&logoColor=white&color=%2324B064&link=https%3A%2F%2Fnf-co.re)](https://github.com/nf-core/tools/releases/tag/3.5.1) [![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/) [![Launch on Seqera Platform](https://img.shields.io/badge/Launch%20%F0%9F%9A%80-Seqera%20Platform-%234256e7)](https://cloud.seqera.io/launch?pipeline=https://github.com/nf-core/coproid) -[![Get help on Slack](http://img.shields.io/badge/slack-nf--core%20%23coproid-4A154B?labelColor=000000&logo=slack)](https://nfcore.slack.com/channels/coproid)[![Follow on Twitter](http://img.shields.io/badge/twitter-%40nf__core-1DA1F2?labelColor=000000&logo=twitter)](https://twitter.com/nf_core)[![Follow on Mastodon](https://img.shields.io/badge/mastodon-nf__core-6364ff?labelColor=FFFFFF&logo=mastodon)](https://mstdn.science/@nf_core)[![Watch on YouTube](http://img.shields.io/badge/youtube-nf--core-FF0000?labelColor=000000&logo=youtube)](https://www.youtube.com/c/nf-core) +[![Get help on Slack](http://img.shields.io/badge/slack-nf--core%20%23coproid-4A154B?labelColor=000000&logo=slack)](https://nfcore.slack.com/channels/coproid)[![Follow on Bluesky](https://img.shields.io/badge/bluesky-%40nf__core-1185fe?labelColor=000000&logo=bluesky)](https://bsky.app/profile/nf-co.re)[![Follow on Mastodon](https://img.shields.io/badge/mastodon-nf__core-6364ff?labelColor=FFFFFF&logo=mastodon)](https://mstdn.science/@nf_core)[![Watch on YouTube](http://img.shields.io/badge/youtube-nf--core-FF0000?labelColor=000000&logo=youtube)](https://www.youtube.com/c/nf-core) ## Introduction @@ -27,22 +29,20 @@ A. First coproID performs comparative mapping of all reads agains two (or three) B. Then coproID performs metagenomic taxonomic profiling, and compares the obtained profiles to modern reference samples of the target species metagenomes. Using machine learning, coproID then estimates the host source from the metagenomic taxonomic composition (SourcepredictProportion). C. Finally, coproID combines the A and B proportions to predict the likely host of the metagenomic sample. - - ![nf-core-coproid workflow overview](docs/images/coproid_workflow.jpg?raw=true "nf-core-coproid workflow overview") -**Wokflow overview** +**Workflow overview** 1. Read QC ([`FastQC`](https://www.bioinformatics.babraham.ac.uk/projects/fastqc/)) -1. Fastp to remove adapters and low-complexity reads ([`fastp`](https://doi.org/10.1002/imt2.107)) -1. Mapping or reads to multiple reference genomes ([`Bowtie2`](https://bowtie-bio.sourceforge.net/bowtie2)) -1. Lowest Common Ancestor analysis to retain only genome specific reads ([`sam2lca`](github.com/maxibor/sam2lca)) -1. Ancient DNA damage estimation with [pyDamage](https://pydamage.readthedocs.io/en/latest/README.html) and [DamageProfiler](https://github.com/Integrative-Transcriptomics/DamageProfiler) -1. Taxonomic profiling of unmapped reads ([`kraken2`](https://ccb.jhu.edu/software/kraken2/)) -1. Source predicting based on taxonic profiles ([`sourcepredict`](https://sourcepredict.readthedocs.io/)) -1. Combining host and microbial predictions to calculate overall proportions. -1. ([`MultiQC`](http://multiqc.info/)) aggregate results of several individual modules. -1. ([Quartonotebook])(https://quarto.org/) creates a report with sample results. +2. Fastp to remove adapters and low-complexity reads ([`fastp`](https://doi.org/10.1002/imt2.107)) +3. Mapping or reads to multiple reference genomes ([`Bowtie2`](https://bowtie-bio.sourceforge.net/bowtie2)) +4. Lowest Common Ancestor analysis to retain only genome specific reads ([`sam2lca`](github.com/maxibor/sam2lca)) +5. Ancient DNA damage estimation with [pyDamage](https://pydamage.readthedocs.io/en/latest/README.html) and [DamageProfiler](https://github.com/Integrative-Transcriptomics/DamageProfiler) +6. Taxonomic profiling of unmapped reads ([`kraken2`](https://ccb.jhu.edu/software/kraken2/)) +7. Source predicting based on taxonic profiles ([`sourcepredict`](https://sourcepredict.readthedocs.io/)) +8. Combining host and microbial predictions to calculate overall proportions. +9. ([`MultiQC`](http://multiqc.info/)) aggregate results of several individual modules. +10. ([Quartonotebook])(https://quarto.org/) creates a report with sample results. The coproID pipeline is built using Nextflow, a workflow tool to run tasks across multiple compute infrastructures in a very portable manner. It comes with docker containers making installation trivial and results highly reproducible. The Nextflow DSL2 implementation of this pipeline uses one container per process which makes it much easier to maintain and update software dependencies. Where possible, these processes have been submitted to and installed from nf-core/modules in order to make them available to all nf-core pipelines, and to everyone within the Nextflow community! @@ -51,7 +51,6 @@ The coproID pipeline is built using Nextflow, a workflow tool to run tasks acros > [!NOTE] > If you are new to Nextflow and nf-core, please refer to [this page](https://nf-co.re/docs/usage/installation) on how to set-up Nextflow. Make sure to [test your setup](https://nf-co.re/docs/usage/introduction#how-to-run-a-pipeline) with `-profile test` before running the workflow on actual data. -Pipeline usage First, prepare a samplesheet with your input data that looks as follows: `samplesheet.csv`: @@ -64,9 +63,8 @@ SINGLE_END,SINGLE_END_S4_L003_R1_001.fastq.gz, Each row represents a fastq file (single-end) or a pair of fastq files (paired end). -:::warning -Make sure that your reference genomes are from ncbi, so sam2lca can extract the taxid! -::: +> [!WARNING] +> Make sure that your reference genomes are from ncbi, so sam2lca can extract the taxid! Second, prepare a genomesheet with your input genome references that looks as follows: @@ -97,16 +95,18 @@ nextflow run nf-core/coproid \ ``` > [!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). + +### Running the pipeline offline or on an HPC with container restrictions + +If you are working offline or on an HPC system with container restrictions, the sam2lca database download may fail. To work around this issue, you can pre-download (see the [sam2lca documentation](https://sam2lca.readthedocs.io/en/latest/README.html)) or transfer the sam2lca database and use the `--sam2lca_db` parameter to point directly to the `~/.sam2lca` directory. In this instance, adjust the name of the pre-downloaded database (for example `nucl`) accordingly with `--sam2lca_acc2tax`. For more details and further functionality, please refer to the [usage documentation](https://nf-co.re/coproid/usage) and the [parameter documentation](https://nf-co.re/coproid/parameters). ## Pipeline output To see the results of an example test run with a full size dataset refer to the [results](https://nf-co.re/coproid/results) tab on the nf-core website pipeline page. -For more details about the output files and reports, please refer to the -[output documentation](https://nf-co.re/coproid/output). +For more details about the output files and reports, please refer to the [output documentation](https://nf-co.re/coproid/output). ## Credits @@ -122,7 +122,7 @@ For further information or help, don't hesitate to get in touch on the [Slack `# ## Citations -If you use nf-core/coproid for your analysis, please cite it using the following doi: [10.5281/zenodo.7292889](https://doi.org/10.5281/zenodo.7292889) +If you use nf-core/coproid for your analysis, please cite it using the following doi: [10.5281/zenodo.2653756](https://doi.org/10.5281/zenodo.2653756) An extensive list of references for the tools used by the pipeline can be found in the [`CITATIONS.md`](CITATIONS.md) file. diff --git a/assets/multiqc_config.yml b/assets/multiqc_config.yml index f684906..b7d6390 100644 --- a/assets/multiqc_config.yml +++ b/assets/multiqc_config.yml @@ -1,7 +1,6 @@ report_comment: > - This report has been generated by the nf-core/coproid - analysis pipeline. For information about how to interpret these results, please see the - documentation. + This report has been generated by the nf-core/coproid analysis pipeline. + For information about how to interpret these results, please see the documentation. report_section_order: general_stats: order: 1000 diff --git a/conf/base.config b/conf/base.config index f78a25a..11d5024 100644 --- a/conf/base.config +++ b/conf/base.config @@ -10,52 +10,56 @@ process { - cpus = { 1 * task.attempt } - memory = { 6.GB * task.attempt } - time = { 4.h * task.attempt } + cpus = { 1 * task.attempt } + memory = { 6.GB * task.attempt } + time = { 4.h * task.attempt } - errorStrategy = { task.exitStatus in ((130..145) + 104) ? 'retry' : 'finish' } + errorStrategy = { task.exitStatus in ((130..145) + 104 + 175) ? 'retry' : 'finish' } maxRetries = 1 maxErrors = '-1' - withLabel:process_single { - cpus = { 1 } + withLabel: process_single { + cpus = { 1 } memory = { 6.GB * task.attempt } - time = { 4.h * task.attempt } + time = { 4.h * task.attempt } } - withLabel:process_low { - cpus = { 2 * task.attempt } + withLabel: process_low { + cpus = { 2 * task.attempt } memory = { 12.GB * task.attempt } - time = { 4.h * task.attempt } + time = { 4.h * task.attempt } } - withLabel:process_medium { - cpus = { 6 * task.attempt } + withLabel: process_medium { + cpus = { 6 * task.attempt } memory = { 36.GB * task.attempt } - time = { 8.h * task.attempt } + time = { 8.h * task.attempt } } - withLabel:process_high { - cpus = { 12 * task.attempt } + withLabel: process_high { + cpus = { 12 * task.attempt } memory = { 72.GB * task.attempt } - time = { 16.h * task.attempt } + time = { 16.h * task.attempt } } - withLabel:process_long { - time = { 20.h * task.attempt } + withLabel: process_long { + time = { 20.h * task.attempt } } - withLabel:process_high_memory { + withLabel: process_high_memory { memory = { 200.GB * task.attempt } } - withLabel:error_ignore { + withLabel: error_ignore { errorStrategy = 'ignore' } - withLabel:error_retry { + withLabel: error_retry { errorStrategy = 'retry' maxRetries = 2 } + withLabel: process_gpu { + ext.use_gpu = { workflow.profile.contains('gpu') } + accelerator = { workflow.profile.contains('gpu') ? 1 : null } + } // Custom label for process that need very few resources - withLabel:process_short { - cpus = { 1 } - memory = { 6.GB * task.attempt } - time = { 10.m * task.attempt } + withLabel: process_short { + cpus = { 1 } + memory = { 6.GB * task.attempt } + time = { 10.m * task.attempt } } } diff --git a/conf/test.config b/conf/test.config index f23315c..c8c65f4 100644 --- a/conf/test.config +++ b/conf/test.config @@ -14,7 +14,7 @@ process { resourceLimits = [ cpus: 4, memory: '15.GB', - time: '1.h' + time: '1.h', ] } @@ -23,16 +23,17 @@ params { config_profile_description = 'Minimal test dataset to check pipeline function' // Input data - input = 'https://github.com/nf-core/test-datasets/raw/coproid/reads/samplesheet.csv' - genome_sheet = 'https://github.com/nf-core/test-datasets/raw/coproid/genomes/genomesheet.csv' - file_prefix = 'coproid' - sam2lca_identity = 0.8 - sam2lca_acc2tax = 'adnamap' //'adnamap' for db build, 'nucl' for default - kraken2_db = 'https://github.com/nf-core/test-datasets/raw/coproid/kraken.tar.gz' - sp_sources = 'https://github.com/nf-core/test-datasets/raw/coproid/sourcepredict/test_sources.csv' - sp_labels = 'https://github.com/nf-core/test-datasets/raw/coproid/sourcepredict/test_labels.csv' - taxa_sqlite = params.modules_testdata_base_path + '/genomics/prokaryotes/metagenome/taxonomy/misc/taxa_sqlite.xz' - taxa_sqlite_traverse_pkl = params.modules_testdata_base_path + '/genomics/prokaryotes/metagenome/taxonomy/misc/taxa_sqlite_traverse.pkl' + input = 'https://github.com/nf-core/test-datasets/raw/coproid/reads/samplesheet.csv' + genome_sheet = 'https://github.com/nf-core/test-datasets/raw/coproid/genomes/genomesheet.csv' + file_prefix = 'coproid' + sam2lca_identity = 0.8 + sam2lca_acc2tax = 'adnamap' + //'adnamap' for db build, 'nucl' for default + kraken2_db = 'https://github.com/nf-core/test-datasets/raw/coproid/kraken.tar.gz' + sp_sources = 'https://github.com/nf-core/test-datasets/raw/coproid/sourcepredict/test_sources.csv' + sp_labels = 'https://github.com/nf-core/test-datasets/raw/coproid/sourcepredict/test_labels.csv' + taxa_sqlite = params.modules_testdata_base_path + '/genomics/prokaryotes/metagenome/taxonomy/misc/taxa_sqlite.xz' + taxa_sqlite_traverse_pkl = params.modules_testdata_base_path + '/genomics/prokaryotes/metagenome/taxonomy/misc/taxa_sqlite_traverse.pkl' } process { @@ -40,4 +41,3 @@ process { ext.args = '-N 1 -D 20 -R 3 -L 20 -i S,1,0.50' } } - diff --git a/docs/images/mqc_fastqc_adapter.png b/docs/images/mqc_fastqc_adapter.png deleted file mode 100755 index 361d0e4..0000000 Binary files a/docs/images/mqc_fastqc_adapter.png and /dev/null differ diff --git a/docs/images/mqc_fastqc_counts.png b/docs/images/mqc_fastqc_counts.png deleted file mode 100755 index cb39ebb..0000000 Binary files a/docs/images/mqc_fastqc_counts.png and /dev/null differ diff --git a/docs/images/mqc_fastqc_quality.png b/docs/images/mqc_fastqc_quality.png deleted file mode 100755 index a4b89bf..0000000 Binary files a/docs/images/mqc_fastqc_quality.png and /dev/null differ diff --git a/docs/usage.md b/docs/usage.md index 646bbe1..61fbe38 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -115,9 +115,8 @@ If you wish to repeatedly use the same parameters for multiple runs, rather than Pipeline settings can be provided in a `yaml` or `json` file via `-params-file `. -:::warning -Do not use `-c ` to specify parameters as this will result in errors. Custom config files specified with `-c` must only be used for [tuning process resource specifications](https://nf-co.re/docs/usage/configuration#tuning-workflow-resources), other infrastructural tweaks (such as output directories), or module arguments (args). -::: +> [!WARNING] +> Do not use `-c ` to specify parameters as this will result in errors. Custom config files specified with `-c` must only be used for [tuning process resource specifications](https://nf-co.re/docs/usage/configuration#tuning-workflow-resources), other infrastructural tweaks (such as output directories), or module arguments (args). The above pipeline run specified with a params file in yaml format: @@ -125,9 +124,9 @@ The above pipeline run specified with a params file in yaml format: nextflow run nf-core/coproid -profile docker -params-file params.yaml ``` -with `params.yaml` containing: +with: -```yaml +```yaml title="params.yaml" input: './samplesheet.csv' genome_sheet: './genomesheet.csv' outdir: './results/' @@ -146,23 +145,21 @@ nextflow pull nf-core/coproid ### Reproducibility -It is a good idea to specify a pipeline version when running the pipeline on your data. This ensures that a specific version of the pipeline code and software are used when you run your pipeline. If you keep using the same tag, you'll be running the same version of the pipeline, even if there have been changes to the code since. +It is a good idea to specify the pipeline version when running the pipeline on your data. This ensures that a specific version of the pipeline code and software are used when you run your pipeline. If you keep using the same tag, you'll be running the same version of the pipeline, even if there have been changes to the code since. First, go to the [nf-core/coproid releases page](https://github.com/nf-core/coproid/releases) and find the latest pipeline version - numeric only (eg. `1.3.1`). Then specify this when running the pipeline with `-r` (one hyphen) - eg. `-r 1.3.1`. Of course, you can switch to another version by changing the number after the `-r` flag. This version number will be logged in reports when you run the pipeline, so that you'll know what you used when you look back in the future. For example, at the bottom of the MultiQC reports. -To further assist in reproducbility, you can use share and re-use [parameter files](#running-the-pipeline) to repeat pipeline runs with the same settings without having to write out a command with every single parameter. +To further assist in reproducibility, you can use share and reuse [parameter files](#running-the-pipeline) to repeat pipeline runs with the same settings without having to write out a command with every single parameter. -:::tip -If you wish to share such profile (such as upload as supplementary material for academic publications), make sure to NOT include cluster specific paths to files, nor institutional specific profiles. -::: +> [!TIP] +> If you wish to share such profile (such as upload as supplementary material for academic publications), make sure to NOT include cluster specific paths to files, nor institutional specific profiles. ## Core Nextflow arguments -:::note -These options are part of Nextflow and use a _single_ hyphen (pipeline parameters use a double-hyphen). -::: +> [!NOTE] +> These options are part of Nextflow and use a _single_ hyphen (pipeline parameters use a double-hyphen) ### `-profile` @@ -192,7 +189,7 @@ If `-profile` is not specified, the pipeline will run locally and expect all sof - `shifter` - A generic configuration profile to be used with [Shifter](https://nersc.gitlab.io/development/shifter/how-to-use/) - `charliecloud` - - A generic configuration profile to be used with [Charliecloud](https://hpc.github.io/charliecloud/) + - A generic configuration profile to be used with [Charliecloud](https://charliecloud.io/) - `apptainer` - A generic configuration profile to be used with [Apptainer](https://apptainer.org/) - `wave` diff --git a/main.nf b/main.nf index dcb3c2d..e0a4cde 100644 --- a/main.nf +++ b/main.nf @@ -15,7 +15,7 @@ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ -include { COPROID } from './workflows/coproid' +include { COPROID } from './workflows/coproid' include { PIPELINE_INITIALISATION } from './subworkflows/local/utils_nfcore_coproid_pipeline' include { PIPELINE_COMPLETION } from './subworkflows/local/utils_nfcore_coproid_pipeline' //include { getGenomeAttribute } from './subworkflows/local/utils_nfcore_coproid_pipeline' @@ -40,7 +40,6 @@ include { PIPELINE_COMPLETION } from './subworkflows/local/utils_nfcore_copr // WORKFLOW: Run main analysis pipeline depending on type of input // workflow NFCORE_COPROID { - take: samplesheet // channel: samplesheet read in from --input genomesheet // channel: genomesheet read in from --genome_sheet @@ -50,14 +49,13 @@ workflow NFCORE_COPROID { // // WORKFLOW: Run pipeline // - COPROID ( + COPROID( samplesheet, - genomesheet + genomesheet, ) emit: multiqc_report = COPROID.out.multiqc_report // channel: /path/to/multiqc_report.html - } /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -66,46 +64,39 @@ workflow NFCORE_COPROID { */ workflow { - - main: - // // SUBWORKFLOW: Run initialisation tasks // - PIPELINE_INITIALISATION ( + PIPELINE_INITIALISATION( params.version, params.validate_params, params.monochrome_logs, args, params.outdir, params.input, - params.genome_sheet + params.help, + params.help_full, + params.show_hidden, + params.genome_sheet, ) // // WORKFLOW: Run main workflow // - NFCORE_COPROID ( + NFCORE_COPROID( PIPELINE_INITIALISATION.out.samplesheet, - PIPELINE_INITIALISATION.out.genomesheet + PIPELINE_INITIALISATION.out.genomesheet, ) - // // SUBWORKFLOW: Run completion tasks // - PIPELINE_COMPLETION ( + PIPELINE_COMPLETION( params.email, params.email_on_fail, params.plaintext_email, params.outdir, params.monochrome_logs, params.hook_url, - NFCORE_COPROID.out.multiqc_report + NFCORE_COPROID.out.multiqc_report, ) } - -/* -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - THE END -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -*/ diff --git a/modules.json b/modules.json index 750a2f0..eec05aa 100644 --- a/modules.json +++ b/modules.json @@ -28,7 +28,7 @@ }, "fastqc": { "branch": "master", - "git_sha": "81880787133db07d9b4c1febd152c090eb8325dc", + "git_sha": "3009f27c4e4b6e99da4eeebe82799e13924a4a1f", "installed_by": ["modules"] }, "kraken2/kraken2": { @@ -38,7 +38,7 @@ }, "multiqc": { "branch": "master", - "git_sha": "81880787133db07d9b4c1febd152c090eb8325dc", + "git_sha": "575e1a4b51a9bad7a8cd1316a88fb85684ef7c7b", "installed_by": ["modules"] }, "pydamage/analyze": { @@ -55,7 +55,7 @@ }, "sam2lca/analyze": { "branch": "master", - "git_sha": "81880787133db07d9b4c1febd152c090eb8325dc", + "git_sha": "e657cdd0198cc26f2e501325eb7f70fbd740b6ee", "installed_by": ["modules"] }, "samtools/index": { @@ -76,7 +76,7 @@ }, "sourcepredict": { "branch": "master", - "git_sha": "81880787133db07d9b4c1febd152c090eb8325dc", + "git_sha": "f827e6ae7ed2e3fec90755907f3ac668c9e6c6a9", "installed_by": ["modules"], "patch": "modules/nf-core/sourcepredict/sourcepredict.diff" }, @@ -96,17 +96,17 @@ "nf-core": { "utils_nextflow_pipeline": { "branch": "master", - "git_sha": "c2b22d85f30a706a3073387f30380704fcae013b", + "git_sha": "05954dab2ff481bcb999f24455da29a5828af08d", "installed_by": ["subworkflows"] }, "utils_nfcore_pipeline": { "branch": "master", - "git_sha": "51ae5406a030d4da1e49e4dab49756844fdd6c7a", + "git_sha": "65f5e638d901a51534c68fd5c1c19e8112fb4df1", "installed_by": ["subworkflows"] }, "utils_nfschema_plugin": { "branch": "master", - "git_sha": "2fd2cd6d0e7b273747f32e465fdc6bcc3ae0814e", + "git_sha": "fdc08b8b1ae74f56686ce21f7ea11ad11990ce57", "installed_by": ["subworkflows"] } } diff --git a/modules.patch b/modules.patch index a26bea4..c9f3aaa 100644 --- a/modules.patch +++ b/modules.patch @@ -9,12 +9,12 @@ Changes in 'bowtie2/align/main.nf': } - def reference = fasta && extension=="cram" ? "--reference ${fasta}" : "" if (!fasta && extension=="cram") error "Fasta reference is required for CRAM output" - + def create_index = "" @@ -112,4 +111,4 @@ END_VERSIONS """ - + -}+} Changes in 'bowtie2/align/meta.yml': @@ -48,11 +48,11 @@ Changes in 'bowtie2/align/bowtie2-align.diff': @@ -15,17 +14,13 @@ val save_unaligned val sort_bam - + -@@ -114,4 +112,4 @@ - END_VERSIONS - """ -- +- --} -+} + @@ -177,11 +177,11 @@ Changes in 'fastqc/main.nf': - tag "$meta.id" + tag "${meta.id}" label 'process_medium' - + conda "${moduleDir}/environment.yml" @@ -19,27 +19,30 @@ task.ext.when == null || task.ext.when - + script: - def args = task.ext.args ?: '' - def prefix = task.ext.prefix ?: "${meta.id}" @@ -193,7 +193,7 @@ Changes in 'fastqc/main.nf': - def renamed_files = old_new_pairs.collect{ old_name, new_name -> new_name }.join(' ') + 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 @@ -201,13 +201,13 @@ Changes in 'fastqc/main.nf': + def memory_in_mb = task.memory ? task.memory.toUnit('MB').toFloat() / task.cpus : null // FastQC memory value allowed range (100 - 10000) def fastqc_memory = memory_in_mb > 10000 ? 10000 : (memory_in_mb < 100 ? 100 : memory_in_mb) - + """ - printf "%s %s\\n" $rename_to | while read old_name new_name; do + printf "%s %s\\n" ${rename_to} | while read old_name new_name; do [ -f "\${new_name}" ] || ln -s \$old_name \$new_name done - + fastqc \\ - $args \\ - --threads $task.cpus \\ @@ -217,7 +217,7 @@ Changes in 'fastqc/main.nf': + --threads ${task.cpus} \\ + --memory ${fastqc_memory} \\ + ${renamed_files} - + cat <<-END_VERSIONS > versions.yml "${task.process}": @@ -225,7 +225,7 @@ Changes in 'fastqc/tests/main.nf.test': --- a/modules/nf-core/fastqc/tests/main.nf.test +++ b/modules/nf-core/fastqc/tests/main.nf.test @@ -23,17 +23,14 @@ - + then { assertAll ( - { assert process.success }, @@ -251,7 +251,7 @@ Changes in 'fastqc/tests/main.nf.test': } } @@ -54,16 +51,14 @@ - + then { assertAll ( - { assert process.success }, @@ -276,7 +276,7 @@ Changes in 'fastqc/tests/main.nf.test': } } @@ -83,13 +78,11 @@ - + then { assertAll ( - { assert process.success }, @@ -295,7 +295,7 @@ Changes in 'fastqc/tests/main.nf.test': } } @@ -109,13 +102,11 @@ - + then { assertAll ( - { assert process.success }, @@ -314,7 +314,7 @@ Changes in 'fastqc/tests/main.nf.test': } } @@ -138,22 +129,20 @@ - + then { assertAll ( - { assert process.success }, @@ -351,7 +351,7 @@ Changes in 'fastqc/tests/main.nf.test': } } @@ -173,21 +162,18 @@ - + then { assertAll ( - { assert process.success }, @@ -369,9 +369,9 @@ Changes in 'fastqc/tests/main.nf.test': ) } } - + test("sarscov2 single-end [fastq] - stub") { - + - options "-stub" - + options "-stub" @@ -379,7 +379,7 @@ Changes in 'fastqc/tests/main.nf.test': process { """ @@ -201,12 +187,123 @@ - + then { assertAll ( - { assert process.success }, @@ -1132,14 +1132,14 @@ Changes in 'multiqc/main.nf': --- a/modules/nf-core/multiqc/main.nf +++ b/modules/nf-core/multiqc/main.nf @@ -3,14 +3,16 @@ - + 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.27--pyhdfd78af_0' : + 'biocontainers/multiqc:1.27--pyhdfd78af_0' }" - + input: path multiqc_files, stageAs: "?/*" path(multiqc_config) @@ -1147,11 +1147,11 @@ Changes in 'multiqc/main.nf': path(multiqc_logo) + path(replace_names) + path(sample_names) - + output: path "*multiqc_report.html", emit: report @@ -23,16 +25,22 @@ - + script: def args = task.ext.args ?: '' + def prefix = task.ext.prefix ? "--filename ${task.ext.prefix}.html" : '' @@ -1172,7 +1172,7 @@ Changes in 'multiqc/main.nf': + $replace \\ + $samples \\ . - + cat <<-END_VERSIONS > versions.yml @@ -44,7 +52,7 @@ stub: @@ -1181,7 +1181,7 @@ Changes in 'multiqc/main.nf': - touch multiqc_plots + mkdir multiqc_plots touch multiqc_report.html - + cat <<-END_VERSIONS > versions.yml 'modules/nf-core/multiqc/tests/tags.yml' is unchanged @@ -1191,11 +1191,11 @@ Changes in 'multiqc/tests/main.nf.test': @@ -8,6 +8,8 @@ tag "modules_nfcore" tag "multiqc" - + + config "./nextflow.config" + test("sarscov2 single-end [fastqc]") { - + when { @@ -17,6 +19,8 @@ input[1] = [] @@ -1292,10 +1292,10 @@ Changes in 'pydamage/analyze/main.nf': $args \\ -p $task.cpus \\ $bam -- +- + mv pydamage_results/pydamage_results.csv pydamage_results/${prefix}_pydamage_results.csv - + cat <<-END_VERSIONS > versions.yml Changes in 'pydamage/analyze/meta.yml': @@ -1367,7 +1367,7 @@ Changes in 'quartonotebook/Dockerfile': - ca-certificates \ - curl \ - && apt-get clean - + -RUN mkdir -p /opt/quarto \ - && curl -o quarto.tar.gz -L "https://github.com/quarto-dev/quarto-cli/releases/download/v${QUARTO_VERSION}/quarto-${QUARTO_VERSION}-linux-${TARGETARCH}.tar.gz" \ - && tar -zxvf quarto.tar.gz -C /opt/quarto/ --strip-components=1 \ @@ -1379,7 +1379,7 @@ Changes in 'quartonotebook/Dockerfile': +LABEL org.opencontainers.image.source="https://github.com/nf-core/modules" +LABEL org.opencontainers.image.vendor="nf-core" +LABEL org.opencontainers.image.license="https://github.com/nf-core/modules/blob/master/LICENSE" - + -# -# Second stage: Conda environment -# @@ -1387,11 +1387,11 @@ Changes in 'quartonotebook/Dockerfile': -COPY --from=quarto /opt/quarto /opt/quarto +ADD https://github.com/quarto-dev/quarto-cli#v${QUARTO_VERSION} /opt/quarto ENV PATH="${PATH}:/opt/quarto/bin" - + # Install packages using Mamba; also remove static libraries, python bytecode @@ -32,7 +24,3 @@ && find /opt/conda -follow -type f -name '*.js.map' -delete - + CMD /bin/bash - -LABEL \ @@ -1425,7 +1425,7 @@ Changes in 'quartonotebook/main.nf': @@ -1,16 +1,17 @@ -include { dumpParamsYaml; indentCodeBlock } from "./parametrize" +include { dumpParamsYaml ; indentCodeBlock } from "./parametrize" - + +// NB: You'll likely want to override this with a container containing all +// required dependencies for your analyses. Or use wave to build the container +// for you from the environment.yml You'll at least need Quarto itself, @@ -1447,12 +1447,12 @@ Changes in 'quartonotebook/main.nf': + container "${workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container + ? 'https://community-cr-prod.seqera.io/docker/registry/v2/blobs/sha256/25/25d0a9decefd5d369b8f9b8c934640bd61493df2c95c39b0c580e765e0d2a644/data' + : 'community.wave.seqera.io/library/quarto_jupyter_matplotlib_papermill_r-rmarkdown:95c6620495eabcd1'}" - + input: tuple val(meta), path(notebook) @@ -19,29 +20,33 @@ path extensions - + output: - tuple val(meta), path("*.html") , emit: html + tuple val(meta), path("*.html"), emit: html @@ -1463,10 +1463,10 @@ Changes in 'quartonotebook/main.nf': tuple val(meta), path("_extensions"), emit: extensions, optional: true - path "versions.yml" , emit: versions + path "versions.yml", emit: versions - + when: task.ext.when == null || task.ext.when - + script: - // Exit if running this module with -profile conda / -profile mamba - // This is because of issues with getting a homogenous environment across @@ -1490,12 +1490,12 @@ Changes in 'quartonotebook/main.nf': + def parametrize = task.ext.parametrize == null ? true : task.ext.parametrize + def implicit_params = task.ext.implicit_params == null ? true : task.ext.implicit_params + def meta_params = task.ext.meta_params == null ? true : task.ext.meta_params - + // Dump parameters to yaml file. // Using a YAML file over using the CLI params because @@ -76,10 +81,10 @@ export XDG_DATA_HOME="./.xdg_data_home" - + # Set parallelism for BLAS/MKL etc. to avoid over-booking of resources - export MKL_NUM_THREADS="$task.cpus" - export OPENBLAS_NUM_THREADS="$task.cpus" @@ -1505,7 +1505,7 @@ Changes in 'quartonotebook/main.nf': + export OPENBLAS_NUM_THREADS="${task.cpus}" + export OMP_NUM_THREADS="${task.cpus}" + export NUMBA_NUM_THREADS="${task.cpus}" - + # Render notebook quarto render \\ @@ -1529,7 +1529,7 @@ Changes in 'quartonotebook/tests/main.nf.test': + { assert path(process.out.html[0][1]).readLines().any { it.contains('Hello world') } }, ) } - + @@ -116,11 +116,11 @@ assertAll( { assert process.success }, @@ -1543,22 +1543,22 @@ Changes in 'quartonotebook/tests/main.nf.test': + { assert path(process.out.html[0][1]).readLines().any { it.contains('Hello world') } }, ) } - + Changes in 'quartonotebook/tests/main.nf.test.snap': --- a/modules/nf-core/quartonotebook/tests/main.nf.test.snap +++ b/modules/nf-core/quartonotebook/tests/main.nf.test.snap @@ -28,7 +28,7 @@ - + ], "5": [ - "versions.yml:md5,93481281b24bb1b44ecc4387e0957a0e" + "versions.yml:md5,ffc26b6a27e3d215b616594faba5c2a9" ], "artifacts": [ - + @@ -56,15 +56,15 @@ - + ], "versions": [ - "versions.yml:md5,93481281b24bb1b44ecc4387e0957a0e" @@ -1592,14 +1592,14 @@ Changes in 'quartonotebook/tests/main.nf.test.snap': ], "1": [ @@ -95,7 +95,7 @@ - + ], "5": [ - "versions.yml:md5,55e1f767fbd72aae14cbbfb638e38a90" + "versions.yml:md5,cb29d1dcc3f1531b5b219f5147739463" ], "artifacts": [ - + @@ -108,7 +108,7 @@ { "id": "test" @@ -1610,7 +1610,7 @@ Changes in 'quartonotebook/tests/main.nf.test.snap': ], "notebook": [ @@ -123,21 +123,18 @@ - + ], "versions": [ - "versions.yml:md5,55e1f767fbd72aae14cbbfb638e38a90" @@ -1677,7 +1677,7 @@ Changes in 'quartonotebook/tests/main.nf.test.snap': ], "1": [ @@ -200,7 +203,7 @@ - + ], "5": [ - "versions.yml:md5,55e1f767fbd72aae14cbbfb638e38a90" @@ -1729,14 +1729,14 @@ Changes in 'quartonotebook/tests/main.nf.test.snap': ], "1": [ @@ -282,7 +285,7 @@ - + ], "5": [ - "versions.yml:md5,55e1f767fbd72aae14cbbfb638e38a90" + "versions.yml:md5,cb29d1dcc3f1531b5b219f5147739463" ], "artifacts": [ - + @@ -295,7 +298,7 @@ { "id": "test" @@ -1775,10 +1775,10 @@ Changes in 'quartonotebook/tests/main.nf.test.snap': - "versions.yml:md5,55e1f767fbd72aae14cbbfb638e38a90" - ], [ - + ], [ - + - ] - ], - "meta": { @@ -1812,7 +1812,7 @@ Changes in 'quartonotebook/tests/main.nf.test.snap': ], "1": [ @@ -382,7 +388,7 @@ - + ], "5": [ - "versions.yml:md5,55e1f767fbd72aae14cbbfb638e38a90" @@ -1897,7 +1897,7 @@ Changes in 'untar/main.nf': - tag "$archive" + tag "${archive}" label 'process_single' - + conda "${moduleDir}/environment.yml" - container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/ubuntu:22.04' : @@ -1905,30 +1905,30 @@ Changes in 'untar/main.nf': + container "${workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container + ? 'https://community-cr-prod.seqera.io/docker/registry/v2/blobs/sha256/52/52ccce28d2ab928ab862e25aae26314d69c8e38bd41ca9431c67ef05221348aa/data' + : 'community.wave.seqera.io/library/coreutils_grep_gzip_lbzip2_pruned:838ba80435a629f8'}" - + input: tuple val(meta), path(archive) - + output: - tuple val(meta), path("$prefix"), emit: untar - path "versions.yml" , emit: versions + tuple val(meta), path("${prefix}"), emit: untar + path "versions.yml", emit: versions - + when: task.ext.when == null || task.ext.when - + script: - def args = task.ext.args ?: '' + def args = task.ext.args ?: '' def args2 = task.ext.args2 ?: '' - prefix = task.ext.prefix ?: ( meta.id ? "${meta.id}" : archive.baseName.toString().replaceFirst(/\.tar$/, "")) + prefix = task.ext.prefix ?: (meta.id ? "${meta.id}" : archive.baseName.toString().replaceFirst(/\.tar$/, "")) - + """ - mkdir $prefix + mkdir ${prefix} - + ## Ensures --strip-components only applied when top level of tar contents is a directory ## If just files or multiple directories, place all in prefix if [[ \$(tar -taf ${archive} | grep -o -P "^.*?\\/" | uniq | wc -l) -eq 1 ]]; then @@ -1954,11 +1954,11 @@ Changes in 'untar/main.nf': + ${archive} \\ + ${args2} fi - + cat <<-END_VERSIONS > versions.yml @@ -50,7 +50,7 @@ """ - + stub: - prefix = task.ext.prefix ?: ( meta.id ? "${meta.id}" : archive.toString().replaceFirst(/\.[^\.]+(.gz)?$/, "")) + prefix = task.ext.prefix ?: (meta.id ? "${meta.id}" : archive.toString().replaceFirst(/\.[^\.]+(.gz)?$/, "")) diff --git a/modules/nf-core/fastqc/environment.yml b/modules/nf-core/fastqc/environment.yml index 691d4c7..f9f54ee 100644 --- a/modules/nf-core/fastqc/environment.yml +++ b/modules/nf-core/fastqc/environment.yml @@ -1,3 +1,5 @@ +--- +# yaml-language-server: $schema=https://raw.githubusercontent.com/nf-core/modules/master/modules/environment-schema.json channels: - conda-forge - bioconda diff --git a/modules/nf-core/fastqc/main.nf b/modules/nf-core/fastqc/main.nf index 033f415..f562952 100644 --- a/modules/nf-core/fastqc/main.nf +++ b/modules/nf-core/fastqc/main.nf @@ -1,6 +1,6 @@ process FASTQC { tag "${meta.id}" - label 'process_medium' + label 'process_low' conda "${moduleDir}/environment.yml" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? @@ -11,9 +11,9 @@ process FASTQC { tuple val(meta), path(reads) output: - tuple val(meta), path("*.html"), emit: html - tuple val(meta), path("*.zip") , emit: zip - path "versions.yml" , emit: versions + tuple val(meta) , path("*.html") , emit: html + tuple val(meta) , path("*.zip") , emit: zip + tuple val("${task.process}"), val('fastqc'), eval('fastqc --version | sed "/FastQC v/!d; s/.*v//"'), emit: versions_fastqc, topic: versions when: task.ext.when == null || task.ext.when @@ -29,7 +29,7 @@ process FASTQC { // 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 = task.memory ? task.memory.toUnit('MB').toFloat() / task.cpus : null + def memory_in_mb = task.memory ? task.memory.toUnit('MB') / task.cpus : null // FastQC memory value allowed range (100 - 10000) def fastqc_memory = memory_in_mb > 10000 ? 10000 : (memory_in_mb < 100 ? 100 : memory_in_mb) @@ -43,11 +43,6 @@ process FASTQC { --threads ${task.cpus} \\ --memory ${fastqc_memory} \\ ${renamed_files} - - cat <<-END_VERSIONS > versions.yml - "${task.process}": - fastqc: \$( fastqc --version | sed '/FastQC v/!d; s/.*v//' ) - END_VERSIONS """ stub: @@ -55,10 +50,5 @@ process FASTQC { """ touch ${prefix}.html touch ${prefix}.zip - - cat <<-END_VERSIONS > versions.yml - "${task.process}": - fastqc: \$( fastqc --version | sed '/FastQC v/!d; s/.*v//' ) - END_VERSIONS """ } diff --git a/modules/nf-core/fastqc/meta.yml b/modules/nf-core/fastqc/meta.yml index 2b2e62b..49164c8 100644 --- a/modules/nf-core/fastqc/meta.yml +++ b/modules/nf-core/fastqc/meta.yml @@ -29,9 +29,10 @@ input: description: | List of input FastQ files of size 1 and 2 for single-end and paired-end data, respectively. + ontologies: [] output: - - html: - - meta: + html: + - - meta: type: map description: | Groovy Map containing sample information @@ -40,8 +41,9 @@ output: type: file description: FastQC report pattern: "*_{fastqc.html}" - - zip: - - meta: + ontologies: [] + zip: + - - meta: type: map description: | Groovy Map containing sample information @@ -50,11 +52,29 @@ output: type: file description: FastQC report archive pattern: "*_{fastqc.zip}" - - versions: - - versions.yml: - type: file - description: File containing software versions - pattern: "versions.yml" + ontologies: [] + versions_fastqc: + - - ${task.process}: + type: string + description: The process the versions were collected from + - fastqc: + type: string + description: The tool name + - fastqc --version | sed "/FastQC v/!d; s/.*v//": + type: eval + description: The expression to obtain the version of the tool + +topics: + versions: + - - ${task.process}: + type: string + description: The process the versions were collected from + - fastqc: + type: string + description: The tool name + - fastqc --version | sed "/FastQC v/!d; s/.*v//": + type: eval + description: The expression to obtain the version of the tool authors: - "@drpatelh" - "@grst" @@ -65,3 +85,27 @@ maintainers: - "@grst" - "@ewels" - "@FelixKrueger" +containers: + conda: + linux_amd64: + lock_file: https://wave.seqera.io/v1alpha1/builds/bd-af7a5314d5015c29_1/condalock + linux_arm64: + lock_file: https://wave.seqera.io/v1alpha1/builds/bd-df99cb252670875a_2/condalock + docker: + linux_amd64: + build_id: bd-af7a5314d5015c29_1 + name: community.wave.seqera.io/library/fastqc:0.12.1--af7a5314d5015c29 + scanId: sc-a618548acbee5a8a_30 + linux_arm64: + build_id: bd-df99cb252670875a_2 + name: community.wave.seqera.io/library/fastqc:0.12.1--df99cb252670875a + scanId: sc-b5913ed5d42b22d2_18 + singularity: + linux_amd64: + build_id: bd-104d26ddd9519960_1 + name: oras://community.wave.seqera.io/library/fastqc:0.12.1--104d26ddd9519960 + https: https://community.wave.seqera.io/v2/library/fastqc/blobs/sha256:e0c976cb2eca5fee72618a581537a4f8ea42fcae24c9b201e2e0f764fd28648a + linux_arm64: + build_id: bd-d56b505a93aef38a_1 + name: oras://community.wave.seqera.io/library/fastqc:0.12.1--d56b505a93aef38a + https: https://community.wave.seqera.io/v2/library/fastqc/blobs/sha256:fd39534bf298698cbe3ee4d4a6f1e73330ec4bca44c38dd9a4d06cb5ea838017 diff --git a/modules/nf-core/fastqc/tests/main.nf.test b/modules/nf-core/fastqc/tests/main.nf.test index e9d79a0..66c44da 100644 --- a/modules/nf-core/fastqc/tests/main.nf.test +++ b/modules/nf-core/fastqc/tests/main.nf.test @@ -30,7 +30,7 @@ nextflow_process { { 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() } + { assert snapshot(sanitizeOutput(process.out).findAll { key, val -> key != 'html' && key != 'zip' }).match() } ) } } @@ -58,7 +58,7 @@ nextflow_process { { 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() } + { assert snapshot(sanitizeOutput(process.out).findAll { key, val -> key != 'html' && key != 'zip' }).match() } ) } } @@ -82,7 +82,7 @@ nextflow_process { { 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() } + { assert snapshot(sanitizeOutput(process.out).findAll { key, val -> key != 'html' && key != 'zip' }).match() } ) } } @@ -106,7 +106,7 @@ nextflow_process { { 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() } + { assert snapshot(sanitizeOutput(process.out).findAll { key, val -> key != 'html' && key != 'zip' }).match() } ) } } @@ -142,7 +142,7 @@ nextflow_process { { 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() } + { assert snapshot(sanitizeOutput(process.out).findAll { key, val -> key != 'html' && key != 'zip' }).match() } ) } } @@ -166,7 +166,7 @@ nextflow_process { { 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() } + { assert snapshot(sanitizeOutput(process.out).findAll { key, val -> key != 'html' && key != 'zip' }).match() } ) } } diff --git a/modules/nf-core/fastqc/tests/main.nf.test.snap b/modules/nf-core/fastqc/tests/main.nf.test.snap index d5db309..c8ee120 100644 --- a/modules/nf-core/fastqc/tests/main.nf.test.snap +++ b/modules/nf-core/fastqc/tests/main.nf.test.snap @@ -1,15 +1,21 @@ { "sarscov2 custom_prefix": { "content": [ - [ - "versions.yml:md5,e1cc25ca8af856014824abd842e93978" - ] + { + "versions_fastqc": [ + [ + "FASTQC", + "fastqc", + "0.12.1" + ] + ] + } ], "meta": { - "nf-test": "0.9.0", - "nextflow": "24.04.3" + "nf-test": "0.9.2", + "nextflow": "25.10.0" }, - "timestamp": "2024-07-22T11:02:16.374038" + "timestamp": "2025-10-28T16:39:14.518503" }, "sarscov2 single-end [fastq] - stub": { "content": [ @@ -33,7 +39,11 @@ ] ], "2": [ - "versions.yml:md5,e1cc25ca8af856014824abd842e93978" + [ + "FASTQC", + "fastqc", + "0.12.1" + ] ], "html": [ [ @@ -44,8 +54,12 @@ "test.html:md5,d41d8cd98f00b204e9800998ecf8427e" ] ], - "versions": [ - "versions.yml:md5,e1cc25ca8af856014824abd842e93978" + "versions_fastqc": [ + [ + "FASTQC", + "fastqc", + "0.12.1" + ] ], "zip": [ [ @@ -59,10 +73,10 @@ } ], "meta": { - "nf-test": "0.9.0", - "nextflow": "24.04.3" + "nf-test": "0.9.2", + "nextflow": "25.10.0" }, - "timestamp": "2024-07-22T11:02:24.993809" + "timestamp": "2025-10-28T16:39:19.309008" }, "sarscov2 custom_prefix - stub": { "content": [ @@ -86,7 +100,11 @@ ] ], "2": [ - "versions.yml:md5,e1cc25ca8af856014824abd842e93978" + [ + "FASTQC", + "fastqc", + "0.12.1" + ] ], "html": [ [ @@ -97,8 +115,12 @@ "mysample.html:md5,d41d8cd98f00b204e9800998ecf8427e" ] ], - "versions": [ - "versions.yml:md5,e1cc25ca8af856014824abd842e93978" + "versions_fastqc": [ + [ + "FASTQC", + "fastqc", + "0.12.1" + ] ], "zip": [ [ @@ -112,58 +134,82 @@ } ], "meta": { - "nf-test": "0.9.0", - "nextflow": "24.04.3" + "nf-test": "0.9.2", + "nextflow": "25.10.0" }, - "timestamp": "2024-07-22T11:03:10.93942" + "timestamp": "2025-10-28T16:39:44.94888" }, "sarscov2 interleaved [fastq]": { "content": [ - [ - "versions.yml:md5,e1cc25ca8af856014824abd842e93978" - ] + { + "versions_fastqc": [ + [ + "FASTQC", + "fastqc", + "0.12.1" + ] + ] + } ], "meta": { - "nf-test": "0.9.0", - "nextflow": "24.04.3" + "nf-test": "0.9.2", + "nextflow": "25.10.0" }, - "timestamp": "2024-07-22T11:01:42.355718" + "timestamp": "2025-10-28T16:38:45.168496" }, "sarscov2 paired-end [bam]": { "content": [ - [ - "versions.yml:md5,e1cc25ca8af856014824abd842e93978" - ] + { + "versions_fastqc": [ + [ + "FASTQC", + "fastqc", + "0.12.1" + ] + ] + } ], "meta": { - "nf-test": "0.9.0", - "nextflow": "24.04.3" + "nf-test": "0.9.2", + "nextflow": "25.10.0" }, - "timestamp": "2024-07-22T11:01:53.276274" + "timestamp": "2025-10-28T16:38:53.268919" }, "sarscov2 multiple [fastq]": { "content": [ - [ - "versions.yml:md5,e1cc25ca8af856014824abd842e93978" - ] + { + "versions_fastqc": [ + [ + "FASTQC", + "fastqc", + "0.12.1" + ] + ] + } ], "meta": { - "nf-test": "0.9.0", - "nextflow": "24.04.3" + "nf-test": "0.9.2", + "nextflow": "25.10.0" }, - "timestamp": "2024-07-22T11:02:05.527626" + "timestamp": "2025-10-28T16:39:05.050305" }, "sarscov2 paired-end [fastq]": { "content": [ - [ - "versions.yml:md5,e1cc25ca8af856014824abd842e93978" - ] + { + "versions_fastqc": [ + [ + "FASTQC", + "fastqc", + "0.12.1" + ] + ] + } ], "meta": { - "nf-test": "0.9.0", - "nextflow": "24.04.3" + "nf-test": "0.9.2", + "nextflow": "25.10.0" }, - "timestamp": "2024-07-22T11:01:31.188871" + "timestamp": "2025-10-28T16:38:37.2373" }, "sarscov2 paired-end [fastq] - stub": { "content": [ @@ -187,7 +233,11 @@ ] ], "2": [ - "versions.yml:md5,e1cc25ca8af856014824abd842e93978" + [ + "FASTQC", + "fastqc", + "0.12.1" + ] ], "html": [ [ @@ -198,8 +248,12 @@ "test.html:md5,d41d8cd98f00b204e9800998ecf8427e" ] ], - "versions": [ - "versions.yml:md5,e1cc25ca8af856014824abd842e93978" + "versions_fastqc": [ + [ + "FASTQC", + "fastqc", + "0.12.1" + ] ], "zip": [ [ @@ -213,10 +267,10 @@ } ], "meta": { - "nf-test": "0.9.0", - "nextflow": "24.04.3" + "nf-test": "0.9.2", + "nextflow": "25.10.0" }, - "timestamp": "2024-07-22T11:02:34.273566" + "timestamp": "2025-10-28T16:39:24.450398" }, "sarscov2 multiple [fastq] - stub": { "content": [ @@ -240,7 +294,11 @@ ] ], "2": [ - "versions.yml:md5,e1cc25ca8af856014824abd842e93978" + [ + "FASTQC", + "fastqc", + "0.12.1" + ] ], "html": [ [ @@ -251,8 +309,12 @@ "test.html:md5,d41d8cd98f00b204e9800998ecf8427e" ] ], - "versions": [ - "versions.yml:md5,e1cc25ca8af856014824abd842e93978" + "versions_fastqc": [ + [ + "FASTQC", + "fastqc", + "0.12.1" + ] ], "zip": [ [ @@ -266,22 +328,28 @@ } ], "meta": { - "nf-test": "0.9.0", - "nextflow": "24.04.3" + "nf-test": "0.9.2", + "nextflow": "25.10.0" }, - "timestamp": "2024-07-22T11:03:02.304411" + "timestamp": "2025-10-28T16:39:39.758762" }, "sarscov2 single-end [fastq]": { "content": [ - [ - "versions.yml:md5,e1cc25ca8af856014824abd842e93978" - ] + { + "versions_fastqc": [ + [ + "FASTQC", + "fastqc", + "0.12.1" + ] + ] + } ], "meta": { - "nf-test": "0.9.0", - "nextflow": "24.04.3" + "nf-test": "0.9.2", + "nextflow": "25.10.0" }, - "timestamp": "2024-07-22T11:01:19.095607" + "timestamp": "2025-10-28T16:38:29.555068" }, "sarscov2 interleaved [fastq] - stub": { "content": [ @@ -305,7 +373,11 @@ ] ], "2": [ - "versions.yml:md5,e1cc25ca8af856014824abd842e93978" + [ + "FASTQC", + "fastqc", + "0.12.1" + ] ], "html": [ [ @@ -316,8 +388,12 @@ "test.html:md5,d41d8cd98f00b204e9800998ecf8427e" ] ], - "versions": [ - "versions.yml:md5,e1cc25ca8af856014824abd842e93978" + "versions_fastqc": [ + [ + "FASTQC", + "fastqc", + "0.12.1" + ] ], "zip": [ [ @@ -331,10 +407,10 @@ } ], "meta": { - "nf-test": "0.9.0", - "nextflow": "24.04.3" + "nf-test": "0.9.2", + "nextflow": "25.10.0" }, - "timestamp": "2024-07-22T11:02:44.640184" + "timestamp": "2025-10-28T16:39:29.193136" }, "sarscov2 paired-end [bam] - stub": { "content": [ @@ -358,7 +434,11 @@ ] ], "2": [ - "versions.yml:md5,e1cc25ca8af856014824abd842e93978" + [ + "FASTQC", + "fastqc", + "0.12.1" + ] ], "html": [ [ @@ -369,8 +449,12 @@ "test.html:md5,d41d8cd98f00b204e9800998ecf8427e" ] ], - "versions": [ - "versions.yml:md5,e1cc25ca8af856014824abd842e93978" + "versions_fastqc": [ + [ + "FASTQC", + "fastqc", + "0.12.1" + ] ], "zip": [ [ @@ -384,9 +468,9 @@ } ], "meta": { - "nf-test": "0.9.0", - "nextflow": "24.04.3" + "nf-test": "0.9.2", + "nextflow": "25.10.0" }, - "timestamp": "2024-07-22T11:02:53.550742" + "timestamp": "2025-10-28T16:39:34.144919" } } \ No newline at end of file diff --git a/modules/nf-core/multiqc/environment.yml b/modules/nf-core/multiqc/environment.yml index a27122c..009874d 100644 --- a/modules/nf-core/multiqc/environment.yml +++ b/modules/nf-core/multiqc/environment.yml @@ -1,5 +1,7 @@ +--- +# yaml-language-server: $schema=https://raw.githubusercontent.com/nf-core/modules/master/modules/environment-schema.json channels: - conda-forge - bioconda dependencies: - - bioconda::multiqc=1.27 + - bioconda::multiqc=1.33 diff --git a/modules/nf-core/multiqc/main.nf b/modules/nf-core/multiqc/main.nf index 58d9313..3b0e975 100644 --- a/modules/nf-core/multiqc/main.nf +++ b/modules/nf-core/multiqc/main.nf @@ -3,11 +3,11 @@ process MULTIQC { conda "${moduleDir}/environment.yml" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/multiqc:1.27--pyhdfd78af_0' : - 'biocontainers/multiqc:1.27--pyhdfd78af_0' }" + 'https://community-cr-prod.seqera.io/docker/registry/v2/blobs/sha256/34/34e733a9ae16a27e80fe00f863ea1479c96416017f24a907996126283e7ecd4d/data' : + 'community.wave.seqera.io/library/multiqc:1.33--ee7739d47738383b' }" input: - path multiqc_files, stageAs: "?/*" + path multiqc_files, stageAs: "?/*" path(multiqc_config) path(extra_multiqc_config) path(multiqc_logo) @@ -15,10 +15,11 @@ process MULTIQC { path(sample_names) output: - path "*multiqc_report.html", emit: report - path "*_data" , emit: data - path "*_plots" , optional:true, emit: plots - path "versions.yml" , emit: versions + path "*.html" , emit: report + path "*_data" , emit: data + path "*_plots" , optional:true, emit: plots + tuple val("${task.process}"), val('multiqc'), eval('multiqc --version | sed "s/.* //g"'), emit: versions + // MultiQC should not push its versions to the `versions` topic. Its input depends on the versions topic to be resolved thus outputting to the topic will let the pipeline hang forever when: task.ext.when == null || task.ext.when @@ -26,38 +27,29 @@ 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 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 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 \\ + ${args} \\ + ${config} \\ + ${prefix} \\ + ${extra_config} \\ + ${logo} \\ + ${replace} \\ + ${samples} \\ . - - cat <<-END_VERSIONS > versions.yml - "${task.process}": - multiqc: \$( multiqc --version | sed -e "s/multiqc, version //g" ) - END_VERSIONS """ stub: """ mkdir multiqc_data + touch multiqc_data/.stub mkdir multiqc_plots touch multiqc_report.html - - cat <<-END_VERSIONS > versions.yml - "${task.process}": - multiqc: \$( multiqc --version | sed -e "s/multiqc, version //g" ) - END_VERSIONS """ } diff --git a/modules/nf-core/multiqc/meta.yml b/modules/nf-core/multiqc/meta.yml index b16c187..9fd34f3 100644 --- a/modules/nf-core/multiqc/meta.yml +++ b/modules/nf-core/multiqc/meta.yml @@ -15,57 +15,74 @@ tools: 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}" - - - replace_names: + - multiqc_files: + type: file + description: | + List of reports / files recognised by MultiQC, for example the html and zip output of FastQC + ontologies: [] + - multiqc_config: + type: file + description: Optional config yml for MultiQC + pattern: "*.{yml,yaml}" + ontologies: + - edam: http://edamontology.org/format_3750 # YAML + - extra_multiqc_config: + type: file + description: Second optional config yml for MultiQC. Will override common sections + in multiqc_config. + pattern: "*.{yml,yaml}" + ontologies: + - edam: http://edamontology.org/format_3750 # YAML + - multiqc_logo: + type: file + description: Optional logo file for MultiQC + pattern: "*.{png}" + ontologies: [] + - 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}" + ontologies: + - edam: http://edamontology.org/format_3475 # TSV + - sample_names: + type: file + description: | + Optional TSV file with headers, passed to the MultiQC --sample_names + argument. + pattern: "*.{tsv}" + ontologies: + - edam: http://edamontology.org/format_3475 # TSV +output: + report: + - "*.html": 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: + description: MultiQC report file + pattern: ".html" + ontologies: [] + data: + - "*_data": + type: directory + description: MultiQC data dir + pattern: "multiqc_data" + plots: + - "*_plots": type: file - description: | - Optional TSV file with headers, passed to the MultiQC --sample_names - argument. - pattern: "*.{tsv}" -output: - - report: - - "*multiqc_report.html": - type: file - description: MultiQC report file - pattern: "multiqc_report.html" - - data: - - "*_data": - type: directory - description: MultiQC data dir - pattern: "multiqc_data" - - plots: - - "*_plots": - type: file - description: Plots created by MultiQC - pattern: "*_data" - - versions: - - versions.yml: - type: file - description: File containing software versions - pattern: "versions.yml" + description: Plots created by MultiQC + pattern: "*_plots" + ontologies: [] + versions: + - - ${task.process}: + type: string + description: The process the versions were collected from + - multiqc: + type: string + description: The tool name + - multiqc --version | sed "s/.* //g": + type: eval + description: The expression to obtain the version of the tool authors: - "@abhi18av" - "@bunop" @@ -76,3 +93,27 @@ maintainers: - "@bunop" - "@drpatelh" - "@jfy133" +containers: + conda: + linux/amd64: + lock_file: https://wave.seqera.io/v1alpha1/builds/bd-ee7739d47738383b_1/condalock + linux/arm64: + lock_file: https://wave.seqera.io/v1alpha1/builds/bd-58d7dee710ab3aa8_1/condalock + docker: + linux/amd64: + build_id: bd-ee7739d47738383b_1 + name: community.wave.seqera.io/library/multiqc:1.33--ee7739d47738383b + scanId: sc-6ddec592dcadd583_4 + linux/arm64: + build_id: bd-58d7dee710ab3aa8_1 + name: community.wave.seqera.io/library/multiqc:1.33--58d7dee710ab3aa8 + scanId: sc-a04c42273e34c55c_2 + singularity: + linux/amd64: + build_id: bd-e3576ddf588fa00d_1 + https: https://community-cr-prod.seqera.io/docker/registry/v2/blobs/sha256/34/34e733a9ae16a27e80fe00f863ea1479c96416017f24a907996126283e7ecd4d/data + name: oras://community.wave.seqera.io/library/multiqc:1.33--e3576ddf588fa00d + linux/arm64: + build_id: bd-2537ca5f8445e3c2_1 + https: https://community-cr-prod.seqera.io/docker/registry/v2/blobs/sha256/78/78b89e91d89e9cc99ad5ade5be311f347838cb2acbfb4f13bc343b170be09ce4/data + name: oras://community.wave.seqera.io/library/multiqc:1.33--2537ca5f8445e3c2 diff --git a/modules/nf-core/multiqc/tests/custom_prefix.config b/modules/nf-core/multiqc/tests/custom_prefix.config new file mode 100644 index 0000000..b30b135 --- /dev/null +++ b/modules/nf-core/multiqc/tests/custom_prefix.config @@ -0,0 +1,5 @@ +process { + withName: 'MULTIQC' { + ext.prefix = "custom_prefix" + } +} diff --git a/modules/nf-core/multiqc/tests/main.nf.test b/modules/nf-core/multiqc/tests/main.nf.test index 33316a7..d1ae8b0 100644 --- a/modules/nf-core/multiqc/tests/main.nf.test +++ b/modules/nf-core/multiqc/tests/main.nf.test @@ -30,7 +30,33 @@ nextflow_process { { assert process.success }, { assert process.out.report[0] ==~ ".*/multiqc_report.html" }, { assert process.out.data[0] ==~ ".*/multiqc_data" }, - { assert snapshot(process.out.versions).match("multiqc_versions_single") } + { assert snapshot(process.out.findAll { key, val -> key.startsWith("versions")}).match() } + ) + } + + } + + test("sarscov2 single-end [fastqc] - custom prefix") { + config "./custom_prefix.config" + + when { + process { + """ + input[0] = Channel.of(file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastqc/test_fastqc.zip', checkIfExists: true)) + input[1] = [] + input[2] = [] + input[3] = [] + input[4] = [] + input[5] = [] + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert process.out.report[0] ==~ ".*/custom_prefix.html" }, + { assert process.out.data[0] ==~ ".*/custom_prefix_data" } ) } @@ -56,7 +82,7 @@ nextflow_process { { assert process.success }, { assert process.out.report[0] ==~ ".*/multiqc_report.html" }, { assert process.out.data[0] ==~ ".*/multiqc_data" }, - { assert snapshot(process.out.versions).match("multiqc_versions_config") } + { assert snapshot(process.out.findAll { key, val -> key.startsWith("versions")}).match() } ) } } @@ -84,7 +110,7 @@ nextflow_process { { assert snapshot(process.out.report.collect { file(it).getName() } + process.out.data.collect { file(it).getName() } + process.out.plots.collect { file(it).getName() } + - process.out.versions ).match("multiqc_stub") } + process.out.findAll { key, val -> key.startsWith("versions")} ).match() } ) } diff --git a/modules/nf-core/multiqc/tests/main.nf.test.snap b/modules/nf-core/multiqc/tests/main.nf.test.snap index 7b7c132..d72d35b 100644 --- a/modules/nf-core/multiqc/tests/main.nf.test.snap +++ b/modules/nf-core/multiqc/tests/main.nf.test.snap @@ -1,41 +1,61 @@ { - "multiqc_versions_single": { + "sarscov2 single-end [fastqc]": { "content": [ - [ - "versions.yml:md5,8f3b8c1cec5388cf2708be948c9fa42f" - ] + { + "versions": [ + [ + "MULTIQC", + "multiqc", + "1.33" + ] + ] + } ], "meta": { - "nf-test": "0.9.2", - "nextflow": "24.10.4" + "nf-test": "0.9.3", + "nextflow": "25.10.2" }, - "timestamp": "2025-01-27T09:29:57.631982377" + "timestamp": "2025-12-09T10:10:43.020315838" }, - "multiqc_stub": { + "sarscov2 single-end [fastqc] - stub": { "content": [ [ "multiqc_report.html", "multiqc_data", "multiqc_plots", - "versions.yml:md5,8f3b8c1cec5388cf2708be948c9fa42f" + { + "versions": [ + [ + "MULTIQC", + "multiqc", + "1.33" + ] + ] + } ] ], "meta": { - "nf-test": "0.9.2", - "nextflow": "24.10.4" + "nf-test": "0.9.3", + "nextflow": "25.10.2" }, - "timestamp": "2025-01-27T09:30:34.743726958" + "timestamp": "2025-12-09T10:11:14.131950776" }, - "multiqc_versions_config": { + "sarscov2 single-end [fastqc] [config]": { "content": [ - [ - "versions.yml:md5,8f3b8c1cec5388cf2708be948c9fa42f" - ] + { + "versions": [ + [ + "MULTIQC", + "multiqc", + "1.33" + ] + ] + } ], "meta": { - "nf-test": "0.9.2", - "nextflow": "24.10.4" + "nf-test": "0.9.3", + "nextflow": "25.10.2" }, - "timestamp": "2025-01-27T09:30:21.44383553" + "timestamp": "2025-12-09T10:11:07.15692209" } } \ No newline at end of file diff --git a/modules/nf-core/multiqc/tests/tags.yml b/modules/nf-core/multiqc/tests/tags.yml deleted file mode 100644 index bea6c0d..0000000 --- a/modules/nf-core/multiqc/tests/tags.yml +++ /dev/null @@ -1,2 +0,0 @@ -multiqc: - - modules/nf-core/multiqc/** diff --git a/modules/nf-core/sam2lca/analyze/environment.yml b/modules/nf-core/sam2lca/analyze/environment.yml index af73164..18875d9 100644 --- a/modules/nf-core/sam2lca/analyze/environment.yml +++ b/modules/nf-core/sam2lca/analyze/environment.yml @@ -1,7 +1,7 @@ ---- -# yaml-language-server: $schema=https://raw.githubusercontent.com/nf-core/modules/master/modules/environment-schema.json channels: - conda-forge - bioconda + dependencies: - - bioconda::sam2lca=1.1.2 + - bioconda::sam2lca=1.1.4 + - conda-forge::setuptools=80 diff --git a/modules/nf-core/sam2lca/analyze/main.nf b/modules/nf-core/sam2lca/analyze/main.nf index aabeee9..52c3096 100644 --- a/modules/nf-core/sam2lca/analyze/main.nf +++ b/modules/nf-core/sam2lca/analyze/main.nf @@ -5,8 +5,8 @@ process SAM2LCA_ANALYZE { conda "${moduleDir}/environment.yml" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/sam2lca:1.1.2--pyhdfd78af_1': - 'biocontainers/sam2lca:1.1.2--pyhdfd78af_1' }" + 'https://depot.galaxyproject.org/singularity/sam2lca:1.1.4--pyhdfd78af_0': + 'biocontainers/sam2lca:1.1.4--pyhdfd78af_0' }" input: tuple val(meta), path(bam), path(bai) @@ -25,11 +25,11 @@ process SAM2LCA_ANALYZE { def args = task.ext.args ?: '' def prefix = task.ext.prefix ?: "${meta.id}" def make_db = database ? "" : "mkdir sam2lca_db" - def database = database ? "${database}" : "sam2lca_db" + def database_path = database ? "${database}" : "sam2lca_db" """ $make_db sam2lca \\ - -d $database \\ + -d $database_path \\ analyze \\ $args \\ -o ${prefix} \\ @@ -37,12 +37,11 @@ process SAM2LCA_ANALYZE { cat <<-END_VERSIONS > versions.yml "${task.process}": - sam2lca: \$(echo \$(sam2lca --version 2>&1) | sed 's/^sam2lca, version //' )) + sam2lca: \$(sam2lca --version | sed 's/.*version //') END_VERSIONS """ stub: - def args = task.ext.args ?: '' def prefix = task.ext.prefix ?: "${meta.id}" """ touch ${prefix}.csv @@ -50,7 +49,7 @@ process SAM2LCA_ANALYZE { cat <<-END_VERSIONS > versions.yml "${task.process}": - sam2lca: \$(echo \$(sam2lca --version 2>&1) | sed 's/^sam2lca, version //' )) + sam2lca: \$(sam2lca --version | sed 's/.*version //') END_VERSIONS """ } diff --git a/modules/nf-core/sam2lca/analyze/meta.yml b/modules/nf-core/sam2lca/analyze/meta.yml index bc09b8f..2d47261 100644 --- a/modules/nf-core/sam2lca/analyze/meta.yml +++ b/modules/nf-core/sam2lca/analyze/meta.yml @@ -26,17 +26,20 @@ input: type: file description: BAM/CRAM/SAM file pattern: "*.{bam,cram,sam}" + ontologies: [] - bai: type: file description: BAM/CRAM/SAM index pattern: "*.{bai,.crai}" - - - database: - type: file - description: Directory containing the sam2lca database - pattern: "*" + ontologies: [] + - database: + type: file + description: Directory containing the sam2lca database + pattern: "*" + ontologies: [] output: - - csv: - - meta: + csv: + - - meta: type: map description: | Groovy Map containing sample information @@ -45,8 +48,10 @@ output: type: file description: CSV file containing the sam2lca results pattern: "*.csv" - - json: - - meta: + ontologies: + - edam: http://edamontology.org/format_3752 # CSV + json: + - - meta: type: map description: | Groovy Map containing sample information @@ -55,8 +60,10 @@ output: type: file description: JSON file containing the sam2lca results pattern: "*.json" - - bam: - - meta: + ontologies: + - edam: http://edamontology.org/format_3464 # JSON + bam: + - - meta: type: map description: | Groovy Map containing sample information @@ -66,11 +73,14 @@ output: description: Optional sorted BAM/CRAM/SAM file annotated with LCA taxonomic information pattern: "*.bam" - - versions: - - versions.yml: - type: file - description: File containing software versions - pattern: "versions.yml" + ontologies: [] + versions: + - versions.yml: + type: file + description: File containing software versions + pattern: "versions.yml" + ontologies: + - edam: http://edamontology.org/format_3750 # YAML authors: - "@maxibor" maintainers: diff --git a/modules/nf-core/sam2lca/analyze/tests/main.nf.test b/modules/nf-core/sam2lca/analyze/tests/main.nf.test index 25ff02a..db471e3 100644 --- a/modules/nf-core/sam2lca/analyze/tests/main.nf.test +++ b/modules/nf-core/sam2lca/analyze/tests/main.nf.test @@ -34,6 +34,7 @@ nextflow_process { file(process.out.csv[0][1]).name, process.out.json, process.out.bam, + //path(process.out.versions[0]).yaml, process.out.versions ).match() } diff --git a/modules/nf-core/sam2lca/analyze/tests/main.nf.test.snap b/modules/nf-core/sam2lca/analyze/tests/main.nf.test.snap index c053618..7e2e4bf 100644 --- a/modules/nf-core/sam2lca/analyze/tests/main.nf.test.snap +++ b/modules/nf-core/sam2lca/analyze/tests/main.nf.test.snap @@ -15,14 +15,14 @@ ], [ - "versions.yml:md5,b6ea36b43b69f73dfa5d2fc7a1ddb1e2" + "versions.yml:md5,e78e99ca8889ed60d7f49a44ea57608d" ] ], "meta": { - "nf-test": "0.8.4", - "nextflow": "24.04.4" + "nf-test": "0.9.3", + "nextflow": "25.10.3" }, - "timestamp": "2024-08-27T16:10:41.635602" + "timestamp": "2026-02-02T10:44:40.997103183" }, "test-sam2lca-analyze-stub": { "content": [ @@ -49,7 +49,7 @@ ], "3": [ - "versions.yml:md5,b6ea36b43b69f73dfa5d2fc7a1ddb1e2" + "versions.yml:md5,e78e99ca8889ed60d7f49a44ea57608d" ], "bam": [ @@ -73,14 +73,14 @@ ] ], "versions": [ - "versions.yml:md5,b6ea36b43b69f73dfa5d2fc7a1ddb1e2" + "versions.yml:md5,e78e99ca8889ed60d7f49a44ea57608d" ] } ], "meta": { - "nf-test": "0.8.4", - "nextflow": "24.04.4" + "nf-test": "0.9.3", + "nextflow": "25.10.3" }, - "timestamp": "2024-08-27T16:10:47.050282" + "timestamp": "2026-02-02T10:44:51.808998086" } } \ No newline at end of file diff --git a/modules/nf-core/sourcepredict/environment.yml b/modules/nf-core/sourcepredict/environment.yml index 1b73065..00f904a 100644 --- a/modules/nf-core/sourcepredict/environment.yml +++ b/modules/nf-core/sourcepredict/environment.yml @@ -5,3 +5,5 @@ channels: - bioconda dependencies: - bioconda::sourcepredict=0.5.1 + - conda-forge::python=3.12.7 + - conda-forge::scikit-learn=1.5.2 diff --git a/modules/nf-core/sourcepredict/main.nf b/modules/nf-core/sourcepredict/main.nf index aa223ba..bf6bca2 100644 --- a/modules/nf-core/sourcepredict/main.nf +++ b/modules/nf-core/sourcepredict/main.nf @@ -1,24 +1,26 @@ process SOURCEPREDICT { - tag "$meta.id" + tag "${meta.id}" label 'process_medium' conda "${moduleDir}/environment.yml" - container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/sourcepredict:0.5.1--pyhdfd78af_0': - 'biocontainers/sourcepredict:0.5.1--pyhdfd78af_0' }" + container "${workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container + ? 'https://depot.galaxyproject.org/singularity/sourcepredict:0.5.1--pyhdfd78af_0' + : 'biocontainers/sourcepredict:0.5.1--pyhdfd78af_0'}" input: tuple val(meta), path(kraken_parse) path sources path labels - path(taxa_sqlite, stageAs: '.etetoolkit/taxa.sqlite') - path(taxa_sqlite_traverse_pkl, stageAs: '.etetoolkit/*') + path taxa_sqlite, stageAs: '.etetoolkit/taxa.sqlit' + path taxa_sqlite_traverse_pkl, stageAs: '.etetoolkit/*' val save_embedding output: - tuple val(meta), path("*.embedding.sourcepredict.csv") , optional:true, emit: embedding - tuple val(meta), path("*.report.sourcepredict.csv") , emit: report - path "versions.yml" , emit: versions + tuple val(meta), path("*.report.sourcepredict.csv"), emit: report + tuple val(meta), path("*.embedding.sourcepredict.csv"), optional: true, emit: embedding + tuple val("${task.process}"), val('sourcepredict'), eval('python -c "import sourcepredict; print(sourcepredict.__version__)"'), topic: versions, emit: versions_sourcepredict + tuple val("${task.process}"), val('python'), eval('python -V | sed "s/Python //g"'), topic: versions, emit: versions_python + tuple val("${task.process}"), val('sklearn'), eval('python -c "import sklearn; print(sklearn.__version__)"'), topic: versions, emit: versions_sklearn when: task.ext.when == null || task.ext.when @@ -26,24 +28,19 @@ process SOURCEPREDICT { script: def args = task.ext.args ?: '' def prefix = task.ext.prefix ?: "${meta.id}" - def save_embedding = save_embedding ? "-e ${prefix}.embedding.sourcepredict.csv" : "" + def save_embedding_cmd = save_embedding ? "-e ${prefix}.embedding.sourcepredict.csv" : "" """ export NUMBA_CACHE_DIR='./tmp' export HOME='./' sourcepredict \\ - -s $sources \\ - -l $labels \\ - $args \\ - $save_embedding \\ - -t $task.cpus \\ + -s ${sources} \\ + -l ${labels} \\ + ${args} \\ + ${save_embedding_cmd} \\ + -t ${task.cpus} \\ -o ${prefix}.report.sourcepredict.csv \\ ${kraken_parse} - - cat <<-END_VERSIONS > versions.yml - "${task.process}": - sourcepredict: \$(python -c "import sourcepredict; print(sourcepredict.__version__)") - END_VERSIONS """ stub: @@ -51,10 +48,5 @@ process SOURCEPREDICT { def prefix = task.ext.prefix ?: "${meta.id}" """ touch ${prefix}.sourcepredict.csv - - cat <<-END_VERSIONS > versions.yml - "${task.process}": - sourcepredict: \$(python -c "import sourcepredict; print(sourcepredict.__version__)") - END_VERSIONS """ } diff --git a/modules/nf-core/sourcepredict/meta.yml b/modules/nf-core/sourcepredict/meta.yml index 435ec96..3b78056 100644 --- a/modules/nf-core/sourcepredict/meta.yml +++ b/modules/nf-core/sourcepredict/meta.yml @@ -11,9 +11,9 @@ tools: documentation: "https://sourcepredict.readthedocs.io/en/latest/index.html" tool_dev_url: "https://github.com/maxibor/sourcepredict" doi: "10.21105/joss.01540" - licence: ["GPL v3-or-later"] + licence: + - "GPL v3-or-later" identifier: biotools:sourcepredict - input: - - meta: type: map @@ -24,52 +24,107 @@ input: type: file description: Sink TAXID count table in csv format pattern: "*.csv" - - - - sources: - type: file - description: Sources TAXID count table in csv format. Default can be downloaded from - https://raw.githubusercontent.com/maxibor/sourcepredict/master/data/modern_gut_microbiomes_sources.csv - pattern: "*.csv" - - - - labels: - type: file - description: Labels for the sources table in csv format. Default can be downloaded from - https://raw.githubusercontent.com/maxibor/sourcepredict/master/data/modern_gut_microbiomes_labels.csv - pattern: "*.csv" - - - - taxa_sqlite: - type: file - description: taxa.sqlite file downloaded with ete3 toolkit - pattern: "taxa.sqlite" - - - - taxa_sqlite_traverse_pkl: - type: file - description: taxa.sqlite.traverse.pkl file downloaded with ete3 toolkit - pattern: "taxa.sqlite.traverse.pkl" - - - - save_embedding: - type: string - description: | - If true, an optional command is added to save a file reporting the embedding file - + ontologies: + - edam: http://edamontology.org/format_3752 # CSV + - sources: + type: file + description: Sources TAXID count table in csv format. Default can be + downloaded from + https://raw.githubusercontent.com/maxibor/sourcepredict/master/data/modern_gut_microbiomes_sources.csv + pattern: "*.csv" + ontologies: + - edam: http://edamontology.org/format_3752 # CSV + - labels: + type: file + description: Labels for the sources table in csv format. Default can be + downloaded from + https://raw.githubusercontent.com/maxibor/sourcepredict/master/data/modern_gut_microbiomes_labels.csv + pattern: "*.csv" + ontologies: + - edam: http://edamontology.org/format_3752 # CSV + - taxa_sqlite: + type: file + description: taxa.sqlite file downloaded with ete3 toolkit + pattern: "taxa.sqlite" + ontologies: [] + - taxa_sqlite_traverse_pkl: + type: file + description: taxa.sqlite.traverse.pkl file downloaded with ete3 toolkit + pattern: "taxa.sqlite.traverse.pkl" + ontologies: [] output: - - report: - - meta: + report: + - - meta: type: map description: | Groovy Map containing sample information e.g. `[ id:'sample1', single_end:false ]` - "*.sourcepredict.csv": type: file - description: Table containing the predicted proportion of each source in each sample + description: Table containing the predicted proportion of each source in + each sample pattern: "*.sourcepredict.csv" - - - versions: - - "versions.yml": - type: file - description: File containing software versions - pattern: "versions.yml" - + ontologies: + - edam: http://edamontology.org/format_3752 # CSV + versions_sourcepredict: + - - ${task.process}: + type: string + description: The name of the process + - sourcepredict: + type: string + description: The name of the tool + - python -c "import sourcepredict; print(sourcepredict.__version__)": + type: eval + description: The expression to obtain the version of the tool + versions_python: + - - ${task.process}: + type: string + description: The name of the process + - python: + type: string + description: The name of the tool + - python -V | sed "s/Python //g": + type: eval + description: The expression to obtain the version of the tool + versions_sklearn: + - - ${task.process}: + type: string + description: The name of the process + - sklearn: + type: string + description: The name of the tool + - python -c "import sklearn; print(sklearn.__version__)": + type: eval + description: The expression to obtain the version of the tool +topics: + versions: + - - ${task.process}: + type: string + description: The name of the process + - sourcepredict: + type: string + description: The name of the tool + - python -c "import sourcepredict; print(sourcepredict.__version__)": + type: eval + description: The expression to obtain the version of the tool + - - ${task.process}: + type: string + description: The name of the process + - python: + type: string + description: The name of the tool + - python -V | sed "s/Python //g": + type: eval + description: The expression to obtain the version of the tool + - - ${task.process}: + type: string + description: The name of the process + - sklearn: + type: string + description: The name of the tool + - python -c "import sklearn; print(sklearn.__version__)": + type: eval + description: The expression to obtain the version of the tool authors: - "@MeriamOs" maintainers: diff --git a/modules/nf-core/sourcepredict/sourcepredict.diff b/modules/nf-core/sourcepredict/sourcepredict.diff index 3406b97..ad0f027 100644 --- a/modules/nf-core/sourcepredict/sourcepredict.diff +++ b/modules/nf-core/sourcepredict/sourcepredict.diff @@ -1,81 +1,70 @@ Changes in component 'nf-core/sourcepredict' 'modules/nf-core/sourcepredict/environment.yml' is unchanged +'modules/nf-core/sourcepredict/meta.yml' is unchanged Changes in 'sourcepredict/main.nf': --- modules/nf-core/sourcepredict/main.nf +++ modules/nf-core/sourcepredict/main.nf -@@ -11,12 +11,14 @@ +@@ -1,25 +1,26 @@ + process SOURCEPREDICT { +- tag "$meta.id" ++ tag "${meta.id}" + label 'process_medium' + + conda "${moduleDir}/environment.yml" +- container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? +- 'https://depot.galaxyproject.org/singularity/sourcepredict:0.5.1--pyhdfd78af_0': +- 'biocontainers/sourcepredict:0.5.1--pyhdfd78af_0' }" ++ container "${workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ++ ? 'https://depot.galaxyproject.org/singularity/sourcepredict:0.5.1--pyhdfd78af_0' ++ : 'biocontainers/sourcepredict:0.5.1--pyhdfd78af_0'}" + + input: tuple val(meta), path(kraken_parse) path sources path labels - path(taxa_sqlite, stageAs: '.etetoolkit/*') -+ path(taxa_sqlite, stageAs: '.etetoolkit/taxa.sqlite') - path(taxa_sqlite_traverse_pkl, stageAs: '.etetoolkit/*') +- path(taxa_sqlite_traverse_pkl, stageAs: '.etetoolkit/*') ++ path taxa_sqlite, stageAs: '.etetoolkit/taxa.sqlit' ++ path taxa_sqlite_traverse_pkl, stageAs: '.etetoolkit/*' + val save_embedding output: -- tuple val(meta), path("*.sourcepredict.csv") , emit: report -- path "versions.yml" , emit: versions -+ tuple val(meta), path("*.embedding.sourcepredict.csv") , optional:true, emit: embedding -+ tuple val(meta), path("*.report.sourcepredict.csv") , emit: report -+ path "versions.yml" , emit: versions +- tuple val(meta), path("*.sourcepredict.csv"), emit: report ++ tuple val(meta), path("*.report.sourcepredict.csv"), emit: report ++ tuple val(meta), path("*.embedding.sourcepredict.csv"), optional: true, emit: embedding + tuple val("${task.process}"), val('sourcepredict'), eval('python -c "import sourcepredict; print(sourcepredict.__version__)"'), topic: versions, emit: versions_sourcepredict + tuple val("${task.process}"), val('python'), eval('python -V | sed "s/Python //g"'), topic: versions, emit: versions_python + tuple val("${task.process}"), val('sklearn'), eval('python -c "import sklearn; print(sklearn.__version__)"'), topic: versions, emit: versions_sklearn +- when: task.ext.when == null || task.ext.when -@@ -24,6 +26,7 @@ +@@ -27,16 +28,18 @@ script: def args = task.ext.args ?: '' def prefix = task.ext.prefix ?: "${meta.id}" -+ def save_embedding = save_embedding ? "-e ${prefix}.embedding.sourcepredict.csv" : "" ++ def save_embedding_cmd = save_embedding ? "-e ${prefix}.embedding.sourcepredict.csv" : "" """ export NUMBA_CACHE_DIR='./tmp' export HOME='./' -@@ -32,8 +35,9 @@ - -s $sources \\ - -l $labels \\ - $args \\ -+ $save_embedding \\ - -t $task.cpus \\ + + sourcepredict \\ +- -s $sources \\ +- -l $labels \\ +- $args \\ +- -t $task.cpus \\ - -o ${prefix}.sourcepredict.csv \\ ++ -s ${sources} \\ ++ -l ${labels} \\ ++ ${args} \\ ++ ${save_embedding_cmd} \\ ++ -t ${task.cpus} \\ + -o ${prefix}.report.sourcepredict.csv \\ ${kraken_parse} + """ - cat <<-END_VERSIONS > versions.yml - -Changes in 'sourcepredict/meta.yml': ---- modules/nf-core/sourcepredict/meta.yml -+++ modules/nf-core/sourcepredict/meta.yml -@@ -47,6 +47,11 @@ - description: taxa.sqlite.traverse.pkl file downloaded with ete3 toolkit - pattern: "taxa.sqlite.traverse.pkl" - -+ - - save_embedding: -+ type: string -+ description: | -+ If true, an optional command is added to save a file reporting the embedding file -+ - output: - - report: - - meta: - -Changes in 'sourcepredict/tests/main.nf.test': ---- modules/nf-core/sourcepredict/tests/main.nf.test -+++ modules/nf-core/sourcepredict/tests/main.nf.test -@@ -36,6 +36,14 @@ - input[2] = file(params.modules_testdata_base_path + 'genomics/prokaryotes/metagenome/taxonomy/misc/sources_labels.csv', checkIfExists: true) - input[3] = XZ_DECOMPRESS.out.file.map{ it[1] } - input[4] = file(params.modules_testdata_base_path + 'genomics/prokaryotes/metagenome/taxonomy/misc/taxa_sqlite_traverse.pkl', checkIfExists: true) -+ input[5] = [] -+ """ -+ } -+ } -+ input[2] = file(params.modules_testdata_base_path + 'genomics/prokaryotes/metagenome/taxonomy/misc/sources_labels.csv', checkIfExists: true) -+ input[3] = XZ_DECOMPRESS.out.file.map{ it[1] } -+ input[4] = file(params.modules_testdata_base_path + 'genomics/prokaryotes/metagenome/taxonomy/misc/taxa_sqlite_traverse.pkl', checkIfExists: true) -+ input[5] = [] - """ - } - } -'modules/nf-core/sourcepredict/tests/main.nf.test.snap' is unchanged +'modules/nf-core/sourcepredict/tests/main.nf.test' is unchanged 'modules/nf-core/sourcepredict/tests/nextflow.config' is unchanged +'modules/nf-core/sourcepredict/tests/main.nf.test.snap' is unchanged ************************************************************ diff --git a/modules/nf-core/sourcepredict/tests/main.nf.test b/modules/nf-core/sourcepredict/tests/main.nf.test index 4b3f978..0899d90 100644 --- a/modules/nf-core/sourcepredict/tests/main.nf.test +++ b/modules/nf-core/sourcepredict/tests/main.nf.test @@ -27,7 +27,7 @@ nextflow_process { when { process { - """ + """ input[0] = [ [ id:'test' ], // meta map file(params.modules_testdata_base_path + 'genomics/prokaryotes/metagenome/taxonomy/misc/sink_taxid.csv', checkIfExists: true), @@ -36,14 +36,6 @@ nextflow_process { input[2] = file(params.modules_testdata_base_path + 'genomics/prokaryotes/metagenome/taxonomy/misc/sources_labels.csv', checkIfExists: true) input[3] = XZ_DECOMPRESS.out.file.map{ it[1] } input[4] = file(params.modules_testdata_base_path + 'genomics/prokaryotes/metagenome/taxonomy/misc/taxa_sqlite_traverse.pkl', checkIfExists: true) - input[5] = [] - """ - } - } - input[2] = file(params.modules_testdata_base_path + 'genomics/prokaryotes/metagenome/taxonomy/misc/sources_labels.csv', checkIfExists: true) - input[3] = XZ_DECOMPRESS.out.file.map{ it[1] } - input[4] = file(params.modules_testdata_base_path + 'genomics/prokaryotes/metagenome/taxonomy/misc/taxa_sqlite_traverse.pkl', checkIfExists: true) - input[5] = [] """ } } @@ -51,7 +43,10 @@ nextflow_process { then { assertAll( { assert process.success }, - { assert snapshot(process.out).match() } + { assert snapshot( + path(process.out.report[0][1]).csv.table.structure(), // floating point issues with second column + process.out.findAll { key, val -> key.startsWith("versions")} + ).match() } ) } @@ -63,7 +58,7 @@ nextflow_process { when { process { - """ + """ input[0] = [ [ id:'test' ], // meta map file(params.modules_testdata_base_path + 'genomics/prokaryotes/metagenome/taxonomy/misc/sink_taxid.csv', checkIfExists: true), diff --git a/modules/nf-core/sourcepredict/tests/main.nf.test.snap b/modules/nf-core/sourcepredict/tests/main.nf.test.snap index 2c4c73c..e6a7b91 100644 --- a/modules/nf-core/sourcepredict/tests/main.nf.test.snap +++ b/modules/nf-core/sourcepredict/tests/main.nf.test.snap @@ -11,7 +11,25 @@ ] ], "1": [ - "versions.yml:md5,d0e1400b2ad38aa0f76376e617a80015" + [ + "SOURCEPREDICT", + "sourcepredict", + "0.5.1" + ] + ], + "2": [ + [ + "SOURCEPREDICT", + "python", + "3.12.7" + ] + ], + "3": [ + [ + "SOURCEPREDICT", + "sklearn", + "1.5.2" + ] ], "report": [ [ @@ -21,48 +39,70 @@ "test.sourcepredict.csv:md5,d41d8cd98f00b204e9800998ecf8427e" ] ], - "versions": [ - "versions.yml:md5,d0e1400b2ad38aa0f76376e617a80015" + "versions_python": [ + [ + "SOURCEPREDICT", + "python", + "3.12.7" + ] + ], + "versions_sklearn": [ + [ + "SOURCEPREDICT", + "sklearn", + "1.5.2" + ] + ], + "versions_sourcepredict": [ + [ + "SOURCEPREDICT", + "sourcepredict", + "0.5.1" + ] ] } ], "meta": { - "nf-test": "0.9.0", - "nextflow": "24.04.4" + "nf-test": "0.9.3", + "nextflow": "25.10.3" }, - "timestamp": "2024-10-28T10:34:52.530249987" + "timestamp": "2026-02-10T17:35:06.218943" }, "metagenome - csv": { "content": [ + [ + [ + [] + ] + ], { - "0": [ + "versions_python": [ [ - { - "id": "test" - }, - "test.sourcepredict.csv:md5,fd90e16e7d7ce577d9b561afb1ebb24b" + "SOURCEPREDICT", + "python", + "3.12.7" ] ], - "1": [ - "versions.yml:md5,d0e1400b2ad38aa0f76376e617a80015" - ], - "report": [ + "versions_sklearn": [ [ - { - "id": "test" - }, - "test.sourcepredict.csv:md5,fd90e16e7d7ce577d9b561afb1ebb24b" + "SOURCEPREDICT", + "sklearn", + "1.5.2" ] ], - "versions": [ - "versions.yml:md5,d0e1400b2ad38aa0f76376e617a80015" + "versions_sourcepredict": [ + [ + "SOURCEPREDICT", + "sourcepredict", + "0.5.1" + ] ] } ], "meta": { - "nf-test": "0.9.0", - "nextflow": "24.04.4" + "nf-test": "0.9.3", + "nextflow": "25.10.3" }, - "timestamp": "2024-10-28T10:34:21.413712277" + "timestamp": "2026-02-10T17:34:40.075987" } -} \ No newline at end of file +} diff --git a/nextflow.config b/nextflow.config index 6c61f77..f79ac8e 100644 --- a/nextflow.config +++ b/nextflow.config @@ -10,28 +10,29 @@ params { // Input options - input = null + input = null + // References - genome_sheet = null - igenomes_base = 's3://ngi-igenomes/igenomes/' - igenomes_ignore = true + genome_sheet = null + igenomes_base = 's3://ngi-igenomes/igenomes/' + igenomes_ignore = true - file_prefix = 'coproid' - sam2lca_db = null - sam2lca_identity = 0.9 - sam2lca_acc2tax = 'adnamap' - kraken2_db = null - sp_sources = null - sp_labels = null - taxa_sqlite = params.modules_testdata_base_path + '/genomics/prokaryotes/metagenome/taxonomy/misc/taxa_sqlite.xz' - taxa_sqlite_traverse_pkl = params.modules_testdata_base_path + '/genomics/prokaryotes/metagenome/taxonomy/misc/taxa_sqlite_traverse.pkl' + file_prefix = 'coproid' + sam2lca_db = null + sam2lca_identity = 0.9 + sam2lca_acc2tax = 'adnamap' + kraken2_db = null + sp_sources = null + sp_labels = null + taxa_sqlite = params.modules_testdata_base_path + '/genomics/prokaryotes/metagenome/taxonomy/misc/taxa_sqlite.xz' + taxa_sqlite_traverse_pkl = params.modules_testdata_base_path + '/genomics/prokaryotes/metagenome/taxonomy/misc/taxa_sqlite_traverse.pkl' // MultiQC options - multiqc_config = null - multiqc_title = 'coproid' - multiqc_logo = null - max_multiqc_email_size = '25.MB' - multiqc_methods_description = null + multiqc_config = null + multiqc_title = 'coproid' + multiqc_logo = null + max_multiqc_email_size = '25.MB' + multiqc_methods_description = null // Boilerplate options outdir = null @@ -40,23 +41,26 @@ params { email_on_fail = null plaintext_email = false monochrome_logs = false - hook_url = null + hook_url = System.getenv('HOOK_URL') help = false help_full = false show_hidden = false version = false modules_testdata_base_path = 'https://raw.githubusercontent.com/nf-core/test-datasets/modules/data' pipelines_testdata_base_path = 'https://raw.githubusercontent.com/nf-core/test-datasets/' + trace_report_suffix = new java.util.Date().format('yyyy-MM-dd_HH-mm-ss') + + // 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 + 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 // Schema validation default options - validate_params = true + validate_params = true } // Load base.config by default for all pipelines @@ -64,90 +68,101 @@ includeConfig 'conf/base.config' profiles { debug { - dumpHashes = true - process.beforeScript = 'echo $HOSTNAME' - cleanup = false + dumpHashes = true + process.beforeScript = 'echo $HOSTNAME' + cleanup = false nextflow.enable.configProcessNamesValidation = true } conda { - conda.enabled = true - docker.enabled = false - singularity.enabled = false - podman.enabled = false - shifter.enabled = false - charliecloud.enabled = false - conda.channels = ['conda-forge', 'bioconda'] - apptainer.enabled = false + conda.enabled = true + docker.enabled = false + singularity.enabled = false + podman.enabled = false + shifter.enabled = false + charliecloud.enabled = false + conda.channels = ['conda-forge', 'bioconda'] + apptainer.enabled = false } mamba { - conda.enabled = true - conda.useMamba = true - docker.enabled = false - singularity.enabled = false - podman.enabled = false - shifter.enabled = false - charliecloud.enabled = false - apptainer.enabled = false + conda.enabled = true + conda.useMamba = true + docker.enabled = false + singularity.enabled = false + podman.enabled = false + shifter.enabled = false + charliecloud.enabled = false + apptainer.enabled = false } docker { - docker.enabled = true - conda.enabled = false - singularity.enabled = false - podman.enabled = false - shifter.enabled = false - charliecloud.enabled = false - apptainer.enabled = false - docker.runOptions = '-u $(id -u):$(id -g)' + docker.enabled = true + conda.enabled = false + singularity.enabled = false + podman.enabled = false + shifter.enabled = false + charliecloud.enabled = false + apptainer.enabled = false + docker.runOptions = '-u $(id -u):$(id -g)' } - arm { - docker.runOptions = '-u $(id -u):$(id -g) --platform=linux/amd64' + arm64 { + process.arch = 'arm64' + // TODO https://github.com/nf-core/modules/issues/6694 + // For now if you're using arm64 you have to use wave for the sake of the maintainers + // wave profile + apptainer.ociAutoPull = true + singularity.ociAutoPull = true + wave.enabled = true + wave.freeze = true + wave.strategy = 'conda,container' + } + emulate_amd64 { + docker.runOptions = '-u $(id -u):$(id -g) --platform=linux/amd64' } singularity { - singularity.enabled = true - singularity.autoMounts = true - conda.enabled = false - docker.enabled = false - podman.enabled = false - shifter.enabled = false - charliecloud.enabled = false - apptainer.enabled = false + singularity.enabled = true + singularity.autoMounts = true + conda.enabled = false + docker.enabled = false + podman.enabled = false + shifter.enabled = false + charliecloud.enabled = false + apptainer.enabled = false } podman { - podman.enabled = true - conda.enabled = false - docker.enabled = false - singularity.enabled = false - shifter.enabled = false - charliecloud.enabled = false - apptainer.enabled = false + podman.enabled = true + conda.enabled = false + docker.enabled = false + singularity.enabled = false + shifter.enabled = false + charliecloud.enabled = false + apptainer.enabled = false } shifter { - shifter.enabled = true - conda.enabled = false - docker.enabled = false - singularity.enabled = false - podman.enabled = false - charliecloud.enabled = false - apptainer.enabled = false + shifter.enabled = true + conda.enabled = false + docker.enabled = false + singularity.enabled = false + podman.enabled = false + charliecloud.enabled = false + apptainer.enabled = false } charliecloud { - charliecloud.enabled = true - conda.enabled = false - docker.enabled = false - singularity.enabled = false - podman.enabled = false - shifter.enabled = false - apptainer.enabled = false + charliecloud.enabled = true + conda.enabled = false + docker.enabled = false + singularity.enabled = false + podman.enabled = false + shifter.enabled = false + apptainer.enabled = false } apptainer { - apptainer.enabled = true - apptainer.autoMounts = true - conda.enabled = false - docker.enabled = false - singularity.enabled = false - podman.enabled = false - shifter.enabled = false - charliecloud.enabled = false + apptainer.enabled = true + apptainer.autoMounts = true + conda.enabled = false + docker.enabled = false + singularity.enabled = false + podman.enabled = false + shifter.enabled = false + charliecloud.enabled = false } wave { apptainer.ociAutoPull = true @@ -156,33 +171,37 @@ profiles { wave.freeze = true wave.strategy = 'conda,container' } - gitpod { - executor.name = 'local' - executor.cpus = 4 - executor.memory = 8.GB - - process { - resourceLimits = [ - memory: 8.GB, - cpus : 4, - time : 1.h - ] - } + gpu { + docker.runOptions = '-u $(id -u):$(id -g) --gpus all' + apptainer.runOptions = '--nv' + singularity.runOptions = '--nv' + } + test { + includeConfig 'conf/test.config' + } + test_full { + includeConfig 'conf/test_full.config' } - test { includeConfig 'conf/test.config' } - test_full { includeConfig 'conf/test_full.config' } } -// 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" +// Load nf-core custom profiles from different institutions + +// If params.custom_config_base is set AND either the NXF_OFFLINE environment variable is not set or params.custom_config_base is a local path, the nfcore_custom.config file from the specified base path is included. +// Load nf-core/coproid custom profiles from different institutions. +includeConfig params.custom_config_base && (!System.getenv('NXF_OFFLINE') || !params.custom_config_base.startsWith('http')) ? "${params.custom_config_base}/nfcore_custom.config" : "/dev/null" + + +// Load nf-core/coproid 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 params.custom_config_base && (!System.getenv('NXF_OFFLINE') || !params.custom_config_base.startsWith('http')) ? "${params.custom_config_base}/pipeline/coproid.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' +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 @@ -202,32 +221,31 @@ env { // Set bash options process.shell = [ "bash", - "-C", // No clobber - prevent output redirection from overwriting files. - "-e", // Exit if a tool returns a non-zero status/exit code - "-u", // Treat unset variables and parameters as an error - "-o", // Returns the status of the last command to exit.. - "pipefail" // ..with a non-zero status or zero if all successfully execute + "-C", + "-e", + "-u", + "-o", + "pipefail", ] // Disable process selector warnings by default. Use debug profile to enable warnings. nextflow.enable.configProcessNamesValidation = false -def trace_timestamp = new java.util.Date().format( 'yyyy-MM-dd_HH-mm-ss') timeline { enabled = true - file = "${params.outdir}/pipeline_info/execution_timeline_${trace_timestamp}.html" + file = "${params.outdir}/pipeline_info/execution_timeline_${params.trace_report_suffix}.html" } report { enabled = true - file = "${params.outdir}/pipeline_info/execution_report_${trace_timestamp}.html" + file = "${params.outdir}/pipeline_info/execution_report_${params.trace_report_suffix}.html" } trace { enabled = true - file = "${params.outdir}/pipeline_info/execution_trace_${trace_timestamp}.txt" + file = "${params.outdir}/pipeline_info/execution_trace_${params.trace_report_suffix}.txt" } dag { enabled = true - file = "${params.outdir}/pipeline_info/pipeline_dag_${trace_timestamp}.html" + file = "${params.outdir}/pipeline_info/pipeline_dag_${params.trace_report_suffix}.html" } manifest { @@ -238,16 +256,16 @@ manifest { affiliation: 'Department of Anatomy, University of Otago, Dunedin, New Zealand', email: 'meriamvanos96@hotmail.com', github: 'MeriamOs', - contribution: ['author'], // List of contribution types ('author', 'maintainer' or 'contributor') - orcid: '0009-0008-9835-8874' + contribution: ['author'], + orcid: '0009-0008-9835-8874', ], [ name: 'Maxime Borry', affiliation: 'Microbiome Sciences Group, Department of Archaeogenetics, Max Planck Institute for Evolutionary Anthropology, Leipzig, Germany', email: '', github: 'maxibor', - contribution: ['author'], // List of contribution types ('author', 'maintainer' or 'contributor') - orcid: '0000-0001-9140-7559' + contribution: ['author'], + orcid: '0000-0001-9140-7559', ], [ name: "Jennifer Reeve", @@ -255,53 +273,26 @@ manifest { email: "jennifer.reeve@netvalue.nz", github: "jen-reeve", contribution: ["contributor"], - orcid: "0000-0002-7321-7038" + orcid: "0000-0002-7321-7038", ], ] homePage = 'https://github.com/nf-core/coproid' description = """ COPROlite host IDentification """ mainScript = 'main.nf' defaultBranch = 'master' - nextflowVersion = '!>=24.10.0' - version = '2.0.0' + nextflowVersion = '!>=25.04.0' + version = '2.0.1' doi = '' } // Nextflow plugins plugins { - id 'nf-schema@2.3.0' // Validation of pipeline parameters and creation of an input channel from a sample sheet + id 'nf-schema@2.5.1' } validation { defaultIgnoreParams = ["genomes"] - monochromeLogs = params.monochrome_logs - help { - enabled = true - command = "nextflow run nf-core/coproid -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 nf-core/coproid ${manifest.version}\033[0m --\033[2m----------------------------------------------------\033[0m- -""" - afterText = """${manifest.doi ? "\n* 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 - -* Software dependencies - https://github.com/nf-core/coproid/blob/master/CITATIONS.md -""" - } - summary { - beforeText = validation.help.beforeText - afterText = validation.help.afterText - } + monochromeLogs = params.monochrome_logs } // Load modules.config for DSL2 module specific options diff --git a/nextflow_schema.json b/nextflow_schema.json index 2b31819..055f520 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -37,6 +37,19 @@ "format": "directory-path", "description": "The output directory where the results will be saved. You have to use absolute paths to storage on Cloud infrastructure.", "fa_icon": "fas fa-folder-open" + }, + "email": { + "type": "string", + "description": "Email address for completion summary.", + "fa_icon": "fas fa-envelope", + "help_text": "Set this parameter to your e-mail address to get a summary e-mail with details of the run sent to you when the workflow exits. If set in your user config file (`~/.nextflow/config`) then you don't need to specify this on the command line for every run.", + "pattern": "^([a-zA-Z0-9_\\-\\.]+)@([a-zA-Z0-9_\\-\\.]+)\\.([a-zA-Z]{2,5})$" + }, + "multiqc_title": { + "type": "string", + "description": "MultiQC report title. Printed as page header, used for filename if not otherwise specified.", + "fa_icon": "fas fa-file-signature", + "default": "coproid" } } }, @@ -45,6 +58,7 @@ "type": "object", "description": "Options to be provided to the pipeline", "default": "", + "required": ["kraken2_db", "sp_sources", "sp_labels"], "properties": { "file_prefix": { "type": "string", @@ -73,13 +87,15 @@ "type": "string", "format": "file-path", "exists": true, - "description": "Path to pre-downloaded ~/.etetoolkit/taxa.sqlite file, if not supplied it will be pulled from the test-data repository" + "description": "Path to pre-downloaded ~/.etetoolkit/taxa.sqlite file, if not supplied it will be pulled from the test-data repository", + "default": "[:]/genomics/prokaryotes/metagenome/taxonomy/misc/taxa_sqlite.xz" }, "taxa_sqlite_traverse_pkl": { "type": "string", "format": "file-path", "exists": true, - "description": "Path to pre-downloaded ~/.etetoolkit/taxa.sqlite.traverse.pkl file, if not supplied it will be pulled from the test-data repository" + "description": "Path to pre-downloaded ~/.etetoolkit/taxa.sqlite.traverse.pkl file, if not supplied it will be pulled from the test-data repository", + "default": "[:]/genomics/prokaryotes/metagenome/taxonomy/misc/taxa_sqlite_traverse.pkl" }, "sam2lca_db": { "type": "string", @@ -94,142 +110,180 @@ "type": "number", "default": 0.9, "description": "Set the sam2lca --identity parameter" + }, + "igenomes_ignore": { + "type": "boolean", + "description": "Do not load the iGenomes reference config.", + "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`.", + "default": true + }, + "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/" } - }, - "required": ["kraken2_db", "sp_sources", "sp_labels"] + } }, - "generic_options": { - "title": "Generic options", + "institutional_config_options": { + "title": "Institutional config options", "type": "object", - "fa_icon": "fas fa-file-import", - "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`.", + "fa_icon": "fas fa-university", + "description": "Parameters used to describe centralised config profiles. These should not be edited.", + "help_text": "The centralised nf-core configuration profiles use a handful of pipeline parameters to describe themselves. This information is then printed to the Nextflow log when you run a pipeline. You should not need to change these values when you run a pipeline.", "properties": { - "version": { - "type": "boolean", - "description": "Display version and exit.", - "fa_icon": "fas fa-question-circle", - "hidden": true - }, - "validate_params": { - "type": "boolean", - "description": "Boolean whether to validate parameters against the schema at runtime", - "default": true, - "fa_icon": "fas fa-check-square", - "hidden": true - }, - "multiqc_title": { + "custom_config_version": { "type": "string", - "default": "coproid", - "description": "MultiQC report title. Printed as page header, used for filename if not otherwise specified." + "description": "Git commit id for Institutional configs.", + "default": "master", + "hidden": true, + "fa_icon": "fas fa-users-cog" }, - "email": { + "custom_config_base": { "type": "string", - "description": "Set this parameter to your e-mail address to get a summary e-mail with details of the run sent to you when the workflow exits.", - "hidden": true + "description": "Base directory for Institutional configs.", + "default": "https://raw.githubusercontent.com/nf-core/configs/master", + "hidden": true, + "help_text": "If you're running offline, Nextflow will not be able to fetch the institutional config files from the internet. If you don't need them, then this is not a problem. If you do need them, you should download the files from the repo and tell Nextflow where to find them with this parameter.", + "fa_icon": "fas fa-users-cog" }, - "email_on_fail": { + "config_profile_name": { "type": "string", + "description": "Institutional config name.", "hidden": true, - "description": "This works exactly as with --email, except emails are only sent if the workflow is not successful." + "fa_icon": "fas fa-users-cog" }, - "igenomes_base": { + "config_profile_description": { "type": "string", - "default": "s3://ngi-igenomes/igenomes/", - "description": "Path to igenomes base", - "hidden": true - }, - "igenomes_ignore": { - "type": "boolean", - "default": true, - "description": "Boolean whether to ignore igenomes", - "hidden": true + "description": "Institutional config description.", + "hidden": true, + "fa_icon": "fas fa-users-cog" }, - "multiqc_methods_description": { + "config_profile_contact": { "type": "string", - "description": "Custom MultiQC yaml file containing HTML including a methods description.", - "hidden": true + "description": "Institutional config contact information.", + "hidden": true, + "fa_icon": "fas fa-users-cog" }, - "modules_testdata_base_path": { + "config_profile_url": { + "type": "string", + "description": "Institutional config URL link.", + "hidden": true, + "fa_icon": "fas fa-users-cog" + } + } + }, + "generic_options": { + "title": "Generic options", + "type": "object", + "fa_icon": "fas fa-file-import", + "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": { + "publish_dir_mode": { "type": "string", - "default": "https://raw.githubusercontent.com/nf-core/test-datasets/modules/data", - "description": "Base path to test data.", + "default": "copy", + "description": "Method used to save pipeline results to output directory.", + "help_text": "The Nextflow `publishDir` option specifies which intermediate files should be saved to the output directory. This option tells the pipeline what method should be used to move these files. See [Nextflow docs](https://www.nextflow.io/docs/latest/process.html#publishdir) for details.", + "fa_icon": "fas fa-copy", + "enum": ["symlink", "rellink", "link", "copy", "copyNoFollow", "move"], "hidden": true }, - "pipelines_testdata_base_path": { + "email_on_fail": { "type": "string", - "default": "https://raw.githubusercontent.com/nf-core/test-datasets/", - "description": "Base path to pipelines data.", + "description": "Email address for completion summary, only when pipeline fails.", + "fa_icon": "fas fa-exclamation-triangle", + "pattern": "^([a-zA-Z0-9_\\-\\.]+)@([a-zA-Z0-9_\\-\\.]+)\\.([a-zA-Z]{2,5})$", + "help_text": "An email address to send a summary email to when the pipeline is completed - ONLY sent if the pipeline does not exit successfully.", "hidden": true }, - "config_profile_name": { - "type": "string", - "description": "Name of the config profile.", + "plaintext_email": { + "type": "boolean", + "description": "Send plain-text email instead of HTML.", + "fa_icon": "fas fa-remove-format", "hidden": true }, - "config_profile_description": { + "max_multiqc_email_size": { "type": "string", - "description": "Description of the config profile.", + "description": "File size limit when attaching MultiQC reports to summary emails.", + "pattern": "^\\d+(\\.\\d+)?\\.?\\s*(K|M|G|T)?B$", + "default": "25.MB", + "fa_icon": "fas fa-file-upload", "hidden": true }, - "custom_config_version": { - "type": "string", - "default": "master", - "description": "Provide git commit id for custom Institutional configs hosted at nf-core/configs. This was implemented for reproducibility purposes. Default: master.", + "monochrome_logs": { + "type": "boolean", + "description": "Do not use coloured log outputs.", + "fa_icon": "fas fa-palette", "hidden": true }, - "custom_config_base": { + "hook_url": { "type": "string", - "default": "https://raw.githubusercontent.com/nf-core/configs/master", - "description": "Base path to custom config files.", + "description": "Incoming hook URL for messaging service", + "fa_icon": "fas fa-people-group", + "help_text": "Incoming hook URL for messaging service. Currently, MS Teams and Slack are supported.", "hidden": true }, - "config_profile_contact": { + "multiqc_config": { "type": "string", - "description": "Contact of the config profile.", + "format": "file-path", + "description": "Custom config file to supply to MultiQC.", + "fa_icon": "fas fa-cog", "hidden": true }, - "config_profile_url": { + "multiqc_logo": { "type": "string", - "description": "URL to config file.", + "description": "Custom logo file to supply to MultiQC. File name must also be set in the MultiQC config file", + "fa_icon": "fas fa-image", "hidden": true }, - "multiqc_config": { + "multiqc_methods_description": { "type": "string", - "hidden": true, - "description": "Config file for multiQC." + "description": "Custom MultiQC yaml file containing HTML including a methods description.", + "fa_icon": "fas fa-cog" }, - "multiqc_logo": { - "type": "string", - "hidden": true, - "description": "Logo for multiQC." + "validate_params": { + "type": "boolean", + "description": "Boolean whether to validate parameters against the schema at runtime", + "default": true, + "fa_icon": "fas fa-check-square", + "hidden": true }, - "max_multiqc_email_size": { + "pipelines_testdata_base_path": { "type": "string", - "default": "25.MB", - "hidden": true, - "description": "Maximum email size." + "fa_icon": "far fa-check-circle", + "description": "Base URL or local path to location of pipeline test dataset files", + "default": "https://raw.githubusercontent.com/nf-core/test-datasets/", + "hidden": true }, - "publish_dir_mode": { + "trace_report_suffix": { "type": "string", - "default": "copy", - "hidden": true, - "description": "Value passed to Nextflow publishDir directive for publishing results in the output directory. Available: 'symlink', 'rellink', 'link', 'copy', 'copyNoFollow' and 'move' (Default: 'copy')." + "fa_icon": "far calendar", + "description": "Suffix to add to the trace report filename. Default is the date and time in the format yyyy-MM-dd_HH-mm-ss.", + "hidden": true }, - "plaintext_email": { + "help": { + "type": ["boolean", "string"], + "description": "Display the help message." + }, + "help_full": { "type": "boolean", - "hidden": true, - "description": "Set to receive plain-text e-mails instead of HTML formatted." + "description": "Display the full detailed help message." }, - "monochrome_logs": { + "show_hidden": { "type": "boolean", - "hidden": true, - "description": "Set to disable colourful command line output and live life in monochrome." + "description": "Display hidden parameters in the help message (only works when --help or --help_full are provided)." }, - "hook_url": { + "modules_testdata_base_path": { "type": "string", - "hidden": true, - "description": "Specify YOUR-HOOK-URL to receive notifications from your pipeline in Microsoft Teams or Slack." + "default": "https://raw.githubusercontent.com/nf-core/test-datasets/modules/data" + }, + "version": { + "type": "boolean" } } } @@ -241,6 +295,9 @@ { "$ref": "#/$defs/pipeline_options" }, + { + "$ref": "#/$defs/institutional_config_options" + }, { "$ref": "#/$defs/generic_options" } diff --git a/nf-test.config b/nf-test.config new file mode 100644 index 0000000..3a1fff5 --- /dev/null +++ b/nf-test.config @@ -0,0 +1,24 @@ +config { + // location for all nf-test tests + testsDir "." + + // nf-test directory including temporary files for each test + workDir System.getenv("NFT_WORKDIR") ?: ".nf-test" + + // location of an optional nextflow.config file specific for executing tests + configFile "tests/nextflow.config" + + // ignore tests coming from the nf-core/modules repo + ignore 'modules/nf-core/**/tests/*', 'subworkflows/nf-core/**/tests/*' + + // run all test with defined profile(s) from the main nextflow.config + profile "test" + + // list of filenames or patterns that should be trigger a full test run + triggers 'nextflow.config', 'nf-test.config', 'conf/test.config', 'tests/nextflow.config', 'tests/.nftignore' + + // load the necessary plugins + plugins { + load "nft-utils@0.0.3" + } +} diff --git a/ro-crate-metadata.json b/ro-crate-metadata.json index 6d040b4..c20bb2c 100644 --- a/ro-crate-metadata.json +++ b/ro-crate-metadata.json @@ -21,9 +21,9 @@ { "@id": "./", "@type": "Dataset", - "creativeWorkStatus": "InProgress", - "datePublished": "2025-03-10T07:50:29+00:00", - "description": "

\n \n \n \"nf-core/coproid\"\n \n

\n\n[![GitHub Actions CI Status](https://github.com/nf-core/coproid/actions/workflows/ci.yml/badge.svg)](https://github.com/nf-core/coproid/actions/workflows/ci.yml)\n[![GitHub Actions Linting Status](https://github.com/nf-core/coproid/actions/workflows/linting.yml/badge.svg)](https://github.com/nf-core/coproid/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/coproid/results)[![Cite with Zenodo](http://img.shields.io/badge/DOI-10.5281/zenodo.XXXXXXX-1073c8?labelColor=000000)](https://doi.org/10.5281/zenodo.XXXXXXX)\n[![nf-test](https://img.shields.io/badge/unit_tests-nf--test-337ab7.svg)](https://www.nf-test.com)\n\n[![Nextflow](https://img.shields.io/badge/nextflow%20DSL2-%E2%89%A524.04.2-23aa62.svg)](https://www.nextflow.io/)\n[![run with conda](http://img.shields.io/badge/run%20with-conda-3EB049?labelColor=000000&logo=anaconda)](https://docs.conda.io/en/latest/)\n[![run with docker](https://img.shields.io/badge/run%20with-docker-0db7ed?labelColor=000000&logo=docker)](https://www.docker.com/)\n[![run with singularity](https://img.shields.io/badge/run%20with-singularity-1d355c.svg?labelColor=000000)](https://sylabs.io/docs/)\n[![Launch on Seqera Platform](https://img.shields.io/badge/Launch%20%F0%9F%9A%80-Seqera%20Platform-%234256e7)](https://cloud.seqera.io/launch?pipeline=https://github.com/nf-core/coproid)\n\n[![Get help on Slack](http://img.shields.io/badge/slack-nf--core%20%23coproid-4A154B?labelColor=000000&logo=slack)](https://nfcore.slack.com/channels/coproid)[![Follow on Twitter](http://img.shields.io/badge/twitter-%40nf__core-1DA1F2?labelColor=000000&logo=twitter)](https://twitter.com/nf_core)[![Follow on Mastodon](https://img.shields.io/badge/mastodon-nf__core-6364ff?labelColor=FFFFFF&logo=mastodon)](https://mstdn.science/@nf_core)[![Watch on YouTube](http://img.shields.io/badge/youtube-nf--core-FF0000?labelColor=000000&logo=youtube)](https://www.youtube.com/c/nf-core)\n\n## Introduction\n\n**nf-core/coproid** is a bioinformatics pipeline that ...\n\n\n\n\n1. Read QC ([`FastQC`](https://www.bioinformatics.babraham.ac.uk/projects/fastqc/))2. Present QC for raw reads ([`MultiQC`](http://multiqc.info/))\n\n## Usage\n\n> [!NOTE]\n> If you are new to Nextflow and nf-core, please refer to [this page](https://nf-co.re/docs/usage/installation) on how to set-up Nextflow. Make sure to [test your setup](https://nf-co.re/docs/usage/introduction#how-to-run-a-pipeline) with `-profile test` before running the workflow on actual data.\n\n\n\nNow, you can run the pipeline using:\n\n\n\n```bash\nnextflow run nf-core/coproid \\\n -profile \\\n --input samplesheet.csv \\\n --outdir \n```\n\n> [!WARNING]\n> 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).\n\nFor more details and further functionality, please refer to the [usage documentation](https://nf-co.re/coproid/usage) and the [parameter documentation](https://nf-co.re/coproid/parameters).\n\n## Pipeline output\n\nTo see the results of an example test run with a full size dataset refer to the [results](https://nf-co.re/coproid/results) tab on the nf-core website pipeline page.\nFor more details about the output files and reports, please refer to the\n[output documentation](https://nf-co.re/coproid/output).\n\n## Credits\n\nnf-core/coproid was originally written by Maxime Borry & Meriam Van Os.\n\nWe thank the following people for their extensive assistance in the development of this pipeline:\n\n\n\n## Contributions and Support\n\nIf you would like to contribute to this pipeline, please see the [contributing guidelines](.github/CONTRIBUTING.md).\n\nFor further information or help, don't hesitate to get in touch on the [Slack `#coproid` channel](https://nfcore.slack.com/channels/coproid) (you can join with [this invite](https://nf-co.re/join/slack)).\n\n## Citations\n\n\n\n\n\n\nAn extensive list of references for the tools used by the pipeline can be found in the [`CITATIONS.md`](CITATIONS.md) file.\n\nYou can cite the `nf-core` publication as follows:\n\n> **The nf-core framework for community-curated bioinformatics pipelines.**\n>\n> Philip Ewels, Alexander Peltzer, Sven Fillinger, Harshil Patel, Johannes Alneberg, Andreas Wilm, Maxime Ulysse Garcia, Paolo Di Tommaso & Sven Nahnsen.\n>\n> _Nat Biotechnol._ 2020 Feb 13. doi: [10.1038/s41587-020-0439-x](https://dx.doi.org/10.1038/s41587-020-0439-x).\n", + "creativeWorkStatus": "Stable", + "datePublished": "2025-11-20T09:31:27+00:00", + "description": "

\n \n \n \"nf-core/coproid\"\n \n

\n\n[![Open in GitHub Codespaces](https://img.shields.io/badge/Open_In_GitHub_Codespaces-black?labelColor=grey&logo=github)](https://github.com/codespaces/new/nf-core/coproid)\n[![GitHub Actions CI Status](https://github.com/nf-core/coproid/actions/workflows/nf-test.yml/badge.svg)](https://github.com/nf-core/coproid/actions/workflows/nf-test.yml)\n[![GitHub Actions Linting Status](https://github.com/nf-core/coproid/actions/workflows/linting.yml/badge.svg)](https://github.com/nf-core/coproid/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/coproid/results)[![Cite with Zenodo](http://img.shields.io/badge/DOI-10.5281/zenodo.2653756-1073c8?labelColor=000000)](https://doi.org/10.5281/zenodo.2653756)\n[![nf-test](https://img.shields.io/badge/unit_tests-nf--test-337ab7.svg)](https://www.nf-test.com)\n\n[![Nextflow](https://img.shields.io/badge/version-%E2%89%A525.04.0-green?style=flat&logo=nextflow&logoColor=white&color=%230DC09D&link=https%3A%2F%2Fnextflow.io)](https://www.nextflow.io/)\n[![nf-core template version](https://img.shields.io/badge/nf--core_template-3.5.1-green?style=flat&logo=nfcore&logoColor=white&color=%2324B064&link=https%3A%2F%2Fnf-co.re)](https://github.com/nf-core/tools/releases/tag/3.5.1)\n[![run with conda](http://img.shields.io/badge/run%20with-conda-3EB049?labelColor=000000&logo=anaconda)](https://docs.conda.io/en/latest/)\n[![run with docker](https://img.shields.io/badge/run%20with-docker-0db7ed?labelColor=000000&logo=docker)](https://www.docker.com/)\n[![run with singularity](https://img.shields.io/badge/run%20with-singularity-1d355c.svg?labelColor=000000)](https://sylabs.io/docs/)\n[![Launch on Seqera Platform](https://img.shields.io/badge/Launch%20%F0%9F%9A%80-Seqera%20Platform-%234256e7)](https://cloud.seqera.io/launch?pipeline=https://github.com/nf-core/coproid)\n\n[![Get help on Slack](http://img.shields.io/badge/slack-nf--core%20%23coproid-4A154B?labelColor=000000&logo=slack)](https://nfcore.slack.com/channels/coproid)[![Follow on Bluesky](https://img.shields.io/badge/bluesky-%40nf__core-1185fe?labelColor=000000&logo=bluesky)](https://bsky.app/profile/nf-co.re)[![Follow on Mastodon](https://img.shields.io/badge/mastodon-nf__core-6364ff?labelColor=FFFFFF&logo=mastodon)](https://mstdn.science/@nf_core)[![Watch on YouTube](http://img.shields.io/badge/youtube-nf--core-FF0000?labelColor=000000&logo=youtube)](https://www.youtube.com/c/nf-core)\n\n## Introduction\n\n**nf-core/coproid** is a bioinformatics pipeline that helps you identify the \"true maker\" of Illumina sequenced (Paleo)faeces by checking the microbiome composition and the endogenous host DNA.\n\nIt combines the analysis of putative host (ancient) DNA with a machine learning prediction of the faeces source based on microbiome taxonomic composition:\n\nA. First coproID performs comparative mapping of all reads agains two (or three) target genomes (genome1, genome2, and potentially genome3) and computes a host-DNA species ratio (NormalisedProportion).\nB. Then coproID performs metagenomic taxonomic profiling, and compares the obtained profiles to modern reference samples of the target species metagenomes. Using machine learning, coproID then estimates the host source from the metagenomic taxonomic composition (SourcepredictProportion).\nC. Finally, coproID combines the A and B proportions to predict the likely host of the metagenomic sample.\n\n![nf-core-coproid workflow overview](docs/images/coproid_workflow.jpg?raw=true \"nf-core-coproid workflow overview\")\n\n**Workflow overview**\n\n1. Read QC ([`FastQC`](https://www.bioinformatics.babraham.ac.uk/projects/fastqc/))\n2. Fastp to remove adapters and low-complexity reads ([`fastp`](https://doi.org/10.1002/imt2.107))\n3. Mapping or reads to multiple reference genomes ([`Bowtie2`](https://bowtie-bio.sourceforge.net/bowtie2))\n4. Lowest Common Ancestor analysis to retain only genome specific reads ([`sam2lca`](github.com/maxibor/sam2lca))\n5. Ancient DNA damage estimation with [pyDamage](https://pydamage.readthedocs.io/en/latest/README.html) and [DamageProfiler](https://github.com/Integrative-Transcriptomics/DamageProfiler)\n6. Taxonomic profiling of unmapped reads ([`kraken2`](https://ccb.jhu.edu/software/kraken2/))\n7. Source predicting based on taxonic profiles ([`sourcepredict`](https://sourcepredict.readthedocs.io/))\n8. Combining host and microbial predictions to calculate overall proportions.\n9. ([`MultiQC`](http://multiqc.info/)) aggregate results of several individual modules.\n10. ([Quartonotebook])(https://quarto.org/) creates a report with sample results.\n\nThe coproID pipeline is built using Nextflow, a workflow tool to run tasks across multiple compute infrastructures in a very portable manner. It comes with docker containers making installation trivial and results highly reproducible. The Nextflow DSL2 implementation of this pipeline uses one container per process which makes it much easier to maintain and update software dependencies. Where possible, these processes have been submitted to and installed from nf-core/modules in order to make them available to all nf-core pipelines, and to everyone within the Nextflow community!\n\n## Usage\n\n> [!NOTE]\n> If you are new to Nextflow and nf-core, please refer to [this page](https://nf-co.re/docs/usage/installation) on how to set-up Nextflow. Make sure to [test your setup](https://nf-co.re/docs/usage/introduction#how-to-run-a-pipeline) with `-profile test` before running the workflow on actual data.\n\nFirst, prepare a samplesheet with your input data that looks as follows:\n\n`samplesheet.csv`:\n\n```csv title=\"samplesheet.csv\"\nsample,fastq_1,fastq_2\nPAIRED_END,PAIRED_END_S1_L002_R1_001.fastq.gz,PAIRED_END_S1_L002_R2_001.fastq.gz\nSINGLE_END,SINGLE_END_S4_L003_R1_001.fastq.gz,\n```\n\nEach row represents a fastq file (single-end) or a pair of fastq files (paired end).\n\n> [!WARNING]\n> Make sure that your reference genomes are from ncbi, so sam2lca can extract the taxid!\n\nSecond, prepare a genomesheet with your input genome references that looks as follows:\n\n`genomesheet.csv`:\n\n```csv title=\"genomesheet.csv\"\ngenome_name,taxid,genome_size,igenome,fasta,index\nEscherichia_coli,562,5000000,,https://github.com/nf-core/test-datasets/raw/coproid/genomes/ecoli/genome.fa,\nBacillus_subtilis,1423,4200000,,https://github.com/nf-core/test-datasets/raw/coproid/genomes/bsubtilis/genome.fa,\n```\n\nBefore running the pipeline, you need to download a kraken2 database, and supply this to the pipeline using --kraken2_db\nThe kraken2 database can be a directory or \\*.tar.gz\n\nYou also need to create/download the reference files for sourcepredict. These include the source anf label files, for more information see [`sourcepredict`](https://sourcepredict.readthedocs.io/)\n\nNow, you can run the pipeline using:\n\n```bash\nnextflow run nf-core/coproid \\\n -profile \\\n --input samplesheet.csv \\\n --genome_sheet genomesheet.csv \\\n --kraken2_db 'PATH/TO/KRAKENDB' \\\n --sp_sources 'PATH/TO/SOURCEPREDICT/SOURCES/FILE' \\\n --sp_labels 'PATH/TO/SOURCEPREDICT/LABELS/FILE' \\\n --outdir \n```\n\n> [!WARNING]\n> 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).\n\n### Running the pipeline offline or on an HPC with container restrictions\n\nIf you are working offline or on an HPC system with container restrictions, the sam2lca database download may fail. To work around this issue, you can pre-download (see the [sam2lca documentation](https://sam2lca.readthedocs.io/en/latest/README.html)) or transfer the sam2lca database and use the `--sam2lca_db` parameter to point directly to the `~/.sam2lca` directory. In this instance, adjust the name of the pre-downloaded database (for example `nucl`) accordingly with `--sam2lca_acc2tax`.\n\nFor more details and further functionality, please refer to the [usage documentation](https://nf-co.re/coproid/usage) and the [parameter documentation](https://nf-co.re/coproid/parameters).\n\n## Pipeline output\n\nTo see the results of an example test run with a full size dataset refer to the [results](https://nf-co.re/coproid/results) tab on the nf-core website pipeline page.\nFor more details about the output files and reports, please refer to the [output documentation](https://nf-co.re/coproid/output).\n\n## Credits\n\nnf-core/coproid was originally written by Maxime Borry & Meriam Van Os.\n\n\n\n## Contributions and Support\n\nIf you would like to contribute to this pipeline, please see the [contributing guidelines](.github/CONTRIBUTING.md).\n\nFor further information or help, don't hesitate to get in touch on the [Slack `#coproid` channel](https://nfcore.slack.com/channels/coproid) (you can join with [this invite](https://nf-co.re/join/slack)).\n\n## Citations\n\nIf you use nf-core/coproid for your analysis, please cite it using the following doi: [10.5281/zenodo.2653756](https://doi.org/10.5281/zenodo.2653756)\n\nAn extensive list of references for the tools used by the pipeline can be found in the [`CITATIONS.md`](CITATIONS.md) file.\n\nYou can cite the `nf-core` publication as follows:\n\n> **The nf-core framework for community-curated bioinformatics pipelines.**\n>\n> Philip Ewels, Alexander Peltzer, Sven Fillinger, Harshil Patel, Johannes Alneberg, Andreas Wilm, Maxime Ulysse Garcia, Paolo Di Tommaso & Sven Nahnsen.\n>\n> _Nat Biotechnol._ 2020 Feb 13. doi: [10.1038/s41587-020-0439-x](https://dx.doi.org/10.1038/s41587-020-0439-x).\n", "hasPart": [ { "@id": "main.nf" @@ -99,7 +99,7 @@ }, "mentions": [ { - "@id": "#ef99d3ce-1275-4c67-9ce2-028586eac1a7" + "@id": "#167a8746-d42b-48bd-9294-603231b25f82" } ], "name": "nf-core/coproid" @@ -127,7 +127,7 @@ "ComputationalWorkflow" ], "dateCreated": "", - "dateModified": "2025-03-10T08:50:29Z", + "dateModified": "2025-11-20T09:31:27Z", "dct:conformsTo": "https://bioschemas.org/profiles/ComputationalWorkflow/1.0-RELEASE/", "keywords": [ "nf-core", @@ -151,10 +151,10 @@ }, "url": [ "https://github.com/nf-core/coproid", - "https://nf-co.re/coproid/dev/" + "https://nf-co.re/coproid/2.0.0/" ], "version": [ - "2.0dev" + "2.0.0" ] }, { @@ -167,14 +167,14 @@ "url": { "@id": "https://www.nextflow.io/" }, - "version": "!>=24.10.0" + "version": "!>=25.04.0" }, { - "@id": "#ef99d3ce-1275-4c67-9ce2-028586eac1a7", + "@id": "#167a8746-d42b-48bd-9294-603231b25f82", "@type": "TestSuite", "instance": [ { - "@id": "#52e01aae-2be8-4cc6-808f-f5ea208c3e6a" + "@id": "#cc939882-e431-4659-ac42-32862c7e2d27" } ], "mainEntity": { @@ -183,10 +183,10 @@ "name": "Test suite for nf-core/coproid" }, { - "@id": "#52e01aae-2be8-4cc6-808f-f5ea208c3e6a", + "@id": "#cc939882-e431-4659-ac42-32862c7e2d27", "@type": "TestInstance", "name": "GitHub Actions workflow for testing nf-core/coproid", - "resource": "repos/nf-core/coproid/actions/workflows/ci.yml", + "resource": "repos/nf-core/coproid/actions/workflows/nf-test.yml", "runsOn": { "@id": "https://w3id.org/ro/terms/test#GithubService" }, diff --git a/subworkflows/local/kraken2_classification/main.nf b/subworkflows/local/kraken2_classification/main.nf index 2149be8..6e74057 100644 --- a/subworkflows/local/kraken2_classification/main.nf +++ b/subworkflows/local/kraken2_classification/main.nf @@ -5,51 +5,55 @@ include { SOURCEPREDICT } from '../../../modules/nf-core/sourcepredict/main' include { UNTAR } from '../../../modules/nf-core/untar/main' include { XZ_DECOMPRESS } from '../../../modules/nf-core/xz/decompress/main' -ch_sp_sources = file(params.sp_sources) -ch_sp_labels = file(params.sp_labels) -ch_sqlite_traverse = file(params.taxa_sqlite_traverse_pkl) + workflow KRAKEN2_CLASSIFICATION { take: - reads - kraken2_db + reads + kraken2_db main: - ch_versions = Channel.empty() + ch_versions = Channel.empty() - if (kraken2_db.name.endsWith( ".tar.gz" )) { - Channel - .value(kraken2_db) - .map { - kraken2_db -> [ - ['id' : 'kraken2_database'], - kraken2_db - ] - } - .set { archive } + ch_sp_sources = file(params.sp_sources) + ch_sp_labels = file(params.sp_labels) + ch_sqlite_traverse = file(params.taxa_sqlite_traverse_pkl) - UNTAR ( archive ) + if (kraken2_db.name.endsWith(".tar.gz")) { + Channel + .value(kraken2_db) + .map { kraken2_db -> + [ + ['id': 'kraken2_database'], + kraken2_db, + ] + } + .set { archive } - database = UNTAR.out.untar.map{ it -> it[1] } - ch_versions = ch_versions.mix(UNTAR.out.versions.first()) + UNTAR(archive) - } else { - database = kraken2_db + database = UNTAR.out.untar.map { it -> it[1] } + ch_versions = ch_versions.mix(UNTAR.out.versions.first()) + } + else { + database = kraken2_db } KRAKEN2_KRAKEN2( reads, database, false, - false + false, ) ch_versions = ch_versions.mix(KRAKEN2_KRAKEN2.out.versions.first()) KRAKEN_PARSE(KRAKEN2_KRAKEN2.out.report) - KRAKEN_PARSE.out.kraken_read_count.map { - it -> it[1] - }.collect() - .set { kraken_read_count } + KRAKEN_PARSE.out.kraken_read_count + .map { it -> + it[1] + } + .collect() + .set { kraken_read_count } KRAKEN_MERGE(kraken_read_count) @@ -57,47 +61,47 @@ workflow KRAKEN2_CLASSIFICATION { ch_versions = ch_versions.mix(KRAKEN_PARSE.out.versions.first()) KRAKEN_MERGE.out.kraken_merged_report - .map { - kraken_merged_report -> + .map { kraken_merged_report -> [ [ - 'id' : 'samples_combined' + 'id': 'samples_combined' ], - kraken_merged_report + kraken_merged_report, ] } .set { ch_kraken_merged } - if (file(params.taxa_sqlite).name.endsWith( ".xz" )) { - - XZ_DECOMPRESS ( [ - [], - file(params.taxa_sqlite) ] - ) - sqlite = XZ_DECOMPRESS.out.file.map{ it[1] } - ch_versions = ch_versions.mix(XZ_DECOMPRESS.out.versions.first()) + if (file(params.taxa_sqlite).name.endsWith(".xz")) { - } else { - sqlite = file(params.taxa_sqlite) + XZ_DECOMPRESS( + [ + [], + file(params.taxa_sqlite), + ] + ) + sqlite = XZ_DECOMPRESS.out.file.map { it[1] } + ch_versions = ch_versions.mix(XZ_DECOMPRESS.out.versions.first()) + } + else { + sqlite = file(params.taxa_sqlite) } // // MODULE: Run sourcepredict // - SOURCEPREDICT ( + SOURCEPREDICT( ch_kraken_merged, ch_sp_sources, ch_sp_labels, sqlite, ch_sqlite_traverse, - true + true, ) - ch_versions = ch_versions.mix(SOURCEPREDICT.out.versions.first()) emit: - sp_report = SOURCEPREDICT.out.report - sp_embedding = SOURCEPREDICT.out.embedding - kraken_merged_report = KRAKEN_MERGE.out.kraken_merged_report - kraken_report = KRAKEN2_KRAKEN2.out.report - versions = ch_versions + sp_report = SOURCEPREDICT.out.report + sp_embedding = SOURCEPREDICT.out.embedding + kraken_merged_report = KRAKEN_MERGE.out.kraken_merged_report + kraken_report = KRAKEN2_KRAKEN2.out.report + versions = ch_versions } diff --git a/subworkflows/local/utils_nfcore_coproid_pipeline/main.nf b/subworkflows/local/utils_nfcore_coproid_pipeline/main.nf index 6b6fa61..8590f32 100644 --- a/subworkflows/local/utils_nfcore_coproid_pipeline/main.nf +++ b/subworkflows/local/utils_nfcore_coproid_pipeline/main.nf @@ -8,129 +8,150 @@ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ -include { UTILS_NFSCHEMA_PLUGIN } from '../../nf-core/utils_nfschema_plugin' -include { paramsSummaryMap } from 'plugin/nf-schema' -include { samplesheetToList } from 'plugin/nf-schema' -include { samplesheetToList as genomesToList } from 'plugin/nf-schema' -include { UTILS_NEXTFLOW_PIPELINE } from '../../nf-core/utils_nextflow_pipeline' -include { UTILS_NFCORE_PIPELINE } from '../../nf-core/utils_nfcore_pipeline' -include { completionEmail } from '../../nf-core/utils_nfcore_pipeline' -include { completionSummary } from '../../nf-core/utils_nfcore_pipeline' -include { imNotification } from '../../nf-core/utils_nfcore_pipeline' +include { UTILS_NFSCHEMA_PLUGIN } from '../../nf-core/utils_nfschema_plugin' +include { paramsSummaryMap } from 'plugin/nf-schema' +include { samplesheetToList } from 'plugin/nf-schema' +include { samplesheetToList as genomesToList } from 'plugin/nf-schema' +include { paramsHelp } from 'plugin/nf-schema' +include { completionEmail } from '../../nf-core/utils_nfcore_pipeline' +include { completionSummary } 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 { UTILS_NEXTFLOW_PIPELINE } from '../../nf-core/utils_nextflow_pipeline' /* -======================================================================================== +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ SUBWORKFLOW TO INITIALISE PIPELINE -======================================================================================== +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ workflow PIPELINE_INITIALISATION { - take: - version // boolean: Display version and exit - validate_params // boolean: Boolean whether to validate parameters against the schema at runtime - monochrome_logs // boolean: Do not use coloured log outputs + version // boolean: Display version and exit + 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 - outdir // string: The output directory where the results will be saved - input // string: Path to input samplesheet - genome_sheet // string: Path to reference genomesheet + outdir // string: The output directory where the results will be saved + input // string: Path to input samplesheet + help // boolean: Display help message and exit + help_full // boolean: Show the full help message + show_hidden // boolean: Show hidden parameters in the help message + genome_sheet // string: Path to reference genomesheet main: - ch_versions = Channel.empty() + ch_versions = channel.empty() // // Print version and exit if required and dump pipeline parameters to JSON file // - UTILS_NEXTFLOW_PIPELINE ( + UTILS_NEXTFLOW_PIPELINE( version, true, outdir, - workflow.profile.tokenize(',').intersect(['conda', 'mamba']).size() >= 1 + workflow.profile.tokenize(',').intersect(['conda', 'mamba']).size() >= 1, ) // // Validate parameters and generate parameter summary to stdout // - UTILS_NFSCHEMA_PLUGIN ( + before_text = """ +-\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 nf-core/coproid ${workflow.manifest.version}\033[0m +-\033[2m----------------------------------------------------\033[0m- +""" + after_text = """${workflow.manifest.doi ? "\n* The pipeline\n" : ""}${workflow.manifest.doi.tokenize(",").collect { doi -> " https://doi.org/${doi.trim().replace('https://doi.org/', '')}" }.join("\n")}${workflow.manifest.doi ? "\n" : ""} +* The nf-core framework + https://doi.org/10.1038/s41587-020-0439-x + +* Software dependencies + https://github.com/nf-core/coproid/blob/master/CITATIONS.md +""" + command = "nextflow run ${workflow.manifest.name} -profile --input samplesheet.csv --outdir " + + UTILS_NFSCHEMA_PLUGIN( workflow, validate_params, - null + null, + help, + help_full, + show_hidden, + before_text, + after_text, + command, ) // // Check config provided to the pipeline // - UTILS_NFCORE_PIPELINE ( + UTILS_NFCORE_PIPELINE( 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 ] ] - } + + 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() ] + .map { meta, fastqs -> + return [meta, fastqs.flatten()] } .set { ch_samplesheet } - emit: - samplesheet = ch_samplesheet - versions = ch_versions - // // Create channel from genomes file provided through params.genome_sheet // - Channel - .fromList(genomesToList(params.genome_sheet, "${projectDir}/assets/schema_genomes.json")) - .map { - meta, igenome, fasta, index -> + channel.fromList(genomesToList(params.genome_sheet, "${projectDir}/assets/schema_genomes.json")) + .map { meta, igenome, fasta, index -> [ - 'genome_name' : meta.genome_name, - 'taxid' : meta.taxid, - 'genome_size' : meta.genome_size, - 'igenome' : igenome, - 'fasta' : fasta, - 'index' : index + 'genome_name': meta.genome_name, + 'taxid': meta.taxid, + 'genome_size': meta.genome_size, + 'igenome': igenome, + 'fasta': fasta, + 'index': index, ] } .set { ch_genomesheet } emit: + samplesheet = ch_samplesheet genomesheet = ch_genomesheet versions = ch_versions } /* -======================================================================================== +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ SUBWORKFLOW FOR PIPELINE COMPLETION -======================================================================================== +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ workflow PIPELINE_COMPLETION { - take: - email // string: email address - email_on_fail // string: email address sent on pipeline failure + 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 + 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 + hook_url // string: hook URL for notifications + multiqc_report // string: Path to MultiQC report main: summary_params = paramsSummaryMap(workflow, parameters_schema: "nextflow_schema.json") @@ -159,7 +180,7 @@ workflow PIPELINE_COMPLETION { } workflow.onError { - log.error "Pipeline failed. Please refer to troubleshooting docs: https://nf-co.re/docs/usage/troubleshooting" + log.error("Pipeline failed. Please refer to troubleshooting docs: https://nf-co.re/docs/usage/troubleshooting") } } @@ -175,14 +196,13 @@ 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 + 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 ] + return [metas[0], fastqs] } - // // Generate methods description for MultiQC // @@ -207,7 +227,7 @@ def validateInputSamplesheet(input) { //} def methodsDescriptionText(mqc_methods_yaml) { - // Convert to a named map so can be used as with familar NXF ${workflow} variable syntax in the MultiQC YML file + // Convert to a named map so can be used as with familiar NXF ${workflow} variable syntax in the MultiQC YML file def meta = [:] meta.workflow = workflow.toMap() meta["manifest_map"] = workflow.manifest.toMap() @@ -223,7 +243,10 @@ def methodsDescriptionText(mqc_methods_yaml) { 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"] = "" + } + 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.
  • " // Tool references @@ -237,7 +260,7 @@ def methodsDescriptionText(mqc_methods_yaml) { def methods_text = mqc_methods_yaml.text - def engine = new groovy.text.SimpleTemplateEngine() + def engine = new groovy.text.SimpleTemplateEngine() def description_html = engine.createTemplate(methods_text).make(meta) return description_html.toString() diff --git a/subworkflows/nf-core/utils_nextflow_pipeline/tests/tags.yml b/subworkflows/nf-core/utils_nextflow_pipeline/tests/tags.yml deleted file mode 100644 index f847611..0000000 --- a/subworkflows/nf-core/utils_nextflow_pipeline/tests/tags.yml +++ /dev/null @@ -1,2 +0,0 @@ -subworkflows/utils_nextflow_pipeline: - - subworkflows/nf-core/utils_nextflow_pipeline/** diff --git a/subworkflows/nf-core/utils_nfcore_pipeline/main.nf b/subworkflows/nf-core/utils_nfcore_pipeline/main.nf index bfd2587..2f30e9a 100644 --- a/subworkflows/nf-core/utils_nfcore_pipeline/main.nf +++ b/subworkflows/nf-core/utils_nfcore_pipeline/main.nf @@ -98,7 +98,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())) } // diff --git a/subworkflows/nf-core/utils_nfcore_pipeline/tests/nextflow.config b/subworkflows/nf-core/utils_nfcore_pipeline/tests/nextflow.config index ae6f3ee..d0a926b 100644 --- a/subworkflows/nf-core/utils_nfcore_pipeline/tests/nextflow.config +++ b/subworkflows/nf-core/utils_nfcore_pipeline/tests/nextflow.config @@ -3,7 +3,7 @@ manifest { author = """nf-core""" homePage = 'https://127.0.0.1' description = """Dummy pipeline""" - nextflowVersion = '!>=24.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/tests/tags.yml b/subworkflows/nf-core/utils_nfcore_pipeline/tests/tags.yml deleted file mode 100644 index ac8523c..0000000 --- a/subworkflows/nf-core/utils_nfcore_pipeline/tests/tags.yml +++ /dev/null @@ -1,2 +0,0 @@ -subworkflows/utils_nfcore_pipeline: - - subworkflows/nf-core/utils_nfcore_pipeline/** diff --git a/subworkflows/nf-core/utils_nfschema_plugin/main.nf b/subworkflows/nf-core/utils_nfschema_plugin/main.nf index 4994303..1df8b76 100644 --- a/subworkflows/nf-core/utils_nfschema_plugin/main.nf +++ b/subworkflows/nf-core/utils_nfschema_plugin/main.nf @@ -4,6 +4,7 @@ include { paramsSummaryLog } from 'plugin/nf-schema' include { validateParameters } from 'plugin/nf-schema' +include { paramsHelp } from 'plugin/nf-schema' workflow UTILS_NFSCHEMA_PLUGIN { @@ -15,32 +16,58 @@ workflow UTILS_NFSCHEMA_PLUGIN { // 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 + help // boolean: show help message + help_full // boolean: show full help message + show_hidden // boolean: show hidden parameters in help message + before_text // string: text to show before the help message and parameters summary + after_text // string: text to show after the help message and parameters summary + command // string: an example command of the pipeline main: + if(help || help_full) { + help_options = [ + beforeText: before_text, + afterText: after_text, + command: command, + showHidden: show_hidden, + fullHelp: help_full, + ] + if(parameters_schema) { + help_options << [parametersSchema: parameters_schema] + } + log.info paramsHelp( + help_options, + (params.help instanceof String && params.help != "true") ? params.help : "", + ) + exit 0 + } + // // Print parameter summary to stdout. This will display the parameters // that differ from the default given in the JSON schema // + + summary_options = [:] if(parameters_schema) { - log.info paramsSummaryLog(input_workflow, parameters_schema:parameters_schema) - } else { - log.info paramsSummaryLog(input_workflow) + summary_options << [parametersSchema: parameters_schema] } + log.info before_text + log.info paramsSummaryLog(summary_options, input_workflow) + log.info after_text // // Validate the parameters using nextflow_schema.json or the schema // given via the validation.parametersSchema configuration option // if(validate_params) { + validateOptions = [:] if(parameters_schema) { - validateParameters(parameters_schema:parameters_schema) - } else { - validateParameters() + validateOptions << [parametersSchema: parameters_schema] } + validateParameters(validateOptions) } emit: dummy_emit = true } - diff --git a/subworkflows/nf-core/utils_nfschema_plugin/tests/main.nf.test b/subworkflows/nf-core/utils_nfschema_plugin/tests/main.nf.test index 8fb3016..c977917 100644 --- a/subworkflows/nf-core/utils_nfschema_plugin/tests/main.nf.test +++ b/subworkflows/nf-core/utils_nfschema_plugin/tests/main.nf.test @@ -25,6 +25,12 @@ nextflow_workflow { input[0] = workflow input[1] = validate_params input[2] = "" + input[3] = false + input[4] = false + input[5] = false + input[6] = "" + input[7] = "" + input[8] = "" """ } } @@ -51,6 +57,12 @@ nextflow_workflow { input[0] = workflow input[1] = validate_params input[2] = "" + input[3] = false + input[4] = false + input[5] = false + input[6] = "" + input[7] = "" + input[8] = "" """ } } @@ -77,6 +89,12 @@ nextflow_workflow { input[0] = workflow input[1] = validate_params input[2] = "${projectDir}/subworkflows/nf-core/utils_nfschema_plugin/tests/nextflow_schema.json" + input[3] = false + input[4] = false + input[5] = false + input[6] = "" + input[7] = "" + input[8] = "" """ } } @@ -103,6 +121,12 @@ nextflow_workflow { input[0] = workflow input[1] = validate_params input[2] = "${projectDir}/subworkflows/nf-core/utils_nfschema_plugin/tests/nextflow_schema.json" + input[3] = false + input[4] = false + input[5] = false + input[6] = "" + input[7] = "" + input[8] = "" """ } } @@ -114,4 +138,36 @@ nextflow_workflow { ) } } + + test("Should create a help message") { + + when { + + params { + test_data = '' + outdir = null + } + + workflow { + """ + validate_params = true + input[0] = workflow + input[1] = validate_params + input[2] = "${projectDir}/subworkflows/nf-core/utils_nfschema_plugin/tests/nextflow_schema.json" + input[3] = true + input[4] = false + input[5] = false + input[6] = "Before" + input[7] = "After" + input[8] = "nextflow run test/test" + """ + } + } + + then { + assertAll( + { assert workflow.success } + ) + } + } } diff --git a/subworkflows/nf-core/utils_nfschema_plugin/tests/nextflow.config b/subworkflows/nf-core/utils_nfschema_plugin/tests/nextflow.config index 0907ac5..f6537cc 100644 --- a/subworkflows/nf-core/utils_nfschema_plugin/tests/nextflow.config +++ b/subworkflows/nf-core/utils_nfschema_plugin/tests/nextflow.config @@ -1,8 +1,8 @@ plugins { - id "nf-schema@2.1.0" + id "nf-schema@2.6.1" } validation { parametersSchema = "${projectDir}/subworkflows/nf-core/utils_nfschema_plugin/tests/nextflow_schema.json" monochromeLogs = true -} \ No newline at end of file +} diff --git a/tests/.nftignore b/tests/.nftignore new file mode 100644 index 0000000..9dbb43d --- /dev/null +++ b/tests/.nftignore @@ -0,0 +1,16 @@ +.DS_Store +multiqc/multiqc_data/fastqc_top_overrepresented_sequences_table.txt +multiqc/multiqc_data/multiqc.parquet +multiqc/multiqc_data/multiqc.log +multiqc/multiqc_data/multiqc_data.json +multiqc/multiqc_data/multiqc_sources.txt +multiqc/multiqc_data/multiqc_software_versions.txt +multiqc/multiqc_data/llms-full.txt +multiqc/multiqc_plots/{svg,pdf,png}/*.{svg,pdf,png} +multiqc/multiqc_report.html +fastqc/*_fastqc.{html,zip} +pipeline_info/*.{html,json,txt,yml} +damageprofiler/** +sam2lca/ +**/*.svg +**/*.pdf diff --git a/tests/default.nf.test b/tests/default.nf.test new file mode 100644 index 0000000..79622b8 --- /dev/null +++ b/tests/default.nf.test @@ -0,0 +1,34 @@ +nextflow_pipeline { + + name "Test pipeline" + script "../main.nf" + tag "pipeline" + tag "test" + + test("-profile test") { + + when { + params { + outdir = "$outputDir" + } + } + + then { + // stable_name: All files + folders in ${params.outdir}/ with a stable name + def stable_name = getAllFilesFromDir(params.outdir, relative: true, includeDir: true, ignore: ['pipeline_info/*.{html,json,txt}']) + // stable_path: All files in ${params.outdir}/ with stable content + def stable_path = getAllFilesFromDir(params.outdir, ignoreFile: 'tests/.nftignore') + assertAll( + { assert workflow.success}, + { assert snapshot( + // pipeline versions.yml file for multiqc from which Nextflow version is removed because we test pipelines on multiple Nextflow versions + removeNextflowVersion("$outputDir/pipeline_info/nf_core_coproid_software_mqc_versions.yml"), + // All stable path name, with a relative path + stable_name, + // All files with stable contents + //stable_path + ).match() } + ) + } + } +} diff --git a/tests/default.nf.test.snap b/tests/default.nf.test.snap new file mode 100644 index 0000000..b1b3515 --- /dev/null +++ b/tests/default.nf.test.snap @@ -0,0 +1,440 @@ +{ + "-profile test": { + "content": [ + { + "BOWTIE2_ALIGN": { + "bowtie2": "2.5.2", + "samtools": 1.18, + "pigz": 2.6 + }, + "BOWTIE2_BUILD": { + "bowtie2": "2.5.2" + }, + "CREATE_ACC2TAX": { + "python": "3.10.13" + }, + "DAMAGEPROFILER": { + "damageprofiler": 1.1 + }, + "DAMAGEPROFILER_MERGE": { + "python": "3.10.6" + }, + "FASTP": { + "fastp": "0.23.4" + }, + "FASTQC": { + "fastqc": "0.12.1" + }, + "PYDAMAGE_ANALYZE": { + "pydamage": 0.7 + }, + "PYDAMAGE_MERGE": { + "python": "3.10.6" + }, + "SAM2LCA_ANALYZE": { + "sam2lca": "1.1.4" + }, + "SAM2LCA_MERGE": { + "python": "3.10.6" + }, + "SAMTOOLS_MERGE": { + "samtools": 1.21 + }, + "SOURCEPREDICT": { + "python": "3.12.7", + "sklearn": "1.5.2", + "sourcepredict": "0.5.1" + }, + "UNTAR": { + "untar": 1.34 + }, + "Workflow": { + "nf-core/coproid": "v2.0.1" + } + }, + [ + "bowtie2", + "bowtie2/SAMPLE_BSUB-Bacillus_subtilis.bam", + "bowtie2/SAMPLE_BSUB-Bacillus_subtilis.bowtie2.log", + "bowtie2/SAMPLE_BSUB-Bacillus_subtilis.unmapped_1.fastq.gz", + "bowtie2/SAMPLE_BSUB-Bacillus_subtilis.unmapped_2.fastq.gz", + "bowtie2/SAMPLE_BSUB-Escherichia_coli.bam", + "bowtie2/SAMPLE_BSUB-Escherichia_coli.bowtie2.log", + "bowtie2/SAMPLE_BSUB-Escherichia_coli.unmapped_1.fastq.gz", + "bowtie2/SAMPLE_BSUB-Escherichia_coli.unmapped_2.fastq.gz", + "bowtie2/SAMPLE_ECOLI-Bacillus_subtilis.bam", + "bowtie2/SAMPLE_ECOLI-Bacillus_subtilis.bowtie2.log", + "bowtie2/SAMPLE_ECOLI-Bacillus_subtilis.unmapped_1.fastq.gz", + "bowtie2/SAMPLE_ECOLI-Bacillus_subtilis.unmapped_2.fastq.gz", + "bowtie2/SAMPLE_ECOLI-Escherichia_coli.bam", + "bowtie2/SAMPLE_ECOLI-Escherichia_coli.bowtie2.log", + "bowtie2/SAMPLE_ECOLI-Escherichia_coli.unmapped_1.fastq.gz", + "bowtie2/SAMPLE_ECOLI-Escherichia_coli.unmapped_2.fastq.gz", + "bowtie2/bowtie2", + "bowtie2/bowtie2/genome.1.bt2", + "bowtie2/bowtie2/genome.2.bt2", + "bowtie2/bowtie2/genome.3.bt2", + "bowtie2/bowtie2/genome.4.bt2", + "bowtie2/bowtie2/genome.rev.1.bt2", + "bowtie2/bowtie2/genome.rev.2.bt2", + "coproid_report", + "coproid_report/_extensions", + "coproid_report/coproid_quarto_report.qmd", + "coproid_report/coproid_report.html", + "coproid_report/params.yml", + "coproid_report/versions.yml", + "damageprofiler", + "damageprofiler/SAMPLE_BSUB-Bacillus_subtilis", + "damageprofiler/SAMPLE_BSUB-Bacillus_subtilis/3pGtoA_freq.txt", + "damageprofiler/SAMPLE_BSUB-Bacillus_subtilis/3p_freq_misincorporations.txt", + "damageprofiler/SAMPLE_BSUB-Bacillus_subtilis/5pCtoT_freq.txt", + "damageprofiler/SAMPLE_BSUB-Bacillus_subtilis/5p_freq_misincorporations.txt", + "damageprofiler/SAMPLE_BSUB-Bacillus_subtilis/DNA_comp_genome.txt", + "damageprofiler/SAMPLE_BSUB-Bacillus_subtilis/DNA_composition_sample.txt", + "damageprofiler/SAMPLE_BSUB-Bacillus_subtilis/DamagePlot.pdf", + "damageprofiler/SAMPLE_BSUB-Bacillus_subtilis/DamagePlot_five_prime.svg", + "damageprofiler/SAMPLE_BSUB-Bacillus_subtilis/DamagePlot_three_prime.svg", + "damageprofiler/SAMPLE_BSUB-Bacillus_subtilis/DamageProfiler.log", + "damageprofiler/SAMPLE_BSUB-Bacillus_subtilis/Length_plot.pdf", + "damageprofiler/SAMPLE_BSUB-Bacillus_subtilis/Length_plot_combined_data.svg", + "damageprofiler/SAMPLE_BSUB-Bacillus_subtilis/Length_plot_forward_reverse_separated.svg", + "damageprofiler/SAMPLE_BSUB-Bacillus_subtilis/dmgprof.json", + "damageprofiler/SAMPLE_BSUB-Bacillus_subtilis/editDistance.txt", + "damageprofiler/SAMPLE_BSUB-Bacillus_subtilis/edit_distance.pdf", + "damageprofiler/SAMPLE_BSUB-Bacillus_subtilis/edit_distance.svg", + "damageprofiler/SAMPLE_BSUB-Bacillus_subtilis/lgdistribution.txt", + "damageprofiler/SAMPLE_BSUB-Bacillus_subtilis/misincorporation.txt", + "damageprofiler/SAMPLE_BSUB-Escherichia_coli", + "damageprofiler/SAMPLE_BSUB-Escherichia_coli/3pGtoA_freq.txt", + "damageprofiler/SAMPLE_BSUB-Escherichia_coli/3p_freq_misincorporations.txt", + "damageprofiler/SAMPLE_BSUB-Escherichia_coli/5pCtoT_freq.txt", + "damageprofiler/SAMPLE_BSUB-Escherichia_coli/5p_freq_misincorporations.txt", + "damageprofiler/SAMPLE_BSUB-Escherichia_coli/DNA_comp_genome.txt", + "damageprofiler/SAMPLE_BSUB-Escherichia_coli/DNA_composition_sample.txt", + "damageprofiler/SAMPLE_BSUB-Escherichia_coli/DamagePlot.pdf", + "damageprofiler/SAMPLE_BSUB-Escherichia_coli/DamagePlot_five_prime.svg", + "damageprofiler/SAMPLE_BSUB-Escherichia_coli/DamagePlot_three_prime.svg", + "damageprofiler/SAMPLE_BSUB-Escherichia_coli/DamageProfiler.log", + "damageprofiler/SAMPLE_BSUB-Escherichia_coli/Length_plot.pdf", + "damageprofiler/SAMPLE_BSUB-Escherichia_coli/Length_plot_combined_data.svg", + "damageprofiler/SAMPLE_BSUB-Escherichia_coli/Length_plot_forward_reverse_separated.svg", + "damageprofiler/SAMPLE_BSUB-Escherichia_coli/dmgprof.json", + "damageprofiler/SAMPLE_BSUB-Escherichia_coli/editDistance.txt", + "damageprofiler/SAMPLE_BSUB-Escherichia_coli/edit_distance.pdf", + "damageprofiler/SAMPLE_BSUB-Escherichia_coli/edit_distance.svg", + "damageprofiler/SAMPLE_BSUB-Escherichia_coli/lgdistribution.txt", + "damageprofiler/SAMPLE_BSUB-Escherichia_coli/misincorporation.txt", + "damageprofiler/SAMPLE_ECOLI-Bacillus_subtilis", + "damageprofiler/SAMPLE_ECOLI-Bacillus_subtilis/3pGtoA_freq.txt", + "damageprofiler/SAMPLE_ECOLI-Bacillus_subtilis/3p_freq_misincorporations.txt", + "damageprofiler/SAMPLE_ECOLI-Bacillus_subtilis/5pCtoT_freq.txt", + "damageprofiler/SAMPLE_ECOLI-Bacillus_subtilis/5p_freq_misincorporations.txt", + "damageprofiler/SAMPLE_ECOLI-Bacillus_subtilis/DNA_comp_genome.txt", + "damageprofiler/SAMPLE_ECOLI-Bacillus_subtilis/DNA_composition_sample.txt", + "damageprofiler/SAMPLE_ECOLI-Bacillus_subtilis/DamagePlot.pdf", + "damageprofiler/SAMPLE_ECOLI-Bacillus_subtilis/DamagePlot_five_prime.svg", + "damageprofiler/SAMPLE_ECOLI-Bacillus_subtilis/DamagePlot_three_prime.svg", + "damageprofiler/SAMPLE_ECOLI-Bacillus_subtilis/DamageProfiler.log", + "damageprofiler/SAMPLE_ECOLI-Bacillus_subtilis/Length_plot.pdf", + "damageprofiler/SAMPLE_ECOLI-Bacillus_subtilis/Length_plot_combined_data.svg", + "damageprofiler/SAMPLE_ECOLI-Bacillus_subtilis/Length_plot_forward_reverse_separated.svg", + "damageprofiler/SAMPLE_ECOLI-Bacillus_subtilis/dmgprof.json", + "damageprofiler/SAMPLE_ECOLI-Bacillus_subtilis/editDistance.txt", + "damageprofiler/SAMPLE_ECOLI-Bacillus_subtilis/edit_distance.pdf", + "damageprofiler/SAMPLE_ECOLI-Bacillus_subtilis/edit_distance.svg", + "damageprofiler/SAMPLE_ECOLI-Bacillus_subtilis/lgdistribution.txt", + "damageprofiler/SAMPLE_ECOLI-Bacillus_subtilis/misincorporation.txt", + "damageprofiler/SAMPLE_ECOLI-Escherichia_coli", + "damageprofiler/SAMPLE_ECOLI-Escherichia_coli/3pGtoA_freq.txt", + "damageprofiler/SAMPLE_ECOLI-Escherichia_coli/3p_freq_misincorporations.txt", + "damageprofiler/SAMPLE_ECOLI-Escherichia_coli/5pCtoT_freq.txt", + "damageprofiler/SAMPLE_ECOLI-Escherichia_coli/5p_freq_misincorporations.txt", + "damageprofiler/SAMPLE_ECOLI-Escherichia_coli/DNA_comp_genome.txt", + "damageprofiler/SAMPLE_ECOLI-Escherichia_coli/DNA_composition_sample.txt", + "damageprofiler/SAMPLE_ECOLI-Escherichia_coli/DamagePlot.pdf", + "damageprofiler/SAMPLE_ECOLI-Escherichia_coli/DamagePlot_five_prime.svg", + "damageprofiler/SAMPLE_ECOLI-Escherichia_coli/DamagePlot_three_prime.svg", + "damageprofiler/SAMPLE_ECOLI-Escherichia_coli/DamageProfiler.log", + "damageprofiler/SAMPLE_ECOLI-Escherichia_coli/Length_plot.pdf", + "damageprofiler/SAMPLE_ECOLI-Escherichia_coli/Length_plot_combined_data.svg", + "damageprofiler/SAMPLE_ECOLI-Escherichia_coli/Length_plot_forward_reverse_separated.svg", + "damageprofiler/SAMPLE_ECOLI-Escherichia_coli/dmgprof.json", + "damageprofiler/SAMPLE_ECOLI-Escherichia_coli/editDistance.txt", + "damageprofiler/SAMPLE_ECOLI-Escherichia_coli/edit_distance.pdf", + "damageprofiler/SAMPLE_ECOLI-Escherichia_coli/edit_distance.svg", + "damageprofiler/SAMPLE_ECOLI-Escherichia_coli/lgdistribution.txt", + "damageprofiler/SAMPLE_ECOLI-Escherichia_coli/misincorporation.txt", + "damageprofiler/coproid.damageprofiler_merged_report.csv", + "fastp", + "fastp/SAMPLE_BSUB.fastp.html", + "fastp/SAMPLE_BSUB.fastp.json", + "fastp/SAMPLE_BSUB.fastp.log", + "fastp/SAMPLE_BSUB.merged.fastq.gz", + "fastp/SAMPLE_BSUB_1.fastp.fastq.gz", + "fastp/SAMPLE_BSUB_2.fastp.fastq.gz", + "fastp/SAMPLE_ECOLI.fastp.html", + "fastp/SAMPLE_ECOLI.fastp.json", + "fastp/SAMPLE_ECOLI.fastp.log", + "fastp/SAMPLE_ECOLI.merged.fastq.gz", + "fastp/SAMPLE_ECOLI_1.fastp.fastq.gz", + "fastp/SAMPLE_ECOLI_2.fastp.fastq.gz", + "fastqc", + "fastqc/SAMPLE_BSUB_1_fastqc.html", + "fastqc/SAMPLE_BSUB_1_fastqc.zip", + "fastqc/SAMPLE_BSUB_2_fastqc.html", + "fastqc/SAMPLE_BSUB_2_fastqc.zip", + "fastqc/SAMPLE_ECOLI_1_fastqc.html", + "fastqc/SAMPLE_ECOLI_1_fastqc.zip", + "fastqc/SAMPLE_ECOLI_2_fastqc.html", + "fastqc/SAMPLE_ECOLI_2_fastqc.zip", + "kraken2", + "kraken2/SAMPLE_BSUB.kmer_kraken_parsed.csv", + "kraken2/SAMPLE_BSUB.kraken2.report.txt", + "kraken2/SAMPLE_BSUB.read_kraken_parsed.csv", + "kraken2/SAMPLE_ECOLI.kmer_kraken_parsed.csv", + "kraken2/SAMPLE_ECOLI.kraken2.report.txt", + "kraken2/SAMPLE_ECOLI.read_kraken_parsed.csv", + "kraken2/coproid.kraken2_merged_report.csv", + "kraken2/versions.yml", + "multiqc", + "multiqc/coproid_multiqc_report.html", + "multiqc/coproid_multiqc_report_data", + "multiqc/coproid_multiqc_report_data/bowtie2_pe_plot.txt", + "multiqc/coproid_multiqc_report_data/fastp-insert-size-plot.txt", + "multiqc/coproid_multiqc_report_data/fastp-seq-content-gc-plot_Merged_and_filtered.txt", + "multiqc/coproid_multiqc_report_data/fastp-seq-content-gc-plot_Read_1_Before_filtering.txt", + "multiqc/coproid_multiqc_report_data/fastp-seq-content-gc-plot_Read_2_Before_filtering.txt", + "multiqc/coproid_multiqc_report_data/fastp-seq-content-n-plot_Merged_and_filtered.txt", + "multiqc/coproid_multiqc_report_data/fastp-seq-content-n-plot_Read_1_Before_filtering.txt", + "multiqc/coproid_multiqc_report_data/fastp-seq-content-n-plot_Read_2_Before_filtering.txt", + "multiqc/coproid_multiqc_report_data/fastp-seq-quality-plot_Merged_and_filtered.txt", + "multiqc/coproid_multiqc_report_data/fastp-seq-quality-plot_Read_1_Before_filtering.txt", + "multiqc/coproid_multiqc_report_data/fastp-seq-quality-plot_Read_2_Before_filtering.txt", + "multiqc/coproid_multiqc_report_data/fastp_filtered_reads_plot.txt", + "multiqc/coproid_multiqc_report_data/fastqc-status-check-heatmap.txt", + "multiqc/coproid_multiqc_report_data/fastqc_adapter_content_plot.txt", + "multiqc/coproid_multiqc_report_data/fastqc_per_base_n_content_plot.txt", + "multiqc/coproid_multiqc_report_data/fastqc_per_base_sequence_quality_plot.txt", + "multiqc/coproid_multiqc_report_data/fastqc_per_sequence_gc_content_plot_Counts.txt", + "multiqc/coproid_multiqc_report_data/fastqc_per_sequence_gc_content_plot_Percentages.txt", + "multiqc/coproid_multiqc_report_data/fastqc_per_sequence_quality_scores_plot.txt", + "multiqc/coproid_multiqc_report_data/fastqc_sequence_counts_plot.txt", + "multiqc/coproid_multiqc_report_data/fastqc_sequence_duplication_levels_plot.txt", + "multiqc/coproid_multiqc_report_data/fiveprime_misinc_plot.txt", + "multiqc/coproid_multiqc_report_data/kraken-top-duplication_plot.txt", + "multiqc/coproid_multiqc_report_data/kraken-top-n-plot_Class.txt", + "multiqc/coproid_multiqc_report_data/kraken-top-n-plot_Domain.txt", + "multiqc/coproid_multiqc_report_data/kraken-top-n-plot_Family.txt", + "multiqc/coproid_multiqc_report_data/kraken-top-n-plot_Genus.txt", + "multiqc/coproid_multiqc_report_data/kraken-top-n-plot_Order.txt", + "multiqc/coproid_multiqc_report_data/kraken-top-n-plot_Phylum.txt", + "multiqc/coproid_multiqc_report_data/kraken-top-n-plot_Species.txt", + "multiqc/coproid_multiqc_report_data/kraken-top-n-plot_Unclassified.txt", + "multiqc/coproid_multiqc_report_data/length-distribution-Forward.txt", + "multiqc/coproid_multiqc_report_data/length-distribution-Reverse.txt", + "multiqc/coproid_multiqc_report_data/llms-full.txt", + "multiqc/coproid_multiqc_report_data/mapdamage-fiveprime_misinc_plot.txt", + "multiqc/coproid_multiqc_report_data/mapdamage-length-distribution-Forward.txt", + "multiqc/coproid_multiqc_report_data/mapdamage-length-distribution-Reverse.txt", + "multiqc/coproid_multiqc_report_data/mapdamage-threeprime_misinc_plot.txt", + "multiqc/coproid_multiqc_report_data/multiqc.log", + "multiqc/coproid_multiqc_report_data/multiqc.parquet", + "multiqc/coproid_multiqc_report_data/multiqc_bowtie2.txt", + "multiqc/coproid_multiqc_report_data/multiqc_citations.txt", + "multiqc/coproid_multiqc_report_data/multiqc_damageprofiler_metrics.txt", + "multiqc/coproid_multiqc_report_data/multiqc_data.json", + "multiqc/coproid_multiqc_report_data/multiqc_fastp.txt", + "multiqc/coproid_multiqc_report_data/multiqc_fastqc.txt", + "multiqc/coproid_multiqc_report_data/multiqc_general_stats.txt", + "multiqc/coproid_multiqc_report_data/multiqc_kraken.txt", + "multiqc/coproid_multiqc_report_data/multiqc_software_versions.txt", + "multiqc/coproid_multiqc_report_data/multiqc_sources.txt", + "multiqc/coproid_multiqc_report_data/threeprime_misinc_plot.txt", + "multiqc/coproid_multiqc_report_plots", + "multiqc/coproid_multiqc_report_plots/pdf", + "multiqc/coproid_multiqc_report_plots/pdf/bowtie2_pe_plot-cnt.pdf", + "multiqc/coproid_multiqc_report_plots/pdf/bowtie2_pe_plot-pct.pdf", + "multiqc/coproid_multiqc_report_plots/pdf/fastp-insert-size-plot.pdf", + "multiqc/coproid_multiqc_report_plots/pdf/fastp-seq-content-gc-plot_Merged_and_filtered.pdf", + "multiqc/coproid_multiqc_report_plots/pdf/fastp-seq-content-gc-plot_Read_1_Before_filtering.pdf", + "multiqc/coproid_multiqc_report_plots/pdf/fastp-seq-content-gc-plot_Read_2_Before_filtering.pdf", + "multiqc/coproid_multiqc_report_plots/pdf/fastp-seq-content-n-plot_Merged_and_filtered.pdf", + "multiqc/coproid_multiqc_report_plots/pdf/fastp-seq-content-n-plot_Read_1_Before_filtering.pdf", + "multiqc/coproid_multiqc_report_plots/pdf/fastp-seq-content-n-plot_Read_2_Before_filtering.pdf", + "multiqc/coproid_multiqc_report_plots/pdf/fastp-seq-quality-plot_Merged_and_filtered.pdf", + "multiqc/coproid_multiqc_report_plots/pdf/fastp-seq-quality-plot_Read_1_Before_filtering.pdf", + "multiqc/coproid_multiqc_report_plots/pdf/fastp-seq-quality-plot_Read_2_Before_filtering.pdf", + "multiqc/coproid_multiqc_report_plots/pdf/fastp_filtered_reads_plot-cnt.pdf", + "multiqc/coproid_multiqc_report_plots/pdf/fastp_filtered_reads_plot-pct.pdf", + "multiqc/coproid_multiqc_report_plots/pdf/fastqc-status-check-heatmap.pdf", + "multiqc/coproid_multiqc_report_plots/pdf/fastqc_adapter_content_plot.pdf", + "multiqc/coproid_multiqc_report_plots/pdf/fastqc_per_base_n_content_plot.pdf", + "multiqc/coproid_multiqc_report_plots/pdf/fastqc_per_base_sequence_quality_plot.pdf", + "multiqc/coproid_multiqc_report_plots/pdf/fastqc_per_sequence_gc_content_plot_Counts.pdf", + "multiqc/coproid_multiqc_report_plots/pdf/fastqc_per_sequence_gc_content_plot_Percentages.pdf", + "multiqc/coproid_multiqc_report_plots/pdf/fastqc_per_sequence_quality_scores_plot.pdf", + "multiqc/coproid_multiqc_report_plots/pdf/fastqc_sequence_counts_plot-cnt.pdf", + "multiqc/coproid_multiqc_report_plots/pdf/fastqc_sequence_counts_plot-pct.pdf", + "multiqc/coproid_multiqc_report_plots/pdf/fastqc_sequence_duplication_levels_plot.pdf", + "multiqc/coproid_multiqc_report_plots/pdf/fiveprime_misinc_plot.pdf", + "multiqc/coproid_multiqc_report_plots/pdf/kraken-top-duplication_plot.pdf", + "multiqc/coproid_multiqc_report_plots/pdf/kraken-top-n-plot_Class-cnt.pdf", + "multiqc/coproid_multiqc_report_plots/pdf/kraken-top-n-plot_Class-pct.pdf", + "multiqc/coproid_multiqc_report_plots/pdf/kraken-top-n-plot_Domain-cnt.pdf", + "multiqc/coproid_multiqc_report_plots/pdf/kraken-top-n-plot_Domain-pct.pdf", + "multiqc/coproid_multiqc_report_plots/pdf/kraken-top-n-plot_Family-cnt.pdf", + "multiqc/coproid_multiqc_report_plots/pdf/kraken-top-n-plot_Family-pct.pdf", + "multiqc/coproid_multiqc_report_plots/pdf/kraken-top-n-plot_Genus-cnt.pdf", + "multiqc/coproid_multiqc_report_plots/pdf/kraken-top-n-plot_Genus-pct.pdf", + "multiqc/coproid_multiqc_report_plots/pdf/kraken-top-n-plot_Order-cnt.pdf", + "multiqc/coproid_multiqc_report_plots/pdf/kraken-top-n-plot_Order-pct.pdf", + "multiqc/coproid_multiqc_report_plots/pdf/kraken-top-n-plot_Phylum-cnt.pdf", + "multiqc/coproid_multiqc_report_plots/pdf/kraken-top-n-plot_Phylum-pct.pdf", + "multiqc/coproid_multiqc_report_plots/pdf/kraken-top-n-plot_Species-cnt.pdf", + "multiqc/coproid_multiqc_report_plots/pdf/kraken-top-n-plot_Species-pct.pdf", + "multiqc/coproid_multiqc_report_plots/pdf/kraken-top-n-plot_Unclassified-cnt.pdf", + "multiqc/coproid_multiqc_report_plots/pdf/kraken-top-n-plot_Unclassified-pct.pdf", + "multiqc/coproid_multiqc_report_plots/pdf/length-distribution-Forward.pdf", + "multiqc/coproid_multiqc_report_plots/pdf/length-distribution-Reverse.pdf", + "multiqc/coproid_multiqc_report_plots/pdf/mapdamage-fiveprime_misinc_plot.pdf", + "multiqc/coproid_multiqc_report_plots/pdf/mapdamage-length-distribution-Forward.pdf", + "multiqc/coproid_multiqc_report_plots/pdf/mapdamage-length-distribution-Reverse.pdf", + "multiqc/coproid_multiqc_report_plots/pdf/mapdamage-threeprime_misinc_plot.pdf", + "multiqc/coproid_multiqc_report_plots/pdf/threeprime_misinc_plot.pdf", + "multiqc/coproid_multiqc_report_plots/png", + "multiqc/coproid_multiqc_report_plots/png/bowtie2_pe_plot-cnt.png", + "multiqc/coproid_multiqc_report_plots/png/bowtie2_pe_plot-pct.png", + "multiqc/coproid_multiqc_report_plots/png/fastp-insert-size-plot.png", + "multiqc/coproid_multiqc_report_plots/png/fastp-seq-content-gc-plot_Merged_and_filtered.png", + "multiqc/coproid_multiqc_report_plots/png/fastp-seq-content-gc-plot_Read_1_Before_filtering.png", + "multiqc/coproid_multiqc_report_plots/png/fastp-seq-content-gc-plot_Read_2_Before_filtering.png", + "multiqc/coproid_multiqc_report_plots/png/fastp-seq-content-n-plot_Merged_and_filtered.png", + "multiqc/coproid_multiqc_report_plots/png/fastp-seq-content-n-plot_Read_1_Before_filtering.png", + "multiqc/coproid_multiqc_report_plots/png/fastp-seq-content-n-plot_Read_2_Before_filtering.png", + "multiqc/coproid_multiqc_report_plots/png/fastp-seq-quality-plot_Merged_and_filtered.png", + "multiqc/coproid_multiqc_report_plots/png/fastp-seq-quality-plot_Read_1_Before_filtering.png", + "multiqc/coproid_multiqc_report_plots/png/fastp-seq-quality-plot_Read_2_Before_filtering.png", + "multiqc/coproid_multiqc_report_plots/png/fastp_filtered_reads_plot-cnt.png", + "multiqc/coproid_multiqc_report_plots/png/fastp_filtered_reads_plot-pct.png", + "multiqc/coproid_multiqc_report_plots/png/fastqc-status-check-heatmap.png", + "multiqc/coproid_multiqc_report_plots/png/fastqc_adapter_content_plot.png", + "multiqc/coproid_multiqc_report_plots/png/fastqc_per_base_n_content_plot.png", + "multiqc/coproid_multiqc_report_plots/png/fastqc_per_base_sequence_quality_plot.png", + "multiqc/coproid_multiqc_report_plots/png/fastqc_per_sequence_gc_content_plot_Counts.png", + "multiqc/coproid_multiqc_report_plots/png/fastqc_per_sequence_gc_content_plot_Percentages.png", + "multiqc/coproid_multiqc_report_plots/png/fastqc_per_sequence_quality_scores_plot.png", + "multiqc/coproid_multiqc_report_plots/png/fastqc_sequence_counts_plot-cnt.png", + "multiqc/coproid_multiqc_report_plots/png/fastqc_sequence_counts_plot-pct.png", + "multiqc/coproid_multiqc_report_plots/png/fastqc_sequence_duplication_levels_plot.png", + "multiqc/coproid_multiqc_report_plots/png/fiveprime_misinc_plot.png", + "multiqc/coproid_multiqc_report_plots/png/kraken-top-duplication_plot.png", + "multiqc/coproid_multiqc_report_plots/png/kraken-top-n-plot_Class-cnt.png", + "multiqc/coproid_multiqc_report_plots/png/kraken-top-n-plot_Class-pct.png", + "multiqc/coproid_multiqc_report_plots/png/kraken-top-n-plot_Domain-cnt.png", + "multiqc/coproid_multiqc_report_plots/png/kraken-top-n-plot_Domain-pct.png", + "multiqc/coproid_multiqc_report_plots/png/kraken-top-n-plot_Family-cnt.png", + "multiqc/coproid_multiqc_report_plots/png/kraken-top-n-plot_Family-pct.png", + "multiqc/coproid_multiqc_report_plots/png/kraken-top-n-plot_Genus-cnt.png", + "multiqc/coproid_multiqc_report_plots/png/kraken-top-n-plot_Genus-pct.png", + "multiqc/coproid_multiqc_report_plots/png/kraken-top-n-plot_Order-cnt.png", + "multiqc/coproid_multiqc_report_plots/png/kraken-top-n-plot_Order-pct.png", + "multiqc/coproid_multiqc_report_plots/png/kraken-top-n-plot_Phylum-cnt.png", + "multiqc/coproid_multiqc_report_plots/png/kraken-top-n-plot_Phylum-pct.png", + "multiqc/coproid_multiqc_report_plots/png/kraken-top-n-plot_Species-cnt.png", + "multiqc/coproid_multiqc_report_plots/png/kraken-top-n-plot_Species-pct.png", + "multiqc/coproid_multiqc_report_plots/png/kraken-top-n-plot_Unclassified-cnt.png", + "multiqc/coproid_multiqc_report_plots/png/kraken-top-n-plot_Unclassified-pct.png", + "multiqc/coproid_multiqc_report_plots/png/length-distribution-Forward.png", + "multiqc/coproid_multiqc_report_plots/png/length-distribution-Reverse.png", + "multiqc/coproid_multiqc_report_plots/png/mapdamage-fiveprime_misinc_plot.png", + "multiqc/coproid_multiqc_report_plots/png/mapdamage-length-distribution-Forward.png", + "multiqc/coproid_multiqc_report_plots/png/mapdamage-length-distribution-Reverse.png", + "multiqc/coproid_multiqc_report_plots/png/mapdamage-threeprime_misinc_plot.png", + "multiqc/coproid_multiqc_report_plots/png/threeprime_misinc_plot.png", + "multiqc/coproid_multiqc_report_plots/svg", + "multiqc/coproid_multiqc_report_plots/svg/bowtie2_pe_plot-cnt.svg", + "multiqc/coproid_multiqc_report_plots/svg/bowtie2_pe_plot-pct.svg", + "multiqc/coproid_multiqc_report_plots/svg/fastp-insert-size-plot.svg", + "multiqc/coproid_multiqc_report_plots/svg/fastp-seq-content-gc-plot_Merged_and_filtered.svg", + "multiqc/coproid_multiqc_report_plots/svg/fastp-seq-content-gc-plot_Read_1_Before_filtering.svg", + "multiqc/coproid_multiqc_report_plots/svg/fastp-seq-content-gc-plot_Read_2_Before_filtering.svg", + "multiqc/coproid_multiqc_report_plots/svg/fastp-seq-content-n-plot_Merged_and_filtered.svg", + "multiqc/coproid_multiqc_report_plots/svg/fastp-seq-content-n-plot_Read_1_Before_filtering.svg", + "multiqc/coproid_multiqc_report_plots/svg/fastp-seq-content-n-plot_Read_2_Before_filtering.svg", + "multiqc/coproid_multiqc_report_plots/svg/fastp-seq-quality-plot_Merged_and_filtered.svg", + "multiqc/coproid_multiqc_report_plots/svg/fastp-seq-quality-plot_Read_1_Before_filtering.svg", + "multiqc/coproid_multiqc_report_plots/svg/fastp-seq-quality-plot_Read_2_Before_filtering.svg", + "multiqc/coproid_multiqc_report_plots/svg/fastp_filtered_reads_plot-cnt.svg", + "multiqc/coproid_multiqc_report_plots/svg/fastp_filtered_reads_plot-pct.svg", + "multiqc/coproid_multiqc_report_plots/svg/fastqc-status-check-heatmap.svg", + "multiqc/coproid_multiqc_report_plots/svg/fastqc_adapter_content_plot.svg", + "multiqc/coproid_multiqc_report_plots/svg/fastqc_per_base_n_content_plot.svg", + "multiqc/coproid_multiqc_report_plots/svg/fastqc_per_base_sequence_quality_plot.svg", + "multiqc/coproid_multiqc_report_plots/svg/fastqc_per_sequence_gc_content_plot_Counts.svg", + "multiqc/coproid_multiqc_report_plots/svg/fastqc_per_sequence_gc_content_plot_Percentages.svg", + "multiqc/coproid_multiqc_report_plots/svg/fastqc_per_sequence_quality_scores_plot.svg", + "multiqc/coproid_multiqc_report_plots/svg/fastqc_sequence_counts_plot-cnt.svg", + "multiqc/coproid_multiqc_report_plots/svg/fastqc_sequence_counts_plot-pct.svg", + "multiqc/coproid_multiqc_report_plots/svg/fastqc_sequence_duplication_levels_plot.svg", + "multiqc/coproid_multiqc_report_plots/svg/fiveprime_misinc_plot.svg", + "multiqc/coproid_multiqc_report_plots/svg/kraken-top-duplication_plot.svg", + "multiqc/coproid_multiqc_report_plots/svg/kraken-top-n-plot_Class-cnt.svg", + "multiqc/coproid_multiqc_report_plots/svg/kraken-top-n-plot_Class-pct.svg", + "multiqc/coproid_multiqc_report_plots/svg/kraken-top-n-plot_Domain-cnt.svg", + "multiqc/coproid_multiqc_report_plots/svg/kraken-top-n-plot_Domain-pct.svg", + "multiqc/coproid_multiqc_report_plots/svg/kraken-top-n-plot_Family-cnt.svg", + "multiqc/coproid_multiqc_report_plots/svg/kraken-top-n-plot_Family-pct.svg", + "multiqc/coproid_multiqc_report_plots/svg/kraken-top-n-plot_Genus-cnt.svg", + "multiqc/coproid_multiqc_report_plots/svg/kraken-top-n-plot_Genus-pct.svg", + "multiqc/coproid_multiqc_report_plots/svg/kraken-top-n-plot_Order-cnt.svg", + "multiqc/coproid_multiqc_report_plots/svg/kraken-top-n-plot_Order-pct.svg", + "multiqc/coproid_multiqc_report_plots/svg/kraken-top-n-plot_Phylum-cnt.svg", + "multiqc/coproid_multiqc_report_plots/svg/kraken-top-n-plot_Phylum-pct.svg", + "multiqc/coproid_multiqc_report_plots/svg/kraken-top-n-plot_Species-cnt.svg", + "multiqc/coproid_multiqc_report_plots/svg/kraken-top-n-plot_Species-pct.svg", + "multiqc/coproid_multiqc_report_plots/svg/kraken-top-n-plot_Unclassified-cnt.svg", + "multiqc/coproid_multiqc_report_plots/svg/kraken-top-n-plot_Unclassified-pct.svg", + "multiqc/coproid_multiqc_report_plots/svg/length-distribution-Forward.svg", + "multiqc/coproid_multiqc_report_plots/svg/length-distribution-Reverse.svg", + "multiqc/coproid_multiqc_report_plots/svg/mapdamage-fiveprime_misinc_plot.svg", + "multiqc/coproid_multiqc_report_plots/svg/mapdamage-length-distribution-Forward.svg", + "multiqc/coproid_multiqc_report_plots/svg/mapdamage-length-distribution-Reverse.svg", + "multiqc/coproid_multiqc_report_plots/svg/mapdamage-threeprime_misinc_plot.svg", + "multiqc/coproid_multiqc_report_plots/svg/threeprime_misinc_plot.svg", + "pipeline_info", + "pipeline_info/nf_core_coproid_software_mqc_versions.yml", + "pydamage", + "pydamage/coproid.pydamage_merged_report.csv", + "pydamage/pydamage_results", + "pydamage/pydamage_results/SAMPLE_BSUB-Bacillus_subtilis_pydamage_results.csv", + "pydamage/pydamage_results/SAMPLE_BSUB-Escherichia_coli_pydamage_results.csv", + "pydamage/pydamage_results/SAMPLE_ECOLI-Bacillus_subtilis_pydamage_results.csv", + "pydamage/pydamage_results/SAMPLE_ECOLI-Escherichia_coli_pydamage_results.csv", + "sam2lca", + "sam2lca/SAMPLE_BSUB.sam2lca.csv", + "sam2lca/SAMPLE_BSUB.sam2lca.json", + "sam2lca/SAMPLE_ECOLI.sam2lca.csv", + "sam2lca/SAMPLE_ECOLI.sam2lca.json", + "sam2lca/coproid.sam2lca_merged_report.csv", + "sam2lca/sam2lca_db", + "sam2lca/sam2lca_db/adnamap.db", + "sam2lca/sam2lca_db/adnamap.db/000005.log", + "sam2lca/sam2lca_db/adnamap.db/CURRENT", + "sam2lca/sam2lca_db/adnamap.db/IDENTITY", + "sam2lca/sam2lca_db/adnamap.db/LOCK", + "sam2lca/sam2lca_db/adnamap.db/LOG", + "sam2lca/sam2lca_db/adnamap.db/MANIFEST-000004", + "sam2lca/sam2lca_db/adnamap.db/OPTIONS-000007", + "sam2lca/sam2lca_db/ncbi.pkl", + "sourcepredict", + "sourcepredict/coproid.embedding.sourcepredict.csv", + "sourcepredict/coproid.report.sourcepredict.csv" + ] + ], + "meta": { + "nf-test": "0.9.2", + "nextflow": "25.10.3" + }, + "timestamp": "2026-02-11T12:21:55.656304605" + } +} \ No newline at end of file diff --git a/tests/nextflow.config b/tests/nextflow.config new file mode 100644 index 0000000..504228e --- /dev/null +++ b/tests/nextflow.config @@ -0,0 +1,14 @@ +/* +======================================================================================== + Nextflow config file for running nf-test tests +======================================================================================== +*/ + +// TODO nf-core: Specify any additional parameters here +// Or any resources requirements +params { + modules_testdata_base_path = 'https://raw.githubusercontent.com/nf-core/test-datasets/modules/data/' + pipelines_testdata_base_path = 'https://raw.githubusercontent.com/nf-core/test-datasets/refs/heads/coproid' +} + +aws.client.anonymous = true // fixes S3 access issues on self-hosted runners diff --git a/workflows/coproid.nf b/workflows/coproid.nf index 3730864..d65e201 100644 --- a/workflows/coproid.nf +++ b/workflows/coproid.nf @@ -3,20 +3,19 @@ IMPORT MODULES / SUBWORKFLOWS / FUNCTIONS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ - -include { FASTQC } from '../modules/nf-core/fastqc/main' -include { MULTIQC } from '../modules/nf-core/multiqc/main' -include { FASTP } from '../modules/nf-core/fastp/main' -include { SAM2LCA_ANALYZE } from '../modules/nf-core/sam2lca/analyze/main' -include { DAMAGEPROFILER } from '../modules/nf-core/damageprofiler/main' -include { PYDAMAGE_ANALYZE } from '../modules/nf-core/pydamage/analyze/main' -include { SAM2LCA_MERGE } from '../modules/local/sam2lca/merge/main' -include { PYDAMAGE_MERGE } from '../modules/local/pydamage/merge/main' -include { DAMAGEPROFILER_MERGE } from '../modules/local/damageprofiler/merge/main' -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_coproid_pipeline' +include { FASTQC } from '../modules/nf-core/fastqc/main' +include { MULTIQC } from '../modules/nf-core/multiqc/main' +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_coproid_pipeline' +include { FASTP } from '../modules/nf-core/fastp/main' +include { SAM2LCA_ANALYZE } from '../modules/nf-core/sam2lca/analyze/main' +include { DAMAGEPROFILER } from '../modules/nf-core/damageprofiler/main' +include { PYDAMAGE_ANALYZE } from '../modules/nf-core/pydamage/analyze/main' +include { SAM2LCA_MERGE } from '../modules/local/sam2lca/merge/main' +include { PYDAMAGE_MERGE } from '../modules/local/pydamage/merge/main' +include { DAMAGEPROFILER_MERGE } from '../modules/local/damageprofiler/merge/main' // // SUBWORKFLOWS: Consisting of a mix of local and nf-core/modules @@ -28,14 +27,6 @@ include { MERGE_SORT_INDEX_SAMTOOLS } from '../subworkflows/local/merge_sort_ind include { KRAKEN2_CLASSIFICATION } from '../subworkflows/local/kraken2_classification/main' include { QUARTO_REPORTING } from '../subworkflows/local/quarto_reporting/main' -/* -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - CREATE CHANNELS -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -*/ - -ch_kraken2_db = file(params.kraken2_db) - /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ RUN MAIN WORKFLOW @@ -43,21 +34,30 @@ ch_kraken2_db = file(params.kraken2_db) */ workflow COPROID { - take: ch_samplesheet // channel: samplesheet FASTQ read in from --input ch_genomesheet // channel: genomesheet genomes from --genome_sheet main: - ch_versions = Channel.empty() - ch_multiqc_files = Channel.empty() + ch_versions = channel.empty() + ch_multiqc_files = channel.empty() + + + /* + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CREATE CHANNELS + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + ch_kraken2_db = file(params.kraken2_db) + // // SUBWORKFLOW: Prepare genomes from genome sheet // - PREPARE_GENOMES ( + PREPARE_GENOMES( ch_genomesheet ) ch_versions = ch_versions.mix(PREPARE_GENOMES.out.versions.first()) @@ -65,34 +65,32 @@ workflow COPROID { // // MODULE: Run FastQC // - FASTQC ( + FASTQC( ch_samplesheet ) - ch_multiqc_files = ch_multiqc_files.mix(FASTQC.out.zip.collect{it[1]}) - ch_versions = ch_versions.mix(FASTQC.out.versions.first()) + ch_multiqc_files = ch_multiqc_files.mix(FASTQC.out.zip.collect { it[1] }) // // MODULE: Preprocessing with fastp // - FASTP ( + FASTP( ch_samplesheet, [], false, false, - true + true, ) - ch_trimmed = FASTP.out.reads - ch_multiqc_files = ch_multiqc_files.mix(FASTP.out.json.collect{it[1]}) - ch_versions = ch_versions.mix(FASTP.out.versions.first()) + ch_trimmed = FASTP.out.reads + ch_multiqc_files = ch_multiqc_files.mix(FASTP.out.json.collect { it[1] }) + ch_versions = ch_versions.mix(FASTP.out.versions.first()) // // SUBWORKFLOW: Align reads to all genomes and index alignments // - FASTP.out.reads // [meta[ID, single_end], merged_reads] - .combine(PREPARE_GENOMES.out.genomes) //[meta[genome_name], fasta, index] - .map { - meta_reads, reads, meta_genome, genome_fasta, genome_index -> + FASTP.out.reads + .combine(PREPARE_GENOMES.out.genomes) + .map { meta_reads, reads, meta_genome, genome_fasta, genome_index -> [ [ 'id': meta_reads.id + '-' + meta_genome.genome_name, @@ -101,105 +99,114 @@ workflow COPROID { 'genome_size': meta_genome.genome_size, 'sample_name': meta_reads.id, 'single_end': meta_reads.single_end, - 'merge': meta_reads.merge + 'merge': meta_reads.merge, ], reads, genome_index, - genome_fasta + genome_fasta, ] } .set { ch_reads_genomes_index } - ALIGN_INDEX ( + ALIGN_INDEX( ch_reads_genomes_index ) - ch_versions = ch_versions.mix(ALIGN_INDEX.out.versions.first()) - ch_multiqc_files = ch_multiqc_files.mix(ALIGN_INDEX.out.multiqc_files.collect{it[1]}) + ch_versions = ch_versions.mix(ALIGN_INDEX.out.versions.first()) + ch_multiqc_files = ch_multiqc_files.mix(ALIGN_INDEX.out.multiqc_files.collect { it[1] }) DAMAGEPROFILER( ALIGN_INDEX.out.bam, [], [], - [] + [], ) - ch_versions = ch_versions.mix(DAMAGEPROFILER.out.versions.first()) - ch_multiqc_files = ch_multiqc_files.mix(DAMAGEPROFILER.out.results.collect{it[1]}) + ch_versions = ch_versions.mix(DAMAGEPROFILER.out.versions.first()) + ch_multiqc_files = ch_multiqc_files.mix(DAMAGEPROFILER.out.results.collect { it[1] }) - DAMAGEPROFILER.out.results.collect({it[1]}) - .set { damageprofiler_reports } + DAMAGEPROFILER.out.results + .collect { it[1] } + .set { damageprofiler_reports } DAMAGEPROFILER_MERGE( damageprofiler_reports ) ch_versions = ch_versions.mix(DAMAGEPROFILER_MERGE.out.versions.first()) - ALIGN_INDEX.out.bam.join( - ALIGN_INDEX.out.bai - ).map { - meta, bam, bai -> [['id':meta.sample_name, 'genome_name':meta.genome_name], bam, bai] // meta.id, bam - } - .set { aligned_index } + ALIGN_INDEX.out.bam + .join( + ALIGN_INDEX.out.bai + ) + .map { meta, bam, bai -> + [['id': meta.sample_name, 'genome_name': meta.genome_name], bam, bai] + } + .set { aligned_index } - PYDAMAGE_ANALYZE ( + PYDAMAGE_ANALYZE( aligned_index ) ch_versions = ch_versions.mix(PYDAMAGE_ANALYZE.out.versions.first()) - PYDAMAGE_ANALYZE.out.csv.collect({it[1]}) - .set { pydamage_reports } + PYDAMAGE_ANALYZE.out.csv + .collect { it[1] } + .set { pydamage_reports } - PYDAMAGE_MERGE ( + PYDAMAGE_MERGE( pydamage_reports ) ch_versions = ch_versions.mix(PYDAMAGE_MERGE.out.versions.first()) // join bam with indices - ALIGN_INDEX.out.bam.join( - ALIGN_INDEX.out.bai - ).map { - meta, bam, bai -> [['id':meta.sample_name], bam] // meta.id, bam - }.groupTuple(size: 2) - .set { bams_synced } + ALIGN_INDEX.out.bam + .join( + ALIGN_INDEX.out.bai + ) + .map { meta, bam, bai -> + [['id': meta.sample_name], bam] + } + .groupTuple(size: 2) + .set { bams_synced } // SUBWORKFLOW: sort indices - MERGE_SORT_INDEX_SAMTOOLS ( + MERGE_SORT_INDEX_SAMTOOLS( bams_synced ) ch_versions = ch_versions.mix(MERGE_SORT_INDEX_SAMTOOLS.out.versions.first()) // Prepare SAM2LCA database channel - if (!params.sam2lca_db ) { + if (!params.sam2lca_db) { SAM2LCA_DB( - PREPARE_GENOMES.out.genomes.map { - meta, fasta, index -> [meta, fasta] - }, - "ncbi", - [], - [], - [] - ) + PREPARE_GENOMES.out.genomes.map { meta, fasta, index -> + [meta, fasta] + }, + "ncbi", + [], + [], + [], + ) ch_sam2lca_db = SAM2LCA_DB.out.sam2lca_db.first() ch_versions = ch_versions.mix(SAM2LCA_DB.out.versions.first()) - } else { + } + else { ch_sam2lca_db = Channel.fromPath(params.sam2lca_db).first() } // // MODULE: Run sam2lca // - SAM2LCA_ANALYZE ( + SAM2LCA_ANALYZE( MERGE_SORT_INDEX_SAMTOOLS.out.bam.join( MERGE_SORT_INDEX_SAMTOOLS.out.bai ), - ch_sam2lca_db + ch_sam2lca_db, ) - ch_sam2lca = SAM2LCA_ANALYZE.out.csv + ch_sam2lca = SAM2LCA_ANALYZE.out.csv ch_versions = ch_versions.mix(SAM2LCA_ANALYZE.out.versions.first()) - SAM2LCA_ANALYZE.out.csv.collect({it[1]}) - .set { sam2lca_reports } + SAM2LCA_ANALYZE.out.csv + .collect { it[1] } + .set { sam2lca_reports } - SAM2LCA_MERGE ( + SAM2LCA_MERGE( sam2lca_reports ) ch_versions = ch_versions.mix(SAM2LCA_MERGE.out.versions.first()) @@ -207,38 +214,60 @@ workflow COPROID { // // SUBWORKFLOW: kraken classification and parse reports // - KRAKEN2_CLASSIFICATION ( + KRAKEN2_CLASSIFICATION( FASTP.out.reads, - ch_kraken2_db + ch_kraken2_db, ) - ch_multiqc_files = ch_multiqc_files.mix(KRAKEN2_CLASSIFICATION.out.kraken_report.collect{it[1]}) - ch_versions = ch_versions.mix(KRAKEN2_CLASSIFICATION.out.versions.first()) + ch_multiqc_files = ch_multiqc_files.mix(KRAKEN2_CLASSIFICATION.out.kraken_report.collect { it[1] }) + ch_versions = ch_versions.mix(KRAKEN2_CLASSIFICATION.out.versions.first()) // // Collate and save software versions // - softwareVersionsToYAML(ch_versions) + def topic_versions = Channel + .topic("versions") + .distinct() + .branch { entry -> + versions_file: entry instanceof Path + versions_tuple: true + } + + def topic_versions_string = topic_versions.versions_tuple + .map { process, tool, version -> + [process[process.lastIndexOf(':') + 1..-1], " ${tool}: ${version}"] + } + .groupTuple(by: 0) + .map { process, tool_versions -> + tool_versions.unique().sort() + "${process}:\n${tool_versions.join('\n')}" + } + + softwareVersionsToYAML(ch_versions.mix(topic_versions.versions_file)) + .mix(topic_versions_string) .collectFile( storeDir: "${params.outdir}/pipeline_info", - name: 'nf_core_' + 'coproid_software_' + 'mqc_' + 'versions.yml', + name: 'nf_core_' + 'coproid_software_' + 'mqc_' + 'versions.yml', sort: true, - newLine: true - ).set { ch_collated_versions } + newLine: true, + ) + .set { ch_collated_versions } // Collect all files for quarto - ch_quarto = SAM2LCA_MERGE.out.sam2lca_merged_report.mix( - KRAKEN2_CLASSIFICATION.out.sp_report.collectFile{it[1]}, - KRAKEN2_CLASSIFICATION.out.sp_embedding.collectFile{it[1]}, + ch_quarto = SAM2LCA_MERGE.out.sam2lca_merged_report + .mix( + KRAKEN2_CLASSIFICATION.out.sp_report.collectFile { it[1] }, + KRAKEN2_CLASSIFICATION.out.sp_embedding.collectFile { it[1] }, PYDAMAGE_MERGE.out.pydamage_merged_report, DAMAGEPROFILER_MERGE.out.damageprofiler_merged_report, Channel.fromPath(params.genome_sheet), - ch_collated_versions - ).toList() + ch_collated_versions, + ) + .toList() // // SUBWORKFLOW: quarto reporting // - QUARTO_REPORTING ( + QUARTO_REPORTING( ch_quarto ) ch_versions = ch_versions.mix(QUARTO_REPORTING.out.versions.first()) @@ -246,53 +275,50 @@ workflow COPROID { // // MODULE: MultiQC // - ch_multiqc_config = Channel.fromPath( - "$projectDir/assets/multiqc_config.yml", checkIfExists: true) - ch_multiqc_custom_config = params.multiqc_config ? - Channel.fromPath(params.multiqc_config, checkIfExists: true) : - Channel.empty() - ch_multiqc_logo = params.multiqc_logo ? - Channel.fromPath(params.multiqc_logo, checkIfExists: true) : - Channel.empty() + ch_multiqc_config = channel.fromPath( + "${projectDir}/assets/multiqc_config.yml", + checkIfExists: true + ) + ch_multiqc_custom_config = params.multiqc_config + ? channel.fromPath(params.multiqc_config, checkIfExists: true) + : channel.empty() + ch_multiqc_logo = params.multiqc_logo + ? 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)) + 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_collated_versions) ch_multiqc_files = ch_multiqc_files.mix( ch_methods_description.collectFile( name: 'methods_description_mqc.yaml', - sort: true + sort: true, ) ) - MULTIQC ( + MULTIQC( ch_multiqc_files.collect(), ch_multiqc_config.toList(), ch_multiqc_custom_config.toList(), ch_multiqc_logo.toList(), [], - [] + [], ) emit: multiqc_report = MULTIQC.out.report.toList() // channel: /path/to/multiqc_report.html - versions = ch_versions // channel: [ path(versions.yml) ] - + versions = ch_versions // channel: [ path(versions.yml) ] } - -/* -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - THE END -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -*/