diff --git a/.dependabot/config.yml b/.dependabot/config.yml deleted file mode 100644 index a0a1799d10..0000000000 --- a/.dependabot/config.yml +++ /dev/null @@ -1,10 +0,0 @@ -version: 2 -updates_configs: - - package_manager: "java:maven" - directory: "/" - update_schedule: "daily" - target-branch: development - default_labels: "dependabot" - commit-message: - prefix: "[DEPENDABOT]" - diff --git a/.generate-reports.py b/.generate-reports.py deleted file mode 100755 index ead7b28fbc..0000000000 --- a/.generate-reports.py +++ /dev/null @@ -1,254 +0,0 @@ -#!/usr/bin/env python -# -*- coding: UTF-8 -*- -# fancy comments come at a cost! - -# Script to generate a Maven site and push reports to a branch (gh-pages by default). -# This script assumes that both git and Maven have been installed and that the following environment variables -# are defined: -# - REPORTS_GITHUB_ACCESS_TOKEN: GitHub personal access token used to push generated reports -# - REPORTS_GITHUB_USERNAME: username used to push generated reports -# -# Yes, these could be passed as arguments, but Travis log would print them out. - -# Output of this script is to populate the gh-pages branch with the reports generated by running "mvn site". -# The structure of the generated reports is similar to: -# -# (branch gh-pages) # pages_branch option -# reports # base_output_dir option -# ├── development # output_dir positional argument -# │  ├── index.html -# │  ├── pmd.html -# │ ├── jacoco.html -# │ └── ... -# │   -# ├── 1.0.0 # output_dir positional argument -# │  ├── index.html -# │  ├── pmd.html -# │ ├── jacoco.html -# │ └── ... -# │   -# ├── 1.0.1 # output_dir positional argument -# │  ├── index.html -# │  ├── pmd.html -# │ ├── jacoco.html -# │ └── ... -# │   -# └── 2.0.0 # output_dir positional argument -#   ├── index.html -#   ├── pmd.html -# ├── jacoco.html -# └── ... -# -# So only one "development" version of the reports is maintained, while reports for all -# tagged commits--assumed to be releases--are maintained on the gh-pages branch. -# -# The content of each of the folders is whatever Maven generates on the target/site folder. - - -import argparse, os, shutil, subprocess, tempfile, sys, re - -# folder where maven outputs reports generated by running "mvn site" -MAVEN_SITE_DIR = os.path.join('target', 'site') -# base directory where reports will be copied to -BASE_REPORT_DIR = 'reports' -# credentials are given via environment variables -TOKEN_ENV_VARIABLE_NAME = 'REPORTS_GITHUB_ACCESS_TOKEN' -# compiled regex to match files that should not be deleted when cleaning the working folder (in gh-pages) -UNTOUCHABLE_FILES_MATCHER = re.compile('^\.git.*') -# regex to validate output folder -REPORTS_VERSION_REGEX = '^(development|[vV]?\d+\.\d+\.\d+)$' - - -# parses arguments and does the thing -def main(): - parser = argparse.ArgumentParser(description='QBiC Javadoc Generator.', prog='generate-javadocs.py', formatter_class=argparse.ArgumentDefaultsHelpFormatter) - parser.add_argument('-s', '--site-dir', default=MAVEN_SITE_DIR, - help='Directory where Maven reports are found (output of running \'mvn site\').') - parser.add_argument('-b', '--base-output-dir', default=BASE_REPORT_DIR, - help='Base directory where the reports will be copied.') - parser.add_argument('-p', '--pages-branch', default="gh-pages", - help='Name of the git branch on which the reports will be pushed.') - parser.add_argument('-a', '--access-token-var-name', default=TOKEN_ENV_VARIABLE_NAME, - help='Name of the environment variable holding the GitHub personal access token used to push changes in reports.') - parser.add_argument('-r', '--validation-regex', default=REPORTS_VERSION_REGEX, - help='Regular expression to validate output_dir; it is assumed that report folders are named after a version.') - parser.add_argument('--dry-run', action='store_true', - help='If present, no changes to the remote repository (git commit/push) will be executed.') - parser.add_argument('--skip-cleanup', action='store_true', - help='Whether cleanup tasks (removing cloned repos) should be skipped.') - parser.add_argument('output_dir', - help='Name of the folder, relative to the base output directory, where reports will be copied to. \ - This folder will be first cleared of its contents before the generated reports are copied. \ - Recommended values are: "development" or a valid release version string (e.g., 1.0.1)') - parser.add_argument('repo_slug', help='Slug of the repository for which reports are being built.') - parser.add_argument('commit_message', nargs='+', help='Message(s) to use when committing changes.') - args = parser.parse_args() - - # check that the required environment variables have been defined - try: - validateArguments(args) - except Exception as e: - print('Error: {}'.format(str(e)), file=sys.stderr) - exit(1) - - # since this will run on Travis, we cannot assume that we can change the current local repo without breaking anything - # the safest way would be to clone this same repository on a temporary folder and leave the current local repo alone - working_dir = tempfile.mkdtemp() - clone_self(working_dir, args) - - # reports are available only in a specific branch - force_checkout_pages_branch(working_dir, args) - - # since new branches have a parent commit, we have to remove everything but: - # * important files (e.g., .git) - # * the base output directory (args.base_output_dir) - # otherwise, the newly created gh-pages branch will contain other non-report files! - # also, it is a good idea to remove everything, since we don't want lingering unused report files - remove_unneeded_files(working_dir, args) - - # move rports to their place - prepare_report_dir(working_dir, args) - - # add, commit, push - push_to_pages_branch(working_dir, args) - - # clean up - if args.skip_cleanup: - print('Skipping cleanup of working folder {}'.format(working_dir)) - else: - print('Removing working folder {}'.format(working_dir)) - shutil.rmtree(working_dir) - - -# Sanity check -def validateArguments(args): - # check that the required environment variables are present - if not args.access_token_var_name in os.environ: - raise Exception('At least one of the required environment variables is missing. See comments on .generate-reports.py for further information.') - - # check if the name of the output_dir matches the regex - regex = re.compile(args.validation_regex) - if not regex.match(args.output_dir): - raise Exception('The provided output directory for the reports, {}, is not valid. It must match the regex {}'.format(args.output_dir, args.validation_regex)) - - # check that the reports are where they should be (you never know!) - if not os.path.exists(args.site_dir) or not os.path.isdir(args.site_dir): - raise Exception('Maven site folder {} does not exist or is not a directory.'.format(args.site_dir)) - - -# Clones this repo into the passed working directory, credentials are used because OAuth has a bigger quota -# plus, we will be pushing changes to gh-pages branch -def clone_self(working_dir, args, exit_if_fail=True): - execute(['git', 'clone', 'https://{}:x-oauth-basic@github.com/{}'.format(os.environ[args.access_token_var_name], args.repo_slug), working_dir], - 'Could not clone {} in directory {}'.format(args.repo_slug, working_dir), exit_if_fail) - - -# Checks out the branch where reports reside (gh-pages) -def force_checkout_pages_branch(working_dir, args): - # we need to add the gh-pages branch if it doesn't exist (git checkout -b gh-pages), - # but if gh-pages already exists, we need to checkout (git checkout gh-pages), luckily, - # "git checkout branch" fails if branch doesn't exist - print('Changing to branch {}'.format(args.pages_branch)) - try: - execute(['git', '-C', working_dir, 'checkout', args.pages_branch], exit_if_fail=False) - except: - execute(['git', '-C', working_dir, 'checkout', '-b', args.pages_branch], 'Could not create branch {}'.format(args.pages_branch)) - - -# Goes through the all files/folders (non-recursively) and deletes them using 'git rm'. -# Files that should not be deleted are ignored -def remove_unneeded_files(working_dir, args): - print('Cleaning local repository ({}) of non-reports files'.format(working_dir)) - for f in os.listdir(working_dir): - if should_delete(f, args): - # instead of using OS calls to delete files/folders, use git rm to stage deletions - print(' Deleting {} from {} branch'.format(f, args.pages_branch)) - execute(['git', '-C', working_dir, 'rm', '-r', '--ignore-unmatch', f], 'Could not remove {}.'.format(f)) - # files that are not part of the repository aren't removed by git and the --ignore-unmatch flag makes - # git be nice so it doesn't exit with errors, so we need to force-remove them - force_delete(os.path.join(working_dir, f)) - else: - print(' Ignoring file/folder {}'.format(f)) - - -# Prepares the report output directory, first by clearing it and then by moving the contents of target/site into it -def prepare_report_dir(working_dir, args): - report_output_dir = os.path.join(working_dir, args.base_output_dir, args.output_dir) - if os.path.exists(report_output_dir): - if not os.path.isdir(report_output_dir): - print('WARNING: Output destination {} exists and is not a directory.'.format(report_output_dir), file=sys.stderr) - # remove the object from git - print('Removing {}'.format(report_output_dir)) - execute(['git', '-C', working_dir, 'rm', '-r', '--ignore-unmatch', os.path.join(args.base_output_dir, args.output_dir)], - 'Could not remove {}.'.format(report_output_dir)) - # just in case git doesn't remove the file (if it wasn't tracked, for instance), force deletion using OS calls - force_delete(report_output_dir) - # we know the output folder doesn't exist, so we can recreate it - print('Creating {}'.format(report_output_dir)) - os.makedirs(report_output_dir) - - # accidentally the whole target/site folder (well, yes, but actually, no, because we need only its contents) - print('Moving contents of {} to {}'.format(args.site_dir, report_output_dir)) - for f in os.listdir(args.site_dir): - print(' Moving {}'.format(f)) - shutil.move(os.path.join(args.site_dir, f), report_output_dir) - - -# Adds, commits and pushes changes -def push_to_pages_branch(working_dir, args): - if args.dry_run: - print('(running in dry run mode) Local/remote repository will not be modified') - else: - # add changes to the index - print('Staging changes for commit') - execute(['git', '-C', working_dir, 'add', '.'], 'Could not stage reports for commit.') - - # build the git-commit command and commit changes - print('Pushing changes upstream') - git_commit_command = ['git', '-C', working_dir, 'commit'] - for commit_message in args.commit_message: - git_commit_command.extend(['-m', commit_message]) - execute(git_commit_command, 'Could not commit changes') - - # https://www.youtube.com/watch?v=vCadcBR95oU - execute(['git', '-C', working_dir, 'push', '-u', 'origin', args.pages_branch], 'Could not push changes using provided credentials.') - - -# Whether it is safe to delete the given path, we won't delete important files/folders (such as .git) -# or the base output directory -def should_delete(path, args): - return not UNTOUCHABLE_FILES_MATCHER.match(path) and path != args.base_output_dir - - -# Forcefully deletes recursively the passed file/folder using OS calls -def force_delete(file): - if os.path.exists(file): - if os.path.isdir(file): - shutil.rmtree(file) - else: - os.remove(file) - - -# Executes an external command -# stderr/stdout are hidden to avoid leaking credentials into log files in Travis, so it might be a pain in the butt to debug, sorry, but safety first! -# if exit_if_fail is set to True, this method will print minimal stacktrace information and exit if a failure is encountered, otherwise, an exception -# will be thrown (this is useful if an error will be handled by the invoking method) -def execute(command, error_message='Error encountered while executing command', exit_if_fail=True): - # do not print the command, stderr or stdout! this might expose usernames/passwords/tokens! - try: - subprocess.run(command, check=True, stderr=subprocess.DEVNULL, stdout=subprocess.DEVNULL) - except: - if exit_if_fail: - stack = traceback.extract_stack() - try: - print('{}\n Error originated at file {}, line {}'.format(error_message, stack[-2].filename, stack[-2].lineno), file=sys.stderr) - except: - print('{}\n No information about the originating call is available.'.format(error_message), file=sys.stderr) - exit(1) - else: - raise Exception() - - - -if __name__ == "__main__": - main() diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index d162979ed6..0000000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -name: Bug report -about: Create a bug report to help us improve -title: 'Bug Summary' -labels: 'bug' -assignees: '' - ---- - -**Describe the bug** -A clear and concise description of what the bug is. - -**To Reproduce** -Steps to reproduce the behavior: -1. ... -2. ... -3. ... - -**Expected behavior** -A clear and concise description of what you expected to happen. - -**System [please complete the following information]:** - - OS: e.g. [Ubuntu 18.04] - - Language Version: [e.g. Python 3.8] - - Virtual environment: [e.g. Conda] - -**Additional context** -Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index 3bed8fc2f7..0000000000 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -name: Feature request -about: Suggest a new feature -title: 'Feature Request Summary' -labels: 'enhancement' -assignees: '' - ---- - -**Is your feature request related to a problem? Please describe.** -A clear and concise description of what the problem is. Ex. I'm always frustrated when ... - -**Describe the solution you'd like** -A clear and concise description of what you want to happen. - -**Describe alternatives you've considered** -A clear and concise description of any alternative solutions or features you've considered. - -**Additional context** -Add any other context or screenshots about the feature request here. diff --git a/.github/ISSUE_TEMPLATE/general_question.md b/.github/ISSUE_TEMPLATE/general_question.md deleted file mode 100644 index 37c39b39b9..0000000000 --- a/.github/ISSUE_TEMPLATE/general_question.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -name: General question -about: Ask a question about anything related to this project -title: 'Question' -labels: 'question' -assignees: '' - ---- - -**Question** - -Please ask your question here. It can be about the usage of this project, the internals, the implementation or whatever interests you. -Please use the BUG template for bugs and the FEATURE REQUEST template for feature requests. diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md deleted file mode 100644 index f6fd3ac9a6..0000000000 --- a/.github/pull_request_template.md +++ /dev/null @@ -1,19 +0,0 @@ -Many thanks for contributing to this project! - -**PR Checklist** -Please fill in the appropriate checklist below (delete whatever is not relevant). These are the most common things requested on pull requests (PRs). - -- [ ] This comment contains a description of changes (with reason) -- [ ] Referenced issue is linked -- [ ] If you've fixed a bug or added code that should be tested, add tests! -- [ ] Documentation in `docs` is updated -- [ ] `CHANGELOG.rst` is updated - -**Description of changes** -Please state what you've changed and how it might affect the user. - -**Technical details** -Please state any technical details such as limitations, reasons for additional dependencies, benchmarks etc. here. - -**Additional context** -Add any other context or screenshots here. diff --git a/.github/workflows/build_package.yml b/.github/workflows/build_package.yml index 9d334b8fa9..d616186cc2 100644 --- a/.github/workflows/build_package.yml +++ b/.github/workflows/build_package.yml @@ -1,6 +1,12 @@ name: Build Maven Package -on: [push] +on: + push: + branches: + - '**' + pull_request: + # The branches below must be a subset of the branches above + branches: [ main, master ] jobs: package: @@ -19,6 +25,5 @@ jobs: key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} restore-keys: | ${{ runner.os }}-maven- - - name: Run mvn package run: mvn -B package --file pom.xml diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index ef963f52a2..d708ad0ff5 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -13,10 +13,10 @@ name: "CodeQL" on: push: - branches: [ master, development, patch/*, release/*, hotfix/* ] + branches: [ main, master, development, release/*, hotfix/* ] pull_request: # The branches below must be a subset of the branches above - branches: [ master ] + branches: [ main, master ] schedule: - cron: '21 1 * * 4' diff --git a/.github/workflows/create-release.yml b/.github/workflows/create-release.yml index 57d5afed65..ebae605662 100644 --- a/.github/workflows/create-release.yml +++ b/.github/workflows/create-release.yml @@ -42,7 +42,7 @@ jobs: || contains(github.event.inputs.versionTag, 'rc')) }} uses: actions/github-script@v4.0.2 with: - github-token: ${{secrets.GITHUB_TOKEN}} + github-token: ${{secrets.JOHNNY_Q5_REPORTS_TOKEN}} script: | await github.request(`POST /repos/${{ github.repository }}/releases`, { tag_name: "${{ github.event.inputs.versionTag }}", @@ -55,7 +55,7 @@ jobs: || contains(github.event.inputs.versionTag, 'rc')) }} uses: actions/github-script@v4.0.2 with: - github-token: ${{secrets.GITHUB_TOKEN}} + github-token: ${{secrets.JOHNNY_Q5_REPORTS_TOKEN}} script: | await github.request(`POST /repos/${{ github.repository }}/releases`, { tag_name: "${{ github.event.inputs.versionTag }}", @@ -71,19 +71,33 @@ jobs: - name: Switch to new branch run: git checkout -b release/set-version-to-${{ github.event.inputs.versionTag }} + - name: Set remote branch + run: git push --set-upstream origin release/set-version-to-${{ github.event.inputs.versionTag }} + - name: Checkin commit run: git commit . -m 'Set version to ${{ github.event.inputs.versionTag }}' - - name: Set remote branch - run: git push --set-upstream origin release/set-version-to-${{ github.event.inputs.versionTag }} + - name: Push to Github + run: git push - name: Open PR with version bump uses: actions/github-script@v4.0.2 with: - github-token: ${{secrets.GITHUB_TOKEN}} + github-token: ${{secrets.JOHNNY_Q5_REPORTS_TOKEN}} script: | await github.request(`POST /repos/${{ github.repository }}/pulls`, { title: 'Update version to ${{ github.event.inputs.versionTag }}', head: 'release/set-version-to-${{ github.event.inputs.versionTag }}', - base: 'main' + base: 'master' + }); + + - name: Open PR to development + uses: actions/github-script@v4.0.2 + with: + github-token: ${{secrets.JOHNNY_Q5_REPORTS_TOKEN}} + script: | + await github.request(`POST /repos/${{ github.repository }}/pulls`, { + title: 'Merge release ${{ github.event.inputs.versionTag }} into development', + head: 'master', + base: 'development' }); diff --git a/.github/workflows/java_checkstyle.yml b/.github/workflows/java_checkstyle.yml deleted file mode 100644 index a41426e130..0000000000 --- a/.github/workflows/java_checkstyle.yml +++ /dev/null @@ -1,23 +0,0 @@ -name: Java Checkstyle - -on: [push] - -jobs: - checkstyle: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - name: Set up JDK 1.11 - uses: actions/setup-java@v1 - with: - java-version: 1.11 - - - name: Download Checkstyle - run: wget https://github.com/checkstyle/checkstyle/releases/download/checkstyle-8.31/checkstyle-8.31-all.jar - - - name: Download Google style xml - run: wget https://raw.githubusercontent.com/checkstyle/checkstyle/checkstyle-8.28/src/main/resources/google_checks.xml - - - name: Run Checkstyle - run: java -jar checkstyle-8.31-all.jar -c google_checks.xml . diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index 452314f2b0..5708a25850 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -1,6 +1,12 @@ name: Run Maven Tests -on: [push] +on: + push: + branches: + - '**' + pull_request: + # The branches below must be a subset of the branches above + branches: [ main, master ] jobs: test: @@ -8,10 +14,10 @@ jobs: steps: - uses: actions/checkout@v2 - - name: Set up JDK 1.11 + - name: Set up JDK 1.8 uses: actions/setup-java@v1 with: - java-version: 1.11 + java-version: 1.8 - name: Load local Maven repository cache uses: actions/cache@v2 diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md deleted file mode 100644 index 8993572ecd..0000000000 --- a/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,46 +0,0 @@ -# Contributor Covenant Code of Conduct - -## Our Pledge - -In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. - -## Our Standards - -Examples of behavior that contributes to creating a positive environment include: - -* Using welcoming and inclusive language -* Being respectful of differing viewpoints and experiences -* Gracefully accepting constructive criticism -* Focusing on what is best for the community -* Showing empathy towards other community members - -Examples of unacceptable behavior by participants include: - -* The use of sexualized language or imagery and unwelcome sexual attention or advances -* Trolling, insulting/derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or electronic address, without explicit permission -* Other conduct which could reasonably be considered inappropriate in a professional setting - -## Our Responsibilities - -Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. - -Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. - -## Scope - -This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. - -## Enforcement - -Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at info@qbic.uni-tuebingen.de. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. - -Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. - -## Attribution - -This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] - -[homepage]: http://contributor-covenant.org -[version]: http://contributor-covenant.org/version/1/4/ diff --git a/README.md b/README.md index d97d27b359..db8b29084a 100644 --- a/README.md +++ b/README.md @@ -101,11 +101,8 @@ Make sure, that you have defined the Github package Maven repository, in order f A Nanopore NGS measurement output is delivered to us as a nested folder structure, following this model: -![Nanopore Data Structure Model](./doc/figures/Nanopore_Data_Structure_Model.svg) +![Nanopore Data Structure Model](./doc/figures/Nanopore_Data_Structure_Model.png) -A more recent model, which places two of the configuration files into a subfolder and adds the barcode alignment file, is also supported: - -![Nanopore Data Structure Model v2](./doc/figures/Nanopore_Data_Structure_Model_v2.svg) #### Nanopore usage example diff --git a/doc/figures/ER_diagram_pipeline_results.png b/doc/figures/ER_diagram_pipeline_results.png index 4ff4b681ff..83f2bfe21c 100644 Binary files a/doc/figures/ER_diagram_pipeline_results.png and b/doc/figures/ER_diagram_pipeline_results.png differ diff --git a/doc/figures/MaxQuant_Data_Structure.png b/doc/figures/MaxQuant_Data_Structure.png index 61b1a33813..bf793296ac 100644 Binary files a/doc/figures/MaxQuant_Data_Structure.png and b/doc/figures/MaxQuant_Data_Structure.png differ diff --git a/doc/figures/Nanopore_Data_Structure_Model.png b/doc/figures/Nanopore_Data_Structure_Model.png new file mode 100644 index 0000000000..d13f4297dc Binary files /dev/null and b/doc/figures/Nanopore_Data_Structure_Model.png differ diff --git a/doc/figures/Nanopore_Data_Structure_Model.svg b/doc/figures/Nanopore_Data_Structure_Model.svg deleted file mode 100644 index d3ffbbd6fd..0000000000 --- a/doc/figures/Nanopore_Data_Structure_Model.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - -
Root Folder
(OxfordNanoporeExperiment)
Root Folder...
Measurement Folder
(OxfordNanoporeMeasurement)
Measurement Folder...
1
1
1..n
1..n
FastQ Fail Folder
FastQ Fail Folder
FastQ Pass Folder
FastQ Pass Folder
Fast5 Pass Folder
Fast5 Pass Folder
Fast5 Fail Folder
Fast5 Fail Folder
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
Sequencing Summary Log
Sequencing Summary Log
Duty Time Log
Duty Time Log
Final Summary Log
Final Summary Log
Throughput Log
Throughput Log
Report MD Log
Report MD Log
Report PDF Log
Report PDF Log
Drift Correction Log
Drift Correction Log
Mux Scan Data Log
Mux Scan Data Log
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
FastQ Folder
FastQ Folder
FastQ File
FastQ File
1
1
0..n
0..n
DataFile
DataFile
BarcodedFolder
BarcodedFolder
Extends
Extends
Extends
Extends
Data File
Data File
Extends
Extends
DataFolder
DataFolder
Extends
Extends
DataFolder
DataFolder
Extends
Extends
Unclassified Folder
Unclassified Folder
1
1
0..n
0..n
1
1
0..n
0..n
FastQ File
FastQ File
1
1
0..n
0..n
1
1
0..n
0..n
FastQ Folder
FastQ Folder
FastQ File
FastQ File
Unclassified Folder
Unclassified Folder
1
1
0..n
0..n
FastQ File
FastQ File
1
1
0..n
0..n
Fast5 Folder
Fast5 Folder
Fast5 File
Fast5 File
Unclassified Folder
Unclassified Folder
1
1
0..n
0..n
Fast5 File
Fast5 File
1
1
0..n
0..n
Fast5 Folder
Fast5 Folder
Fast5 File
Fast5 File
Unclassified Folder
Unclassified Folder
1
1
0..n
0..n
Fast5 File
Fast5 File
1
1
0..n
0..n
1
1
0..n
0..n
1
1
0..n
0..n
1
1
0..n
0..n
1
1
0..n
0..n
1
1
0..n
0..n
1
1
0..n
0..n
1
1
0..n
0..n
1
1
0..n
0..n
1
1
0..n
0..n
FastQ Folder
FastQ Folder
Fast5 Folder
Fast5 Folder
DataFolder
DataFolder
Text is not SVG - cannot display
\ No newline at end of file diff --git a/doc/figures/Nanopore_Data_Structure_Model_v2.svg b/doc/figures/Nanopore_Data_Structure_Model_v2.svg deleted file mode 100644 index 330e266c8b..0000000000 --- a/doc/figures/Nanopore_Data_Structure_Model_v2.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - -
Root Folder
(OxfordNanoporeExperiment)
Root Folder...
Measurement Folder
(OxfordNanoporeMeasurement)
Measurement Folder...
1
1
1..n
1..n
FastQ Fail Folder
FastQ Fail Folder
FastQ Pass Folder
FastQ Pass Folder
Fast5 Pass Folder
Fast5 Pass Folder
Fast5 Fail Folder
Fast5 Fail Folder
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
Barcode Alignment Log
Barcode Alignment Log
Duty Time Log
Duty Time Log
Final Summary Log
Final Summary Log
Throughput Log
Throughput Log
Report MD Log
Report MD Log
Report PDF Log
Report PDF Log
Sequencing Summary Log
Sequencing Summary Log
Mux Scan Data Log
Mux Scan Data Log
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
FastQ Folder
FastQ Folder
FastQ File
FastQ File
1
1
0..n
0..n
DataFile
DataFile
BarcodedFolder
BarcodedFolder
Extends
Extends
Extends
Extends
Data File
Data File
Extends
Extends
DataFolder
DataFolder
Extends
Extends
DataFolder
DataFolder
Extends
Extends
Unclassified Folder
Unclassified Folder
1
1
0..n
0..n
1
1
0..n
0..n
FastQ File
FastQ File
1
1
0..n
0..n
1
1
0..n
0..n
FastQ Folder
FastQ Folder
FastQ File
FastQ File
Unclassified Folder
Unclassified Folder
1
1
0..n
0..n
FastQ File
FastQ File
1
1
0..n
0..n
Fast5 Folder
Fast5 Folder
Fast5 File
Fast5 File
Unclassified Folder
Unclassified Folder
1
1
0..n
0..n
Fast5 File
Fast5 File
1
1
0..n
0..n
Fast5 Folder
Fast5 Folder
Fast5 File
Fast5 File
Unclassified Folder
Unclassified Folder
1
1
0..n
0..n
Fast5 File
Fast5 File
1
1
0..n
0..n
1
1
0..n
0..n
1
1
0..n
0..n
1
1
0..n
0..n
1
1
0..n
0..n
1
1
0..n
0..n
1
1
0..n
0..n
1
1
0..n
0..n
1
1
0..n
0..n
1
1
0..n
0..n
FastQ Folder
FastQ Folder
Fast5 Folder
Fast5 Folder
DataFolder
DataFolder
Drift Correction Log
Drift Correction Log
Extends
Extends
1:1
1:1
1:1
1:1
Other Reports Subfolder
Other Reports Subfolder
Text is not SVG - cannot display
\ No newline at end of file diff --git a/pom.xml b/pom.xml index d6f64bf56f..7476796209 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ life.qbic data-model-lib - 2.15.0 + 2.25.1 data-model-lib http://github.com/qbicsoftware/data-model-lib Data models. A collection of QBiC's central data models and DTOs. @@ -81,20 +81,20 @@ org.codehaus.groovy groovy-bom - 2.5.14 + 3.0.9 pom import - - org.codehaus.groovy - groovy-all - 2.5.14 - pom - ${osgi.scope} - + + org.codehaus.groovy + groovy-all + 3.0.10 + pom + ${osgi.scope} + org.osgi osgi.core @@ -111,7 +111,7 @@ life.qbic xml-manager-lib - 1.6.0 + 1.7.0 ${osgi.scope} @@ -144,7 +144,7 @@ org.spockframework spock-core - 2.0-groovy-2.5 + 2.1-groovy-3.0 test @@ -171,7 +171,7 @@ maven-surefire-plugin - 2.22.2 + 3.0.0-M5 **/*Spec @@ -181,7 +181,7 @@ org.codehaus.gmavenplus gmavenplus-plugin - 1.12.1 + 1.13.1 @@ -217,12 +217,12 @@ org.apache.maven.plugins maven-site-plugin - 3.9.1 + 3.11.0 org.apache.maven.plugins maven-project-info-reports-plugin - 3.1.1 + 3.2.1 life.qbic @@ -269,7 +269,7 @@ biz.aQute.bnd bnd-maven-plugin - 5.1.2 + 6.1.0 diff --git a/src/main/groovy/life/qbic/datamodel/datasets/MaxQuantRunResult.groovy b/src/main/groovy/life/qbic/datamodel/datasets/MaxQuantRunResult.groovy index a64a9f924e..bde58328ca 100644 --- a/src/main/groovy/life/qbic/datamodel/datasets/MaxQuantRunResult.groovy +++ b/src/main/groovy/life/qbic/datamodel/datasets/MaxQuantRunResult.groovy @@ -29,19 +29,18 @@ final class MaxQuantRunResult { private final static Set maxQuantFileTypes = [ FQDN_FILES + ".AllPeptides", FQDN_FILES + ".Evidence", - FQDN_FILES + ".ExperimentalDesignTemplate", FQDN_FILES + ".Parameters", FQDN_FILES + ".Peptides", FQDN_FILES + ".ProteinGroups", FQDN_FILES + ".RunParameters", GENERAL_FILES + ".SampleIds", - FQDN_FILES + ".Summary" ] private final AllPeptides allPeptides private final Evidence evidence + @Deprecated private final ExperimentalDesignTemplate experimentalDesignTemplate private final Parameters parameters @@ -54,8 +53,10 @@ final class MaxQuantRunResult { private final SampleIds sampleIds + @Deprecated private final Summary summary + @Deprecated MaxQuantRunResult(AllPeptides allPeptides, Evidence evidence, ExperimentalDesignTemplate experimentalDesignTemplate, Parameters parameters, Peptides peptides, ProteinGroups proteinGroups, RunParameters runParameters, SampleIds sampleIds, Summary summary) { this.allPeptides = Objects.requireNonNull(allPeptides, "allPeptides must not be null.") this.evidence = Objects.requireNonNull(evidence, "evidence must not be null.") @@ -68,9 +69,19 @@ final class MaxQuantRunResult { this.summary = Objects.requireNonNull(summary, "summary must not be null.") } + MaxQuantRunResult(AllPeptides allPeptides, Evidence evidence, Parameters parameters, Peptides peptides, ProteinGroups proteinGroups, RunParameters runParameters, SampleIds sampleIds) { + this.allPeptides = Objects.requireNonNull(allPeptides, "allPeptides must not be null.") + this.evidence = Objects.requireNonNull(evidence, "evidence must not be null.") + this.parameters = Objects.requireNonNull(parameters, "parameters must not be null.") + this.peptides = Objects.requireNonNull(peptides, "peptides must not be null.") + this.proteinGroups = Objects.requireNonNull(proteinGroups, "proteinGroups must not be null.") + this.runParameters = Objects.requireNonNull(runParameters, "runParameters must not be null.") + this.sampleIds = Objects.requireNonNull(sampleIds, "sampleIds must not be null.") + } + /** - * Static factory method that creates a new maxQuantRunResult instance from the bioinformatic pipeline output. - * See this @{link example} + * Static factory method that creates a new maxQuantRunResult instance from the MaxQuant output. + * See this @{link example} * for a JSON representation of a valid map structure * * @param Map maxQuantRunOutput @@ -82,27 +93,23 @@ final class MaxQuantRunResult { //Check if the required folders are in the root directory Objects.requireNonNull(maxQuantRunOutput.get("allPeptides"), "The provided directory must contain a allPeptides.txt file.") Objects.requireNonNull(maxQuantRunOutput.get("evidence"), "The provided directory must contain a evidence.txt file.") - Objects.requireNonNull(maxQuantRunOutput.get("experimentalDesignTemplate"), "The provided directory must contain a experimentalDesignTemplate.txt file.") Objects.requireNonNull(maxQuantRunOutput.get("parameters"), "The provided directory must contain a parameters.txt file.") Objects.requireNonNull(maxQuantRunOutput.get("peptides"), "The provided directory must contain a peptides.txt file.") Objects.requireNonNull(maxQuantRunOutput.get("proteinGroups"), "The provided directory must contain a proteinGroups.txt file.") Objects.requireNonNull(maxQuantRunOutput.get("runParameters"), "The provided director must contain a runParameters.xml file.") Objects.requireNonNull(maxQuantRunOutput.get("sampleIds"), "The provided directory must contain a sampleIds.txt file.") - Objects.requireNonNull(maxQuantRunOutput.get("summary"), "The provided directory must contain a summary.pdf file.") //Get Files from Root Directory AllPeptides allPeptides = parseFile(maxQuantRunOutput.get("allPeptides") as Map) as AllPeptides Evidence evidence = parseFile(maxQuantRunOutput.get("evidence") as Map) as Evidence - ExperimentalDesignTemplate experimentalDesignTemplate = parseFile(maxQuantRunOutput.get("experimentalDesignTemplate") as Map) as ExperimentalDesignTemplate Parameters parameters = parseFile(maxQuantRunOutput.get("parameters") as Map) as Parameters Peptides peptides = parseFile(maxQuantRunOutput.get("peptides") as Map) as Peptides ProteinGroups proteinGroups = parseFile(maxQuantRunOutput.get("proteinGroups") as Map) as ProteinGroups RunParameters runParameters = parseFile(maxQuantRunOutput.get("runParameters") as Map) as RunParameters SampleIds sampleIds = parseFile(maxQuantRunOutput.get("sampleIds") as Map) as SampleIds - Summary summary = parseFile(maxQuantRunOutput.get("summary") as Map) as Summary //Create new MaxQuantRunResult object with parsed information - return new MaxQuantRunResult(allPeptides, evidence, experimentalDesignTemplate, parameters, peptides, proteinGroups, runParameters, sampleIds, summary) + return new MaxQuantRunResult(allPeptides, evidence, parameters, peptides, proteinGroups, runParameters, sampleIds) } /** @@ -128,6 +135,7 @@ final class MaxQuantRunResult { * @return an ExperimentalDesignTemplate file generated by MaxQuant * @since 2.10.0 */ + @Deprecated ExperimentalDesignTemplate getExperimentalDesignTemplate() { return experimentalDesignTemplate } @@ -182,6 +190,7 @@ final class MaxQuantRunResult { * @return a Summary file generated by MaxQuant * @since 2.10.0 */ + @Deprecated Summary getSummary() { return summary } diff --git a/src/main/groovy/life/qbic/datamodel/datasets/NfCorePipelineResult.groovy b/src/main/groovy/life/qbic/datamodel/datasets/NfCorePipelineResult.groovy index 7c7e5e126f..65f030dd17 100644 --- a/src/main/groovy/life/qbic/datamodel/datasets/NfCorePipelineResult.groovy +++ b/src/main/groovy/life/qbic/datamodel/datasets/NfCorePipelineResult.groovy @@ -3,7 +3,7 @@ package life.qbic.datamodel.datasets import life.qbic.datamodel.datasets.datastructure.files.DataFile import life.qbic.datamodel.datasets.datastructure.files.nfcore.ExecutionReport -import life.qbic.datamodel.datasets.datastructure.files.nfcore.PipelineReport + import life.qbic.datamodel.datasets.datastructure.files.nfcore.RunId import life.qbic.datamodel.datasets.datastructure.files.general.SampleIds import life.qbic.datamodel.datasets.datastructure.files.nfcore.SoftwareVersions @@ -33,7 +33,6 @@ final class NfCorePipelineResult { private final static Set nfCoreFileTypes = [ FQDN_FILES + ".ExecutionReport", GENERAL_FILES + ".SampleIds", - FQDN_FILES + ".PipelineReport", FQDN_FILES + ".SoftwareVersions", FQDN_FILES + ".RunId" ] @@ -46,6 +45,7 @@ final class NfCorePipelineResult { private SampleIds sampleIds + // The RunId is only generated if the result was generated by a NF-Tower instance private RunId runId private PipelineInformationFolder pipelineInformationFolder @@ -54,7 +54,7 @@ final class NfCorePipelineResult { private List processFolders - NfCorePipelineResult(PipelineInformationFolder pipelineInformationFolder, QualityControlFolder qualityControlFolder, List processFolders, RunId runId, SampleIds sampleIds) { + NfCorePipelineResult(PipelineInformationFolder pipelineInformationFolder, QualityControlFolder qualityControlFolder, List processFolders, RunId runId, SampleIds sampleIds) { Objects.requireNonNull(pipelineInformationFolder, "Please provide a PipelineInformation folder.") Objects.requireNonNull(qualityControlFolder, "Please provide a QualityControl folder") Objects.requireNonNull(processFolders, "Please provide a List of process folders") @@ -68,6 +68,17 @@ final class NfCorePipelineResult { this.sampleIds = sampleIds } + NfCorePipelineResult(PipelineInformationFolder pipelineInformationFolder, QualityControlFolder qualityControlFolder, List processFolders, SampleIds sampleIds) { + Objects.requireNonNull(pipelineInformationFolder, "Please provide a PipelineInformation folder.") + Objects.requireNonNull(qualityControlFolder, "Please provide a QualityControl folder") + Objects.requireNonNull(processFolders, "Please provide a List of process folders") + Objects.requireNonNull(sampleIds, "Please provide a sampleIds file") + this.pipelineInformationFolder = pipelineInformationFolder + this.qualityControlFolder = qualityControlFolder + this.processFolders = processFolders + this.sampleIds = sampleIds + } + /** * Static factory method that creates a new nfcoreExperiment instance from the bioinformatic pipeline output. * See this @{link example} @@ -81,15 +92,13 @@ final class NfCorePipelineResult { //Check if all required folders are in root directory Objects.requireNonNull(bioinformaticPipelineOutput.get("pipelineInformation"), "The root folder must contain a PipelineInformation folder.") - Objects.requireNonNull(bioinformaticPipelineOutput.get("qualityControl"),"The root folder must contain a QualityControl folder.") + Objects.requireNonNull(bioinformaticPipelineOutput.get("qualityControl"), "The root folder must contain a QualityControl folder.") Objects.requireNonNull(bioinformaticPipelineOutput.get("processFolders"), "The root folder must contain at least one process folder.") //Check if all required files are in the pipeline_info directory Map pipelineInfoMap = bioinformaticPipelineOutput["pipelineInformation"] as Map - Objects.requireNonNull(pipelineInfoMap.get("softwareVersions"), "The pipeline_info folder must contain a softwareVersions.csv file.") - Objects.requireNonNull(pipelineInfoMap.get("executionReport"), "The pipeline_info folder must contain a executionReport.txt file.") - Objects.requireNonNull(pipelineInfoMap.get("pipelineReport"), "The pipeline_info folder must contain a pipeline_info.txt file.") + Objects.requireNonNull(pipelineInfoMap.get("softwareVersions"), "The pipeline_info folder must contain a softwareVersions.yml file.") + Objects.requireNonNull(pipelineInfoMap.get("executionReport"), "The pipeline_info folder must contain a executionReport.html file.") //Check if all required files are in root directory - Objects.requireNonNull(bioinformaticPipelineOutput.get("runId"), "The root folder must contain a run_id.txt file.") Objects.requireNonNull(bioinformaticPipelineOutput.get("sampleIds"), "The root folder must contain an sample_ids.txt file.") //Parse all folders in the root directory @@ -105,19 +114,21 @@ final class NfCorePipelineResult { //These files are not stored as children but as properties of the pipeline_info folder DataFile softwareVersions = parseFile(pipelineInfoMap.get("softwareVersions") as Map) DataFile executionReport = parseFile(pipelineInfoMap.get("executionReport") as Map) - DataFile pipelineReport = parseFile(pipelineInfoMap.get("pipelineReport") as Map) //Set information of pipelineInformation properties pipelineInformation.softwareVersions = softwareVersions as SoftwareVersions - pipelineInformation.pipelineReport = pipelineReport as PipelineReport pipelineInformation.executionReport = executionReport as ExecutionReport - //Parse all files in the root directory - DataFile runId = parseFile(bioinformaticPipelineOutput.get("runId") as Map) as RunId + //Parse all mandatory files in the root directory DataFile sampleIds = parseFile(bioinformaticPipelineOutput.get("sampleIds") as Map) as SampleIds - //Create new NfCorePipelineResult with parsed information - return new NfCorePipelineResult(pipelineInformation, qualityControl, processFolders, runId, sampleIds) + // Parse optional Files in the root directory and generate NfCorePipelineResult accordingly + if (bioinformaticPipelineOutput.get("runId") != null) { + DataFile runId = parseFile(bioinformaticPipelineOutput.get("runId") as Map) as RunId + return new NfCorePipelineResult(pipelineInformation, qualityControl, processFolders, runId, sampleIds) + } else { + return new NfCorePipelineResult(pipelineInformation, qualityControl, processFolders, sampleIds) + } } /** @@ -169,11 +180,12 @@ final class NfCorePipelineResult { /* * Helper method that creates a DataFile instance from a map */ + private static DataFile parseFile(Map fileTree) throws IllegalArgumentException { String name = fileTree.get("name") String fileType = fileTree.get("fileType") String path = fileTree.get("path") - + for (String nfCoreFileType : nfCoreFileTypes) { Class c = Class.forName(nfCoreFileType) Method method = c.getDeclaredMethod("create", String.class, String.class) @@ -187,15 +199,15 @@ final class NfCorePipelineResult { } } // We have to check for files of unknown type since this Parser will encounter variable file output dependent on the pipeline - if(!fileType) - { - throw new IllegalArgumentException("File $name with path $path is of unknown nfcore file type.") + if (!fileType) { + throw new IllegalArgumentException("File $name with path $path is of unknown nfcore file type.") } } /* * Helper method that creates a DataFolder instance from a map */ + private static DataFolder parseFolder(Map fileTree) throws IllegalArgumentException { def name = fileTree.get("name") as String @@ -219,18 +231,19 @@ final class NfCorePipelineResult { * Helper method that tries to create a DataFolder instance * based on the DataFolder's different static factory create methods. */ + private static Optional tryToCreateDataFolder(Method method, String name, String relativePath, List children) { Optional folder = Optional.empty() - try { - // We only have named Folders - def dataFolder = method.invoke(null, name, relativePath, children) as DataFolder - folder = Optional.of(dataFolder) - } catch (InvocationTargetException e2) { - // Do nothing - } + try { + // We only have named Folders + def dataFolder = method.invoke(null, name, relativePath, children) as DataFolder + folder = Optional.of(dataFolder) + } catch (InvocationTargetException e2) { + // Do nothing + } return folder } @@ -238,6 +251,7 @@ final class NfCorePipelineResult { /* * Helper method that parses the children of a folder. */ + private static List parseChildren(List children) { def parsedChildren = [] children.each { Map unknownChild -> diff --git a/src/main/groovy/life/qbic/datamodel/datasets/OxfordNanoporeExperiment.groovy b/src/main/groovy/life/qbic/datamodel/datasets/OxfordNanoporeExperiment.groovy index 367fb89315..a8179982b1 100644 --- a/src/main/groovy/life/qbic/datamodel/datasets/OxfordNanoporeExperiment.groovy +++ b/src/main/groovy/life/qbic/datamodel/datasets/OxfordNanoporeExperiment.groovy @@ -1,7 +1,9 @@ package life.qbic.datamodel.datasets import life.qbic.datamodel.datasets.datastructure.files.DataFile +import life.qbic.datamodel.datasets.datastructure.files.nanopore.OptionalFile import life.qbic.datamodel.datasets.datastructure.folders.DataFolder +import life.qbic.datamodel.datasets.datastructure.folders.nanopore.OptionalFolder import life.qbic.datamodel.identifiers.SampleCodeFunctions import java.lang.reflect.InvocationTargetException @@ -14,231 +16,332 @@ import java.lang.reflect.Method */ final class OxfordNanoporeExperiment implements ExperimentFolder { - // Fully qualified domain name of the nanopore folder structure package - private final static String FQDN_FOLDERS = "life.qbic.datamodel.datasets.datastructure.folders.nanopore" - // Fully qualified domain name of the nanopore file structure package - private final static String FQDN_FILES = "life.qbic.datamodel.datasets.datastructure.files.nanopore" - - private final List measurements - - private final String sampleId - - private final static Set nanoporeFileTypes = [ - FQDN_FILES + ".DriftCorrectionLog", - FQDN_FILES + ".DutyTimeLog", - FQDN_FILES + ".Fast5File", - FQDN_FILES + ".FastQFile", - FQDN_FILES + ".FastQZippedFile", - FQDN_FILES + ".FinalSummaryLog", - FQDN_FILES + ".MuxScanDataLog", - FQDN_FILES + ".ReportMdLog", - FQDN_FILES + ".ReportPDFLog", - FQDN_FILES + ".SequencingSummaryLog", - FQDN_FILES + ".ThroughputLog", - FQDN_FILES + ".BarcodeAlignmentLog" - ] - - private final static Set nanoporeFolderTypes = [ - FQDN_FOLDERS + ".Fast5Folder", - FQDN_FOLDERS + ".FastQFolder", - FQDN_FOLDERS + ".Fast5PassFolder", - FQDN_FOLDERS + ".Fast5FailFolder", - FQDN_FOLDERS + ".FastQPassFolder", - FQDN_FOLDERS + ".FastQFailFolder", - FQDN_FOLDERS + ".UnclassifiedFast5Folder", - FQDN_FOLDERS + ".UnclassifiedFastQFolder", - FQDN_FOLDERS + ".OtherReportsFolder" - ] - - private OxfordNanoporeExperiment(String sampleId, List measurements) { - this.measurements = Objects.requireNonNull(measurements, "measurements must not be null") - this.sampleId = Objects.requireNonNull(sampleId, "sampleId must not be null") - } + private final List measurements - /** - * Static factory method that creates a new instance from a Oxford Nanopore sequencer output. - * - * @param Map nanoPoreSequencerOutput - * @return OxfordNanoporeExperiment A new instance of a nanopore experiment. - */ - static OxfordNanoporeExperiment create(Map nanoPoreSequencerOutput) { - final String sampleId = parseQbicIdFromRootFolder(nanoPoreSequencerOutput) - final List measurements = parseMeasurements(nanoPoreSequencerOutput) - return new OxfordNanoporeExperiment(sampleId, measurements) - } + private final String sampleId - /** - * Provides a list of measurements contained within the experiment folder. - * @return - */ - List getMeasurements() { - return this.measurements + private OxfordNanoporeExperiment(String sampleId, List measurements) { + this.measurements = Objects.requireNonNull(measurements, "measurements must not be null") + this.sampleId = Objects.requireNonNull(sampleId, "sampleId must not be null") + } + + /** + * Static factory method that creates a new instance from a Oxford Nanopore sequencer output. + * + * @param Map nanoPoreSequencerOutput + * @return OxfordNanoporeExperiment A new instance of a nanopore experiment. + */ + static OxfordNanoporeExperiment create(Map nanoPoreSequencerOutput) { + final String sampleId = parseQbicIdFromRootFolder(nanoPoreSequencerOutput) + final List measurements = parseMeasurements(nanoPoreSequencerOutput) + return new OxfordNanoporeExperiment(sampleId, measurements) + } + + /** + * Provides a list of measurements contained within the experiment folder. + * @return + */ + List getMeasurements() { + return this.measurements + } + + @Override + String getSampleCode() { + return this.sampleId + } + + /** + * Helper method that parses the QBiC identifier from the root folder name*/ + private static String parseQbicIdFromRootFolder(Map nanoPoreSequencerOutput) { + def name = Objects.requireNonNull(nanoPoreSequencerOutput.get("name"), "The root folder must contain a name property.") + final def ids = SampleCodeFunctions.findAllQbicSampleCodes(name as String) + if (ids.isEmpty()) { + throw new IllegalArgumentException("No QBiC sample identifier found!") + } + if (ids.size() > 1) { + throw new IllegalArgumentException("Name contained more than one valid sample id!") } + return ids.get(0) + } - @Override - String getSampleCode() { - return this.sampleId + /** + * Helper method that creates the measurements from the sequencer output*/ + private static List parseMeasurements(Map nanoPoreSequencerOutput) { + final def measurements = [] + Objects.requireNonNull(nanoPoreSequencerOutput.get("children"), "The root folder must contain at least one measurement folder.") + nanoPoreSequencerOutput.get("children").each { Map measurementItem -> + def name = measurementItem.get("name") as String + def relativePath = measurementItem.get("path") as String + def children = parseMeasurementItems(measurementItem.get("children") as List) + def metadata = measurementItem.get("metadata") as Map + measurements.add(new OxfordNanoporeMeasurement(name, relativePath, children, metadata)) } + return measurements + } - /** - * Helper method that parses the QBiC identifier from the root folder name - */ - private static String parseQbicIdFromRootFolder(Map nanoPoreSequencerOutput) { - def name = Objects.requireNonNull(nanoPoreSequencerOutput.get("name"), "The root folder must contain a name property.") - final def ids = SampleCodeFunctions.findAllQbicSampleCodes(name as String) - if (ids.isEmpty()) { - throw new IllegalArgumentException("No QBiC sample identifier found!") - } - if (ids.size() > 1) { - throw new IllegalArgumentException("Name contained more than one valid sample id!") + /* + * Helper method that creates a list of mixed DataFolders and DataFiles instances + */ + + private static List parseMeasurementItems(List items) { + final def children = [] + items.each { item -> + { + if (!item.isEmpty()) { + if (isFile(item)) { + // Lets try to parse it as a subclass of a DataFile + DataFile putativeFile = parseFile(item) + children.add(putativeFile) + } + if (isFolder(item)) { + // Lets try to parse it as a subclass of a DataFile + DataFolder putativeFolder = parseFolder(item) + children.add(putativeFolder) + } } - return ids.get(0) + } } + return children + } - /** - * Helper method that creates the measurements from the sequencer output - */ - private static List parseMeasurements(Map nanoPoreSequencerOutput) { - final def measurements = [] - Objects.requireNonNull(nanoPoreSequencerOutput.get("children"), "The root folder must contain at least one measurement folder.") - nanoPoreSequencerOutput.get("children").each { Map measurementItem -> - def name = measurementItem.get("name") as String - def relativePath = measurementItem.get("path") as String - def children = parseMeasurementItems(measurementItem.get("children") as List) - def metadata = measurementItem.get("metadata") as Map - measurements.add(new OxfordNanoporeMeasurement(name, relativePath, children, metadata)) - } - return measurements + /* + * Helper method that creates a DataFile instance from a map + */ + + private static DataFile parseFile(Map fileTree) throws IllegalArgumentException { + String name = fileTree.get("name") + String path = fileTree.get("path") + for (String nanoPoreFileType : NanoporeFileTypes.values()) { + Class c = Class.forName(nanoPoreFileType) + Method method = c.getDeclaredMethod("create", String.class, String.class) + try { + DataFile dataFile = method.invoke(null, name, path) as DataFile + return dataFile + } catch (InvocationTargetException e) { + // Do nothing as we need to try out all specialisations that extend the + // DataFile class + } } + try { + String fileSuffix = fileTree.get("file_type") + // Since the file structure is highly variable we want to allow for unknown files to be included in the experiment + OptionalFile optionalFile = OptionalFile.create(name, path, fileSuffix) + return optionalFile + } catch (Exception ignored) { + throw new IllegalArgumentException("File $name with path $path is not a valid Data File") + } + } - /* - * Helper method that creates a list of mixed DataFolders and DataFiles instances - */ + /* + * Helper method that creates a DataFolder instance from a map + */ - private static List parseMeasurementItems(List items) { - final def children = [] - items.each { item -> - try { - // Lets try to parse it as a subclass of a DataFile - def putativeFile = parseFile(item) - children.add(putativeFile) - } catch (IllegalArgumentException e) { - // In this case, no DataFile could be created, try to convert to a DataFolder then - def putativeFolder = parseFolder(item) - children.add(putativeFolder) - } - } - return children + private static DataFolder parseFolder(Map fileTree) throws IllegalArgumentException { + String name = fileTree.get("name") as String + String path = fileTree.get("path") as String + def children = parseChildren(fileTree.get("children") as List) + + for (String nanoPoreFolderType : NanoporeFolderTypes.values()) { + Method method = determineMethod(Class.forName(nanoPoreFolderType)) + Optional folder = tryToCreateDataFolder(method, name, path, children) + if (folder.isPresent()) { + return folder.get() + } } + // Since the file structure is highly variable we want to allow for unknown folders to be included in the experiment + try { + OptionalFolder optionalFolder = OptionalFolder.create(name, path, children) + return optionalFolder + } catch (Exception ignored) { + throw new IllegalArgumentException("Folder $name with path $path is not a valid Data Folder") + } + } - /* - * Helper method that creates a DataFile instance from a map - */ + /* + * Helper method that tries to create a DataFolder instance + * based on the DataFolder's different static factory create methods. + * As we do not know, whether a folder element is another typed folder + * such as FastQPassFolder or a named folder such as Fast5Folder, we have to + * try and fail. + */ + + private static Optional tryToCreateDataFolder(Method method, + String name, + String relativePath, + List children) { + Optional folder = Optional.empty() + try { + // Try typed folder + def dataFolder = method.invoke(null, relativePath, children) as DataFolder + folder = Optional.of(dataFolder) + } catch (InvocationTargetException e) { + // Do nothing + } catch (IllegalArgumentException e) { + try { + // Try named folder + def dataFolder = method.invoke(null, name, relativePath, children) as DataFolder + folder = Optional.of(dataFolder) + } catch (InvocationTargetException e2) { + // Do nothing + } + } + return folder + } - private static DataFile parseFile(Map fileTree) throws IllegalArgumentException { - def name = fileTree.get("name") - def path = fileTree.get("path") - for (String nanoPoreFileType : nanoporeFileTypes) { - Class c = Class.forName(nanoPoreFileType) - Method method = c.getDeclaredMethod("create", String.class, String.class) - try { - DataFile dataFile = method.invoke(null, name, path) as DataFile - return dataFile - } catch (InvocationTargetException e) { - // Do nothing as we need to try out all specialisations that extend the - // DataFile class - } + /* + * Helper method that parses the children of a folder. + */ + + private static List parseChildren(List children) { + def parsedChildren = [] + children.forEach({ Map unknownChild -> + if (!unknownChild.isEmpty()) { + if (isFile(unknownChild)) { + def child = parseFile(unknownChild) + parsedChildren.add(child) + } + if (isFolder(unknownChild)) { + def child = parseFolder(unknownChild) + parsedChildren.add(child) } - // If we cannot create a DataFile object at all, throw an exception - throw new IllegalArgumentException("File $name with path $path is of unknown Oxford Nanopore file type.") + } + }) + return parsedChildren + } + + /* + Determines the correct static create method for a data folder. + */ + + private static Method determineMethod(Class c) { + def method + try { + // named folder (i.e. Fast5Folder) + method = c.getDeclaredMethod("create", String.class, String.class, List.class) + } catch (NoSuchMethodException e) { + // typed folder (i.e. FastQPassFolder) + method = c.getDeclaredMethod("create", String.class, List.class) } + return method + } - /* - * Helper method that creates a DataFolder instance from a map + private static boolean isFile(Map parsedChild) { + //Unique key in fileTreeMap identifying a child as a file + final String file_key = "file_type" + return parsedChild.containsKey(file_key) + } + + private static boolean isFolder(Map parsedChild) { + //Unique key in fileTreeMap identifyng a child as a folder + final String folder_key = "children" + return parsedChild.containsKey(folder_key) + } + + private enum NanoporeFileTypes { + + DRIFT_CORRECTION_LOG(FQDN_FILES + ".DriftCorrectionLog"), + DUTY_TIME_LOG(FQDN_FILES + ".DutyTimeLog"), + FAST5_FILE(FQDN_FILES + ".Fast5File"), + FASTQ_FILE(FQDN_FILES + ".FastQFile"), + FASTQ_ZIPPED_FILE(FQDN_FILES + ".FastQZippedFile"), + POD5_FILE(FQDN_FILES + ".Pod5File"), + FINAL_SUMMARY_LOG(FQDN_FILES + ".FinalSummaryLog"), + MUX_SCAN_DATA_LOG(FQDN_FILES + ".MuxScanDataLog"), + REPORT_MD_LOG(FQDN_FILES + ".ReportMdLog"), + REPORT_PDF_LOG(FQDN_FILES + ".ReportPDFLog"), + REPORT_HTML_LOG(FQDN_FILES + ".ReportHTMLLog"), + REPORT_JSON_LOG(FQDN_FILES + ".ReportJSONLog"), + SEQUENCING_SUMMARY_LOG(FQDN_FILES + ".SequencingSummaryLog"), + THROUGHPUT_LOG(FQDN_FILES + ".ThroughputLog"), + BARCODE_ALIGNMENT_LOG(FQDN_FILES + ".BarcodeAlignmentLog"), + PORE_ACTIVITY_LOG(FQDN_FILES + ".PoreActivityLog"), + SAMPLE_SHEET_LOG(FQDN_FILES + ".SampleSheetLog"), + PORE_SCAN_DATA_LOG(FQDN_FILES + ".PoreScanDataLog"), + SEQUENCING_TELEMETRY_LOG(FQDN_FILES + ".SequencingTelemetryLog"), + GUPPY_BASECALL_LOG(FQDN_FILES + ".GuppyBasecallLog") + + /** + Holds the String value of the enum */ + private final String value - private static DataFolder parseFolder(Map fileTree) throws IllegalArgumentException { - def name = fileTree.get("name") as String - def path = fileTree.get("path") as String - def children = parseChildren(fileTree.get("children") as List) - - for (String nanoPoreFolderType : nanoporeFolderTypes) { - Method method = determineMethod(Class.forName(nanoPoreFolderType)) - Optional folder = tryToCreateDataFolder(method, name, path, children) - if (folder.isPresent()) { - return folder.get() - } - } - // If we reach this point, no DataFolder could be created based on the known folder types - // in life.qbic.datamodel.datasets.datastructure.folders.nanopore.* - throw new IllegalArgumentException("Folder $name with path $path is of unknown Oxford Nanopore folder type.") + // Fully qualified domain name of the nanopore file structure package + private static final String FQDN_FILES = "life.qbic.datamodel.datasets.datastructure.files.nanopore" + + /** + * Private constructor to create different NanoporeFileTypes enum items + * @param value + */ + private NanoporeFileTypes(String value) { + this.value = value } - /* - * Helper method that tries to create a DataFolder instance - * based on the DataFolder's different static factory create methods. - * As we do not know, whether a folder element is another typed folder - * such as FastQPassFolder or a named folder such as Fast5Folder, we have to - * try and fail. + /** + * Returns to the enum item value + * @return */ + String getValue() { + return value + } - private static Optional tryToCreateDataFolder(Method method, - String name, - String relativePath, - List children) { - Optional folder = Optional.empty() - try { - // Try typed folder - def dataFolder = method.invoke(null, relativePath, children) as DataFolder - folder = Optional.of(dataFolder) - } catch (InvocationTargetException e) { - // Do nothing - } catch (IllegalArgumentException e) { - try { - // Try named folder - def dataFolder = method.invoke(null, name, relativePath, children) as DataFolder - folder = Optional.of(dataFolder) - } catch (InvocationTargetException e2) { - // Do nothing - } - } - return folder + /** + * Returns a String representation of the enum item + * @return + */ + @Override + String toString() { + return this.getValue() } + } + + private enum NanoporeFolderTypes { + + FAST5_FOLDER(FQDN_FOLDERS + ".Fast5Folder"), + FASTQ_FOLDER(FQDN_FOLDERS + ".FastQFolder"), + FAST5_PASS_FOLDER(FQDN_FOLDERS + ".Fast5PassFolder"), + FAST5_FAIL_FOLDER(FQDN_FOLDERS + ".Fast5FailFolder"), + FAST5_SKIP_FOLDER(FQDN_FOLDERS + ".Fast5SkipFolder"), + FASTQ_PASS_FOLDER(FQDN_FOLDERS + ".FastQPassFolder"), + FASTQ_FAIL_FOLDER(FQDN_FOLDERS + ".FastQFailFolder"), + UNCLASSIFIED_FAST5_FOLDER(FQDN_FOLDERS + ".UnclassifiedFast5Folder"), + UNCLASSIFIED_FASTQ_FOLDER(FQDN_FOLDERS + ".UnclassifiedFastQFolder"), + POD5_PASS_FOLDER(FQDN_FOLDERS + ".Pod5PassFolder"), + POD5_FAIL_FOLDER(FQDN_FOLDERS + ".Pod5FailFolder"), + POD5_SKIP_FOLDER(FQDN_FOLDERS + ".Pod5SkipFolder"), + OTHER_REPORTS_FOLDER(FQDN_FOLDERS + ".OtherReportsFolder"), + BASECALLING_FOLDER(FQDN_FOLDERS + ".BasecallingFolder"), + + // Fully qualified domain name of the nanopore folder structure package + private static final String FQDN_FOLDERS = "life.qbic.datamodel.datasets.datastructure.folders.nanopore" - /* - * Helper method that parses the children of a folder. + /** + Holds the String value of the enum */ + private final String value - private static List parseChildren(List children) { - def parsedChildren = [] - children.each { Map unknownChild -> - try { - def child = parseFile(unknownChild) - parsedChildren.add(child) - } catch (IllegalArgumentException e) { - // We do not capture the second parse call, as we want to fail the parsing at this point. - // This means that we ultimately found a child of unknown type, which should - // break the parsing. - def child = parseFolder(unknownChild) - parsedChildren.add(child) - } - } - return parsedChildren + /** + * Private constructor to create different NanoporeFolderTypes enum items + * @param value + */ + private NanoporeFolderTypes(String value) { + this.value = value } - /* - Determines the correct static create method for a data folder. + /** + * Returns to the enum item value + * @return */ + String getValue() { + return value + } - private static Method determineMethod(Class c) { - def method - try { - // named folder (i.e. Fast5Folder) - method = c.getDeclaredMethod("create", String.class, String.class, List.class) - } catch (NoSuchMethodException e) { - // typed folder (i.e. FastQPassFolder) - method = c.getDeclaredMethod("create", String.class, List.class) - } - return method + /** + * Returns a String representation of the enum item + * @return + */ + @Override + String toString() { + return this.getValue() } + } } diff --git a/src/main/groovy/life/qbic/datamodel/datasets/OxfordNanoporeMeasurement.groovy b/src/main/groovy/life/qbic/datamodel/datasets/OxfordNanoporeMeasurement.groovy index 5d79cf489d..4630752485 100644 --- a/src/main/groovy/life/qbic/datamodel/datasets/OxfordNanoporeMeasurement.groovy +++ b/src/main/groovy/life/qbic/datamodel/datasets/OxfordNanoporeMeasurement.groovy @@ -29,6 +29,8 @@ final class OxfordNanoporeMeasurement { private boolean pooledSamplesMeasurement + private boolean hasBasecallingData + protected OxfordNanoporeMeasurement(String name, String path, List children, Map metadata) { this.logFilesCollection = new ArrayList<>() this.folders = new HashMap<>() @@ -39,6 +41,7 @@ final class OxfordNanoporeMeasurement { createContent() assessPooledStatus() + assessBasecallingStatus() assessState() } @@ -53,12 +56,16 @@ final class OxfordNanoporeMeasurement { private void assessPooledStatus() { this.pooledSamplesMeasurement = containsAtLeastOneBarcodedFolder(folders["fast5pass"]) // There can be still pooled samples in the failed folder, worst case is all - // samples failed, so we need to check there to - if (! pooledSamplesMeasurement) { + // samples failed, so we need to check there too + if (!pooledSamplesMeasurement) { this.pooledSamplesMeasurement = containsAtLeastOneBarcodedFolder(folders["fast5fail"]) } } + private void assessBasecallingStatus() { + this.hasBasecallingData = folders["basecalling"] + } + private static boolean containsAtLeastOneBarcodedFolder(DataFolder folder) { if (!folder) { return false @@ -77,38 +84,67 @@ final class OxfordNanoporeMeasurement { case Fast5FailFolder: folders["fast5fail"] = element as Fast5FailFolder break + case Fast5SkipFolder: + folders["fast5skip"] = element as Fast5SkipFolder + break case FastQPassFolder: folders["fastqpass"] = element as FastQPassFolder break case FastQFailFolder: folders["fastqfail"] = element as FastQFailFolder break + case Pod5PassFolder: + folders["pod5pass"] = element as Pod5PassFolder + break + case Pod5FailFolder: + folders["pod5fail"] = element as Pod5FailFolder + break + case Pod5SkipFolder: + folders["pod5skip"] = element as Pod5SkipFolder + break case DataFile: logFilesCollection.add(element as DataFile) break + case BasecallingFolder: + folders["basecalling"] = element as BasecallingFolder + break } } } private void assessState() throws IllegalStateException { - // Condition one: Don't allow Fast5 pass and fail folder are empty - assessFast5Content() - // Condition two: Don't allow Fastq pass and fail folder are empty - assessFastQContent() + boolean isValid = false + // We need to ensure that fastq and fast5 information is provided if guppy basecaller was used + if (areFast5FoldersInMeasurement() && areFastQFoldersInMeasurement()) { + isValid = true + } + //// We need to ensure that pod5_skip and fast5_skip information is provided if dorado basecaller was used + if (arePod5FoldersInMeasurement()) { + isValid = true + } + if (isValid == false) { + throw new IllegalStateException("No valid data is contained in measurement") + } } - private void assessFast5Content() throws IllegalStateException { - if (folders["fast5pass"].getChildren().isEmpty() && folders["fast5fail"].getChildren() - .isEmpty()) { - throw new IllegalStateException("The fast5 pass folder and fail folder are empty.") - } + // Condition one: Don't allow empty Fast5 pass and fail folder + private boolean areFast5FoldersInMeasurement() { + return isDataFolderInMeasurement("fast5pass") || isDataFolderInMeasurement("fast5fail") + } + // Condition two: Don't allow empty Fastq pass and fail folder + private boolean areFastQFoldersInMeasurement() { + return isDataFolderInMeasurement("fastqpass") || isDataFolderInMeasurement("fastqfail") + } + // Condition three: Don't allow empty Pod5 skip and fast5 skip folder + private boolean arePod5FoldersInMeasurement() { + return isDataFolderInMeasurement("fast5skip") || isDataFolderInMeasurement("pod5skip") } - private void assessFastQContent() throws IllegalStateException { - if (folders["fastqpass"].getChildren().isEmpty() && folders["fastqfail"].getChildren() - .isEmpty()) { - throw new IllegalStateException("The fastq pass folder and fail folder are empty.") + private boolean isDataFolderInMeasurement(String string) { + if (folders[string] == null) { + return false } + return !folders[string].getChildren().isEmpty() } /** @@ -273,13 +309,27 @@ final class OxfordNanoporeMeasurement { private Map> prepareRawData(String sampleId) { final def result = new HashMap() - final def folders = [ - "fast5fail": (folders.get("fast5fail") as DataFolder), - "fast5pass": (folders.get("fast5pass") as DataFolder), - "fastqpass": (folders.get("fastqpass") as DataFolder), - "fastqfail": (folders.get("fastqfail") as DataFolder) + final def dataFolders = [ + "fast5fail" : (folders.get("fast5fail") as DataFolder), + "fast5pass" : (folders.get("fast5pass") as DataFolder), + "fastqpass" : (folders.get("fastqpass") as DataFolder), + "fastqfail" : (folders.get("fastqfail") as DataFolder) ] - result.put(sampleId, folders) + if (hasBasecallingData) dataFolders.put("basecalling", (folders.get("basecalling") as DataFolder)) + //Only add dorado based minimal required datafolders if present + if (folders.get("fast5skip") != null) { + dataFolders.put("fast5skip", (folders.get("fast5skip") as DataFolder)) + } + if (folders.get("pod5skip") != null) { + dataFolders.put("pod5skip", (folders.get("pod5skip") as DataFolder)) + } + if (folders.get("pod5fail") != null) { + dataFolders.put("pod5fail", (folders.get("pod5fail") as DataFolder)) + } + if (folders.get("pod5pass") != null) { + dataFolders.put("pod5pass", (folders.get("pod5pass") as DataFolder)) + } + result.put(sampleId, dataFolders) return result } diff --git a/src/main/groovy/life/qbic/datamodel/datasets/datastructure/files/nanopore/GuppyBasecallLog.groovy b/src/main/groovy/life/qbic/datamodel/datasets/datastructure/files/nanopore/GuppyBasecallLog.groovy new file mode 100644 index 0000000000..c749b0564c --- /dev/null +++ b/src/main/groovy/life/qbic/datamodel/datasets/datastructure/files/nanopore/GuppyBasecallLog.groovy @@ -0,0 +1,30 @@ +package life.qbic.datamodel.datasets.datastructure.files.nanopore + +import life.qbic.datamodel.datasets.datastructure.files.DataFile + +/** + * A specialisation of a DataFile, represents an Oxford Nanopore guppy basecalling client log file + */ +class GuppyBasecallLog extends DataFile { + + final private static String FILE_TYPE = "log" + + final private static String NAME_SCHEMA = $/guppy_basecall_client_log-.*/$ + + protected GuppyBasecallLog() {} + + protected GuppyBasecallLog(String name, String relativePath) { + super(name, relativePath, FILE_TYPE) + validateName() + } + + static GuppyBasecallLog create(String name, String relativePath) { + return new GuppyBasecallLog(name, relativePath) + } + + private void validateName() { + if (!(this.name =~ NAME_SCHEMA)) { + throw new IllegalArgumentException("Name must match the Nanopore guppy basecall client log schema!") + } + } +} diff --git a/src/main/groovy/life/qbic/datamodel/datasets/datastructure/files/nanopore/OptionalFile.groovy b/src/main/groovy/life/qbic/datamodel/datasets/datastructure/files/nanopore/OptionalFile.groovy new file mode 100644 index 0000000000..d27c31ca7d --- /dev/null +++ b/src/main/groovy/life/qbic/datamodel/datasets/datastructure/files/nanopore/OptionalFile.groovy @@ -0,0 +1,29 @@ +package life.qbic.datamodel.datasets.datastructure.files.nanopore + +import life.qbic.datamodel.datasets.datastructure.files.DataFile + +/** + * Unspecific OptionalFile used to store unexpected file in the nanopore registration + * + * The high variety of the nanopore datasets registered made an optional all-purpose Datafile structure necessary. + * + */ +class OptionalFile extends DataFile { + + protected OptionalFile() {} + + protected OptionalFile(String name, String relativePath, String fileType) { + super(name, relativePath, fileType) + } + + /** + * Creates a new instance of a OptionalFile object + * @param name The name of the file + * @param relativePath The relative path of the file + * @param fileType The suffix specifying the file type of the file + * @return A new instance of a OptionalFile object + */ + static OptionalFile create(String name, String relativePath, String fileType) { + return new OptionalFile(name, relativePath, fileType) + } +} diff --git a/src/main/groovy/life/qbic/datamodel/datasets/datastructure/files/nanopore/Pod5File.groovy b/src/main/groovy/life/qbic/datamodel/datasets/datastructure/files/nanopore/Pod5File.groovy new file mode 100644 index 0000000000..24b4993808 --- /dev/null +++ b/src/main/groovy/life/qbic/datamodel/datasets/datastructure/files/nanopore/Pod5File.groovy @@ -0,0 +1,30 @@ +package life.qbic.datamodel.datasets.datastructure.files.nanopore + +import life.qbic.datamodel.datasets.datastructure.files.DataFile + +/** + * A specialisation of a DataFile, represents an Oxford Nanopore pod5 file + * + */ +class Pod5File extends DataFile { + + final private static String FILE_TYPE = "pod5" + + final private static String NAME_SCHEMA = /.*\.pod5$/ + + protected Pod5File(String name, String relativePath) { + super(name, relativePath, FILE_TYPE) + validateName() + } + + static Pod5File create(String name, String relativePath) { + return new Pod5File(name, relativePath) + } + + private void validateName() { + if (!(this.name =~ NAME_SCHEMA)) { + throw new IllegalArgumentException("Name must match the Nanopore summary schema!") + } + } + +} diff --git a/src/main/groovy/life/qbic/datamodel/datasets/datastructure/files/nanopore/PoreActivityLog.groovy b/src/main/groovy/life/qbic/datamodel/datasets/datastructure/files/nanopore/PoreActivityLog.groovy new file mode 100644 index 0000000000..871f6dd12c --- /dev/null +++ b/src/main/groovy/life/qbic/datamodel/datasets/datastructure/files/nanopore/PoreActivityLog.groovy @@ -0,0 +1,32 @@ +package life.qbic.datamodel.datasets.datastructure.files.nanopore + +import life.qbic.datamodel.datasets.datastructure.files.DataFile + +/** + * A specialisation of a DataFile, represents an Oxford Nanopore pore activity log file + * + */ +class PoreActivityLog extends DataFile { + + final private static String FILE_TYPE = "csv" + + final private static String NAME_SCHEMA = $/pore_activity_.*/$ + + protected PoreActivityLog() {} + + protected PoreActivityLog(String name, String relativePath) { + super(name, relativePath, FILE_TYPE) + validateName() + } + + static PoreActivityLog create(String name, String relativePath) { + return new PoreActivityLog(name, relativePath) + } + + private void validateName() { + if (!(this.name =~ NAME_SCHEMA)) { + throw new IllegalArgumentException("Name must match the Nanopore pore activity log name schema!") + } + } + +} diff --git a/src/main/groovy/life/qbic/datamodel/datasets/datastructure/files/nanopore/PoreScanDataLog.groovy b/src/main/groovy/life/qbic/datamodel/datasets/datastructure/files/nanopore/PoreScanDataLog.groovy new file mode 100644 index 0000000000..cff4bd7898 --- /dev/null +++ b/src/main/groovy/life/qbic/datamodel/datasets/datastructure/files/nanopore/PoreScanDataLog.groovy @@ -0,0 +1,32 @@ +package life.qbic.datamodel.datasets.datastructure.files.nanopore + +import life.qbic.datamodel.datasets.datastructure.files.DataFile + +/** + * A specialisation of a DataFile, represents an Oxford Nanopore pore scan data log file + * + */ +class PoreScanDataLog extends DataFile { + + final private static String FILE_TYPE = "csv" + + final private static String NAME_SCHEMA = $/pore_scan_data_.*/$ + + protected PoreScanDataLog() {} + + protected PoreScanDataLog(String name, String relativePath) { + super(name, relativePath, FILE_TYPE) + validateName() + } + + static PoreScanDataLog create(String name, String relativePath) { + return new PoreScanDataLog(name, relativePath) + } + + private void validateName() { + if (!(this.name =~ NAME_SCHEMA)) { + throw new IllegalArgumentException("Name must match the Nanopore pore scan data log name schema!") + } + } + +} diff --git a/src/main/groovy/life/qbic/datamodel/datasets/datastructure/files/nanopore/ReportHTMLLog.groovy b/src/main/groovy/life/qbic/datamodel/datasets/datastructure/files/nanopore/ReportHTMLLog.groovy new file mode 100644 index 0000000000..e8d3a6063d --- /dev/null +++ b/src/main/groovy/life/qbic/datamodel/datasets/datastructure/files/nanopore/ReportHTMLLog.groovy @@ -0,0 +1,31 @@ +package life.qbic.datamodel.datasets.datastructure.files.nanopore + +import life.qbic.datamodel.datasets.datastructure.files.DataFile + +/** + * A specialisation of a DataFile, represents an Oxford Nanopore report HTML log file + * + */ +class ReportHTMLLog extends DataFile { + + final private static String FILE_TYPE = "html" + + final private static String NAME_SCHEMA = $/report_.*/$ + + protected ReportHTMLLog() {} + + protected ReportHTMLLog(String name, String relativePath) { + super(name, relativePath, FILE_TYPE) + validateName() + } + + static ReportHTMLLog create(String name, String relativePath) { + return new ReportHTMLLog(name, relativePath) + } + + private void validateName() { + if (!(this.name =~ NAME_SCHEMA)) { + throw new IllegalArgumentException("Name must match the Nanopore report name schema!") + } + } +} diff --git a/src/main/groovy/life/qbic/datamodel/datasets/datastructure/files/nanopore/ReportJSONLog.groovy b/src/main/groovy/life/qbic/datamodel/datasets/datastructure/files/nanopore/ReportJSONLog.groovy new file mode 100644 index 0000000000..19ccf17490 --- /dev/null +++ b/src/main/groovy/life/qbic/datamodel/datasets/datastructure/files/nanopore/ReportJSONLog.groovy @@ -0,0 +1,31 @@ +package life.qbic.datamodel.datasets.datastructure.files.nanopore + +import life.qbic.datamodel.datasets.datastructure.files.DataFile + +/** + * A specialisation of a DataFile, represents an Oxford Nanopore report JSON log file + * + */ +class ReportJSONLog extends DataFile { + + final private static String FILE_TYPE = "json" + + final private static String NAME_SCHEMA = $/report_.*/$ + + protected ReportJSONLog() {} + + protected ReportJSONLog(String name, String relativePath) { + super(name, relativePath, FILE_TYPE) + validateName() + } + + static ReportJSONLog create(String name, String relativePath) { + return new ReportJSONLog(name, relativePath) + } + + private void validateName() { + if (!(this.name =~ NAME_SCHEMA)) { + throw new IllegalArgumentException("Name must match the Nanopore report name schema!") + } + } +} diff --git a/src/main/groovy/life/qbic/datamodel/datasets/datastructure/files/nanopore/SampleSheetLog.groovy b/src/main/groovy/life/qbic/datamodel/datasets/datastructure/files/nanopore/SampleSheetLog.groovy new file mode 100644 index 0000000000..37cbcce350 --- /dev/null +++ b/src/main/groovy/life/qbic/datamodel/datasets/datastructure/files/nanopore/SampleSheetLog.groovy @@ -0,0 +1,33 @@ +package life.qbic.datamodel.datasets.datastructure.files.nanopore + +import life.qbic.datamodel.datasets.datastructure.files.DataFile + +/** + * A specialisation of a DataFile, represents an Oxford Nanopore sample sheet log file + * + * @author: Andreas Friedrich + */ +class SampleSheetLog extends DataFile { + + final private static String FILE_TYPE = "csv" + + final private static String NAME_SCHEMA = $/sample_sheet_.*/$ + + protected SampleSheetLog() {} + + protected SampleSheetLog(String name, String relativePath) { + super(name, relativePath, FILE_TYPE) + validateName() + } + + static SampleSheetLog create(String name, String relativePath) { + return new SampleSheetLog(name, relativePath) + } + + private void validateName() { + if (!(this.name =~ NAME_SCHEMA)) { + throw new IllegalArgumentException("Name must match the Nanopore sample sheet log name schema!") + } + } + +} diff --git a/src/main/groovy/life/qbic/datamodel/datasets/datastructure/files/nanopore/SequencingTelemetryLog.groovy b/src/main/groovy/life/qbic/datamodel/datasets/datastructure/files/nanopore/SequencingTelemetryLog.groovy new file mode 100644 index 0000000000..2ae6d07cf5 --- /dev/null +++ b/src/main/groovy/life/qbic/datamodel/datasets/datastructure/files/nanopore/SequencingTelemetryLog.groovy @@ -0,0 +1,32 @@ +package life.qbic.datamodel.datasets.datastructure.files.nanopore + +import life.qbic.datamodel.datasets.datastructure.files.DataFile + +/** + * A specialisation of a DataFile, represents an Oxford Nanopore sequencing telemetry log file + * + */ +class SequencingTelemetryLog extends DataFile { + + final private static String FILE_TYPE = "js" + + final private static String NAME_SCHEMA = $/sequencing_telemetry_.*/$ + + protected SequencingTelemetryLog() {} + + protected SequencingTelemetryLog(String name, String relativePath) { + super(name, relativePath, FILE_TYPE) + validateName() + } + + static SequencingTelemetryLog create(String name, String relativePath) { + return new SequencingTelemetryLog(name, relativePath) + } + + private void validateName() { + if (!(this.name =~ NAME_SCHEMA)) { + throw new IllegalArgumentException("Name must match the Nanopore sequencing telemetry log name schema!") + } + } + +} diff --git a/src/main/groovy/life/qbic/datamodel/datasets/datastructure/files/nfcore/ExecutionReport.groovy b/src/main/groovy/life/qbic/datamodel/datasets/datastructure/files/nfcore/ExecutionReport.groovy index 911e187307..736d180ec7 100644 --- a/src/main/groovy/life/qbic/datamodel/datasets/datastructure/files/nfcore/ExecutionReport.groovy +++ b/src/main/groovy/life/qbic/datamodel/datasets/datastructure/files/nfcore/ExecutionReport.groovy @@ -9,9 +9,9 @@ import life.qbic.datamodel.datasets.datastructure.files.DataFile */ class ExecutionReport extends DataFile { - final private static String FILE_TYPE = "txt" + final private static String FILE_TYPE = "html" - final private static String NAME_SCHEMA = $/execution_report.*/$ + final private static String NAME_SCHEMA = $/^execution_report.*/$ protected ExecutionReport() {} diff --git a/src/main/groovy/life/qbic/datamodel/datasets/datastructure/files/nfcore/PipelineReport.groovy b/src/main/groovy/life/qbic/datamodel/datasets/datastructure/files/nfcore/PipelineReport.groovy deleted file mode 100644 index 3375ae323d..0000000000 --- a/src/main/groovy/life/qbic/datamodel/datasets/datastructure/files/nfcore/PipelineReport.groovy +++ /dev/null @@ -1,40 +0,0 @@ -package life.qbic.datamodel.datasets.datastructure.files.nfcore - -import life.qbic.datamodel.datasets.datastructure.files.DataFile - -/** - * A specialisation of a DataFile, represents a nf-core Pipeline report file - * - * @since 2.6.0 - */ -class PipelineReport extends DataFile { - - final private static String FILE_TYPE = "txt" - - final private static String NAME_SCHEMA = $/pipeline_report.*/$ - - protected PipelineReport() {} - - protected PipelineReport(String name, String relativePath) { - super(name, relativePath, FILE_TYPE) - validateName() - } - - /** - * Creates the PipelineReport object based on a report name and a relative path of it - * @param name The file name of the pipeline report - * @param relativePath The relative path to the file in a file system - * @return the PipelineReport object, which also validates if the filename is valid - * @since 2.6.0 - */ - static PipelineReport create(String name, String relativePath) { - return new PipelineReport(name, relativePath) - } - - private void validateName() { - if (!(this.name =~ NAME_SCHEMA)) { - throw new IllegalArgumentException("Name must match the nf-core pipeline report schema!") - } - } - -} diff --git a/src/main/groovy/life/qbic/datamodel/datasets/datastructure/files/nfcore/SoftwareVersions.groovy b/src/main/groovy/life/qbic/datamodel/datasets/datastructure/files/nfcore/SoftwareVersions.groovy index 14829c4199..f2608dd9c7 100644 --- a/src/main/groovy/life/qbic/datamodel/datasets/datastructure/files/nfcore/SoftwareVersions.groovy +++ b/src/main/groovy/life/qbic/datamodel/datasets/datastructure/files/nfcore/SoftwareVersions.groovy @@ -9,7 +9,7 @@ import life.qbic.datamodel.datasets.datastructure.files.DataFile */ class SoftwareVersions extends DataFile { - final private static String FILE_TYPE = "csv" + final private static String FILE_TYPE = "yml" final private static String NAME_SCHEMA = $/software_versions.*/$ diff --git a/src/main/groovy/life/qbic/datamodel/datasets/datastructure/folders/nanopore/BasecallingFolder.groovy b/src/main/groovy/life/qbic/datamodel/datasets/datastructure/folders/nanopore/BasecallingFolder.groovy new file mode 100644 index 0000000000..eba6e31afa --- /dev/null +++ b/src/main/groovy/life/qbic/datamodel/datasets/datastructure/folders/nanopore/BasecallingFolder.groovy @@ -0,0 +1,42 @@ +package life.qbic.datamodel.datasets.datastructure.folders.nanopore + +import life.qbic.datamodel.datasets.datastructure.folders.DataFolder + +/** + * + * + * + * + * @since + * + */ +class BasecallingFolder extends DataFolder { + /** + * The name schema of a basecalling folder contained within the nanopore dataset. + * + */ + final private static String NAME_SCHEMA = /basecalling/ + + protected BasecallingFolder() {} + + protected BasecallingFolder(String name, String relativePath, List children) { + super(name, relativePath, children) + validateName() + } + + /** + * Creates a new instance of a BasecallingFolder object + * @param relativePath The relative path of the folder + * @param children A list with child elements of unknown type of the folder + * @return A new instance of a BasecallingFolder object + */ + static BasecallingFolder create(String name, String relativePath, List children) { + new BasecallingFolder(name, relativePath, children) + } + + private void validateName() { + if (!(this.name =~ NAME_SCHEMA)) { + throw new IllegalArgumentException("Name must match the Nanopore Basecalling schema!") + } + } +} diff --git a/src/main/groovy/life/qbic/datamodel/datasets/datastructure/folders/nanopore/Fast5SkipFolder.groovy b/src/main/groovy/life/qbic/datamodel/datasets/datastructure/folders/nanopore/Fast5SkipFolder.groovy new file mode 100644 index 0000000000..9fe46e9cfb --- /dev/null +++ b/src/main/groovy/life/qbic/datamodel/datasets/datastructure/folders/nanopore/Fast5SkipFolder.groovy @@ -0,0 +1,38 @@ +package life.qbic.datamodel.datasets.datastructure.folders.nanopore + +import life.qbic.datamodel.datasets.datastructure.files.nanopore.Fast5File +import life.qbic.datamodel.datasets.datastructure.folders.DataFolder + +/** + * A special case of a DataFolder, its name is always fast5_skip. + * + * Its children field contains a list of type List + * + */ +class Fast5SkipFolder extends DataFolder { + + final private static String NAME_SCHEMA = /fast5_skip/ + + protected Fast5SkipFolder() {} + + protected Fast5SkipFolder(String name, String relativePath, List children) { + super(name, relativePath, children) + validateName() + } + + /** + * Creates a new instance of a Fast5SkipFolder object + * @param relativePath The relative path of the folder + * @param children A list with child elements of the folder + * @return A new instance of a Fast5SkipFolder object + */ + static Fast5SkipFolder create(String name, String relativePath, List children) { + return new Fast5SkipFolder(name, relativePath, children) + } + + private void validateName() { + if (!(this.name =~ NAME_SCHEMA)) { + throw new IllegalArgumentException("Name must match the Nanopore Fast5Skip directory schema!") + } + } +} diff --git a/src/main/groovy/life/qbic/datamodel/datasets/datastructure/folders/nanopore/OptionalFolder.groovy b/src/main/groovy/life/qbic/datamodel/datasets/datastructure/folders/nanopore/OptionalFolder.groovy new file mode 100644 index 0000000000..8cd7eae73b --- /dev/null +++ b/src/main/groovy/life/qbic/datamodel/datasets/datastructure/folders/nanopore/OptionalFolder.groovy @@ -0,0 +1,29 @@ +package life.qbic.datamodel.datasets.datastructure.folders.nanopore + +import life.qbic.datamodel.datasets.datastructure.folders.DataFolder + +/** + * Unspecific Optional Folder used to store unexpected folders in the nanopore registration + * + * The high variety of the nanopore datasets registered made an optional all-purpose Datafolder structure necessary. + * + */ +class OptionalFolder extends DataFolder { + + protected OptionalFolder() {} + + protected OptionalFolder(String name, String relativePath, List children) { + super(name, relativePath, children) + } + + /** + * Creates a new instance of a OptionalFolder object + * @param relativePath The relative path of the folder + * @param children A list with child elements of unknown type of the folder + * @return A new instance of a OptionalFolder object + */ + static OptionalFolder create(String name, String relativePath, List children) { + new OptionalFolder(name, relativePath, children) + } + +} diff --git a/src/main/groovy/life/qbic/datamodel/datasets/datastructure/folders/nanopore/Pod5FailFolder.groovy b/src/main/groovy/life/qbic/datamodel/datasets/datastructure/folders/nanopore/Pod5FailFolder.groovy new file mode 100644 index 0000000000..b1cbdc9341 --- /dev/null +++ b/src/main/groovy/life/qbic/datamodel/datasets/datastructure/folders/nanopore/Pod5FailFolder.groovy @@ -0,0 +1,39 @@ +package life.qbic.datamodel.datasets.datastructure.folders.nanopore + +import life.qbic.datamodel.datasets.datastructure.folders.DataFolder + +/** + * A special case of a DataFolder, its name is always pod5_fail. + * + * Its children field contains either a list of type List or List + * + */ +class Pod5FailFolder extends DataFolder { + + final private static String NAME_SCHEMA = /pod5_fail/ + + protected Pod5FailFolder() {} + + protected Pod5FailFolder(String name, String relativePath, List children) { + super(name, relativePath, children) + validateName() + } + + /** + * Creates a new instance of a Pod5FailFolder object + * + * @param name The folder name + * @param relativePath The relative path of the folder + * @param children A list with child elements of the folder + * @return A new instance of a Pod5FailFolder object + */ + static Pod5FailFolder create(String name, String relativePath, List children) { + new Pod5FailFolder(name, relativePath, children) + } + + private void validateName() { + if (!(this.name =~ NAME_SCHEMA)) { + throw new IllegalArgumentException("Name must match the Nanopore Pod5Fail directory schema!") + } + } +} diff --git a/src/main/groovy/life/qbic/datamodel/datasets/datastructure/folders/nanopore/Pod5PassFolder.groovy b/src/main/groovy/life/qbic/datamodel/datasets/datastructure/folders/nanopore/Pod5PassFolder.groovy new file mode 100644 index 0000000000..5de6adedfb --- /dev/null +++ b/src/main/groovy/life/qbic/datamodel/datasets/datastructure/folders/nanopore/Pod5PassFolder.groovy @@ -0,0 +1,39 @@ +package life.qbic.datamodel.datasets.datastructure.folders.nanopore + +import life.qbic.datamodel.datasets.datastructure.folders.DataFolder + +/** + * A special case of a DataFolder, its name is always pod5_pass. + * + * Its children field contains either a list of type List or List + * + */ +class Pod5PassFolder extends DataFolder { + + final private static String NAME_SCHEMA = /pod5_pass/ + + protected Pod5PassFolder() {} + + protected Pod5PassFolder(String name, String relativePath, List children) { + super(name, relativePath, children) + validateName() + } + + /** + * Creates a new instance of a Pod5PassFolder object + * + * @param name The folder name + * @param relativePath The relative path of the folder + * @param children A list with child elements of the folder + * @return A new instance of a Pod5PassFolder object + */ + static Pod5PassFolder create(String name, String relativePath, List children) { + new Pod5PassFolder(name, relativePath, children) + } + + private void validateName() { + if (!(this.name =~ NAME_SCHEMA)) { + throw new IllegalArgumentException("Name must match the Nanopore Pod5Pass directory schema!") + } + } +} diff --git a/src/main/groovy/life/qbic/datamodel/datasets/datastructure/folders/nanopore/Pod5SkipFolder.groovy b/src/main/groovy/life/qbic/datamodel/datasets/datastructure/folders/nanopore/Pod5SkipFolder.groovy new file mode 100644 index 0000000000..fdd66ad0e3 --- /dev/null +++ b/src/main/groovy/life/qbic/datamodel/datasets/datastructure/folders/nanopore/Pod5SkipFolder.groovy @@ -0,0 +1,38 @@ +package life.qbic.datamodel.datasets.datastructure.folders.nanopore + +import life.qbic.datamodel.datasets.datastructure.files.nanopore.Pod5File +import life.qbic.datamodel.datasets.datastructure.folders.DataFolder + +/** + * A special case of a DataFolder, its name is always pod5_skip. + * + * Its children field contains a list of type List + * + */ +class Pod5SkipFolder extends DataFolder { + + final private static String NAME_SCHEMA = /pod5_skip/ + + protected Pod5SkipFolder() {} + + protected Pod5SkipFolder(String name, String relativePath, List children) { + super(name, relativePath, children) + validateName() + } + + /** + * Creates a new instance of a Pod5SkipFolder object + * @param relativePath The relative path of the folder + * @param children A list with child elements of the folder + * @return A new instance of a Pod5SkipFolder object + */ + static Pod5SkipFolder create(String name, String relativePath, List children) { + return new Pod5SkipFolder(name, relativePath, children) + } + + private void validateName() { + if (!(this.name =~ NAME_SCHEMA)) { + throw new IllegalArgumentException("Name must match the Nanopore Pod5Skip directory schema!") + } + } +} diff --git a/src/main/groovy/life/qbic/datamodel/datasets/datastructure/folders/nfcore/PipelineInformationFolder.groovy b/src/main/groovy/life/qbic/datamodel/datasets/datastructure/folders/nfcore/PipelineInformationFolder.groovy index 8f6509fc7d..02fe748c42 100644 --- a/src/main/groovy/life/qbic/datamodel/datasets/datastructure/folders/nfcore/PipelineInformationFolder.groovy +++ b/src/main/groovy/life/qbic/datamodel/datasets/datastructure/folders/nfcore/PipelineInformationFolder.groovy @@ -2,7 +2,6 @@ package life.qbic.datamodel.datasets.datastructure.folders.nfcore import life.qbic.datamodel.datasets.datastructure.files.DataFile import life.qbic.datamodel.datasets.datastructure.files.nfcore.ExecutionReport -import life.qbic.datamodel.datasets.datastructure.files.nfcore.PipelineReport import life.qbic.datamodel.datasets.datastructure.files.nfcore.SoftwareVersions import life.qbic.datamodel.datasets.datastructure.folders.DataFolder @@ -19,8 +18,6 @@ class PipelineInformationFolder extends DataFolder { SoftwareVersions softwareVersions - PipelineReport pipelineReport - ExecutionReport executionReport protected PipelineInformationFolder() {} @@ -58,15 +55,6 @@ class PipelineInformationFolder extends DataFolder { return softwareVersions } - /** - * Provides access to the information stored in the runId file - * @return the pipeline report generated by nextflow - * @since 2.6.0 - */ - PipelineReport getPipelineReport() { - return pipelineReport - } - /** * Provides access to the information stored in the sampleIds file * @return the execution report diff --git a/src/main/groovy/life/qbic/datamodel/dtos/business/Affiliation.groovy b/src/main/groovy/life/qbic/datamodel/dtos/business/Affiliation.groovy index 95f840d115..1bf6d8ed4e 100644 --- a/src/main/groovy/life/qbic/datamodel/dtos/business/Affiliation.groovy +++ b/src/main/groovy/life/qbic/datamodel/dtos/business/Affiliation.groovy @@ -8,9 +8,16 @@ import groovy.transform.EqualsAndHashCode * @author Sven Fillinger * @since 1.11.0 */ -@EqualsAndHashCode +@EqualsAndHashCode(excludes = ["id"]) final class Affiliation { + /** + * The database id of an affiliation. + * + * For example "1" + */ + final int id + /** * The organisation label of an affiliation. * @@ -46,10 +53,10 @@ final class Affiliation { final String country /** - * An affiliation category @link{AffiliationCategory}. - * - * Defaults to 'external non-academic'. - */ + * An affiliation category @link{AffiliationCategory}. + * + * Defaults to 'external non-academic'. + */ final AffiliationCategory category /** @@ -59,8 +66,16 @@ final class Affiliation { */ final AffiliationLabel label + /** + * Boolean flag if an affiliation is active + * @since 2.23.0 + */ + final Boolean active + static class Builder { + int id + String organisation String addressAddition @@ -77,7 +92,10 @@ final class Affiliation { AffiliationLabel label + Boolean active + Builder(String organisation, String street, String postalCode, String city) { + this.id = id this.organisation = organisation this.street = street this.postalCode = postalCode @@ -86,6 +104,12 @@ final class Affiliation { this.country = "Germany" this.category = AffiliationCategory.EXTERNAL this.label = AffiliationLabel.MNF + this.active = Boolean.TRUE + } + + Builder id(int id) { + this.id = id + return this } /** @@ -113,6 +137,16 @@ final class Affiliation { return this } + Builder setInactive() { + this.active = Boolean.FALSE + return this + } + + Builder setActive() { + this.active = Boolean.TRUE + return this + } + Affiliation build() { return new Affiliation(this) @@ -121,6 +155,7 @@ final class Affiliation { } private Affiliation(Builder builder) { + this.id = builder.id this.addressAddition = builder.addressAddition this.organisation = builder.organisation this.street = builder.street @@ -129,6 +164,7 @@ final class Affiliation { this.category = builder.category this.city = builder.city this.label = builder.label + this.active = builder.active } @Override diff --git a/src/main/groovy/life/qbic/datamodel/dtos/business/Offer.groovy b/src/main/groovy/life/qbic/datamodel/dtos/business/Offer.groovy index 6ca9075bc4..e6289c00f3 100644 --- a/src/main/groovy/life/qbic/datamodel/dtos/business/Offer.groovy +++ b/src/main/groovy/life/qbic/datamodel/dtos/business/Offer.groovy @@ -10,9 +10,15 @@ import life.qbic.datamodel.dtos.projectmanagement.ProjectIdentifier * * @since: 1.12.0 */ -@EqualsAndHashCode +@EqualsAndHashCode(excludes = ["id"]) class Offer { + + /** + * database id of an offer + */ + final int id + /** * The checksum of the offer */ @@ -111,6 +117,7 @@ class Offer { static class Builder { + int id /* Overall offer describing properties */ @@ -189,6 +196,11 @@ class Offer { this.projectDescription = Objects.requireNonNull(projectObjective, "Project Objective must not be null") } + Builder id(int id) { + this.id = id + return this + } + Builder checksum(String checksum) { this.checksum = checksum return this @@ -235,12 +247,12 @@ class Offer { } Builder itemsWithOverhead(List itemsWithOverhead) { - this.itemsWithOverhead= itemsWithOverhead + this.itemsWithOverhead = itemsWithOverhead return this } Builder itemsWithoutOverhead(List itemsWithoutOverhead) { - this.itemsWithoutOverhead= itemsWithoutOverhead + this.itemsWithoutOverhead = itemsWithoutOverhead return this } @@ -254,7 +266,7 @@ class Offer { return this } - Builder overheadRatio(double overheadRatio){ + Builder overheadRatio(double overheadRatio) { this.overheadRatio = overheadRatio return this } @@ -264,12 +276,12 @@ class Offer { return this } - Builder experimentalDesign(String experimentalDesign){ + Builder experimentalDesign(String experimentalDesign) { this.experimentalDesign = Optional.of(experimentalDesign) return this } - Builder totalDiscountPrice(double totalDiscountPrice){ + Builder totalDiscountPrice(double totalDiscountPrice) { this.totalDiscountPrice = totalDiscountPrice return this } @@ -280,6 +292,9 @@ class Offer { } private Offer(Builder builder) { + + this.id = builder.id + /* Offer Related Properties */ diff --git a/src/main/groovy/life/qbic/datamodel/dtos/business/ProductItem.groovy b/src/main/groovy/life/qbic/datamodel/dtos/business/ProductItem.groovy index d9a92fae11..e22298e873 100644 --- a/src/main/groovy/life/qbic/datamodel/dtos/business/ProductItem.groovy +++ b/src/main/groovy/life/qbic/datamodel/dtos/business/ProductItem.groovy @@ -10,9 +10,16 @@ import life.qbic.datamodel.dtos.business.services.Product * The unit price is always provided in euros. * @since: 1.9.0 */ -@EqualsAndHashCode +@EqualsAndHashCode(excludes = ["id"]) class ProductItem { + /** + * The database of an id of a ProductItem + * + * For example "1" + */ + final int id + /** * Describes the amount of a given item */ @@ -33,6 +40,13 @@ class ProductItem { */ final double quantityDiscount + /** + * Stores the latest position on the offer. + * + * A negative value indicates no specific position was stored in the item. + */ + private int offerPosition = -1 + /** * * @param quantity The quantity of a product @@ -55,6 +69,35 @@ class ProductItem { this.quantityDiscount = quantityDiscount } + ProductItem(int id, double quantity, Product product, double totalPrice, double quantityDiscount) { + this.id = id + this.quantity = quantity + this.product = product + this.totalPrice = totalPrice + this.quantityDiscount = quantityDiscount + } + + /** + * Sets the position information on the offer the item should be placed + * + * A negative value indicates no positional information. + * @param position a positive value >= 0 indicating a position on the offer + * @since 2.22.0 + */ + void setOrderPosition(int position) { + offerPosition = position + } + + /** + * The position on the offer. + * + * Is negative, if no positional information is available. + * @return + * @since 2.22.0 + */ + int offerPosition() { + return offerPosition + } } diff --git a/src/main/groovy/life/qbic/datamodel/dtos/business/ProjectApplication.groovy b/src/main/groovy/life/qbic/datamodel/dtos/business/ProjectApplication.groovy index 1e833681c4..a9bdb434ce 100644 --- a/src/main/groovy/life/qbic/datamodel/dtos/business/ProjectApplication.groovy +++ b/src/main/groovy/life/qbic/datamodel/dtos/business/ProjectApplication.groovy @@ -1,8 +1,6 @@ package life.qbic.datamodel.dtos.business -import life.qbic.datamodel.dtos.business.Customer -import life.qbic.datamodel.dtos.business.OfferId -import life.qbic.datamodel.dtos.business.ProjectManager +import groovy.transform.EqualsAndHashCode import life.qbic.datamodel.dtos.projectmanagement.ProjectCode import life.qbic.datamodel.dtos.projectmanagement.ProjectSpace @@ -19,6 +17,7 @@ import life.qbic.datamodel.dtos.projectmanagement.ProjectSpace * * @since 2.3.0 */ +@EqualsAndHashCode class ProjectApplication { /** diff --git a/src/main/groovy/life/qbic/datamodel/dtos/business/facilities/Facility.groovy b/src/main/groovy/life/qbic/datamodel/dtos/business/facilities/Facility.groovy index fb533ebb7a..b894b62b14 100644 --- a/src/main/groovy/life/qbic/datamodel/dtos/business/facilities/Facility.groovy +++ b/src/main/groovy/life/qbic/datamodel/dtos/business/facilities/Facility.groovy @@ -11,6 +11,7 @@ package life.qbic.datamodel.dtos.business.facilities *
  • MGM: Institute for Medical Microbiology and Hygiene
  • *
  • QBIC: Quantitative Biology Center
  • *
  • CFMB_PCT: Proteomics Facility Tübingen
  • + *
  • PCT: Proteome Center Tübingen/li> *
  • CEGAT: CeGaT GmbH
  • * * @@ -23,8 +24,13 @@ enum Facility { MGM("Institute for Medical Microbiology and Hygiene", "MGM"), QBIC("Quantitative Biology Center", "QBIC"), CFMB_PCT("Proteomics Facility Tübingen", "Proteomics Facility"), - CEGAT("CeGaT GmbH", "CeGaT GmbH") + PCT("Proteome Center Tübingen", "PCT"), + CEGAT("CeGaT GmbH", "CeGaT GmbH"), + METABOLOMICS("Metabolomics Facility Tübingen", "Metabolomics Facility"), + METABOLOMICS_BACTERIAL("Bacterial Metabolomics", "Bacterial Metabolomics"), + METABOLOMICS_FUNCTIONAL("Functional Metabolomics", "Functional Metabolomics") + private final String fullName private final String label diff --git a/src/main/groovy/life/qbic/datamodel/dtos/business/facilities/FacilityFactory.groovy b/src/main/groovy/life/qbic/datamodel/dtos/business/facilities/FacilityFactory.groovy index 82199a6ce5..f6e56fcda4 100644 --- a/src/main/groovy/life/qbic/datamodel/dtos/business/facilities/FacilityFactory.groovy +++ b/src/main/groovy/life/qbic/datamodel/dtos/business/facilities/FacilityFactory.groovy @@ -21,11 +21,20 @@ class FacilityFactory extends EnumFactory { */ @Override Facility getForString(String value) { - Facility desiredKey - desiredKey = Facility.values().find {it.fullName.equals(value.trim())} - if (!desiredKey) { - throw new IllegalArgumentException("Invalid value '$value' for ${Facility.getSimpleName()}") - } - return desiredKey + Optional matchingFullName = Arrays.stream(Facility.values()) + .filter(it -> it.fullName == value) + .findFirst() + Optional matchingLabel = Arrays.stream(Facility.values()) + .filter(it -> it.label == value) + .findFirst() + Optional matchingEnumValue = Arrays.stream(Facility.values()) + .filter(it -> it.name() == value) + .findFirst() + + return matchingFullName + .orElse( matchingLabel + .orElse(matchingEnumValue + .orElseThrow(() -> + new IllegalArgumentException("Invalid value '$value' for ${Facility.getSimpleName()}")))) } } diff --git a/src/main/groovy/life/qbic/datamodel/dtos/business/services/AtomicProduct.groovy b/src/main/groovy/life/qbic/datamodel/dtos/business/services/AtomicProduct.groovy index d32b9d9ac1..c13b6867ae 100644 --- a/src/main/groovy/life/qbic/datamodel/dtos/business/services/AtomicProduct.groovy +++ b/src/main/groovy/life/qbic/datamodel/dtos/business/services/AtomicProduct.groovy @@ -50,4 +50,24 @@ class AtomicProduct extends Product { AtomicProduct(String name, String description, double internalUnitPrice, double externalUnitPrice, ProductUnit unit, ProductId productId, Facility serviceProvider) { super(name, description, internalUnitPrice, externalUnitPrice, unit, productId, serviceProvider) } + + /** + * Basic product constructor with id. + * + * Checks that all passed arguments except id are not null. + * + * @paran id the id of the product + * @param name The name of the product. + * @param description The description of what the product is about. + * @param internalUnitPrice The price in € per unit for internal customers + * @param externalUnitPrice The price in € per unit for external customers + * @param unit The product unit + * @param productId The product identifier + * @param serviceProvider The facility providing the service product + * + * @since 2.17.0 + */ + AtomicProduct(int id, String name, String description, double internalUnitPrice, double externalUnitPrice, ProductUnit unit, ProductId productId, Facility serviceProvider) { + super(id, name, description, internalUnitPrice, externalUnitPrice, unit, productId, serviceProvider) + } } diff --git a/src/main/groovy/life/qbic/datamodel/dtos/business/services/DataStorage.groovy b/src/main/groovy/life/qbic/datamodel/dtos/business/services/DataStorage.groovy index cc937b81f9..927cb8ccc0 100644 --- a/src/main/groovy/life/qbic/datamodel/dtos/business/services/DataStorage.groovy +++ b/src/main/groovy/life/qbic/datamodel/dtos/business/services/DataStorage.groovy @@ -64,4 +64,25 @@ class DataStorage extends PartialProduct { DataStorage(String name, String description, double internalUnitPrice, double externalUnitPrice, ProductUnit unit, long runningNumber, Facility serviceProvider) { super(name, description, internalUnitPrice, externalUnitPrice, unit, new ProductId.Builder(ProductCategory.DATA_STORAGE.getAbbreviation(), runningNumber).build(), serviceProvider) } + + /** + * Basic product constructor. + * + * Checks that all passed arguments except id are not null. + * + * @param id The id of the product. + * @param name The name of the product. + * @param description The description of what the product is about. + * @param internalUnitPrice The price in € per unit for internal customers + * @param externalUnitPrice The price in € per unit for external customers + * @param unit The product unit + * @param runningNumber Number used in conjunction with {@link ProductCategory} to identify product + * + * @since 2.17.0 + */ + DataStorage(int id, String name, String description, double internalUnitPrice, double externalUnitPrice, ProductUnit unit, long runningNumber, Facility serviceProvider) { + super(id, name, description, internalUnitPrice, externalUnitPrice, unit, new ProductId.Builder(ProductCategory.DATA_STORAGE.getAbbreviation(), runningNumber).build(), serviceProvider) + } + + } diff --git a/src/main/groovy/life/qbic/datamodel/dtos/business/services/ExternalServiceProduct.groovy b/src/main/groovy/life/qbic/datamodel/dtos/business/services/ExternalServiceProduct.groovy index ae089d1f15..b17b90254e 100644 --- a/src/main/groovy/life/qbic/datamodel/dtos/business/services/ExternalServiceProduct.groovy +++ b/src/main/groovy/life/qbic/datamodel/dtos/business/services/ExternalServiceProduct.groovy @@ -28,4 +28,20 @@ class ExternalServiceProduct extends PartialProduct { super(name, description, internalUnitPrice, externalUnitPrice, unit, new ProductId.Builder( ProductCategory.EXTERNAL_SERVICE.getAbbreviation(), runningNumber).build(), serviceProvider) } + + /** + * Creates an instance of an {@link ExternalServiceProduct}. + * @param id the id of the product + * @param name The name of the product + * @param description A product description + * @param internalUnitPrice The net internal unit price of the product + * @param externalUnitPrice The net external unit price of the product + * @param unit The unit of the product + * @param productId A product id, uniquely identifying the product in the offer environment + * @param serviceProvider The service provider + */ + ExternalServiceProduct(int id, String name, String description, double internalUnitPrice, double externalUnitPrice, ProductUnit unit, long runningNumber, Facility serviceProvider) { + super(id, name, description, internalUnitPrice, externalUnitPrice, unit, new ProductId.Builder( + ProductCategory.EXTERNAL_SERVICE.getAbbreviation(), runningNumber).build(), serviceProvider) + } } diff --git a/src/main/groovy/life/qbic/datamodel/dtos/business/services/MetabolomicAnalysis.groovy b/src/main/groovy/life/qbic/datamodel/dtos/business/services/MetabolomicAnalysis.groovy index ca23f95678..a7d5ad0bc8 100644 --- a/src/main/groovy/life/qbic/datamodel/dtos/business/services/MetabolomicAnalysis.groovy +++ b/src/main/groovy/life/qbic/datamodel/dtos/business/services/MetabolomicAnalysis.groovy @@ -11,7 +11,7 @@ import life.qbic.datamodel.dtos.business.facilities.Facility * @since 2.4.0 */ @EqualsAndHashCode(callSuper = true) -class MetabolomicAnalysis extends AtomicProduct { +class MetabolomicAnalysis extends PartialProduct { /** * Basic product constructor. * @@ -65,5 +65,24 @@ class MetabolomicAnalysis extends AtomicProduct { MetabolomicAnalysis(String name, String description, double internalUnitPrice, double externalUnitPrice, ProductUnit unit, long runningNumber, Facility serviceProvider) { super(name, description, internalUnitPrice, externalUnitPrice, unit, new ProductId.Builder(ProductCategory.METABOLOMIC.getAbbreviation(), runningNumber).build(), serviceProvider) } -} + /** + * Basic product constructor with id. + * + * Checks that all passed arguments except id are not null. + * + * @paran id the id of the product. + * @param name The name of the product. + * @param description The description of what the product is about. + * @param internalUnitPrice The price in € per unit for internal customers + * @param externalUnitPrice The price in € per unit for external customers + * @param unit The product unit + * @param runningNumber Number used in conjunction with {@link ProductCategory} to identify product + * @param serviceProvider The facility providing the service product + * + * @since 2.17.0 + */ + MetabolomicAnalysis(int id, String name, String description, double internalUnitPrice, double externalUnitPrice, ProductUnit unit, long runningNumber, Facility serviceProvider) { + super(id, name, description, internalUnitPrice, externalUnitPrice, unit, new ProductId.Builder(ProductCategory.METABOLOMIC.getAbbreviation(), runningNumber).build(), serviceProvider) + } +} diff --git a/src/main/groovy/life/qbic/datamodel/dtos/business/services/PartialProduct.groovy b/src/main/groovy/life/qbic/datamodel/dtos/business/services/PartialProduct.groovy index 6aa73238ff..fb6f69c56a 100644 --- a/src/main/groovy/life/qbic/datamodel/dtos/business/services/PartialProduct.groovy +++ b/src/main/groovy/life/qbic/datamodel/dtos/business/services/PartialProduct.groovy @@ -51,4 +51,24 @@ class PartialProduct extends Product { PartialProduct(String name, String description, double internalUnitPrice, double externalUnitPrice, ProductUnit unit, ProductId productId, Facility serviceProvider) { super(name, description, internalUnitPrice, externalUnitPrice, unit, productId, serviceProvider) } + + /** + * Basic product constructor. + * + * Checks that all passed arguments except id are not null. + * + * @param id The id of the product. + * @param name The name of the product. + * @param description The description of what the product is about. + * @param internalUnitPrice The price in € per unit for internal customers + * @param externalUnitPrice The price in € per unit for external customers + * @param unit The product unit + * @param productId The product identifier + * @param serviceProvider The facility providing the service product + * + * @since 2.17.0 + */ + PartialProduct(int id, String name, String description, double internalUnitPrice, double externalUnitPrice, ProductUnit unit, ProductId productId, Facility serviceProvider) { + super(id, name, description, internalUnitPrice, externalUnitPrice, unit, productId, serviceProvider) + } } diff --git a/src/main/groovy/life/qbic/datamodel/dtos/business/services/PrimaryAnalysis.groovy b/src/main/groovy/life/qbic/datamodel/dtos/business/services/PrimaryAnalysis.groovy index 67badca36e..93211068db 100644 --- a/src/main/groovy/life/qbic/datamodel/dtos/business/services/PrimaryAnalysis.groovy +++ b/src/main/groovy/life/qbic/datamodel/dtos/business/services/PrimaryAnalysis.groovy @@ -11,7 +11,7 @@ import life.qbic.datamodel.dtos.business.facilities.Facility * @since 1.12.0 */ @EqualsAndHashCode(callSuper = true) -class PrimaryAnalysis extends AtomicProduct { +class PrimaryAnalysis extends PartialProduct { /** * Basic product constructor. * @@ -65,4 +65,24 @@ class PrimaryAnalysis extends AtomicProduct { PrimaryAnalysis(String name, String description, double internalUnitPrice, double externalUnitPrice, ProductUnit unit, long runningNumber, Facility serviceProvider) { super(name, description, internalUnitPrice, externalUnitPrice, unit, new ProductId.Builder(ProductCategory.PRIMARY_BIOINFO.getAbbreviation(), runningNumber).build(), serviceProvider) } + + /** + * Basic product constructor with id. + * + * Checks that all passed arguments except id are not null. + * + * @param id the id of the product + * @param name The name of the product. + * @param description The description of what the product is about. + * @param internalUnitPrice The price in € per unit for internal customers + * @param externalUnitPrice The price in € per unit for external customers + * @param unit The product unit + * @param runningNumber Number used in conjunction with {@link ProductCategory} to identify product + * @param serviceProvider The facility providing the service product + * + * @since 2.11.0 + */ + PrimaryAnalysis(int id, String name, String description, double internalUnitPrice, double externalUnitPrice, ProductUnit unit, long runningNumber, Facility serviceProvider) { + super(id, name, description, internalUnitPrice, externalUnitPrice, unit, new ProductId.Builder(ProductCategory.PRIMARY_BIOINFO.getAbbreviation(), runningNumber).build(), serviceProvider) + } } diff --git a/src/main/groovy/life/qbic/datamodel/dtos/business/services/Product.groovy b/src/main/groovy/life/qbic/datamodel/dtos/business/services/Product.groovy index 6595905457..c508cc9353 100644 --- a/src/main/groovy/life/qbic/datamodel/dtos/business/services/Product.groovy +++ b/src/main/groovy/life/qbic/datamodel/dtos/business/services/Product.groovy @@ -12,9 +12,16 @@ import life.qbic.datamodel.dtos.business.facilities.Facility * * @since 1.12.0 */ -@EqualsAndHashCode +@EqualsAndHashCode(excludes = ["id"]) abstract class Product { + /** + * The database id of a Product. + * + * For example "1" + */ + final int id + /** * Some text describing what the the product providing. */ @@ -128,4 +135,41 @@ abstract class Product { this.serviceProvider = Objects.requireNonNull(serviceProvider, "Service provider must not be null") } + /** + * Basic product constructor. + * + * Checks that all passed arguments except id are not null. + * + * @param id The id of the product. + * @param name The name of the product. + * @param description The description of what the product is about. + * @param internalUnitPrice The price in € per unit for internal customers + * @param externalUnitPrice The price in € per unit for external customers + * @param unit The product unit + * @param productId The Id of the product + * + * @since 2.17.0 + */ + + Product(int id, + String name, + String description, + double internalUnitPrice, + double externalUnitPrice, + ProductUnit unit, + ProductId productId, + Facility serviceProvider) { + this.id = id + this.productName = Objects.requireNonNull(name, "Name must not be null") + this.description = Objects.requireNonNull(description, "Description must not be null") + this.internalUnitPrice = Objects.requireNonNull(internalUnitPrice, "Internal unit price must not be null") + this.externalUnitPrice = Objects.requireNonNull(externalUnitPrice, "External unit price must not be null") + this.unitPrice = 0.00 + this.unit = Objects.requireNonNull(unit, "Unit must not be null") + this.productId = Objects.requireNonNull(productId, "ProductId must not be null") + //currency is on default in euro + this.currency = Currency.getInstance(Locale.GERMANY) + this.serviceProvider = Objects.requireNonNull(serviceProvider, "Service provider must not be null") + } + } diff --git a/src/main/groovy/life/qbic/datamodel/dtos/business/services/ProjectManagement.groovy b/src/main/groovy/life/qbic/datamodel/dtos/business/services/ProjectManagement.groovy index 76aed29824..9016263b07 100644 --- a/src/main/groovy/life/qbic/datamodel/dtos/business/services/ProjectManagement.groovy +++ b/src/main/groovy/life/qbic/datamodel/dtos/business/services/ProjectManagement.groovy @@ -65,4 +65,23 @@ class ProjectManagement extends PartialProduct { ProjectManagement(String name, String description, double internalUnitPrice, double externalUnitPrice, ProductUnit unit, long runningNumber, Facility serviceProvider) { super(name, description, internalUnitPrice, externalUnitPrice, unit, new ProductId.Builder(ProductCategory.PROJECT_MANAGEMENT.getAbbreviation(), runningNumber).build(), serviceProvider) } + + /** + * Basic product constructor with id + * + * Checks that all passed arguments except id are not null. + * @param id the id of the product + * @param name The name of the product. + * @param description The description of what the product is about. + * @param internalUnitPrice The price in € per unit for internal customers + * @param externalUnitPrice The price in € per unit for external customers + * @param unit The product unit + * @param runningNumber Number used in conjunction with {@link ProductCategory} to identify product + * @param serviceProvider The facility providing the service product + * + * @since 2.17.0 + */ + ProjectManagement(int id, String name, String description, double internalUnitPrice, double externalUnitPrice, ProductUnit unit, long runningNumber, Facility serviceProvider) { + super(id, name, description, internalUnitPrice, externalUnitPrice, unit, new ProductId.Builder(ProductCategory.PROJECT_MANAGEMENT.getAbbreviation(), runningNumber).build(), serviceProvider) + } } diff --git a/src/main/groovy/life/qbic/datamodel/dtos/business/services/ProteomicAnalysis.groovy b/src/main/groovy/life/qbic/datamodel/dtos/business/services/ProteomicAnalysis.groovy index 174bffce32..09093ac9fd 100644 --- a/src/main/groovy/life/qbic/datamodel/dtos/business/services/ProteomicAnalysis.groovy +++ b/src/main/groovy/life/qbic/datamodel/dtos/business/services/ProteomicAnalysis.groovy @@ -11,7 +11,7 @@ import life.qbic.datamodel.dtos.business.facilities.Facility * @since 2.4.0 */ @EqualsAndHashCode(callSuper = true) -class ProteomicAnalysis extends AtomicProduct { +class ProteomicAnalysis extends PartialProduct { /** * Basic product constructor. @@ -66,4 +66,24 @@ class ProteomicAnalysis extends AtomicProduct { ProteomicAnalysis(String name, String description, double internalUnitPrice, double externalUnitPrice, ProductUnit unit, long runningNumber, Facility serviceProvider) { super(name, description, internalUnitPrice, externalUnitPrice, unit, new ProductId.Builder(ProductCategory.PROTEOMIC.getAbbreviation(), runningNumber).build(), serviceProvider) } + + /** + * Basic product constructor with id. + * + * Checks that all passed arguments except id are not null. + * + * @param id the id of the product. + * @param name The name of the product. + * @param description The description of what the product is about. + * @param internalUnitPrice The price in € per unit for internal customers + * @param externalUnitPrice The price in € per unit for external customers + * @param unit The product unit + * @param runningNumber Number used in conjunction with {@link ProductCategory} to identify product + * @param serviceProvider The facility providing the service product + * + * @since 2.17.0 + */ + ProteomicAnalysis(int id, String name, String description, double internalUnitPrice, double externalUnitPrice, ProductUnit unit, long runningNumber, Facility serviceProvider) { + super(id, name, description, internalUnitPrice, externalUnitPrice, unit, new ProductId.Builder(ProductCategory.PROTEOMIC.getAbbreviation(), runningNumber).build(), serviceProvider) + } } diff --git a/src/main/groovy/life/qbic/datamodel/dtos/business/services/SecondaryAnalysis.groovy b/src/main/groovy/life/qbic/datamodel/dtos/business/services/SecondaryAnalysis.groovy index 9a69ea244d..a5b8fb5289 100644 --- a/src/main/groovy/life/qbic/datamodel/dtos/business/services/SecondaryAnalysis.groovy +++ b/src/main/groovy/life/qbic/datamodel/dtos/business/services/SecondaryAnalysis.groovy @@ -12,7 +12,7 @@ import life.qbic.datamodel.dtos.business.facilities.Facility * @since 1.12.0 */ @EqualsAndHashCode(callSuper = true) -class SecondaryAnalysis extends AtomicProduct { +class SecondaryAnalysis extends PartialProduct { /** * Basic product constructor. * @@ -65,4 +65,23 @@ class SecondaryAnalysis extends AtomicProduct { SecondaryAnalysis(String name, String description, double internalUnitPrice, double externalUnitPrice, ProductUnit unit, long runningNumber, Facility serviceProvider) { super(name, description, internalUnitPrice, externalUnitPrice, unit, new ProductId.Builder(ProductCategory.SECONDARY_BIOINFO.getAbbreviation(), runningNumber).build(), serviceProvider) } + + /** + * Basic product constructor with id. + * + * Checks that all passed arguments except id are not null. + * + * @param id The id of the product. + * @param name The name of the product. + * @param description The description of what the product is about. + * @param internalUnitPrice The price in € per unit for internal customers + * @param externalUnitPrice The price in € per unit for external customers + * @param unit The product unit + * @param runningNumber Number used in conjunction with {@link ProductCategory} to identify product + * + * @since 2.17.0 + */ + SecondaryAnalysis(int id, String name, String description, double internalUnitPrice, double externalUnitPrice, ProductUnit unit, long runningNumber, Facility serviceProvider) { + super(id, name, description, internalUnitPrice, externalUnitPrice, unit, new ProductId.Builder(ProductCategory.SECONDARY_BIOINFO.getAbbreviation(), runningNumber).build(), serviceProvider) + } } diff --git a/src/main/groovy/life/qbic/datamodel/dtos/business/services/Sequencing.groovy b/src/main/groovy/life/qbic/datamodel/dtos/business/services/Sequencing.groovy index 7d41f2e149..377171609d 100644 --- a/src/main/groovy/life/qbic/datamodel/dtos/business/services/Sequencing.groovy +++ b/src/main/groovy/life/qbic/datamodel/dtos/business/services/Sequencing.groovy @@ -11,7 +11,7 @@ import life.qbic.datamodel.dtos.business.facilities.Facility * @since 1.12.0 */ @EqualsAndHashCode(callSuper = true) -class Sequencing extends AtomicProduct { +class Sequencing extends PartialProduct { /** * Basic product constructor. * @@ -65,4 +65,24 @@ class Sequencing extends AtomicProduct { Sequencing(String name, String description, double internalUnitPrice, double externalUnitPrice, ProductUnit unit, long runningNumber, Facility serviceProvider) { super(name, description, internalUnitPrice, externalUnitPrice, unit, new ProductId.Builder(ProductCategory.SEQUENCING.getAbbreviation(), runningNumber).build(), serviceProvider) } + + /** + * Basic product constructor with id. + * + * Checks that all passed arguments are not null. + * + * @param id the id of the product. + * @param name The name of the product. + * @param description The description of what the product is about. + * @param internalUnitPrice The price in € per unit for internal customers + * @param externalUnitPrice The price in € per unit for external customers + * @param unit The product unit + * @param runningNumber Number used in conjunction with {@link ProductCategory} to identify product + * @param serviceProvider The facility providing the service product + * + * @since 2.17.0 + */ + Sequencing(int id, String name, String description, double internalUnitPrice, double externalUnitPrice, ProductUnit unit, long runningNumber, Facility serviceProvider) { + super(id, name, description, internalUnitPrice, externalUnitPrice, unit, new ProductId.Builder(ProductCategory.SEQUENCING.getAbbreviation(), runningNumber).build(), serviceProvider) + } } diff --git a/src/main/groovy/life/qbic/datamodel/dtos/general/CommonPerson.groovy b/src/main/groovy/life/qbic/datamodel/dtos/general/CommonPerson.groovy index 7ccd235b05..aea1c298f6 100644 --- a/src/main/groovy/life/qbic/datamodel/dtos/general/CommonPerson.groovy +++ b/src/main/groovy/life/qbic/datamodel/dtos/general/CommonPerson.groovy @@ -1,5 +1,7 @@ package life.qbic.datamodel.dtos.general +import groovy.transform.EqualsAndHashCode + /** * A person without a specific context. * @@ -7,6 +9,7 @@ package life.qbic.datamodel.dtos.general * * @since 1.12.0 */ +@EqualsAndHashCode(callSuper = true) class CommonPerson extends Person { static class Builder extends Person.Builder { diff --git a/src/main/groovy/life/qbic/datamodel/dtos/general/Person.groovy b/src/main/groovy/life/qbic/datamodel/dtos/general/Person.groovy index 5f3ae0baf8..6db10ec838 100644 --- a/src/main/groovy/life/qbic/datamodel/dtos/general/Person.groovy +++ b/src/main/groovy/life/qbic/datamodel/dtos/general/Person.groovy @@ -10,7 +10,7 @@ import life.qbic.datamodel.dtos.business.Affiliation * @author Sven Fillinger * @since 1.11.0 */ -@EqualsAndHashCode +@EqualsAndHashCode(excludes = ["id"]) abstract class Person { /** @@ -20,6 +20,19 @@ abstract class Person { */ final String personType + /** + * The database id of a person. + * + * For example "1" + */ + final int id + + /** + * Unique entity identifier (UUID) + * @since 2.22.0 + */ + final String referenceId + /** * The person's first name */ @@ -46,6 +59,9 @@ abstract class Person { final List affiliations abstract static class Builder> { + + int id + String firstName String lastName @@ -56,12 +72,20 @@ abstract class Person { List affiliations + String referenceId + Builder(String firstName, String lastName, String emailAddress) { this.firstName = Objects.requireNonNull(firstName, "First name must not be null") this.lastName = Objects.requireNonNull(lastName, "Last name must not be null") this.emailAddress = Objects.requireNonNull(emailAddress, "Email must not be null") this.title = AcademicTitle.NONE this.affiliations = new ArrayList<>() + this.referenceId = UUID.randomUUID().toString() + } + + T id(int id) { + this.id = id + return self() } T title(AcademicTitle title) { @@ -79,6 +103,11 @@ abstract class Person { return self() } + T referenceId(UUID id) { + this.referenceId = id.toString() + return self() + } + abstract Person build() /** @@ -90,11 +119,13 @@ abstract class Person { } Person(Builder builder) { + id = builder.id firstName = builder.firstName lastName = builder.lastName emailAddress = builder.emailAddress title = builder.title affiliations = builder.affiliations + referenceId = builder.referenceId } /** diff --git a/src/main/groovy/life/qbic/datamodel/dtos/projectmanagement/Project.groovy b/src/main/groovy/life/qbic/datamodel/dtos/projectmanagement/Project.groovy index 92a760ca27..7e378e004f 100644 --- a/src/main/groovy/life/qbic/datamodel/dtos/projectmanagement/Project.groovy +++ b/src/main/groovy/life/qbic/datamodel/dtos/projectmanagement/Project.groovy @@ -11,9 +11,16 @@ import life.qbic.datamodel.dtos.business.OfferId * * @since 2.3.0 */ -@EqualsAndHashCode +@EqualsAndHashCode(excludes = ["id"]) class Project { + /** + * The database id of a project + * + * For example "1" + */ + final int id + /** * A short but descriptive project title */ @@ -30,12 +37,14 @@ class Project { final OfferId linkedOffer private Project(Builder builder) { + this.id = builder.id this.projectId = Objects.requireNonNull(builder.projectIdentifier) this.projectTitle = Objects.requireNonNull(builder.projectTitle) this.linkedOffer = builder.linkedOfferId } static class Builder { + private int id private ProjectIdentifier projectIdentifier private String projectTitle private OfferId linkedOfferId @@ -46,6 +55,11 @@ class Project { this.linkedOfferId = null } + Builder id(int id) { + this.id = id + return this + } + Builder projectIdentifier(ProjectIdentifier projectIdentifier) { this.projectIdentifier = projectIdentifier return this diff --git a/src/main/groovy/life/qbic/datamodel/dtos/projectmanagement/ProjectIdentifier.groovy b/src/main/groovy/life/qbic/datamodel/dtos/projectmanagement/ProjectIdentifier.groovy index 47087d1bfb..164ba98452 100644 --- a/src/main/groovy/life/qbic/datamodel/dtos/projectmanagement/ProjectIdentifier.groovy +++ b/src/main/groovy/life/qbic/datamodel/dtos/projectmanagement/ProjectIdentifier.groovy @@ -1,5 +1,7 @@ package life.qbic.datamodel.dtos.projectmanagement +import groovy.transform.EqualsAndHashCode + /** * Global project identifier for QBiC projects * @@ -7,6 +9,7 @@ package life.qbic.datamodel.dtos.projectmanagement * * @since 2.3.0 */ +@EqualsAndHashCode class ProjectIdentifier { /** diff --git a/src/main/groovy/life/qbic/datamodel/dtos/projectmanagement/ProjectSpace.groovy b/src/main/groovy/life/qbic/datamodel/dtos/projectmanagement/ProjectSpace.groovy index 934f3d5a95..7e3e8855c5 100644 --- a/src/main/groovy/life/qbic/datamodel/dtos/projectmanagement/ProjectSpace.groovy +++ b/src/main/groovy/life/qbic/datamodel/dtos/projectmanagement/ProjectSpace.groovy @@ -44,7 +44,6 @@ final class ProjectSpace { private static String formatSpaceName(String name) { def capitalizedName = name.trim().toUpperCase() def refactoredName = capitalizedName.replaceAll("\\s+", "_") - .replaceAll("-", "_") return refactoredName } diff --git a/src/main/groovy/life/qbic/datamodel/instruments/OxfordNanoporeInstrumentOutputDoradoMinimal.groovy b/src/main/groovy/life/qbic/datamodel/instruments/OxfordNanoporeInstrumentOutputDoradoMinimal.groovy new file mode 100644 index 0000000000..fcee2f0e86 --- /dev/null +++ b/src/main/groovy/life/qbic/datamodel/instruments/OxfordNanoporeInstrumentOutputDoradoMinimal.groovy @@ -0,0 +1,19 @@ +package life.qbic.datamodel.instruments + + +/** + * Represents the Nanopore instrument output data structure schema generated by employing the dorado basecaller with Pod5Files. + * + * The original schema is defined in as resource and is + * referenced here, wrapped in a Groovy class for reference + * in applications that want to validate the instrument + * output structure against the schema. + */ +class OxfordNanoporeInstrumentOutputDoradoMinimal { + + private static final String SCHEMA_PATH = "/schemas/nanopore-instrument-output_minimal_dorado.schema.json" + + static InputStream getSchemaAsStream() { + return OxfordNanoporeInstrumentOutputDoradoMinimal.getResourceAsStream(SCHEMA_PATH) + } +} diff --git a/src/main/groovy/life/qbic/datamodel/instruments/OxfordNanoporeInstrumentOutputMinimal.groovy b/src/main/groovy/life/qbic/datamodel/instruments/OxfordNanoporeInstrumentOutputMinimal.groovy new file mode 100644 index 0000000000..6420b7c307 --- /dev/null +++ b/src/main/groovy/life/qbic/datamodel/instruments/OxfordNanoporeInstrumentOutputMinimal.groovy @@ -0,0 +1,19 @@ +package life.qbic.datamodel.instruments + + +/** + * Represents the Nanopore instrument output data structure schema. + * + * The original schema is defined in as resource and is + * referenced here, wrapped in a Groovy class for reference + * in applications that want to validate the instrument + * output structure against the schema. + */ +class OxfordNanoporeInstrumentOutputMinimal { + + private static final String SCHEMA_PATH = "/schemas/nanopore-instrument-output_minimal.schema.json" + + static InputStream getSchemaAsStream() { + return OxfordNanoporeInstrumentOutputMinimal.getResourceAsStream(SCHEMA_PATH) + } +} diff --git a/src/main/groovy/life/qbic/datamodel/instruments/OxfordNanoporeInstrumentOutputV3.groovy b/src/main/groovy/life/qbic/datamodel/instruments/OxfordNanoporeInstrumentOutputV3.groovy new file mode 100644 index 0000000000..1cf1192e8d --- /dev/null +++ b/src/main/groovy/life/qbic/datamodel/instruments/OxfordNanoporeInstrumentOutputV3.groovy @@ -0,0 +1,22 @@ +package life.qbic.datamodel.instruments + + +/** + * Represents the Nanopore instrument output data structure schema. + * + * The original schema is defined in as resource and is + * referenced here, wrapped in a Groovy class for reference + * in applications that want to validate the instrument + * output structure against the schema. + * + * @author Sven Fillinger + * @since 1.9.0 + */ +class OxfordNanoporeInstrumentOutputV3 { + + private static final String SCHEMA_PATH = "/schemas/nanopore-instrument-output_v3.schema.json" + + static InputStream getSchemaAsStream() { + return OxfordNanoporeInstrumentOutputV3.getResourceAsStream(SCHEMA_PATH) + } +} diff --git a/src/main/groovy/life/qbic/datamodel/instruments/OxfordNanoporeInstrumentOutputV4.groovy b/src/main/groovy/life/qbic/datamodel/instruments/OxfordNanoporeInstrumentOutputV4.groovy new file mode 100644 index 0000000000..14f4d5050e --- /dev/null +++ b/src/main/groovy/life/qbic/datamodel/instruments/OxfordNanoporeInstrumentOutputV4.groovy @@ -0,0 +1,22 @@ +package life.qbic.datamodel.instruments + + +/** + * Represents the Nanopore instrument output data structure schema. + * + * The original schema is defined in as resource and is + * referenced here, wrapped in a Groovy class for reference + * in applications that want to validate the instrument + * output structure against the schema. + * + * @author Steffen Greiner + * @since 1.9.0 + */ +class OxfordNanoporeInstrumentOutputV4 { + + private static final String SCHEMA_PATH = "/schemas/nanopore-instrument-output_v4.schema.json" + + static InputStream getSchemaAsStream() { + return OxfordNanoporeInstrumentOutputV4.getResourceAsStream(SCHEMA_PATH) + } +} diff --git a/src/main/java/life/qbic/datamodel/experiments/ExperimentType.java b/src/main/java/life/qbic/datamodel/experiments/ExperimentType.java index 7a7988ce51..540fd51ba1 100644 --- a/src/main/java/life/qbic/datamodel/experiments/ExperimentType.java +++ b/src/main/java/life/qbic/datamodel/experiments/ExperimentType.java @@ -22,5 +22,5 @@ * */ public enum ExperimentType { - Q_WF_NGS_QUALITYCONTROL, Q_BMI_GENERIC_IMAGING, Q_WF_MS_QUALITYCONTROL, Q__WF_NGS_MAPPING, Q_WF_MS_MAXQUANT, Q_NGS_VARIANT_CALLING, Q_NGS_MEASUREMENT, Q_NGS_MAPPING, Q_NGS_IMMUNE_MONITORING, Q_NGS_HLATYPING, Q_NGS_FLOWCELL_RUN, Q_NGS_EPITOPE_PREDICTION, Q_WF_NGS_EPITOPE_PREDICTION, Q_WF_NGS_VARIANT_ANNOTATION, Q_WF_NGS_HLATYPING, Q_EXPERIMENTAL_DESIGN, Q_SAMPLE_EXTRACTION, Q_SAMPLE_PREPARATION, Q_MHC_LIGAND_EXTRACTION, Q_MS_MEASUREMENT, Q_NGS_SINGLE_SAMPLE_RUN, Q_PROJECT_DETAILS, Q_MICROARRAY_MEASUREMENT + Q_WF_NGS_QUALITYCONTROL, Q_BMI_GENERIC_IMAGING, Q_WF_MS_QUALITYCONTROL, Q__WF_NGS_MAPPING, Q_WF_MS_MAXQUANT, Q_NGS_VARIANT_CALLING, Q_NGS_MEASUREMENT, Q_NGS_MAPPING, Q_NGS_IMMUNE_MONITORING, Q_NGS_HLATYPING, Q_NGS_FLOWCELL_RUN, Q_NGS_EPITOPE_PREDICTION, Q_WF_NGS_EPITOPE_PREDICTION, Q_WF_NGS_VARIANT_ANNOTATION, Q_WF_NGS_HLATYPING, Q_EXPERIMENTAL_DESIGN, Q_SAMPLE_EXTRACTION, Q_SAMPLE_PREPARATION, Q_MHC_LIGAND_EXTRACTION, Q_MS_MEASUREMENT, Q_NGS_SINGLE_SAMPLE_RUN, Q_PROJECT_DETAILS, Q_MICROARRAY_MEASUREMENT, Q_DIGIWEST_MEASUREMENT } diff --git a/src/main/java/life/qbic/datamodel/samples/SampleType.java b/src/main/java/life/qbic/datamodel/samples/SampleType.java index 2da4bbbb2a..6b5c6fa807 100644 --- a/src/main/java/life/qbic/datamodel/samples/SampleType.java +++ b/src/main/java/life/qbic/datamodel/samples/SampleType.java @@ -22,6 +22,6 @@ * */ public enum SampleType { - Q_ATTACHMENT_SAMPLE, Q_BIOLOGICAL_ENTITY, Q_BIOLOGICAL_SAMPLE, Q_BMI_GENERIC_IMAGING_RUN, Q_EDDA_BENCHMARK, Q_EXT_MS_QUALITYCONTROL_RUN, Q_EXT_NGS_QUALITYCONTROL_RUN, Q_FASTA, Q_HT_QPCR_RUN, Q_MHC_LIGAND_EXTRACT, Q_MICROARRAY_RUN, Q_MS_RUN, Q_NGS_EPITOPES, Q_NGS_FLOWCELL_RUN, Q_NGS_HLATYPING, Q_NGS_IMMUNE_MONITORING, Q_NGS_IONTORRENT_RUN, Q_NGS_MAPPING, Q_NGS_MTB_DIAGNOSIS_RUN, Q_NGS_READ_MATCH_ALIGNMENT_RUN, Q_NGS_SINGLE_SAMPLE_RUN, Q_NGS_VARIANT_CALLING, Q_TEST_SAMPLE, Q_VACCINE_CONSTRUCT, Q_WF_MA_QUALITYCONTROL_RUN, Q_WF_MS_INDIVIDUALIZED_PROTEOME_RUN, Q_WF_MS_LIGANDOMICS_ID_RUN, Q_WF_MS_LIGANDOMICS_QC_RUN, Q_WF_MS_MAXQUANT_RUN, Q_WF_MS_PEPTIDEID_RUN, Q_WF_MS_QUALITYCONTROL_RUN, Q_WF_NGS_16S_TAXONOMIC_PROFILING, Q_WF_NGS_EPITOPE_PREDICTION_RUN, Q_WF_NGS_HLATYPING_RUN, Q_WF_NGS_MAPPING_RUN, Q_WF_NGS_QUALITYCONTROL_RUN, Q_WF_NGS_RNA_EXPRESSION_ANALYSIS_RUN, Q_WF_NGS_SHRNA_COUNTING_RUN, Q_WF_NGS_VARIANT_ANNOTATION_RUN, Q_WF_NGS_VARIANT_CALLING_RUN + Q_ATTACHMENT_SAMPLE, Q_BIOLOGICAL_ENTITY, Q_BIOLOGICAL_SAMPLE, Q_BMI_GENERIC_IMAGING_RUN, Q_EDDA_BENCHMARK, Q_EXT_MS_QUALITYCONTROL_RUN, Q_EXT_NGS_QUALITYCONTROL_RUN, Q_FASTA, Q_HT_QPCR_RUN, Q_MHC_LIGAND_EXTRACT, Q_MICROARRAY_RUN, Q_MS_RUN, Q_NGS_EPITOPES, Q_NGS_FLOWCELL_RUN, Q_NGS_HLATYPING, Q_NGS_IMMUNE_MONITORING, Q_NGS_IONTORRENT_RUN, Q_NGS_MAPPING, Q_NGS_MTB_DIAGNOSIS_RUN, Q_NGS_READ_MATCH_ALIGNMENT_RUN, Q_NGS_SINGLE_SAMPLE_RUN, Q_NGS_VARIANT_CALLING, Q_TEST_SAMPLE, Q_VACCINE_CONSTRUCT, Q_WF_MA_QUALITYCONTROL_RUN, Q_WF_MS_INDIVIDUALIZED_PROTEOME_RUN, Q_WF_MS_LIGANDOMICS_ID_RUN, Q_WF_MS_LIGANDOMICS_QC_RUN, Q_WF_MS_MAXQUANT_RUN, Q_WF_MS_PEPTIDEID_RUN, Q_WF_MS_QUALITYCONTROL_RUN, Q_WF_NGS_16S_TAXONOMIC_PROFILING, Q_WF_NGS_EPITOPE_PREDICTION_RUN, Q_WF_NGS_HLATYPING_RUN, Q_WF_NGS_MAPPING_RUN, Q_WF_NGS_QUALITYCONTROL_RUN, Q_WF_NGS_RNA_EXPRESSION_ANALYSIS_RUN, Q_WF_NGS_SHRNA_COUNTING_RUN, Q_WF_NGS_VARIANT_ANNOTATION_RUN, Q_WF_NGS_VARIANT_CALLING_RUN, Q_DIGIWEST_RUN } diff --git a/src/main/resources/schemas/bioinformatics-analysis-result-set.schema.json b/src/main/resources/schemas/bioinformatics-analysis-result-set.schema.json index 391dea4341..96b7b6113e 100644 --- a/src/main/resources/schemas/bioinformatics-analysis-result-set.schema.json +++ b/src/main/resources/schemas/bioinformatics-analysis-result-set.schema.json @@ -14,12 +14,10 @@ "properties": { "name": { "pattern": "pipeline_info"}, "softwareVersions": { "$ref": "#/definitions/softwareVersions" }, - "pipelineReport": { "$ref": "#/definitions/pipelineReport" }, "executionReport": { "$ref": "#/definitions/executionReport" } }, "required": [ "softwareVersions", - "pipelineReport", "executionReport" ] } @@ -34,21 +32,7 @@ { "properties": { "name": { "pattern": "software_versions"}, - "fileType": { "pattern": "csv" } - } - } - ] - }, - "pipelineReport": { - "description": "A data file that describes the pipeline configuration.", - "allOf": [ - { - "$ref": "data-structure-commons.json#/definitions/dataFile" - }, - { - "properties": { - "name": { "pattern": "pipeline_report"}, - "fileType": { "pattern": "txt" } + "fileType": { "pattern": "yml" } } } ] @@ -62,7 +46,7 @@ { "properties": { "name": { "pattern": "execution_report"}, - "fileType": { "pattern": "txt" } + "fileType": { "pattern": "html" } } } ] @@ -131,7 +115,6 @@ "pipelineInformation", "qualityControl", "processFolders", - "sampleIds", - "runId" + "sampleIds" ] } diff --git a/src/main/resources/schemas/maxquant-result-set.schema.json b/src/main/resources/schemas/maxquant-result-set.schema.json index 36bde0bf84..97e9c0ff45 100644 --- a/src/main/resources/schemas/maxquant-result-set.schema.json +++ b/src/main/resources/schemas/maxquant-result-set.schema.json @@ -115,7 +115,7 @@ { "properties": { "name": { - "pattern": "sample_ids" + "pattern": "Q\\w{4}_sample_ids" }, "fileType": { "pattern": "txt" @@ -143,23 +143,19 @@ "properties": { "allPeptides": {"$ref": "#/definitions/allPeptides"}, "evidence": {"$ref": "#/definitions/evidence"}, - "experimentalDesignTemplate": {"$ref": "#/definitions/experimentalDesignTemplate"}, "parameters": {"$ref": "#/definitions/parameters"}, "peptides": {"$ref": "#/definitions/peptides"}, "proteinGroups": {"$ref": "#/definitions/proteinGroups"}, "runParameters": {"$ref": "#/definitions/runParameters"}, "sampleIds": {"$ref": "#/definitions/sampleIds"}, - "summary": {"$ref": "#/definitions/summary"} }, "required": [ "allPeptides", "evidence", - "experimentalDesignTemplate", "parameters", "peptides", "proteinGroups", "runParameters", - "sampleIds", - "summary" + "sampleIds" ] } diff --git a/src/main/resources/schemas/nanopore-instrument-output.schema.json b/src/main/resources/schemas/nanopore-instrument-output.schema.json index b9d83e14dd..72aa2ac3b7 100644 --- a/src/main/resources/schemas/nanopore-instrument-output.schema.json +++ b/src/main/resources/schemas/nanopore-instrument-output.schema.json @@ -2,7 +2,7 @@ "$schema": "http://json-schema.org/draft-07/schema", "$id": "http://qbic.life/nanopore-instrument-output.schema.json", "title": "Nanopore Instrument Output", - "description": "Describes in which form Nanopore data is received from the lab.", + "description": "Describes in which form PromethION/MinION sequenced Nanopore is received from the Microbiology lab.", "definitions": { "folder": { "description": "Describes a folder", @@ -402,6 +402,9 @@ { "$ref": "#/definitions/report_pdf_log" }, + { + "$ref": "#/definitions/report_html_log" + }, { "$ref": "#/definitions/sequencing_summary_log" }, @@ -517,6 +520,23 @@ } ] }, + "report_html_log": { + "allOf": [ + { + "$ref": "#/definitions/file" + }, + { + "properties": { + "name": { + "pattern": "report_.*" + }, + "file_type": { + "pattern": "html" + } + } + } + ] + }, "sequencing_summary_log": { "allOf": [ { diff --git a/src/main/resources/schemas/nanopore-instrument-output_minimal.schema.json b/src/main/resources/schemas/nanopore-instrument-output_minimal.schema.json new file mode 100644 index 0000000000..1f345cf04f --- /dev/null +++ b/src/main/resources/schemas/nanopore-instrument-output_minimal.schema.json @@ -0,0 +1,484 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "http://qbic.life/nanopore-instrument-output_minimal.schema.json", + "title": "Nanopore Instrument Output minimal", + "description": "Describes in which form PromethION/MinION sequenced Nanopore data is received from the medical genetics lab. To be used if no other schema fits the description and ensure that the minimal necessary files are provided", + "definitions": { + "folder": { + "description": "Describes a folder", + "type": "object", + "required": [ + "name", + "path", + "children" + ], + "properties": { + "name": { + "description": "Folder name", + "type": "string", + "minLength": 1 + }, + "path": { + "description": "relative folderpath", + "type": "string", + "minLength": 1 + }, + "children": { + "description": "Describes files and/or sub-folders if existent", + "type": "array", + "items": { + "oneOf": [ + { + "$ref": "#/definitions/folder" + }, + { + "$ref": "#/definitions/file" + } + ] + } + } + } + }, + "file": { + "description": "Describes a file", + "type": "object", + "required": [ + "name", + "path", + "file_type" + ], + "properties": { + "name": { + "type": "string", + "minLength": 1 + }, + "path": { + "type": "string", + "minLength": 1 + }, + "file_type": { + "type": "string", + "minLength": 1 + } + } + }, + "qbic_code": { + "description": "Describes a QBiC code used as a prefix", + "type": "string", + "pattern": "Q\\w{4}\\d{3}[A-X][A-X0-9].*" + }, + "barcoded_folder": { + "description": "folder starting with qbic barcode prefix", + "allOf": [ + { + "$ref": "#/definitions/folder" + }, + { + "properties": { + "name": { + "$ref": "#/definitions/qbic_code" + } + } + } + ] + }, + "fast5_file": { + "allOf": [ + { + "$ref": "#/definitions/file" + }, + { + "properties": { + "file_type": { + "pattern": "fast5" + } + } + } + ] + }, + "fastqgz_file": { + "allOf": [ + { + "$ref": "#/definitions/file" + }, + { + "properties": { + "file_type": { + "pattern": "fastq.gz" + } + } + } + ] + }, + "fastq_file": { + "allOf": [ + { + "$ref": "#/definitions/file" + }, + { + "properties": { + "file_type": { + "pattern": "fastq" + } + } + } + ] + }, + "unclassified_folder": { + "description": "folder containing unassigned read file(s)", + "allOf": [ + { + "$ref": "#/definitions/folder" + }, + { + "properties": { + "name": { + "pattern": "unclassified" + } + } + } + ] + }, + "fast5_unclassified_folder": { + "description": "folder containing fast5 data from a pooling experiment, that could not be assigned to one of the known samples", + "allOf": [ + { + "$ref": "#/definitions/unclassified_folder" + }, + { + "properties": { + "children": { + "items": { + "$ref": "#/definitions/fast5_file" + }, + "minItems": 0 + } + } + } + ] + }, + "fastq_unclassified_folder": { + "description": "folder containing fastq and/or fastq.gz data from a pooling experiment, that could not be assigned to one of the known samples", + "allOf": [ + { + "$ref": "#/definitions/unclassified_folder" + }, + { + "properties": { + "children": { + "items": { + "anyOf": [ + { + "$ref": "#/definitions/fastqgz_file" + }, + { + "$ref": "#/definitions/fastq_file" + } + ] + }, + "minItems": 0 + } + } + } + ] + }, + "fast5_subfolder": { + "description": "folder containing fast5 data from a single sample (only when pooling is used)", + "allOf": [ + { + "$ref": "#/definitions/barcoded_folder" + }, + { + "properties": { + "children": { + "items": { + "$ref": "#/definitions/fast5_file" + }, + "minItems": 1 + } + } + } + ] + }, + "fast5_fail": { + "allOf": [ + { + "$ref": "#/definitions/folder" + }, + { + "properties": { + "name": { + "pattern": "fast5_fail" + }, + "children": { + "items": { + "anyOf": [ + { + "$ref": "#/definitions/fast5_subfolder" + }, + { + "$ref": "#/definitions/fast5_unclassified_folder" + }, + { + "$ref": "#/definitions/fast5_file" + } + ] + } + } + } + } + ] + }, + "fast5_pass": { + "allOf": [ + { + "$ref": "#/definitions/folder" + }, + { + "properties": { + "name": { + "pattern": "fast5_pass" + }, + "children": { + "items": { + "anyOf": [ + { + "$ref": "#/definitions/fast5_subfolder" + }, + { + "$ref": "#/definitions/fast5_unclassified_folder" + }, + { + "$ref": "#/definitions/fast5_file" + } + ] + } + } + } + } + ] + }, + "fastq_fail": { + "allOf": [ + { + "$ref": "#/definitions/folder" + }, + { + "properties": { + "name": { + "pattern": "fastq_fail" + }, + "children": { + "items": { + "anyOf": [ + { + "$ref": "#/definitions/fastq_subfolder" + }, + { + "$ref": "#/definitions/fastq_unclassified_folder" + }, + { + "$ref": "#/definitions/fastqgz_file" + } + ] + } + } + } + } + ] + }, + "fastq_pass": { + "allOf": [ + { + "$ref": "#/definitions/folder" + }, + { + "properties": { + "name": { + "pattern": "fastq_pass" + }, + "children": { + "items": { + "anyOf": [ + { + "$ref": "#/definitions/fastq_subfolder" + }, + { + "$ref": "#/definitions/fastq_unclassified_folder" + }, + { + "$ref": "#/definitions/fastqgz_file" + } + ] + } + } + } + } + ] + }, + "fastq_subfolder": { + "description": "folder containing gzipped fastq data from a single sample (only when pooling is used)", + "allOf": [ + { + "$ref": "#/definitions/barcoded_folder" + }, + { + "properties": { + "children": { + "items": { + "$ref": "#/definitions/fastqgz_file" + }, + "minItems": 1 + } + } + } + ] + }, + "optional_folder": { + "description": "Folder not expected in the current schemas but not invalidating the minimal datastructure required", + "allOf": [ + { + "$ref": "#/definitions/folder" + }, + { + "properties": {} + } + ] + }, + "measurements": { + "description": "Top folder generated by the facility, containing one or more timestamped measurements", + "allOf": [ + { + "$ref": "#/definitions/barcoded_folder" + }, + { + "properties": { + "children": { + "items": { + "allOf": [ + { + "$ref": "#/definitions/measurement" + } + ] + }, + "minItems": 1 + } + } + } + ] + }, + "measurement": { + "allOf": [ + { + "$ref": "#/definitions/folder" + }, + { + "properties": { + "name": { + "pattern": "\\d{4}(0?[1-9]|1[012])(0?[1-9]|[12][0-9]|3[01])_([01][0-9]|2[0-3])([0-5][0-9]).*", + "description": "Name of measurement subfolder. Starts with date and time of measurement e.g. 20200122_1217..." + }, + "children": { + "type": "array", + "minItems": 7, + "contains": { + "oneOf": [ + { + "$ref": "#/definitions/fastq_fail" + }, + { + "$ref": "#/definitions/fastq_pass" + }, + { + "$ref": "#/definitions/fast5_pass" + }, + { + "$ref": "#/definitions/fast5_fail" + }, + { + "$ref": "#/definitions/final_summary_log" + }, + { + "$ref": "#/definitions/report_md_log" + }, + { + "$ref": "#/definitions/sequencing_summary_log" + } + ] + }, + "minContains": 7, + "uniqueItems": true + } + } + } + ] + }, + "final_summary_log": { + "allOf": [ + { + "$ref": "#/definitions/file" + }, + { + "properties": { + "name": { + "pattern": "final_summary_.*" + }, + "file_type": { + "pattern": "txt" + } + } + } + ] + }, + "report_md_log": { + "allOf": [ + { + "$ref": "#/definitions/file" + }, + { + "properties": { + "name": { + "pattern": "report_.*" + }, + "file_type": { + "pattern": "md" + } + } + } + ] + }, + "sequencing_summary_log": { + "allOf": [ + { + "$ref": "#/definitions/file" + }, + { + "properties": { + "name": { + "pattern": "sequencing_summary_.*" + }, + "file_type": { + "pattern": "txt" + } + } + } + ] + }, + "optional_file": { + "description": "File not expected in the current schemas but not invalidating the minimal datastructure required", + "allOf": [ + { + "$ref": "#/definitions/file" + }, + { + "properties": {} + } + ] + } + }, + "allOf": [ + { + "$ref": "#/definitions/measurements" + } + ] +} diff --git a/src/main/resources/schemas/nanopore-instrument-output_minimal_dorado.schema.json b/src/main/resources/schemas/nanopore-instrument-output_minimal_dorado.schema.json new file mode 100644 index 0000000000..35fc42fce7 --- /dev/null +++ b/src/main/resources/schemas/nanopore-instrument-output_minimal_dorado.schema.json @@ -0,0 +1,661 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "http://qbic.life/nanopore-instrument-output_minimal_dorado.schema.json", + "title": "Nanopore Instrument Output dorado basecalled minimal", + "description": "Describes in which form PromethION/MinION sequenced Nanopore data is received from the medical genetics lab. To be used if no other schema fits the description and ensure that the minimal necessary files are provided if the dorado basecaller was employed", + "definitions": { + "folder": { + "description": "Describes a folder", + "type": "object", + "required": [ + "name", + "path", + "children" + ], + "properties": { + "name": { + "description": "Folder name", + "type": "string", + "minLength": 1 + }, + "path": { + "description": "relative folderpath", + "type": "string", + "minLength": 1 + }, + "children": { + "description": "Describes files and/or sub-folders if existent", + "type": "array", + "items": { + "oneOf": [ + { + "$ref": "#/definitions/folder" + }, + { + "$ref": "#/definitions/file" + } + ] + } + } + } + }, + "file": { + "description": "Describes a file", + "type": "object", + "required": [ + "name", + "path", + "file_type" + ], + "properties": { + "name": { + "type": "string", + "minLength": 1 + }, + "path": { + "type": "string", + "minLength": 1 + }, + "file_type": { + "type": "string", + "minLength": 1 + } + } + }, + "qbic_code": { + "description": "Describes a QBiC code used as a prefix", + "type": "string", + "pattern": "Q\\w{4}\\d{3}[A-X][A-X0-9].*" + }, + "barcoded_folder": { + "description": "folder starting with qbic barcode prefix", + "allOf": [ + { + "$ref": "#/definitions/folder" + }, + { + "properties": { + "name": { + "$ref": "#/definitions/qbic_code" + } + } + } + ] + }, + "fast5_file": { + "allOf": [ + { + "$ref": "#/definitions/file" + }, + { + "properties": { + "file_type": { + "pattern": "fast5" + } + } + } + ] + }, + "pod5_file": { + "allOf": [ + { + "$ref": "#/definitions/file" + }, + { + "properties": { + "file_type": { + "pattern": "pod5" + } + } + } + ] + }, + "fastqgz_file": { + "allOf": [ + { + "$ref": "#/definitions/file" + }, + { + "properties": { + "file_type": { + "pattern": "fastq.gz" + } + } + } + ] + }, + "fastq_file": { + "allOf": [ + { + "$ref": "#/definitions/file" + }, + { + "properties": { + "file_type": { + "pattern": "fastq" + } + } + } + ] + }, + "unclassified_folder": { + "description": "folder containing unassigned read file(s)", + "allOf": [ + { + "$ref": "#/definitions/folder" + }, + { + "properties": { + "name": { + "pattern": "unclassified" + } + } + } + ] + }, + "fast5_unclassified_folder": { + "description": "folder containing fast5 data from a pooling experiment, that could not be assigned to one of the known samples", + "allOf": [ + { + "$ref": "#/definitions/unclassified_folder" + }, + { + "properties": { + "children": { + "items": { + "$ref": "#/definitions/fast5_file" + }, + "minItems": 0 + } + } + } + ] + }, + "pod5_unclassified_folder": { + "description": "folder containing pod5 data from a pooling experiment, that could not be assigned to one of the known samples", + "allOf": [ + { + "$ref": "#/definitions/unclassified_folder" + }, + { + "properties": { + "children": { + "items": { + "$ref": "#/definitions/pod5_file" + }, + "minItems": 0 + } + } + } + ] + }, + "fastq_unclassified_folder": { + "description": "folder containing fastq and/or fastq.gz data from a pooling experiment, that could not be assigned to one of the known samples", + "allOf": [ + { + "$ref": "#/definitions/unclassified_folder" + }, + { + "properties": { + "children": { + "items": { + "anyOf": [ + { + "$ref": "#/definitions/fastqgz_file" + }, + { + "$ref": "#/definitions/fastq_file" + } + ] + }, + "minItems": 0 + } + } + } + ] + }, + "fast5_subfolder": { + "description": "folder containing fast5 data from a single sample (only when pooling is used)", + "allOf": [ + { + "$ref": "#/definitions/barcoded_folder" + }, + { + "properties": { + "children": { + "items": { + "$ref": "#/definitions/fast5_file" + }, + "minItems": 1 + } + } + } + ] + }, + "pod5_subfolder": { + "description": "folder containing pod5 data from a single sample (only when pooling is used)", + "allOf": [ + { + "$ref": "#/definitions/barcoded_folder" + }, + { + "properties": { + "children": { + "items": { + "$ref": "#/definitions/pod5_file" + }, + "minItems": 1 + } + } + } + ] + }, + "fast5_fail": { + "allOf": [ + { + "$ref": "#/definitions/folder" + }, + { + "properties": { + "name": { + "pattern": "fast5_fail" + }, + "children": { + "items": { + "anyOf": [ + { + "$ref": "#/definitions/fast5_subfolder" + }, + { + "$ref": "#/definitions/fast5_unclassified_folder" + }, + { + "$ref": "#/definitions/fast5_file" + } + ] + } + } + } + } + ] + }, + "fast5_pass": { + "allOf": [ + { + "$ref": "#/definitions/folder" + }, + { + "properties": { + "name": { + "pattern": "fast5_pass" + }, + "children": { + "items": { + "anyOf": [ + { + "$ref": "#/definitions/fast5_subfolder" + }, + { + "$ref": "#/definitions/fast5_unclassified_folder" + }, + { + "$ref": "#/definitions/fast5_file" + } + ] + } + } + } + } + ] + }, + "fast5_skip": { + "allOf": [ + { + "$ref": "#/definitions/folder" + }, + { + "properties": { + "name": { + "pattern": "fast5_skip" + }, + "children": { + "items": { + "anyOf": [ + { + "$ref": "#/definitions/fast5_file" + } + ] + } + } + } + } + ] + }, + "pod5_skip": { + "allOf": [ + { + "$ref": "#/definitions/folder" + }, + { + "properties": { + "name": { + "pattern": "pod5_skip" + }, + "children": { + "items": { + "anyOf": [ + { + "$ref": "#/definitions/pod5_file" + } + ] + } + } + } + } + ] + }, + "pod5_fail": { + "allOf": [ + { + "$ref": "#/definitions/folder" + }, + { + "properties": { + "name": { + "pattern": "pod5_fail" + }, + "children": { + "items": { + "anyOf": [ + { + "$ref": "#/definitions/pod5_subfolder" + }, + { + "$ref": "#/definitions/pod5_unclassified_folder" + }, + { + "$ref": "#/definitions/pod5_file" + } + ] + } + } + } + } + ] + }, + "pod5_pass": { + "allOf": [ + { + "$ref": "#/definitions/folder" + }, + { + "properties": { + "name": { + "pattern": "pod5_pass" + }, + "children": { + "items": { + "anyOf": [ + { + "$ref": "#/definitions/pod5_subfolder" + }, + { + "$ref": "#/definitions/pod5_unclassified_folder" + }, + { + "$ref": "#/definitions/pod5_file" + } + ] + } + } + } + } + ] + }, + "fastq_fail": { + "allOf": [ + { + "$ref": "#/definitions/folder" + }, + { + "properties": { + "name": { + "pattern": "fastq_fail" + }, + "children": { + "items": { + "anyOf": [ + { + "$ref": "#/definitions/fastq_subfolder" + }, + { + "$ref": "#/definitions/fastq_unclassified_folder" + }, + { + "$ref": "#/definitions/fastqgz_file" + } + ] + } + } + } + } + ] + }, + "basecalling": { + "allOf": [ + { + "$ref": "#/definitions/folder" + }, + { + "properties": { + "name": { + "pattern": "basecalling" + }, + "children": { + "items": { + "anyOf": [ + { + "$ref": "#/definitions/fastq_fail" + }, + { + "$ref": "#/definitions/fastq_pass" + }, + { + "$ref": "#/definitions/optional_file" + } + ] + } + } + } + } + ] + }, + "fastq_pass": { + "allOf": [ + { + "$ref": "#/definitions/folder" + }, + { + "properties": { + "name": { + "pattern": "fastq_pass" + }, + "children": { + "items": { + "anyOf": [ + { + "$ref": "#/definitions/fastq_subfolder" + }, + { + "$ref": "#/definitions/fastq_unclassified_folder" + }, + { + "$ref": "#/definitions/fastqgz_file" + } + ] + } + } + } + } + ] + }, + "fastq_subfolder": { + "description": "folder containing gzipped fastq data from a single sample (only when pooling is used)", + "allOf": [ + { + "$ref": "#/definitions/barcoded_folder" + }, + { + "properties": { + "children": { + "items": { + "$ref": "#/definitions/fastqgz_file" + }, + "minItems": 1 + } + } + } + ] + }, + "optional_folder": { + "description": "Folder not expected in the current schemas but not invalidating the minimal datastructure required", + "allOf": [ + { + "$ref": "#/definitions/folder" + }, + { + "properties": {} + } + ] + }, + "measurements": { + "description": "Top folder generated by the facility, containing one or more timestamped measurements", + "allOf": [ + { + "$ref": "#/definitions/barcoded_folder" + }, + { + "properties": { + "children": { + "items": { + "allOf": [ + { + "$ref": "#/definitions/measurement" + } + ] + }, + "minItems": 1 + } + } + } + ] + }, + "measurement": { + "allOf": [ + { + "$ref": "#/definitions/folder" + }, + { + "properties": { + "name": { + "pattern": "\\d{4}(0?[1-9]|1[012])(0?[1-9]|[12][0-9]|3[01])_([01][0-9]|2[0-3])([0-5][0-9]).*", + "description": "Name of measurement subfolder. Starts with date and time of measurement e.g. 20200122_1217..." + }, + "children": { + "type": "array", + "minItems": 6, + "contains": { + "oneOf": [ + { + "$ref": "#/definitions/fast5_skip" + }, + { + "$ref": "#/definitions/pod5_skip" + }, + { + "$ref": "#/definitions/final_summary_log" + }, + { + "$ref": "#/definitions/report_md_log" + }, + { + "$ref": "#/definitions/sequencing_summary_log" + } + ] + }, + "minContains": 5, + "uniqueItems": true + } + } + } + ] + }, + "final_summary_log": { + "allOf": [ + { + "$ref": "#/definitions/file" + }, + { + "properties": { + "name": { + "pattern": "final_summary_.*" + }, + "file_type": { + "pattern": "txt" + } + } + } + ] + }, + "report_md_log": { + "allOf": [ + { + "$ref": "#/definitions/file" + }, + { + "properties": { + "name": { + "pattern": "report_.*" + }, + "file_type": { + "pattern": "md" + } + } + } + ] + }, + "sequencing_summary_log": { + "allOf": [ + { + "$ref": "#/definitions/file" + }, + { + "properties": { + "name": { + "pattern": "sequencing_summary_.*" + }, + "file_type": { + "pattern": "txt" + } + } + } + ] + }, + "optional_file": { + "description": "File not expected in the current schemas but not invalidating the minimal datastructure required", + "allOf": [ + { + "$ref": "#/definitions/file" + }, + { + "properties": {} + } + ] + } + }, + "allOf": [ + { + "$ref": "#/definitions/measurements" + } + ] +} diff --git a/src/main/resources/schemas/nanopore-instrument-output_v2.schema.json b/src/main/resources/schemas/nanopore-instrument-output_v2.schema.json index 452feeefea..a568991d00 100644 --- a/src/main/resources/schemas/nanopore-instrument-output_v2.schema.json +++ b/src/main/resources/schemas/nanopore-instrument-output_v2.schema.json @@ -2,7 +2,7 @@ "$schema": "http://json-schema.org/draft-07/schema", "$id": "http://qbic.life/nanopore-instrument-output_v2.schema.json", "title": "Nanopore Instrument Output V2", - "description": "Describes in which form Nanopore data is received from the lab.", + "description": "Describes in which form PromethION/MinION sequenced Nanopore data is received from the medical genetics lab. Accounts for 'other reports' folder created by the lab", "definitions": { "folder": { "description": "Describes a folder", diff --git a/src/main/resources/schemas/nanopore-instrument-output_v3.schema.json b/src/main/resources/schemas/nanopore-instrument-output_v3.schema.json new file mode 100644 index 0000000000..6ca89f7a6b --- /dev/null +++ b/src/main/resources/schemas/nanopore-instrument-output_v3.schema.json @@ -0,0 +1,621 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema", + "$id": "http://qbic.life/nanopore-instrument-output_v3.schema.json", + "title": "Nanopore Instrument Output V3", + "description": "Describes in which form PromethION/MinION sequenced Nanopore data is received from the medical genetics lab. Accounts for the adapted 'other_reports' folder structure provided by the lab", + "definitions": { + "folder": { + "description": "Describes a folder", + "type": "object", + "required": [ + "name", + "path", + "children" + ], + "properties": { + "name": { + "description": "Folder name", + "type": "string", + "minLength": 1 + }, + "path": { + "description": "relative folderpath", + "type": "string", + "minLength": 1 + }, + "children": { + "description": "Describes files and/or sub-folders if existent", + "type": "array", + "items": { + "oneOf": [ + { + "$ref": "#/definitions/folder" + }, + { + "$ref": "#/definitions/file" + } + ] + } + } + } + }, + "file": { + "description": "Describes a file", + "type": "object", + "required": [ + "name", + "path", + "file_type" + ], + "properties": { + "name": { + "type": "string", + "minLength": 1 + }, + "path": { + "type": "string", + "minLength": 1 + }, + "file_type": { + "type": "string", + "minLength": 1 + } + } + }, + "qbic_code": { + "description": "Describes a QBiC code used as a prefix", + "type": "string", + "pattern": "Q\\w{4}\\d{3}[A-X][A-X0-9].*" + }, + "barcoded_folder": { + "description": "folder starting with qbic barcode prefix", + "allOf": [ + { + "$ref": "#/definitions/folder" + }, + { + "properties": { + "name": { + "$ref": "#/definitions/qbic_code" + } + } + } + ] + }, + "other_reports_folder": { + "description": "subfolder containing some of the report files", + "allOf": [ + { + "$ref": "#/definitions/folder" + }, + { + "properties": { + "name": { + "pattern": "other_reports" + }, + "children": { + "items": { + "$ref": "#/definitions/pore_scan_data_log" + }, + "minItems": 1 + } + } + } + ] + }, + "fast5_file": { + "allOf": [ + { + "$ref": "#/definitions/file" + }, + { + "properties": { + "file_type": { + "pattern": "fast5" + } + } + } + ] + }, + "fastqgz_file": { + "allOf": [ + { + "$ref": "#/definitions/file" + }, + { + "properties": { + "file_type": { + "pattern": "fastq.gz" + } + } + } + ] + }, + "fastq_file": { + "allOf": [ + { + "$ref": "#/definitions/file" + }, + { + "properties": { + "file_type": { + "pattern": "fastq" + } + } + } + ] + }, + "unclassified_folder": { + "description": "folder containing unassigned read file(s)", + "allOf": [ + { + "$ref": "#/definitions/folder" + }, + { + "properties": { + "name": { + "pattern": "unclassified" + } + } + } + ] + }, + "fast5_unclassified_folder": { + "description": "folder containing fast5 data from a pooling experiment, that could not be assigned to one of the known samples", + "allOf": [ + { + "$ref": "#/definitions/unclassified_folder" + }, + { + "properties": { + "children": { + "items": { + "$ref": "#/definitions/fast5_file" + }, + "minItems": 0 + } + } + } + ] + }, + "fastq_unclassified_folder": { + "description": "folder containing fastq and/or fastq.gz data from a pooling experiment, that could not be assigned to one of the known samples", + "allOf": [ + { + "$ref": "#/definitions/unclassified_folder" + }, + { + "properties": { + "children": { + "items": { + "anyOf": [ + { + "$ref": "#/definitions/fastqgz_file" + }, + { + "$ref": "#/definitions/fastq_file" + } + ] + }, + "minItems": 0 + } + } + } + ] + }, + "fast5_subfolder": { + "description": "folder containing fast5 data from a single sample (only when pooling is used)", + "allOf": [ + { + "$ref": "#/definitions/barcoded_folder" + }, + { + "properties": { + "children": { + "items": { + "$ref": "#/definitions/fast5_file" + }, + "minItems": 1 + } + } + } + ] + }, + "fast5_fail": { + "allOf": [ + { + "$ref": "#/definitions/folder" + }, + { + "properties": { + "name": { + "pattern": "fast5_fail" + }, + "children": { + "items": { + "anyOf": [ + { + "$ref": "#/definitions/fast5_subfolder" + }, + { + "$ref": "#/definitions/fast5_unclassified_folder" + }, + { + "$ref": "#/definitions/fast5_file" + } + ] + } + } + } + } + ] + }, + "fast5_pass": { + "allOf": [ + { + "$ref": "#/definitions/folder" + }, + { + "properties": { + "name": { + "pattern": "fast5_pass" + }, + "children": { + "items": { + "anyOf": [ + { + "$ref": "#/definitions/fast5_subfolder" + }, + { + "$ref": "#/definitions/fast5_unclassified_folder" + }, + { + "$ref": "#/definitions/fast5_file" + } + ] + } + } + } + } + ] + }, + "fastq_fail": { + "allOf": [ + { + "$ref": "#/definitions/folder" + }, + { + "properties": { + "name": { + "pattern": "fastq_fail" + }, + "children": { + "items": { + "anyOf": [ + { + "$ref": "#/definitions/fastq_subfolder" + }, + { + "$ref": "#/definitions/fastq_unclassified_folder" + }, + { + "$ref": "#/definitions/fastqgz_file" + } + ] + } + } + } + } + ] + }, + "fastq_pass": { + "allOf": [ + { + "$ref": "#/definitions/folder" + }, + { + "properties": { + "name": { + "pattern": "fastq_pass" + }, + "children": { + "items": { + "anyOf": [ + { + "$ref": "#/definitions/fastq_subfolder" + }, + { + "$ref": "#/definitions/fastq_unclassified_folder" + }, + { + "$ref": "#/definitions/fastqgz_file" + } + ] + } + } + } + } + ] + }, + "fastq_subfolder": { + "description": "folder containing gzipped fastq data from a single sample (only when pooling is used)", + "allOf": [ + { + "$ref": "#/definitions/barcoded_folder" + }, + { + "properties": { + "children": { + "items": { + "$ref": "#/definitions/fastqgz_file" + }, + "minItems": 1 + } + } + } + ] + }, + "measurements": { + "description": "Top folder generated by the facility, containing one or more timestamped measurements", + "allOf": [ + { + "$ref": "#/definitions/barcoded_folder" + }, + { + "properties": { + "children": { + "items": { + "allOf": [ + { + "$ref": "#/definitions/measurement" + } + ] + }, + "minItems": 1 + } + } + } + ] + }, + "measurement": { + "allOf": [ + { + "$ref": "#/definitions/folder" + }, + { + "properties": { + "name": { + "pattern": "\\d{4}(0?[1-9]|1[012])(0?[1-9]|[12][0-9]|3[01])_([01][0-9]|2[0-3])([0-5][0-9]).*", + "description": "Name of measurement subfolder. Starts with date and time of measurement." + }, + "children": { + "uniqueItems": true, + "minItems": 14, + "items": { + "oneOf": [ + { + "$ref": "#/definitions/fastq_fail" + }, + { + "$ref": "#/definitions/fastq_pass" + }, + { + "$ref": "#/definitions/fast5_pass" + }, + { + "$ref": "#/definitions/fast5_fail" + }, + { + "$ref": "#/definitions/barcode_alignment_log" + }, + { + "$ref": "#/definitions/pore_activity_log" + }, + { + "$ref": "#/definitions/final_summary_log" + }, + { + "$ref": "#/definitions/report_md_log" + }, + { + "$ref": "#/definitions/report_json_log" + }, + { + "$ref": "#/definitions/report_html_log" + }, + { + "$ref": "#/definitions/sequencing_summary_log" + }, + { + "$ref": "#/definitions/throughput_log" + }, + { + "$ref": "#/definitions/sample_sheet_log" + }, + { + "$ref": "#/definitions/other_reports_folder" + } + ] + } + } + } + } + ] + }, + "barcode_alignment_log": { + "allOf": [ + { + "$ref": "#/definitions/file" + }, + { + "properties": { + "name": { + "pattern": "barcode_alignment_.*" + }, + "file_type": { + "pattern": "tsv" + } + } + } + ] + }, + "pore_activity_log": { + "allOf": [ + { + "$ref": "#/definitions/file" + }, + { + "properties": { + "name": { + "pattern": "pore_activity_.*" + }, + "file_type": { + "pattern": "csv" + } + } + } + ] + }, + "sample_sheet_log": { + "allOf": [ + { + "$ref": "#/definitions/file" + }, + { + "properties": { + "name": { + "pattern": "sample_sheet_.*" + }, + "file_type": { + "pattern": "csv" + } + } + } + ] + }, + "final_summary_log": { + "allOf": [ + { + "$ref": "#/definitions/file" + }, + { + "properties": { + "name": { + "pattern": "final_summary_.*" + }, + "file_type": { + "pattern": "txt" + } + } + } + ] + }, + "pore_scan_data_log": { + "allOf": [ + { + "$ref": "#/definitions/file" + }, + { + "properties": { + "name": { + "pattern": "pore_scan_data_.*" + }, + "file_type": { + "pattern": "csv" + } + } + } + ] + }, + "report_md_log": { + "allOf": [ + { + "$ref": "#/definitions/file" + }, + { + "properties": { + "name": { + "pattern": "report_.*" + }, + "file_type": { + "pattern": "md" + } + } + } + ] + }, + "report_json_log": { + "allOf": [ + { + "$ref": "#/definitions/file" + }, + { + "properties": { + "name": { + "pattern": "report_.*" + }, + "file_type": { + "pattern": "json" + } + } + } + ] + }, + "report_html_log": { + "allOf": [ + { + "$ref": "#/definitions/file" + }, + { + "properties": { + "name": { + "pattern": "report_.*" + }, + "file_type": { + "pattern": "html" + } + } + } + ] + }, + "sequencing_summary_log": { + "allOf": [ + { + "$ref": "#/definitions/file" + }, + { + "properties": { + "name": { + "pattern": "sequencing_summary_.*" + }, + "file_type": { + "pattern": "txt" + } + } + } + ] + }, + "throughput_log": { + "allOf": [ + { + "$ref": "#/definitions/file" + }, + { + "properties": { + "name": { + "pattern": "throughput_.*" + }, + "file_type": { + "pattern": "csv" + } + } + } + ] + } + }, + "allOf": [ + { + "$ref": "#/definitions/measurements" + } + ] +} diff --git a/src/main/resources/schemas/nanopore-instrument-output_v4.schema.json b/src/main/resources/schemas/nanopore-instrument-output_v4.schema.json new file mode 100644 index 0000000000..0c22e107a9 --- /dev/null +++ b/src/main/resources/schemas/nanopore-instrument-output_v4.schema.json @@ -0,0 +1,635 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema", + "$id": "http://qbic.life/nanopore-instrument-output_v4.schema.json", + "title": "Nanopore Instrument Output V4", + "description": "Describes in which form PromethION/MinION sequenced Nanopore data is received from the microbiology lab. For this dataset a second basecalling with higher accuracy was performed after the an initial fast basecalling during sequencing", + "definitions": { + "folder": { + "description": "Describes a folder", + "type": "object", + "required": [ + "name", + "path", + "children" + ], + "properties": { + "name": { + "description": "Folder name", + "type": "string", + "minLength": 1 + }, + "path": { + "description": "relative folderpath", + "type": "string", + "minLength": 1 + }, + "children": { + "description": "Describes files and/or sub-folders if existent", + "type": "array", + "items": { + "oneOf": [ + { + "$ref": "#/definitions/folder" + }, + { + "$ref": "#/definitions/file" + } + ] + } + } + } + }, + "file": { + "description": "Describes a file", + "type": "object", + "required": [ + "name", + "path", + "file_type" + ], + "properties": { + "name": { + "type": "string", + "minLength": 1 + }, + "path": { + "type": "string", + "minLength": 1 + }, + "file_type": { + "type": "string", + "minLength": 1 + } + } + }, + "qbic_code": { + "description": "Describes a QBiC code used as a prefix", + "type": "string", + "pattern": "Q\\w{4}\\d{3}[A-X][A-X0-9].*" + }, + "barcoded_folder": { + "description": "folder starting with qbic barcode prefix", + "allOf": [ + { + "$ref": "#/definitions/folder" + }, + { + "properties": { + "name": { + "$ref": "#/definitions/qbic_code" + } + } + } + ] + }, + "fast5_file": { + "allOf": [ + { + "$ref": "#/definitions/file" + }, + { + "properties": { + "file_type": { + "pattern": "fast5" + } + } + } + ] + }, + "fastqgz_file": { + "allOf": [ + { + "$ref": "#/definitions/file" + }, + { + "properties": { + "file_type": { + "pattern": "fastq.gz" + } + } + } + ] + }, + "fastq_file": { + "allOf": [ + { + "$ref": "#/definitions/file" + }, + { + "properties": { + "file_type": { + "pattern": "fastq" + } + } + } + ] + }, + "unclassified_folder": { + "description": "folder containing unassigned read file(s)", + "allOf": [ + { + "$ref": "#/definitions/folder" + }, + { + "properties": { + "name": { + "pattern": "unclassified" + } + } + } + ] + }, + "fast5_unclassified_folder": { + "description": "folder containing fast5 data from a pooling experiment, that could not be assigned to one of the known samples", + "allOf": [ + { + "$ref": "#/definitions/unclassified_folder" + }, + { + "properties": { + "children": { + "items": { + "$ref": "#/definitions/fast5_file" + }, + "minItems": 0 + } + } + } + ] + }, + "fastq_unclassified_folder": { + "description": "folder containing fastq and/or fastq.gz data from a pooling experiment, that could not be assigned to one of the known samples", + "allOf": [ + { + "$ref": "#/definitions/unclassified_folder" + }, + { + "properties": { + "children": { + "items": { + "anyOf": [ + { + "$ref": "#/definitions/fastqgz_file" + }, + { + "$ref": "#/definitions/fastq_file" + } + ] + }, + "minItems": 0 + } + } + } + ] + }, + "fast5_subfolder": { + "description": "folder containing fast5 data from a single sample (only when pooling is used)", + "allOf": [ + { + "$ref": "#/definitions/barcoded_folder" + }, + { + "properties": { + "children": { + "items": { + "$ref": "#/definitions/fast5_file" + }, + "minItems": 1 + } + } + } + ] + }, + "fast5_fail": { + "allOf": [ + { + "$ref": "#/definitions/folder" + }, + { + "properties": { + "name": { + "pattern": "fast5_fail" + }, + "children": { + "items": { + "anyOf": [ + { + "$ref": "#/definitions/fast5_subfolder" + }, + { + "$ref": "#/definitions/fast5_unclassified_folder" + }, + { + "$ref": "#/definitions/fast5_file" + } + ] + } + } + } + } + ] + }, + "fast5_pass": { + "allOf": [ + { + "$ref": "#/definitions/folder" + }, + { + "properties": { + "name": { + "pattern": "fast5_pass" + }, + "children": { + "items": { + "anyOf": [ + { + "$ref": "#/definitions/fast5_subfolder" + }, + { + "$ref": "#/definitions/fast5_unclassified_folder" + }, + { + "$ref": "#/definitions/fast5_file" + } + ] + } + } + } + } + ] + }, + "fastq_fail": { + "allOf": [ + { + "$ref": "#/definitions/folder" + }, + { + "properties": { + "name": { + "pattern": "fastq_fail" + }, + "children": { + "items": { + "anyOf": [ + { + "$ref": "#/definitions/fastq_subfolder" + }, + { + "$ref": "#/definitions/fastq_unclassified_folder" + }, + { + "$ref": "#/definitions/fastqgz_file" + } + ] + } + } + } + } + ] + }, + "fastq_pass": { + "allOf": [ + { + "$ref": "#/definitions/folder" + }, + { + "properties": { + "name": { + "pattern": "fastq_pass" + }, + "children": { + "items": { + "anyOf": [ + { + "$ref": "#/definitions/fastq_subfolder" + }, + { + "$ref": "#/definitions/fastq_unclassified_folder" + }, + { + "$ref": "#/definitions/fastqgz_file" + } + ] + } + } + } + } + ] + }, + "fastq_subfolder": { + "description": "folder containing gzipped fastq data from a single sample (only when pooling is used)", + "allOf": [ + { + "$ref": "#/definitions/barcoded_folder" + }, + { + "properties": { + "children": { + "items": { + "$ref": "#/definitions/fastqgz_file" + }, + "minItems": 1 + } + } + } + ] + }, + "basecalling_folder": { + "description": "folder containing the files resulting from a second high accuracy basecalling performed after the initial sequencing", + "allOf": [ + { + "$ref": "#/definitions/folder" + }, + { + "properties": { + "name": { + "pattern": "basecalling" + }, + "children": { + "items": { + "uniqueItems": true, + "minItems": 5, + "anyOf": [ + { + "$ref": "#/definitions/fastq_pass" + }, + { + "$ref": "#/definitions/fastq_fail" + }, + { + "$ref": "#/definitions/sequencing_summary_log" + }, + { + "$ref": "#/definitions/sequencing_telemetry" + }, + { + "$ref": "#/definitions/guppy_basecall_client_log" + } + ] + } + } + } + } + ] + }, + "measurements": { + "description": "Top folder generated by the facility, containing one or more timestamped measurements", + "allOf": [ + { + "$ref": "#/definitions/barcoded_folder" + }, + { + "properties": { + "children": { + "items": { + "allOf": [ + { + "$ref": "#/definitions/measurement" + } + ] + }, + "minItems": 1 + } + } + } + ] + }, + "measurement": { + "allOf": [ + { + "$ref": "#/definitions/folder" + }, + { + "properties": { + "name": { + "pattern": "\\d{4}(0?[1-9]|1[012])(0?[1-9]|[12][0-9]|3[01])_([01][0-9]|2[0-3])([0-5][0-9]).*", + "description": "Name of measurement subfolder. Starts with date and time of measurement." + }, + "children": { + "uniqueItems": true, + "minItems": 11, + "items": { + "oneOf": [ + { + "$ref": "#/definitions/fastq_fail" + }, + { + "$ref": "#/definitions/fastq_pass" + }, + { + "$ref": "#/definitions/fast5_pass" + }, + { + "$ref": "#/definitions/fast5_fail" + }, + { + "$ref": "#/definitions/drift_correction_log" + }, + { + "$ref": "#/definitions/duty_time_log" + }, + { + "$ref": "#/definitions/final_summary_log" + }, + { + "$ref": "#/definitions/mux_scan_data_log" + }, + { + "$ref": "#/definitions/report_md_log" + }, + { + "$ref": "#/definitions/report_html_log" + }, + { + "$ref": "#/definitions/sequencing_summary_log" + }, + { + "$ref": "#/definitions/throughput_log" + }, + { + "$ref": "#/definitions/basecalling_folder" + } + ] + } + } + } + } + ] + }, + "drift_correction_log": { + "allOf": [ + { + "$ref": "#/definitions/file" + }, + { + "properties": { + "name": { + "pattern": "drift_correction_.*" + }, + "file_type": { + "pattern": "csv" + } + } + } + ] + }, + "duty_time_log": { + "allOf": [ + { + "$ref": "#/definitions/file" + }, + { + "properties": { + "name": { + "pattern": "duty_time_.*" + }, + "file_type": { + "pattern": "csv" + } + } + } + ] + }, + "final_summary_log": { + "allOf": [ + { + "$ref": "#/definitions/file" + }, + { + "properties": { + "name": { + "pattern": "final_summary_.*" + }, + "file_type": { + "pattern": "txt" + } + } + } + ] + }, + "mux_scan_data_log": { + "allOf": [ + { + "$ref": "#/definitions/file" + }, + { + "properties": { + "name": { + "pattern": "mux_scan_data_.*" + }, + "file_type": { + "pattern": "csv" + } + } + } + ] + }, + "report_md_log": { + "allOf": [ + { + "$ref": "#/definitions/file" + }, + { + "properties": { + "name": { + "pattern": "report_.*" + }, + "file_type": { + "pattern": "md" + } + } + } + ] + }, + "report_html_log": { + "allOf": [ + { + "$ref": "#/definitions/file" + }, + { + "properties": { + "name": { + "pattern": "report_.*" + }, + "file_type": { + "pattern": "html" + } + } + } + ] + }, + "sequencing_summary_log": { + "allOf": [ + { + "$ref": "#/definitions/file" + }, + { + "properties": { + "name": { + "pattern": "sequencing_summary_.*" + }, + "file_type": { + "pattern": "txt" + } + } + } + ] + }, + "throughput_log": { + "allOf": [ + { + "$ref": "#/definitions/file" + }, + { + "properties": { + "name": { + "pattern": "throughput_.*" + }, + "file_type": { + "pattern": "csv" + } + } + } + ] + }, + "sequencing_telemetry": { + "allOf": [ + { + "$ref": "#/definitions/file" + }, + { + "properties": { + "name": { + "pattern": "sequencing_telemetry_.*" + }, + "file_type": { + "pattern": "js" + } + } + } + ] + }, + "guppy_basecall_client_log": { + "allOf": [ + { + "$ref": "#/definitions/file" + }, + { + "properties": { + "name": { + "pattern": "guppy_basecall_client_log-.*" + }, + "file_type": { + "pattern": "log" + } + } + } + ] + } + }, + "allOf": [ + { + "$ref": "#/definitions/measurements" + } + ] +} diff --git a/src/test/groovy/life/qbic/datamodel/datasets/datastructure/MaxQuantRunResultSpec.groovy b/src/test/groovy/life/qbic/datamodel/datasets/datastructure/MaxQuantRunResultSpec.groovy index 4d72d797b4..8fae0883a3 100644 --- a/src/test/groovy/life/qbic/datamodel/datasets/datastructure/MaxQuantRunResultSpec.groovy +++ b/src/test/groovy/life/qbic/datamodel/datasets/datastructure/MaxQuantRunResultSpec.groovy @@ -31,11 +31,11 @@ class MaxQuantRunResultSpec extends Specification { Map invalidDataStructure def setupSpec() { - InputStream validStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("examples/resultset/maxquant/valid-resultset-example.json") + InputStream validStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("examples/resultset/maxquant/valid-resultset-example_latest.json") validDataStructure = (Map) new JsonSlurper().parse(validStream) validStream.close() - InputStream invalidStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("examples/resultset/maxquant/invalid-resultset-example.json") + InputStream invalidStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("examples/resultset/maxquant/no-sampleid-resultset-example.json") invalidDataStructure = (Map) new JsonSlurper().parse(invalidStream) invalidStream.close() } @@ -48,24 +48,20 @@ class MaxQuantRunResultSpec extends Specification { final MaxQuantRunResult maxQuantRunResult = MaxQuantRunResult.createFrom(validExample) AllPeptides allPeptides = maxQuantRunResult.allPeptides Evidence evidence = maxQuantRunResult.evidence - ExperimentalDesignTemplate experimentalDesignTemplate = maxQuantRunResult.experimentalDesignTemplate Parameters parameters = maxQuantRunResult.parameters Peptides peptides = maxQuantRunResult.peptides ProteinGroups proteinGroups = maxQuantRunResult.proteinGroups RunParameters runParameters = maxQuantRunResult.runParameters SampleIds sampleIds = maxQuantRunResult.sampleIds - Summary summary = maxQuantRunResult.summary then: allPeptides.name == "allPeptides.txt" evidence.name == "evidence.txt" - experimentalDesignTemplate.name == "experimentalDesignTemplate.txt" parameters.name == "parameters.txt" peptides.name == "peptides.txt" proteinGroups.name == "proteinGroups.txt" runParameters.name == "mqpar.xml" - sampleIds.name == "sample_ids.txt" - summary.name == "summary_1234.pdf" + sampleIds.name == "Q0010_sample_ids.txt" } def "Invalid fileTree will return a NullPointerException"() { diff --git a/src/test/groovy/life/qbic/datamodel/datasets/datastructure/NfCorePipelineResultSpec.groovy b/src/test/groovy/life/qbic/datamodel/datasets/datastructure/NfCorePipelineResultSpec.groovy index fd3fdc8512..1ba3baefd3 100644 --- a/src/test/groovy/life/qbic/datamodel/datasets/datastructure/NfCorePipelineResultSpec.groovy +++ b/src/test/groovy/life/qbic/datamodel/datasets/datastructure/NfCorePipelineResultSpec.groovy @@ -35,6 +35,9 @@ class NfCorePipelineResultSpec extends Specification { @Shared Map missingQualityControlDataStructure + @Shared + Map validDataStructureWithoutRunId + def setupSpec() { InputStream validStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("examples/resultset/valid-resultset-example.json") validDataStructure = (Map) new JsonSlurper().parse(validStream) @@ -55,6 +58,10 @@ class NfCorePipelineResultSpec extends Specification { InputStream missingQualityControlStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("examples/resultset/missing-quality-control-resultset-example.json") missingQualityControlDataStructure = (Map) new JsonSlurper().parse(missingQualityControlStream) missingQualityControlStream.close() + + InputStream validStreamWithoutRunId = Thread.currentThread().getContextClassLoader().getResourceAsStream("examples/resultset/valid-resultset-no-run_id-example.json") + validDataStructureWithoutRunId = (Map) new JsonSlurper().parse(validStreamWithoutRunId) + validStreamWithoutRunId.close() } def "Create NfCorePipelineOutput from Map successfully"() { @@ -74,9 +81,8 @@ class NfCorePipelineResultSpec extends Specification { runId.name == "run_id.txt" processFolders.get(0).name == "salmon" qualityControlFolder.name == "multiqc" - pipelineInformationFolder.getSoftwareVersions().name == "software_versions.csv" - pipelineInformationFolder.getPipelineReport().name == "pipeline_report.txt" - pipelineInformationFolder.getExecutionReport().name == "execution_report.txt" + pipelineInformationFolder.getSoftwareVersions().name == "software_versions.yml" + pipelineInformationFolder.getExecutionReport().name == "execution_report.html" } def "Invalid fileTree will return a NullPointerException"() { @@ -123,4 +129,24 @@ class NfCorePipelineResultSpec extends Specification { thrown(NullPointerException) } + def "Create NfCorePipelineOutput from Map without RunId successfully"() { + given: + final Map validExample = validDataStructureWithoutRunId + + when: + final NfCorePipelineResult validPipelineResult = NfCorePipelineResult.createFrom(validExample) + SampleIds sampleIds = validPipelineResult.getSampleIds() + List processFolders = validPipelineResult.getProcessFolders() + QualityControlFolder qualityControlFolder = validPipelineResult.getQualityControlFolder() + PipelineInformationFolder pipelineInformationFolder = validPipelineResult.getPipelineInformation() + + then: + sampleIds.name == "sample_ids.txt" + processFolders.get(0).name == "salmon" + qualityControlFolder.name == "multiqc" + pipelineInformationFolder.getSoftwareVersions().name == "software_versions.yml" + pipelineInformationFolder.getExecutionReport().name == "execution_report.html" + assert validPipelineResult.runId == null + } + } diff --git a/src/test/groovy/life/qbic/datamodel/datasets/datastructure/OxfordNanoporeExperimentSpec.groovy b/src/test/groovy/life/qbic/datamodel/datasets/datastructure/OxfordNanoporeExperimentSpec.groovy index 7a404849b7..fc051ce6b0 100644 --- a/src/test/groovy/life/qbic/datamodel/datasets/datastructure/OxfordNanoporeExperimentSpec.groovy +++ b/src/test/groovy/life/qbic/datamodel/datasets/datastructure/OxfordNanoporeExperimentSpec.groovy @@ -16,13 +16,32 @@ class OxfordNanoporeExperimentSpec extends Specification { * according to the schema */ @Shared - Map minimalWorkingSimpleDataStructure + Map minimalSimpleDataStructure /** - * Newer map that stores the Oxford Nanopore folder structure - * according to the schema that puts some reports in its own folder and adds a new report + * Map that stores the Oxford Nanopore folder structure + * according to the schema with an alternate report file ending + */ + @Shared + Map minimalSimpleDataStructureWithHtmlReport + /** + * Newer map that stores the Oxford Nanopore folder structure according to the + * schema that puts some reports in its own folder and adds a barcode alignment report */ @Shared - Map minimalWorkingSimpleDataStructureWithReportsFolder + Map extendedDataStructureWithReportsFolder + /** + * Addition to the newer structure, that changes report file types and adds a pore + * activity report + */ + @Shared + Map extendedDataStructureWithReportsFolderV3 + + /** + * Addition to the newest structure, containing a second basecalling run + */ + @Shared + Map extendedDataStructureWithReportsFolderV4 + /** * Map that that stores the Oxford Nanopore folder structure * according to the schema containing unclassified read information @@ -34,26 +53,75 @@ class OxfordNanoporeExperimentSpec extends Specification { * according to the schema containing pooled samples read information */ @Shared - Map minimalWorkingPooledDataStructure + Map pooledDataStructure + + @Shared + Map minimalDataStructure + + @Shared + Map minimalDataStructurePooled + + @Shared + Map minimalDoradoDataStructure + + @Shared + Map fullDoradoDataStructure def setupSpec() { - InputStream stream = Thread.currentThread().getContextClassLoader().getResourceAsStream("valid-example.json") - minimalWorkingSimpleDataStructure = (Map) new JsonSlurper().parse(stream) - // new example with slightly different structure - stream = Thread.currentThread().getContextClassLoader().getResourceAsStream("valid-example-newer.json") - minimalWorkingSimpleDataStructureWithReportsFolder = (Map) new JsonSlurper().parse(stream) + def folder = "nanopore/" + InputStream stream = Thread.currentThread().getContextClassLoader().getResourceAsStream(folder+"valid-example.json") + minimalSimpleDataStructure = (Map) new JsonSlurper().parse(stream) + //example with report.html instead of report.html + stream = Thread.currentThread().getContextClassLoader().getResourceAsStream(folder+"valid-example-html-report.json") + minimalSimpleDataStructureWithHtmlReport = (Map) new JsonSlurper().parse(stream) + // example with slightly different structure + stream = Thread.currentThread().getContextClassLoader().getResourceAsStream(folder+"valid-example-v2.json") + extendedDataStructureWithReportsFolder = (Map) new JsonSlurper().parse(stream) + // latest example with slightly different structure + stream = Thread.currentThread().getContextClassLoader().getResourceAsStream(folder+"valid-example-v3.json") + extendedDataStructureWithReportsFolderV3 = (Map) new JsonSlurper().parse(stream) + // nanopore structure containing a second basecalling folder + stream = Thread.currentThread().getContextClassLoader().getResourceAsStream(folder+"valid-example-v4-with-basecalling.json") + extendedDataStructureWithReportsFolderV4 = (Map) new JsonSlurper().parse(stream) // read in unclassified example - stream = Thread.currentThread().getContextClassLoader().getResourceAsStream("valid-example-unclassified.json") + stream = Thread.currentThread().getContextClassLoader().getResourceAsStream(folder+"valid-example-unclassified.json") unclassifiedWorkingDataStructure = (Map) new JsonSlurper().parse(stream) // read in pooled example - stream = Thread.currentThread().getContextClassLoader().getResourceAsStream("valid-example-pooled.json") - minimalWorkingPooledDataStructure = (Map) new JsonSlurper().parse(stream) + stream = Thread.currentThread().getContextClassLoader().getResourceAsStream(folder+"valid-example-pooled.json") + pooledDataStructure = (Map) new JsonSlurper().parse(stream) + // read in minimal required example + stream = Thread.currentThread().getContextClassLoader().getResourceAsStream(folder+"valid-minimal-structure.json") + minimalDataStructure = (Map) new JsonSlurper().parse(stream) + // read in minimal required pooled example + stream = Thread.currentThread().getContextClassLoader().getResourceAsStream(folder+"valid-minimal-structure-pooled.json") + minimalDataStructurePooled = (Map) new JsonSlurper().parse(stream) + // read in minimal required example with dorado based basecalling + stream = Thread.currentThread().getContextClassLoader().getResourceAsStream(folder+"valid-minimal-structure-dorado-basecaller.json") + minimalDoradoDataStructure = (Map) new JsonSlurper().parse(stream) + stream.close() + // read in minimal required example with dorado based basecalling + stream = Thread.currentThread().getContextClassLoader().getResourceAsStream(folder+"valid-example-dorado-basecaller.json") + fullDoradoDataStructure = (Map) new JsonSlurper().parse(stream) stream.close() } def "Create simple sample Oxford Nanopore experiment successfully"() { given: - final def example = minimalWorkingSimpleDataStructure + final def example = minimalSimpleDataStructure + + when: + final def experiment = OxfordNanoporeExperiment.create(example) + final def measurements = experiment.getMeasurements() + + then: + assert experiment.sampleCode == "QABCD001AB" + assert measurements.size() == 1 + assert measurements[0].libraryPreparationKit == "SQK-LSK109" + } + + def "Create simple sample Oxford Nanopore experiment including an html report successfully"() { + given: + final def example = minimalSimpleDataStructureWithHtmlReport when: final def experiment = OxfordNanoporeExperiment.create(example) @@ -65,9 +133,102 @@ class OxfordNanoporeExperimentSpec extends Specification { assert measurements[0].libraryPreparationKit == "SQK-LSK109" } - def "Create simple sample Oxford Nanopore experiment successfully for newer structure"() { + def "Create sample Oxford Nanopore experiment successfully for newer structure"() { + given: + final def example = extendedDataStructureWithReportsFolder + + when: + final def experiment = OxfordNanoporeExperiment.create(example) + final def measurements = experiment.getMeasurements() + + then: + assert experiment.sampleCode == "QABCD001AB" + assert measurements.size() == 1 + assert measurements[0].asicTemp == "32.631687" + } + + def "Create sample Oxford Nanopore experiment successfully for latest structure"() { + given: + final def example = extendedDataStructureWithReportsFolderV3 + + when: + final def experiment = OxfordNanoporeExperiment.create(example) + final def measurements = experiment.getMeasurements() + + then: + assert experiment.sampleCode == "QABCD001AB" + assert measurements.size() == 1 + assert measurements[0].asicTemp == "32.631687" + } + + def "Create sample Oxford Nanopore experiment successfully for structure with second basecalling"() { + given: + final def example = extendedDataStructureWithReportsFolderV4 + + when: + final def experiment = OxfordNanoporeExperiment.create(example) + final def measurements = experiment.getMeasurements() + + then: + assert experiment.sampleCode == "QABCD001AB" + assert measurements.size() == 1 + assert measurements[0].asicTemp == "32.631687" + assert measurements[0].getRawDataPerSample(experiment).get("QABCD001AB").containsKey("basecalling") + assert !measurements[0].getRawDataPerSample(experiment).get("QABCD001AB").get("basecalling").relativePath.isEmpty() + assert !measurements[0].getRawDataPerSample(experiment).get("QABCD001AB").get("basecalling").children.isEmpty() + assert measurements[0].getRawDataPerSample(experiment).get("QABCD001AB").get("basecalling").name.size() > 0 + assert measurements[0].getRawDataPerSample(experiment).get("QABCD001AB").containsKey("fastqfail") + } + + def "Create sample Oxford Nanopore experiment successfully for the minimal required structure"() { + given: + final def example = minimalDataStructure + + when: + final def experiment = OxfordNanoporeExperiment.create(example) + final def measurements = experiment.getMeasurements() + + then: + assert experiment.sampleCode == "QABCD001AB" + assert measurements.size() == 1 + assert measurements[0].asicTemp == "32.631687" + assert !measurements[0].getRawDataPerSample(experiment).get("QABCD001AB").containsKey("pod5_skip") + assert !measurements[0].getRawDataPerSample(experiment).get("QABCD001AB").containsKey("fast5_skip") + } + + def "Create sample Oxford Nanopore experiment successfully for the minimal required pooled structure"() { + given: + final def example = minimalDataStructurePooled + + when: + final def experiment = OxfordNanoporeExperiment.create(example) + final def measurements = experiment.getMeasurements() + + then: + assert experiment.sampleCode == "QABCD001AB" + assert measurements.size() == 1 + assert measurements[0].asicTemp == "32.631687" + } + + def "Create sample Oxford Nanopore experiment successfully for dorado basecaller generated minimal structure"() { + given: + final def example = minimalDoradoDataStructure + + when: + final def experiment = OxfordNanoporeExperiment.create(example) + final def measurements = experiment.getMeasurements() + + then: + assert experiment.sampleCode == "QABCD001AB" + assert measurements.size() == 1 + assert measurements[0].asicTemp == "32.631687" + assert measurements[0].getRawDataPerSample(experiment).get("QABCD001AB").containsKey("pod5skip") + assert measurements[0].getRawDataPerSample(experiment).get("QABCD001AB").containsKey("fast5skip") + } + + def "Create sample Oxford Nanopore experiment successfully for dorado basecaller generated full structure"() { given: - final def example = minimalWorkingSimpleDataStructureWithReportsFolder + final def example = fullDoradoDataStructure when: final def experiment = OxfordNanoporeExperiment.create(example) @@ -76,12 +237,18 @@ class OxfordNanoporeExperimentSpec extends Specification { then: assert experiment.sampleCode == "QABCD001AB" assert measurements.size() == 1 - assert measurements[0].libraryPreparationKit == "SQK-LSK109-XL" + assert measurements[0].asicTemp == "32.631687" + assert measurements[0].getRawDataPerSample(experiment).get("QABCD001AB").containsKey("pod5skip") + assert measurements[0].getRawDataPerSample(experiment).get("QABCD001AB").containsKey("fast5skip") + assert measurements[0].getRawDataPerSample(experiment).get("QABCD001AB").containsKey("fast5pass") + assert measurements[0].getRawDataPerSample(experiment).get("QABCD001AB").containsKey("fast5fail") + assert measurements[0].getRawDataPerSample(experiment).get("QABCD001AB").containsKey("pod5pass") + assert measurements[0].getRawDataPerSample(experiment).get("QABCD001AB").containsKey("pod5fail") } def "Create a simple pooled Oxford Nanopore experiment successfully"() { given: - final def example = minimalWorkingPooledDataStructure + final def example = pooledDataStructure when: final def experiment = OxfordNanoporeExperiment.create(example) @@ -97,7 +264,7 @@ class OxfordNanoporeExperimentSpec extends Specification { } - def "Create unclassified example Oxford Nanopore experiment sucessfully"() { + def "Create unclassified example Oxford Nanopore experiment successfully"() { given: final def example = unclassifiedWorkingDataStructure diff --git a/src/test/groovy/life/qbic/datamodel/datasets/datastructure/OxfordNanoporeMeasurementSpec.groovy b/src/test/groovy/life/qbic/datamodel/datasets/datastructure/OxfordNanoporeMeasurementSpec.groovy index 8a322abc3d..dfc8277579 100644 --- a/src/test/groovy/life/qbic/datamodel/datasets/datastructure/OxfordNanoporeMeasurementSpec.groovy +++ b/src/test/groovy/life/qbic/datamodel/datasets/datastructure/OxfordNanoporeMeasurementSpec.groovy @@ -37,6 +37,11 @@ class OxfordNanoporeMeasurementSpec extends Specification { UnclassifiedFast5Folder unclassifiedFast5Folder @Shared UnclassifiedFastQFolder unclassifiedFastQFolder + @Shared + Pod5SkipFolder pod5SkipFolder + @Shared + Fast5SkipFolder fast5SkipFolder + @Shared Map metaData @@ -94,6 +99,7 @@ class OxfordNanoporeMeasurementSpec extends Specification { assert result.get("QABCD001AE").get("fast5pass") instanceof DataFolder assert result.get("QABCD001AE").get("fastqfail") instanceof DataFolder assert result.get("QABCD001AE").get("fastqpass") instanceof DataFolder + assert result.get("QABCD001AE").get("basecalling") == null assert measurement.relativePath == "path/20200219_1107_1-E3-H3_PAE26974_454b8dc6" assert libraryKit == "SQK-LSK109" assert adapter.equals("flongle") @@ -229,6 +235,23 @@ class OxfordNanoporeMeasurementSpec extends Specification { } + def "If both pod5 skip and fast5 skip folder are empty, an IllegalStateException shall be thrown"() { + given: + def emptyPod5SkipFolder = Pod5SkipFolder.create("pod5_skip","root/pod5_skip", []) + def emptyFast5SkipFolder = Fast5SkipFolder.create("fast5_skip","root/fast5_skip", []) + + when: + OxfordNanoporeMeasurement.create( + "20200219_1107_1-E3-H3_PAE26974_454b8dc6", + "path/20200219_1107_1-E3-H3_PAE26974_454b8dc6", + [emptyPod5SkipFolder, emptyFast5SkipFolder], + metaData) + + then: + thrown(IllegalStateException) + + } + def "If either fastq pass or fail folder is empty, no IllegalStateException shall be thrown"() { given: def emptyFastQFailedFolder = FastQFailFolder.create("fastq_fail","root/fastq_fail", []) diff --git a/src/test/groovy/life/qbic/datamodel/datasets/datastructure/files/nanopore/GuppyBasecallLogSpec.groovy b/src/test/groovy/life/qbic/datamodel/datasets/datastructure/files/nanopore/GuppyBasecallLogSpec.groovy new file mode 100644 index 0000000000..27b5722952 --- /dev/null +++ b/src/test/groovy/life/qbic/datamodel/datasets/datastructure/files/nanopore/GuppyBasecallLogSpec.groovy @@ -0,0 +1,37 @@ +package life.qbic.datamodel.datasets.datastructure.files.nanopore + +import spock.lang.Specification + +/** + * + * + */ +class GuppyBasecallLogSpec extends Specification { + + def "shall create a GuppyBasecallingLog instance"() { + given: + final name = "guppy_basecall_client_log-.log" + final relativePath = "root/basecalling/guppy_basecall_client_log-.log" + + when: + def dataObject = GuppyBasecallLog.create(name, relativePath) + + then: + assert dataObject instanceof GuppyBasecallLog + assert dataObject.relativePath == relativePath + assert dataObject.name == name + } + + def "name not matching schema shall throw IllegalArgumentException"() { + given: + final name = "guppy_basecall.log" + final relativePath = "root/basecalling/guppy_basecall.log" + + when: + def dataObject = GuppyBasecallLog.create(name, relativePath) + + then: + thrown(IllegalArgumentException) + } + +} diff --git a/src/test/groovy/life/qbic/datamodel/datasets/datastructure/files/nanopore/SequencingTelemetryLogSpec.groovy b/src/test/groovy/life/qbic/datamodel/datasets/datastructure/files/nanopore/SequencingTelemetryLogSpec.groovy new file mode 100644 index 0000000000..d80b92861c --- /dev/null +++ b/src/test/groovy/life/qbic/datamodel/datasets/datastructure/files/nanopore/SequencingTelemetryLogSpec.groovy @@ -0,0 +1,37 @@ +package life.qbic.datamodel.datasets.datastructure.files.nanopore + +import spock.lang.Specification + +/** + * + * + */ +class SequencingTelemetryLogSpec extends Specification { + + def "shall create a SequencingTelemetryLog instance"() { + given: + final name = "sequencing_telemetry_.js" + final relativePath = "root/basecalling/sequencing_telemetry_.js" + + when: + def dataObject = SequencingTelemetryLog.create(name, relativePath) + + then: + assert dataObject instanceof SequencingTelemetryLog + assert dataObject.relativePath == relativePath + assert dataObject.name == name + } + + def "name not matching schema shall throw IllegalArgumentException"() { + given: + final name = "telemetry.log" + final relativePath = "root/basecalling/telemetry.log" + + when: + def dataObject = SequencingTelemetryLog.create(name, relativePath) + + then: + thrown(IllegalArgumentException) + } + +} diff --git a/src/test/groovy/life/qbic/datamodel/datasets/datastructure/files/nfcore/ExecutionReportSpec.groovy b/src/test/groovy/life/qbic/datamodel/datasets/datastructure/files/nfcore/ExecutionReportSpec.groovy index 5491ff3520..a0a4e79daa 100644 --- a/src/test/groovy/life/qbic/datamodel/datasets/datastructure/files/nfcore/ExecutionReportSpec.groovy +++ b/src/test/groovy/life/qbic/datamodel/datasets/datastructure/files/nfcore/ExecutionReportSpec.groovy @@ -11,8 +11,8 @@ class ExecutionReportSpec extends Specification { def "shall create a ExecutionReport instance"() { given: - final name = "execution_report.txt" - final relativePath = "root/execution_report.txt" + final name = "execution_report.html" + final relativePath = "root/execution_report.html" when: def dataObject = ExecutionReport.create(name, relativePath) diff --git a/src/test/groovy/life/qbic/datamodel/datasets/datastructure/files/nfcore/PipelineReportSpec.groovy b/src/test/groovy/life/qbic/datamodel/datasets/datastructure/files/nfcore/PipelineReportSpec.groovy deleted file mode 100644 index 3573c12c5d..0000000000 --- a/src/test/groovy/life/qbic/datamodel/datasets/datastructure/files/nfcore/PipelineReportSpec.groovy +++ /dev/null @@ -1,39 +0,0 @@ -package life.qbic.datamodel.datasets.datastructure.files.nfcore - -import spock.lang.Specification - -/** - * Test for the PipelineReport class - * - * @since 2.6.0 - */ -class PipelineReportSpec extends Specification { - - def "shall create a PipelineReport instance"() { - given: - final name = "pipeline_report.txt" - final relativePath = "root/pipeline_report.txt" - - when: - def dataObject = PipelineReport.create(name, relativePath) - - then: - assert dataObject instanceof PipelineReport - assert dataObject.relativePath == relativePath - assert dataObject.name == name - } - - def "name not matching schema shall throw IllegalArgumentException"() { - given: - final name = "bucket_report.txt" - final relativePath = "root/bucket_report.txt" - - when: - def dataObject = PipelineReport.create(name, relativePath) - - then: - thrown(IllegalArgumentException) - } - - -} diff --git a/src/test/groovy/life/qbic/datamodel/datasets/datastructure/files/nfcore/SoftwareVersionsSpec.groovy b/src/test/groovy/life/qbic/datamodel/datasets/datastructure/files/nfcore/SoftwareVersionsSpec.groovy index 40bdc06282..ed5997ca02 100644 --- a/src/test/groovy/life/qbic/datamodel/datasets/datastructure/files/nfcore/SoftwareVersionsSpec.groovy +++ b/src/test/groovy/life/qbic/datamodel/datasets/datastructure/files/nfcore/SoftwareVersionsSpec.groovy @@ -11,8 +11,8 @@ class SoftwareVersionsSpec extends Specification { def "shall create a SoftwareVersions instance"() { given: - final name = "software_versions.csv" - final path = "root/software_versions.csv" + final name = "software_versions.yml" + final path = "root/software_versions.yml" when: def dataObject = SoftwareVersions.create(name, path) diff --git a/src/test/groovy/life/qbic/datamodel/datasets/datastructure/folders/nanopore/BasecallingFolderSpec.groovy b/src/test/groovy/life/qbic/datamodel/datasets/datastructure/folders/nanopore/BasecallingFolderSpec.groovy new file mode 100644 index 0000000000..074c073b28 --- /dev/null +++ b/src/test/groovy/life/qbic/datamodel/datasets/datastructure/folders/nanopore/BasecallingFolderSpec.groovy @@ -0,0 +1,43 @@ +package life.qbic.datamodel.datasets.datastructure.folders.nanopore + +import life.qbic.datamodel.datasets.datastructure.files.DataFile +import life.qbic.datamodel.datasets.datastructure.files.nanopore.GuppyBasecallLog +import spock.lang.Specification + +/** + * + */ +class BasecallingFolderSpec extends Specification { + + def "create basecalling folder"() { + given: + final def name = "basecalling" + final def relativePath = "root/basecalling" + final def children = [] + final def datafile = GuppyBasecallLog.create("guppy_basecall_client_log-.log", "root/basecalling/guppy_basecall_client_log-.log") + children.add(datafile) + + when: + final def dataFolder = BasecallingFolder.create(name, relativePath, children) + + then: + assert dataFolder.getChildren().get(0) instanceof DataFile + + } + + def "naming schema violation should raise an IllegalArgumentException"() { + given: + final def name = "basedcall" + final def relativePath = "root/basedcall" + final def children = [] + final def datafile = GuppyBasecallLog.create("guppy_basecall_client_log-.log", "root/basedcall/guppy_basecall_client_log-.log") + children.add(datafile) + + when: + final def dataFolder = BasecallingFolder.create(name, relativePath, children) + + then: + thrown(IllegalArgumentException) + + } +} diff --git a/src/test/groovy/life/qbic/datamodel/datasets/datastructure/folders/nfcore/PipelineInformationFolderSpec.groovy b/src/test/groovy/life/qbic/datamodel/datasets/datastructure/folders/nfcore/PipelineInformationFolderSpec.groovy index 8e46114814..c7319f81b6 100644 --- a/src/test/groovy/life/qbic/datamodel/datasets/datastructure/folders/nfcore/PipelineInformationFolderSpec.groovy +++ b/src/test/groovy/life/qbic/datamodel/datasets/datastructure/folders/nfcore/PipelineInformationFolderSpec.groovy @@ -1,7 +1,6 @@ package life.qbic.datamodel.datasets.datastructure.folders.nfcore import life.qbic.datamodel.datasets.datastructure.files.nfcore.ExecutionReport -import life.qbic.datamodel.datasets.datastructure.files.nfcore.PipelineReport import life.qbic.datamodel.datasets.datastructure.files.nfcore.SoftwareVersions import spock.lang.Specification @@ -17,18 +16,15 @@ class PipelineInformationFolderSpec extends Specification { final def name = "pipeline_info" final def relativePath = "root/pipeline_info" final def children = [] - final def executionReport = ExecutionReport.create("execution_report.txt", "root/execution_report.txt") - final def pipelineReport = PipelineReport.create("pipeline_report.txt", "root/pipeline_report.txt") - final def softwareVersions = SoftwareVersions.create("software_versions.csv", "root/software_versions.csv") + final def executionReport = ExecutionReport.create("execution_report.html", "root/execution_report.html") + final def softwareVersions = SoftwareVersions.create("software_versions.yml", "root/software_versions.yml") when: final def pipelineInformationFolder = PipelineInformationFolder.create(name, relativePath, children) pipelineInformationFolder.executionReport = executionReport - pipelineInformationFolder.pipelineReport = pipelineReport pipelineInformationFolder.softwareVersions = softwareVersions then: assert pipelineInformationFolder.getExecutionReport() == executionReport - assert pipelineInformationFolder.getPipelineReport() == pipelineReport assert pipelineInformationFolder.getSoftwareVersions() == softwareVersions } @@ -38,7 +34,7 @@ class PipelineInformationFolderSpec extends Specification { final def name = "pipeline_unsatisfied" final def relativePath = "root/pipeline_unsatisfied" final def children = [] - final def datafile = ExecutionReport.create("execution_report_test.txt", "root/execution_report_test.txt") + final def datafile = ExecutionReport.create("execution_report_test.html", "root/execution_report_test.html") children.add(datafile) when: diff --git a/src/test/groovy/life/qbic/datamodel/datasets/datastructure/folders/nfcore/ProcessFolderSpec.groovy b/src/test/groovy/life/qbic/datamodel/datasets/datastructure/folders/nfcore/ProcessFolderSpec.groovy index df0dc07540..2cbaa6825b 100644 --- a/src/test/groovy/life/qbic/datamodel/datasets/datastructure/folders/nfcore/ProcessFolderSpec.groovy +++ b/src/test/groovy/life/qbic/datamodel/datasets/datastructure/folders/nfcore/ProcessFolderSpec.groovy @@ -15,7 +15,7 @@ class ProcessFolderSpec extends Specification { final def name = "processtest" final def relativePath = "root/processtest" final def children = [] - final def datafile = ExecutionReport.create("execution_report.txt", "root/processtest/execution_report.txt") + final def datafile = ExecutionReport.create("execution_report.html", "root/processtest/execution_report.html") children.add(datafile) when: diff --git a/src/test/groovy/life/qbic/datamodel/datasets/datastructure/folders/nfcore/QualityControlFolderSpec.groovy b/src/test/groovy/life/qbic/datamodel/datasets/datastructure/folders/nfcore/QualityControlFolderSpec.groovy index 694e46d481..d7a80dadb8 100644 --- a/src/test/groovy/life/qbic/datamodel/datasets/datastructure/folders/nfcore/QualityControlFolderSpec.groovy +++ b/src/test/groovy/life/qbic/datamodel/datasets/datastructure/folders/nfcore/QualityControlFolderSpec.groovy @@ -15,7 +15,7 @@ class QualityControlFolderSpec extends Specification { final def name = "multiqc" final def relativePath = "root/multiqc" final def children = [] - final def datafile = ExecutionReport.create("execution_report_test.txt", "root/execution_report_test.txt") + final def datafile = ExecutionReport.create("execution_report_test.html", "root/execution_report_test.html") children.add(datafile) when: @@ -31,7 +31,7 @@ class QualityControlFolderSpec extends Specification { final def name = "simpleqc" final def relativePath = "root/simpleqc" final def children = [] - final def datafile = ExecutionReport.create("execution_report_test.txt", "root/execution_report_test.txt") + final def datafile = ExecutionReport.create("execution_report_test.html", "root/execution_report_test.html") children.add(datafile) when: diff --git a/src/test/groovy/life/qbic/datamodel/dtos/business/ProductItemSpec.groovy b/src/test/groovy/life/qbic/datamodel/dtos/business/ProductItemSpec.groovy index 99a15799d3..a9aba0b287 100644 --- a/src/test/groovy/life/qbic/datamodel/dtos/business/ProductItemSpec.groovy +++ b/src/test/groovy/life/qbic/datamodel/dtos/business/ProductItemSpec.groovy @@ -1,9 +1,15 @@ package life.qbic.datamodel.dtos.business import life.qbic.datamodel.dtos.business.facilities.Facility +import life.qbic.datamodel.dtos.business.services.DataStorage +import life.qbic.datamodel.dtos.business.services.ExternalServiceProduct +import life.qbic.datamodel.dtos.business.services.MetabolomicAnalysis import life.qbic.datamodel.dtos.business.services.PrimaryAnalysis import life.qbic.datamodel.dtos.business.services.Product import life.qbic.datamodel.dtos.business.services.ProductUnit +import life.qbic.datamodel.dtos.business.services.ProjectManagement +import life.qbic.datamodel.dtos.business.services.ProteomicAnalysis +import life.qbic.datamodel.dtos.business.services.SecondaryAnalysis import life.qbic.datamodel.dtos.business.services.Sequencing import spock.lang.Specification import spock.lang.Unroll @@ -101,5 +107,23 @@ class ProductItemSpec extends Specification { product.currency.displayName == "Euro" } - + def "ProductItems allow partial quantities for all services"() { + + when: "ProductItems for all service products can be created" + ProductItem productItem = new ProductItem(quantity, product, totalPrice, quantityDiscount) + + then: "No exceptions are thrown" + noExceptionThrown() + + where: "for every service product" + quantity | internalUnitPrice | product | totalPrice | quantityDiscount + 1.1 | 1 | new Sequencing("Sequencing", "Sequencing description", 1.0, 3.0, ProductUnit.PER_SAMPLE, 1, Facility.QBIC) | quantity * internalUnitPrice | 0.0 + 2.2 | 2 | new PrimaryAnalysis("Primary Analysis", "Primary Analysis description", 2.0, 4.0, ProductUnit.PER_CYCLE, 2, Facility.QBIC) | quantity * internalUnitPrice | 0.0 + 3.3 | 3 | new SecondaryAnalysis("Secondary Analysis", "Secondary Analysis description", 3.0, 5.0, ProductUnit.PER_DATASET, 3, Facility.QBIC) | quantity * internalUnitPrice | 0.0 + 4.4 | 4 | new ProteomicAnalysis("Proteomic Analysis", "Proteomic Analysis description", 4.0, 6.0, ProductUnit.PER_FLOW_CELL, 4, Facility.QBIC) | quantity * internalUnitPrice | 0.0 + 5.5 | 5 | new MetabolomicAnalysis("Metabolomic Analysis", "Metabolomic Analysis description", 5.0, 7.0, ProductUnit.PER_100_MICROGRAM_PEPTIDE_CHANNEL, 5, Facility.QBIC) | quantity * internalUnitPrice | 0.0 + 6.6 | 6 | new ProjectManagement("Project Management", "Project Management description", 6.0, 8.0, ProductUnit.PER_HOUR, 6, Facility.QBIC) | quantity * internalUnitPrice | 0.0 + 7.7 | 7 | new DataStorage("Data Storage", "Data Storage description", 7.0, 9.0, ProductUnit.PER_GIGABYTE, 7, Facility.QBIC) | quantity * internalUnitPrice | 0.0 + 8.8 | 8 | new ExternalServiceProduct("External Service", "External Service description", 8.0, 10.0, ProductUnit.PER_PROJECT, 8, Facility.CEGAT) | quantity * 10 | 0.0 + } } diff --git a/src/test/groovy/life/qbic/datamodel/dtos/projectmanagement/ProjectSpaceSpec.groovy b/src/test/groovy/life/qbic/datamodel/dtos/projectmanagement/ProjectSpaceSpec.groovy index 7d9b9ee2fa..268fcbc4c7 100644 --- a/src/test/groovy/life/qbic/datamodel/dtos/projectmanagement/ProjectSpaceSpec.groovy +++ b/src/test/groovy/life/qbic/datamodel/dtos/projectmanagement/ProjectSpaceSpec.groovy @@ -29,17 +29,5 @@ class ProjectSpaceSpec extends Specification { then: thrown(IllegalArgumentException) - - } - - def "Dashes are replaced by underscores"() { - given: - String projectSpaceName = "my-new-space" - - when: - ProjectSpace space = new ProjectSpace(projectSpaceName) - - then: - space.name.equals("MY_NEW_SPACE") } } diff --git a/src/test/groovy/life/qbic/datamodel/maxquant/MaxQuantOutputSpec.groovy b/src/test/groovy/life/qbic/datamodel/maxquant/MaxQuantOutputSpec.groovy index 10cc845c82..642f1249ff 100644 --- a/src/test/groovy/life/qbic/datamodel/maxquant/MaxQuantOutputSpec.groovy +++ b/src/test/groovy/life/qbic/datamodel/maxquant/MaxQuantOutputSpec.groovy @@ -1,5 +1,6 @@ package life.qbic.datamodel.maxquant +import groovy.json.JsonSlurper import org.everit.json.schema.Schema import org.everit.json.schema.ValidationException import org.everit.json.schema.loader.SchemaClient @@ -35,7 +36,7 @@ class MaxQuantOutputSpec extends Specification { and: String validMaxQuantOutput = this.class.getClassLoader() - .getResourceAsStream("examples/resultset/maxquant/valid-resultset-example.json") + .getResourceAsStream("examples/resultset/maxquant/valid-resultset-example_latest.json") .text and: @@ -101,4 +102,52 @@ class MaxQuantOutputSpec extends Specification { then: thrown(ValidationException) } + + def "Validating the outdated schema v2 shall raise a validation exception"() { + given: + def stream = MaxQuantOutput.getSchemaAsStream() + + and: + String wrongMaxQuantOutput = this.class.getClassLoader() + .getResourceAsStream("examples/resultset/maxquant/old_valid-resultset-example.json") + .text + + and: + SchemaLoader schemaLoader = SchemaLoader.builder() + .schemaClient(SchemaClient.classPathAwareClient()) + .schemaJson(new JSONObject(new JSONTokener(stream))) + .resolutionScope("classpath://schemas/") + .build() + Schema schema = schemaLoader.load().build() + + when: + schema.validate(new JSONObject(wrongMaxQuantOutput)) + + then: + thrown(ValidationException) + } + + def "An invalid project code in the sample ids file shall raise a validation exception"() { + given: + def stream = MaxQuantOutput.getSchemaAsStream() + + and: + String missingMaxQuantOutput = this.class.getClassLoader() + .getResourceAsStream("examples/resultset/maxquant/invalid-project-resultset-example.json") + .text + + and: + SchemaLoader schemaLoader = SchemaLoader.builder() + .schemaClient(SchemaClient.classPathAwareClient()) + .schemaJson(new JSONObject(new JSONTokener(stream))) + .resolutionScope("classpath://schemas/") + .build() + Schema schema = schemaLoader.load().build() + + when: + schema.validate(new JSONObject(missingMaxQuantOutput)) + + then: + thrown(ValidationException) + } } diff --git a/src/test/resources/examples/resultset/maxquant/invalid-project-resultset-example.json b/src/test/resources/examples/resultset/maxquant/invalid-project-resultset-example.json new file mode 100644 index 0000000000..db9de06722 --- /dev/null +++ b/src/test/resources/examples/resultset/maxquant/invalid-project-resultset-example.json @@ -0,0 +1,37 @@ +{ + "allPeptides": { + "name": "allPeptides.txt", + "fileType": "txt", + "path": "./txt/allPeptides.txt" + }, + "evidence": { + "name": "evidence.txt", + "fileType": "txt", + "path": "./txt/evidence.txt" + }, + "parameters": { + "name": "parameters.txt", + "fileType": "txt", + "path": "./txt/parameters.txt" + }, + "peptides": { + "name": "peptides.txt", + "fileType": "txt", + "path": "./txt/peptides.txt" + }, + "proteinGroups": { + "name": "proteinGroups.txt", + "fileType": "txt", + "path": "./txt/proteinGroups.txt" + }, + "runParameters": { + "name": "mqpar.xml", + "fileType": "xml", + "path": "./mqpar.xml" + }, + "sampleIds": { + "name": "0010_sample_ids.txt", + "fileType": "txt", + "path": "./0010_sample_ids.txt" + } +} diff --git a/src/test/resources/examples/resultset/maxquant/valid-resultset-example.json b/src/test/resources/examples/resultset/maxquant/old_valid-resultset-example.json similarity index 100% rename from src/test/resources/examples/resultset/maxquant/valid-resultset-example.json rename to src/test/resources/examples/resultset/maxquant/old_valid-resultset-example.json diff --git a/src/test/resources/examples/resultset/maxquant/valid-resultset-example_latest.json b/src/test/resources/examples/resultset/maxquant/valid-resultset-example_latest.json new file mode 100644 index 0000000000..035e9c74f1 --- /dev/null +++ b/src/test/resources/examples/resultset/maxquant/valid-resultset-example_latest.json @@ -0,0 +1,37 @@ +{ + "allPeptides": { + "name": "allPeptides.txt", + "fileType": "txt", + "path": "./txt/allPeptides.txt" + }, + "evidence": { + "name": "evidence.txt", + "fileType": "txt", + "path": "./txt/evidence.txt" + }, + "parameters": { + "name": "parameters.txt", + "fileType": "txt", + "path": "./txt/parameters.txt" + }, + "peptides": { + "name": "peptides.txt", + "fileType": "txt", + "path": "./txt/peptides.txt" + }, + "proteinGroups": { + "name": "proteinGroups.txt", + "fileType": "txt", + "path": "./txt/proteinGroups.txt" + }, + "runParameters": { + "name": "mqpar.xml", + "fileType": "xml", + "path": "./mqpar.xml" + }, + "sampleIds": { + "name": "Q0010_sample_ids.txt", + "fileType": "txt", + "path": "./Q0010_sample_ids.txt" + } +} diff --git a/src/test/resources/examples/resultset/missing-process-folders-resultset-example.json b/src/test/resources/examples/resultset/missing-process-folders-resultset-example.json index 081ad35d20..a7e888821d 100644 --- a/src/test/resources/examples/resultset/missing-process-folders-resultset-example.json +++ b/src/test/resources/examples/resultset/missing-process-folders-resultset-example.json @@ -4,19 +4,14 @@ "path": "./", "children": [], "softwareVersions": { - "name": "software_versions.csv", - "fileType": "csv", - "path": "pipeline_info/software_versions.csv" - }, - "pipelineReport": { - "name": "pipeline_report.txt", - "fileType": "txt", - "path": "pipeline_info/pipeline_report.txt" + "name": "software_versions.yml", + "fileType": "yml", + "path": "pipeline_info/software_versions.yml" }, "executionReport": { - "name": "execution_report.txt", - "fileType": "txt", - "path": "pipeline_info/execution_report.txt" + "name": "execution_report.html", + "fileType": "html", + "path": "pipeline_info/execution_report.html" } }, "qualityControl": { diff --git a/src/test/resources/examples/resultset/missing-quality-control-resultset-example.json b/src/test/resources/examples/resultset/missing-quality-control-resultset-example.json index a92343ab7b..92ae052c1e 100644 --- a/src/test/resources/examples/resultset/missing-quality-control-resultset-example.json +++ b/src/test/resources/examples/resultset/missing-quality-control-resultset-example.json @@ -4,19 +4,14 @@ "path": "./", "children": [], "softwareVersions": { - "name": "software_versions.csv", - "fileType": "csv", - "path": "pipeline_info/software_versions.csv" - }, - "pipelineReport": { - "name": "pipeline_report.txt", - "fileType": "txt", - "path": "pipeline_info/pipeline_report.txt" + "name": "software_versions.yml", + "fileType": "yml", + "path": "pipeline_info/software_versions.yml" }, "executionReport": { - "name": "execution_report.txt", + "name": "execution_report.html", "fileType": "txt", - "path": "pipeline_info/execution_report.txt" + "path": "pipeline_info/execution_report.html" } }, "processFolders": [ diff --git a/src/test/resources/examples/resultset/valid-resultset-example.json b/src/test/resources/examples/resultset/valid-resultset-example.json index 2fade75833..c4c0887a6e 100644 --- a/src/test/resources/examples/resultset/valid-resultset-example.json +++ b/src/test/resources/examples/resultset/valid-resultset-example.json @@ -4,19 +4,14 @@ "path": "./pipeline_info", "children": [], "softwareVersions": { - "name": "software_versions.csv", - "fileType": "csv", - "path": "./pipeline_info/software_versions.csv" - }, - "pipelineReport": { - "name": "pipeline_report.txt", - "fileType": "txt", - "path": "./pipeline_info/pipeline_report.txt" + "name": "software_versions.yml", + "fileType": "yml", + "path": "./pipeline_info/software_versions.yml" }, "executionReport": { - "name": "execution_report.txt", - "fileType": "txt", - "path": "./pipeline_info/execution_report.txt" + "name": "execution_report.html", + "fileType": "html", + "path": "./pipeline_info/execution_report.html" } }, "qualityControl": { diff --git a/src/test/resources/examples/resultset/valid-resultset-no-run_id-example.json b/src/test/resources/examples/resultset/valid-resultset-no-run_id-example.json new file mode 100644 index 0000000000..746261055a --- /dev/null +++ b/src/test/resources/examples/resultset/valid-resultset-no-run_id-example.json @@ -0,0 +1,52 @@ +{ + "pipelineInformation": { + "name": "pipeline_info", + "path": "./pipeline_info", + "children": [], + "softwareVersions": { + "name": "software_versions.yml", + "fileType": "yml", + "path": "./pipeline_info/software_versions.yml" + }, + "executionReport": { + "name": "execution_report.html", + "fileType": "html", + "path": "./pipeline_info/execution_report.html" + } + }, + "qualityControl": { + "name": "multiqc", + "path": "./multiqc", + "children": [ + { + "name": "star_salmon", + "path": "./multiqc/star_salmon", + "children": [ + { + "name": "multiqc_report.html", + "path": "./multiqc/star_salmon/multiqc_report.html", + "fileType": "html" + } + ] + } + ] + }, + "processFolders": [ + { + "name": "salmon", + "path": "./salmon", + "children": [ + { + "name": "salmon.merged.gene_tpm.tsv", + "fileType": "tsv", + "path": "./salmon/salmon.merged.gene_tpm.tsv" + } + ] + } + ], + "sampleIds": { + "name": "sample_ids.txt", + "fileType": "txt", + "path": "./sample_ids.txt" + } +} diff --git a/src/test/resources/nanopore/valid-example-dorado-basecaller.json b/src/test/resources/nanopore/valid-example-dorado-basecaller.json new file mode 100644 index 0000000000..411f70d873 --- /dev/null +++ b/src/test/resources/nanopore/valid-example-dorado-basecaller.json @@ -0,0 +1,269 @@ +{ + "name": "QABCD001AB_E12A345a01_PAE12345", + "path": "./", + "children": [ + { + "name": "20200122_1217_1-A1-B1-PAE12345_1234567a", + "metadata": { + "adapter": "flongle", + "asic_temp": "32.631687", + "base_caller": "", + "base_caller_version": "3.2.8+bd67289", + "device_type": "promethion", + "flow_cell_id": "PAE26306", + "flow_cell_product_code": "FLO-PRO002", + "flow_cell_position": "2-A3-D3", + "hostname": "PCT0094", + "protocol": "sequencing/sequencing_PRO002_DNA:FLO-PRO002:SQK-LSK109:True", + "started": "2020-02-11T15:52:10.465982+01:00" + }, + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a", + "children": [ + { + "name": "report_.md", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/report_.md", + "file_type": "md" + }, + { + "name": "final_summary_.txt", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/final_summary_.txt", + "file_type": "txt" + }, + { + "name": "sequencing_summary_.txt", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/sequencing_summary_.txt", + "file_type": "txt" + }, + { + "name": "additional_file_.new", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/additional_file_.new", + "file_type": "new" + }, + { + "name": "not_relevant_file_.wow", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/not_relevant_file_.wow", + "file_type": "wow" + }, + { + "name": "unknown_folder", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/unknown_folder", + "children": [ + { + "name": "unknown_child_folder", + "path": "20200122_1217_1-A1-B1-PAE12345_1234567a/unknown_folder/unknown_child_folder", + "children": [ + { + "name": "unknown_file_.new", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/unknown_folder/unknown_child_folder/unknown_file_.new", + "file_type": "new" + } + ] + }, + { + "name": "unknown_file_.new", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/unknown_folder/unknown_file_.new", + "file_type": "new" + } + ] + }, + { + "name": "fast5_skip", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_skip/", + "children": [ + { + "name": "myfile2.fast5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_skip/myfile2.fast5", + "file_type": "fast5" + }, + { + "name": "myfile4.fast5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_skip/myfile4.fast5", + "file_type": "fast5" + }, + { + "name": "myfile3.fast5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_skip/myfile3.fast5", + "file_type": "fast5" + }, + { + "name": "myfile5.fast5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_skip/myfile5.fast5", + "file_type": "fast5" + }, + { + "name": "myfile.fast5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_skip/myfile.fast5", + "file_type": "fast5" + } + ] + }, + { + "name": "pod5_skip", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/pod5_skip/", + "children": [ + { + "name": "myfile2.pod5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/pod5_skip/myfile2.pod5", + "file_type": "pod5" + }, + { + "name": "myfile4.pod5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/pod5_skip/myfile4.pod5", + "file_type": "pod5" + }, + { + "name": "myfile3.pod5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/pod5_skip/myfile3.pod5", + "file_type": "pod5" + }, + { + "name": "myfile5.pod5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/pod5_skip/myfile5.pod5", + "file_type": "pod5" + }, + { + "name": "myfile.pod5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/pod5_skip/myfile.pod5", + "file_type": "pod5" + } + ] + }, + { + "name": "pod5_pass", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/pod5_pass", + "children": [ + { + "name": "myfile2.pod5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/pod5_pass/myfile2.pod5", + "file_type": "pod5" + }, + { + "name": "myfile4.pod5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/pod5_pass/myfile4.pod5", + "file_type": "pod5" + }, + { + "name": "myfile3.pod5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/pod5_pass/myfile3.pod5", + "file_type": "pod5" + }, + { + "name": "myfile5.pod5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/pod5_pass/myfile5.pod5", + "file_type": "pod5" + }, + { + "name": "myfile.pod5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/pod5_pass/myfile.pod5", + "file_type": "pod5" + } + ] + }, + { + "name": "pod5_fail", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/pod5_fail/", + "children": [ + { + "name": "myfile2.pod5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/pod5_fail/myfile2.pod5", + "file_type": "pod5" + }, + { + "name": "myfile4.pod5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/pod5_fail/myfile4.pod5", + "file_type": "pod5" + }, + { + "name": "myfile3.pod5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/pod5_fail/myfile3.pod5", + "file_type": "pod5" + }, + { + "name": "myfile5.pod5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/pod5_fail/myfile5.pod5", + "file_type": "pod5" + }, + { + "name": "myfile.pod5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/pod5_fail/myfile.pod5", + "file_type": "pod5" + } + ] + }, + { + "name": "basecalling", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/basecalling", + "children": [ + { + "name": "fastq_pass", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/basecalling/fastq_pass", + "children": [ + { + "name": "myfile3.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/basecalling/fastq_pass/myfile3.fastq.gz", + "file_type": "fastq.gz" + }, + { + "name": "myfile2.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/basecalling/fastq_pass/myfile2.fastq.gz", + "file_type": "fastq.gz" + }, + { + "name": "myfile4.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/basecalling/fastq_pass/myfile4.fastq.gz", + "file_type": "fastq.gz" + }, + { + "name": "myfile5.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/basecalling/fastq_pass/myfile5.fastq.gz", + "file_type": "fastq.gz" + }, + { + "name": "myfile1.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/basecalling/fastq_pass/myfile1.fastq.gz", + "file_type": "fastq.gz" + } + ] + }, + { + "name": "fastq_fail", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/basecalling/fastq_fail/", + "children": [ + { + "name": "myfile3.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/basecalling/fastq_fail/myfile3.fastq.gz", + "file_type": "fastq.gz" + }, + { + "name": "myfile2.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/basecalling/fastq_fail/myfile2.fastq.gz", + "file_type": "fastq.gz" + }, + { + "name": "myfile4.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/basecalling/fastq_fail/myfile4.fastq.gz", + "file_type": "fastq.gz" + }, + { + "name": "myfile5.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/basecalling/fastq_fail/myfile5.fastq.gz", + "file_type": "fastq.gz" + }, + { + "name": "myfile.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/basecalling/fastq_fail/myfile.fastq.gz", + "file_type": "fastq.gz" + } + ] + }, + { + "name": "guppy_basecall_client_log*.log", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/basecalling/guppy_basecall_client_log*.log", + "file_type": "log" + } + ] + } + ] + } + ] +} diff --git a/src/test/resources/nanopore/valid-example-html-report.json b/src/test/resources/nanopore/valid-example-html-report.json new file mode 100644 index 0000000000..62359785f3 --- /dev/null +++ b/src/test/resources/nanopore/valid-example-html-report.json @@ -0,0 +1,189 @@ +{ + "name": "QABCD001AB_E12A345a01_PAE12345", + "path": "./", + "children": [ + { + "name": "20200122_1217_1-A1-B1-PAE12345_1234567a", + "metadata": { + "adapter": "flongle", + "asic_temp": "32.631687", + "base_caller": "Guppy", + "base_caller_version": "3.2.8+bd67289", + "device_type" : "promethion", + "flow_cell_id": "PAE26306", + "flow_cell_product_code": "FLO-PRO002", + "flow_cell_position": "2-A3-D3", + "hostname": "PCT0094", + "protocol": "sequencing/sequencing_PRO002_DNA:FLO-PRO002:SQK-LSK109:True", + "started": "2020-02-11T15:52:10.465982+01:00" + }, + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a", + "children": [ + { + "name": "throughput_.csv", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/throughput_.csv", + "file_type": "csv" + }, + { + "name": "report_.md", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/report_.md", + "file_type": "md" + }, + { + "name": "final_summary_.txt", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/final_summary_.txt", + "file_type": "txt" + }, + { + "name": "fastq_pass", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fastq_pass", + "children": [ + { + "name": "myfile3.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fastq_pass/myfile3.fastq.gz", + "file_type": "fastq.gz" + }, + { + "name": "myfile2.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fastq_pass/myfile2.fastq.gz", + "file_type": "fastq.gz" + }, + { + "name": "myfile4.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fastq_pass/myfile4.fastq.gz", + "file_type": "fastq.gz" + }, + { + "name": "myfile5.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fastq_pass/myfile5.fastq.gz", + "file_type": "fastq.gz" + }, + { + "name": "myfile1.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fastq_pass/myfile1.fastq.gz", + "file_type": "fastq.gz" + } + ] + }, + { + "name": "fastq_fail", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fastq_fail/", + "children": [ + { + "name": "myfile3.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fastq_fail/myfile3.fastq.gz", + "file_type": "fastq.gz" + }, + { + "name": "myfile2.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fastq_fail/myfile2.fastq.gz", + "file_type": "fastq.gz" + }, + { + "name": "myfile4.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fastq_fail/myfile4.fastq.gz", + "file_type": "fastq.gz" + }, + { + "name": "myfile5.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fastq_fail/myfile5.fastq.gz", + "file_type": "fastq.gz" + }, + { + "name": "myfile.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fastq_fail/myfile.fastq.gz", + "file_type": "fastq.gz" + } + ] + }, + { + "name": "duty_time_.csv", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/duty_time_.csv", + "file_type": "csv" + }, + { + "name": "sequencing_summary_.txt", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/sequencing_summary_.txt", + "file_type": "txt" + }, + { + "name": "mux_scan_data.csv", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/mux_scan_data.csv", + "file_type": "csv" + }, + { + "name": "drift_correction_.csv", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/drift_correction_.csv", + "file_type": "csv" + }, + { + "name": "report_test.html", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/report_test.html", + "file_type": "html" + }, + { + "name": "fast5_fail", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_fail/", + "children": [ + { + "name": "myfile2.fast5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_fail/myfile2.fast5", + "file_type": "fast5" + }, + { + "name": "myfile4.fast5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_fail/myfile4.fast5", + "file_type": "fast5" + }, + { + "name": "myfile3.fast5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_fail/myfile3.fast5", + "file_type": "fast5" + }, + { + "name": "myfile5.fast5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_fail/myfile5.fast5", + "file_type": "fast5" + }, + { + "name": "myfile.fast5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_fail/myfile.fast5", + "file_type": "fast5" + } + ] + }, + { + "name": "fast5_pass", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_pass/", + "children": [ + { + "name": "myfile2.fast5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_pass/myfile2.fast5", + "file_type": "fast5" + }, + { + "name": "myfile4.fast5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_pass/myfile4.fast5", + "file_type": "fast5" + }, + { + "name": "myfile3.fast5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_pass/myfile3.fast5", + "file_type": "fast5" + }, + { + "name": "myfile5.fast5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_pass/myfile5.fast5", + "file_type": "fast5" + }, + { + "name": "myfile.fast5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_pass/myfile.fast5", + "file_type": "fast5" + } + ] + } + ] + } + ] +} diff --git a/src/test/resources/valid-example-pooled.json b/src/test/resources/nanopore/valid-example-pooled.json similarity index 100% rename from src/test/resources/valid-example-pooled.json rename to src/test/resources/nanopore/valid-example-pooled.json diff --git a/src/test/resources/valid-example-unclassified.json b/src/test/resources/nanopore/valid-example-unclassified.json similarity index 100% rename from src/test/resources/valid-example-unclassified.json rename to src/test/resources/nanopore/valid-example-unclassified.json diff --git a/src/test/resources/valid-example-newer.json b/src/test/resources/nanopore/valid-example-v2.json similarity index 100% rename from src/test/resources/valid-example-newer.json rename to src/test/resources/nanopore/valid-example-v2.json diff --git a/src/test/resources/nanopore/valid-example-v3.json b/src/test/resources/nanopore/valid-example-v3.json new file mode 100644 index 0000000000..1d09b7c337 --- /dev/null +++ b/src/test/resources/nanopore/valid-example-v3.json @@ -0,0 +1,205 @@ +{ + "name": "QABCD001AB_E12A345a01_PAE12345", + "path": "./", + "children": [ + { + "name": "20200122_1217_1-A1-B1-PAE12345_1234567a", + "metadata": { + "adapter": "flongle", + "asic_temp": "32.631687", + "base_caller": "Guppy", + "base_caller_version": "3.2.8+bd67289", + "device_type" : "promethion", + "flow_cell_id": "PAE26306", + "flow_cell_product_code": "FLO-PRO002", + "flow_cell_position": "2-A3-D3", + "hostname": "PCT0094", + "protocol": "protocol=sequencing/sequencing_PRO002_DNA:FLO-PRO002:SQK-LSK109-XL", + "started": "2020-02-11T15:52:10.465982+01:00" + }, + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a", + "children": [ + { + "name": "throughput_.csv", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/throughput_.csv", + "file_type": "csv" + }, + { + "name": "report_.md", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/report_.md", + "file_type": "md" + }, + { + "name": "final_summary_.txt", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/final_summary_.txt", + "file_type": "txt" + }, + { + "name": "barcode_alignment_.tsv", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/barcode_alignment_.tsv", + "file_type": "tsv" + }, + { + "name": "pore_activity_.csv", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/pore_activity_.csv", + "file_type": "tsv" + }, + { + "name": "other_reports", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/other_reports", + "children": [ + { + "name": "pore_scan_data_.csv", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/other_reports/pore_scan_data_.csv", + "file_type": "csv" + } + ] + }, + { + "name": "fastq_pass", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fastq_pass", + "children": [ + { + "name": "myfile3.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fastq_pass/myfile3.fastq.gz", + "file_type": "fastq.gz" + }, + { + "name": "myfile2.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fastq_pass/myfile2.fastq.gz", + "file_type": "fastq.gz" + }, + { + "name": "myfile4.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fastq_pass/myfile4.fastq.gz", + "file_type": "fastq.gz" + }, + { + "name": "myfile5.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fastq_pass/myfile5.fastq.gz", + "file_type": "fastq.gz" + }, + { + "name": "myfile1.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fastq_pass/myfile1.fastq.gz", + "file_type": "fastq.gz" + } + ] + }, + { + "name": "fastq_fail", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fastq_fail/", + "children": [ + { + "name": "myfile3.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fastq_fail/myfile3.fastq.gz", + "file_type": "fastq.gz" + }, + { + "name": "myfile2.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fastq_fail/myfile2.fastq.gz", + "file_type": "fastq.gz" + }, + { + "name": "myfile4.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fastq_fail/myfile4.fastq.gz", + "file_type": "fastq.gz" + }, + { + "name": "myfile5.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fastq_fail/myfile5.fastq.gz", + "file_type": "fastq.gz" + }, + { + "name": "myfile.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fastq_fail/myfile.fastq.gz", + "file_type": "fastq.gz" + } + ] + }, + { + "name": "sequencing_summary_.txt", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/sequencing_summary_.txt", + "file_type": "txt" + }, + { + "name": "report_test.html", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/report_test.html", + "file_type": "html" + }, + { + "name": "report_test.json", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/report_test.json", + "file_type": "json" + }, + { + "name": "sample_sheet_test.csv", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/sample_sheet_test.csv", + "file_type": "csv" + }, + { + "name": "fast5_fail", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_fail/", + "children": [ + { + "name": "myfile2.fast5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_fail/myfile2.fast5", + "file_type": "fast5" + }, + { + "name": "myfile4.fast5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_fail/myfile4.fast5", + "file_type": "fast5" + }, + { + "name": "myfile3.fast5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_fail/myfile3.fast5", + "file_type": "fast5" + }, + { + "name": "myfile5.fast5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_fail/myfile5.fast5", + "file_type": "fast5" + }, + { + "name": "myfile.fast5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_fail/myfile.fast5", + "file_type": "fast5" + } + ] + }, + { + "name": "fast5_pass", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_pass/", + "children": [ + { + "name": "myfile2.fast5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_pass/myfile2.fast5", + "file_type": "fast5" + }, + { + "name": "myfile4.fast5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_pass/myfile4.fast5", + "file_type": "fast5" + }, + { + "name": "myfile3.fast5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_pass/myfile3.fast5", + "file_type": "fast5" + }, + { + "name": "myfile5.fast5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_pass/myfile5.fast5", + "file_type": "fast5" + }, + { + "name": "myfile.fast5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_pass/myfile.fast5", + "file_type": "fast5" + } + ] + } + ] + } + ] +} diff --git a/src/test/resources/nanopore/valid-example-v4-with-basecalling.json b/src/test/resources/nanopore/valid-example-v4-with-basecalling.json new file mode 100644 index 0000000000..2471b09b2c --- /dev/null +++ b/src/test/resources/nanopore/valid-example-v4-with-basecalling.json @@ -0,0 +1,267 @@ +{ + "name": "QABCD001AB_E12A345a01_PAE12345", + "path": "./", + "children": [ + { + "name": "20200122_1217_1-A1-B1-PAE12345_1234567a", + "metadata": { + "adapter": "flongle", + "asic_temp": "32.631687", + "base_caller": "Guppy", + "base_caller_version": "3.2.8+bd67289", + "device_type": "promethion", + "flow_cell_id": "PAE26306", + "flow_cell_product_code": "FLO-PRO002", + "flow_cell_position": "2-A3-D3", + "hostname": "PCT0094", + "protocol": "sequencing/sequencing_PRO002_DNA:FLO-PRO002:SQK-LSK109:True", + "started": "2020-02-11T15:52:10.465982+01:00" + }, + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a", + "children": [ + { + "name": "throughput_.csv", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/throughput_.csv", + "file_type": "csv" + }, + { + "name": "report_.md", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/report_.md", + "file_type": "md" + }, + { + "name": "final_summary_.txt", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/final_summary_.txt", + "file_type": "txt" + }, + { + "name": "fastq_pass", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fastq_pass", + "children": [ + { + "name": "myfile3.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fastq_pass/myfile3.fastq.gz", + "file_type": "fastq.gz" + }, + { + "name": "myfile2.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fastq_pass/myfile2.fastq.gz", + "file_type": "fastq.gz" + }, + { + "name": "myfile4.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fastq_pass/myfile4.fastq.gz", + "file_type": "fastq.gz" + }, + { + "name": "myfile5.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fastq_pass/myfile5.fastq.gz", + "file_type": "fastq.gz" + }, + { + "name": "myfile1.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fastq_pass/myfile1.fastq.gz", + "file_type": "fastq.gz" + } + ] + }, + { + "name": "fastq_fail", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fastq_fail/", + "children": [ + { + "name": "myfile3.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fastq_fail/myfile3.fastq.gz", + "file_type": "fastq.gz" + }, + { + "name": "myfile2.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fastq_fail/myfile2.fastq.gz", + "file_type": "fastq.gz" + }, + { + "name": "myfile4.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fastq_fail/myfile4.fastq.gz", + "file_type": "fastq.gz" + }, + { + "name": "myfile5.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fastq_fail/myfile5.fastq.gz", + "file_type": "fastq.gz" + }, + { + "name": "myfile.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fastq_fail/myfile.fastq.gz", + "file_type": "fastq.gz" + } + ] + }, + { + "name": "duty_time_.csv", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/duty_time_.csv", + "file_type": "csv" + }, + { + "name": "sequencing_summary_.txt", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/sequencing_summary_.txt", + "file_type": "txt" + }, + { + "name": "mux_scan_data.csv", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/mux_scan_data.csv", + "file_type": "csv" + }, + { + "name": "drift_correction_.csv", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/drift_correction_.csv", + "file_type": "csv" + }, + { + "name": "fast5_fail", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_fail/", + "children": [ + { + "name": "myfile2.fast5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_fail/myfile2.fast5", + "file_type": "fast5" + }, + { + "name": "myfile4.fast5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_fail/myfile4.fast5", + "file_type": "fast5" + }, + { + "name": "myfile3.fast5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_fail/myfile3.fast5", + "file_type": "fast5" + }, + { + "name": "myfile5.fast5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_fail/myfile5.fast5", + "file_type": "fast5" + }, + { + "name": "myfile.fast5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_fail/myfile.fast5", + "file_type": "fast5" + } + ] + }, + { + "name": "fast5_pass", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_pass/", + "children": [ + { + "name": "myfile2.fast5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_pass/myfile2.fast5", + "file_type": "fast5" + }, + { + "name": "myfile4.fast5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_pass/myfile4.fast5", + "file_type": "fast5" + }, + { + "name": "myfile3.fast5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_pass/myfile3.fast5", + "file_type": "fast5" + }, + { + "name": "myfile5.fast5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_pass/myfile5.fast5", + "file_type": "fast5" + }, + { + "name": "myfile.fast5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_pass/myfile.fast5", + "file_type": "fast5" + } + ] + }, + { + "name": "basecalling", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/basecalling/", + "children": [ + { + "name": "sequencing_summary_.txt", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/basecalling/sequencing_summary_.txt", + "file_type": "txt" + }, + { + "name": "sequencing_telemetry_.js", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/basecalling/sequencing_telemetry_.js", + "file_type": "js" + }, + { + "name": "guppy_basecall_client_log-1234-56-78_90.log", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/basecalling/guppy_basecall_client_log-1234-56-78_90.log", + "file_type": "log" + }, + { + "name": "fastq_pass", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/basecalling/fastq_pass/", + "children": [ + { + "name": "myfile3.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/basecalling/fastq_pass/myfile3.fastq.gz", + "file_type": "fastq.gz" + }, + { + "name": "myfile2.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/basecalling/fastq_pass/myfile2.fastq.gz", + "file_type": "fastq.gz" + }, + { + "name": "myfile4.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/basecalling/fastq_pass/myfile4.fastq.gz", + "file_type": "fastq.gz" + }, + { + "name": "myfile5.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/basecalling/fastq_pass/myfile5.fastq.gz", + "file_type": "fastq.gz" + }, + { + "name": "myfile.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/basecalling/fastq_pass/myfile.fastq.gz", + "file_type": "fastq.gz" + } + ] + }, + { + "name": "fastq_fail", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/basecalling/fastq_fail/", + "children": [ + { + "name": "myfile3.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/basecalling/fastq_fail/myfile3.fastq.gz", + "file_type": "fastq.gz" + }, + { + "name": "myfile2.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/basecalling/fastq_fail/myfile2.fastq.gz", + "file_type": "fastq.gz" + }, + { + "name": "myfile4.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/basecalling/fastq_fail/myfile4.fastq.gz", + "file_type": "fastq.gz" + }, + { + "name": "myfile5.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/basecalling/fastq_fail/myfile5.fastq.gz", + "file_type": "fastq.gz" + }, + { + "name": "myfile.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/basecalling/fastq_fail/myfile.fastq.gz", + "file_type": "fastq.gz" + } + ] + } + ] + } + ] + } + ] +} diff --git a/src/test/resources/valid-example.json b/src/test/resources/nanopore/valid-example.json similarity index 100% rename from src/test/resources/valid-example.json rename to src/test/resources/nanopore/valid-example.json diff --git a/src/test/resources/nanopore/valid-minimal-structure-dorado-basecaller.json b/src/test/resources/nanopore/valid-minimal-structure-dorado-basecaller.json new file mode 100644 index 0000000000..9a60ed1cdd --- /dev/null +++ b/src/test/resources/nanopore/valid-minimal-structure-dorado-basecaller.json @@ -0,0 +1,207 @@ +{ + "name": "QABCD001AB_E12A345a01_PAE12345", + "path": "./", + "children": [ + { + "name": "20200122_1217_1-A1-B1-PAE12345_1234567a", + "metadata": { + "adapter": "flongle", + "asic_temp": "32.631687", + "base_caller": "", + "base_caller_version": "3.2.8+bd67289", + "device_type": "promethion", + "flow_cell_id": "PAE26306", + "flow_cell_product_code": "FLO-PRO002", + "flow_cell_position": "2-A3-D3", + "hostname": "PCT0094", + "protocol": "sequencing/sequencing_PRO002_DNA:FLO-PRO002:SQK-LSK109:True", + "started": "2020-02-11T15:52:10.465982+01:00" + }, + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a", + "children": [ + { + "name": "report_.md", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/report_.md", + "file_type": "md" + }, + { + "name": "final_summary_.txt", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/final_summary_.txt", + "file_type": "txt" + }, + { + "name": "sequencing_summary_.txt", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/sequencing_summary_.txt", + "file_type": "txt" + }, + { + "name": "additional_file_.new", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/additional_file_.new", + "file_type": "new" + }, + { + "name": "not_relevant_file_.wow", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/not_relevant_file_.wow", + "file_type": "wow" + }, + { + "name": "unknown_folder", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/unknown_folder", + "children": [ + { + "name": "unknown_child_folder", + "path": "20200122_1217_1-A1-B1-PAE12345_1234567a/unknown_folder/unknown_child_folder", + "children": [ + { + "name": "unknown_file_.new", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/unknown_folder/unknown_child_folder/unknown_file_.new", + "file_type": "new" + } + ] + }, + { + "name": "unknown_file_.new", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/unknown_folder/unknown_file_.new", + "file_type": "new" + } + ] + }, + { + "name": "fast5_skip", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_skip/", + "children": [ + { + "name": "myfile2.fast5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_skip/myfile2.fast5", + "file_type": "fast5" + }, + { + "name": "myfile4.fast5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_skip/myfile4.fast5", + "file_type": "fast5" + }, + { + "name": "myfile3.fast5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_skip/myfile3.fast5", + "file_type": "fast5" + }, + { + "name": "myfile5.fast5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_skip/myfile5.fast5", + "file_type": "fast5" + }, + { + "name": "myfile.fast5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_skip/myfile.fast5", + "file_type": "fast5" + } + ] + }, + { + "name": "pod5_skip", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/pod5_skip/", + "children": [ + { + "name": "myfile2.pod5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/pod5_skip/myfile2.pod5", + "file_type": "pod5" + }, + { + "name": "myfile4.pod5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/pod5_skip/myfile4.pod5", + "file_type": "pod5" + }, + { + "name": "myfile3.pod5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/pod5_skip/myfile3.pod5", + "file_type": "pod5" + }, + { + "name": "myfile5.pod5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/pod5_skip/myfile5.pod5", + "file_type": "pod5" + }, + { + "name": "myfile.pod5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/pod5_skip/myfile.pod5", + "file_type": "pod5" + } + ] + }, + { + "name": "basecalling", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/basecalling", + "children": [ + { + "name": "fastq_pass", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/basecalling/fastq_pass", + "children": [ + { + "name": "myfile3.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/basecalling/fastq_pass/myfile3.fastq.gz", + "file_type": "fastq.gz" + }, + { + "name": "myfile2.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/basecalling/fastq_pass/myfile2.fastq.gz", + "file_type": "fastq.gz" + }, + { + "name": "myfile4.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/basecalling/fastq_pass/myfile4.fastq.gz", + "file_type": "fastq.gz" + }, + { + "name": "myfile5.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/basecalling/fastq_pass/myfile5.fastq.gz", + "file_type": "fastq.gz" + }, + { + "name": "myfile1.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/basecalling/fastq_pass/myfile1.fastq.gz", + "file_type": "fastq.gz" + } + ] + }, + { + "name": "fastq_fail", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/basecalling/fastq_fail/", + "children": [ + { + "name": "myfile3.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/basecalling/fastq_fail/myfile3.fastq.gz", + "file_type": "fastq.gz" + }, + { + "name": "myfile2.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/basecalling/fastq_fail/myfile2.fastq.gz", + "file_type": "fastq.gz" + }, + { + "name": "myfile4.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/basecalling/fastq_fail/myfile4.fastq.gz", + "file_type": "fastq.gz" + }, + { + "name": "myfile5.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/basecalling/fastq_fail/myfile5.fastq.gz", + "file_type": "fastq.gz" + }, + { + "name": "myfile.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/basecalling/fastq_fail/myfile.fastq.gz", + "file_type": "fastq.gz" + } + ] + }, + { + "name": "guppy_basecall_client_log*.log", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/basecalling/guppy_basecall_client_log*.log", + "file_type": "log" + } + ] + } + ] + } + ] +} diff --git a/src/test/resources/nanopore/valid-minimal-structure-pooled.json b/src/test/resources/nanopore/valid-minimal-structure-pooled.json new file mode 100644 index 0000000000..976f4be218 --- /dev/null +++ b/src/test/resources/nanopore/valid-minimal-structure-pooled.json @@ -0,0 +1,344 @@ +{ + "name": "QABCD001AB_E12A345a01_PAE12345", + "path": "./", + "children": [ + { + "name": "20200122_1217_1-A1-B1-PAE12345_1234567a", + "metadata": { + "adapter": "flongle", + "asic_temp": "32.631687", + "base_caller": "Guppy", + "base_caller_version": "3.2.8+bd67289", + "device_type": "promethion", + "flow_cell_id": "PAE26306", + "flow_cell_product_code": "FLO-PRO002", + "flow_cell_position": "2-A3-D3", + "hostname": "PCT0094", + "protocol": "sequencing/sequencing_PRO002_DNA:FLO-PRO002:SQK-LSK109:True", + "started": "2020-02-11T15:52:10.465982+01:00" + }, + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a", + "children": [ + { + "name": "report_.md", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/report_.md", + "file_type": "md" + }, + { + "name": "final_summary_.txt", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/final_summary_.txt", + "file_type": "txt" + }, + { + "name": "sequencing_summary_.txt", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/sequencing_summary_.txt", + "file_type": "txt" + }, + { + "name": "additional_file_.new", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/additional_file_.new", + "file_type": "new" + }, + { + "name": "not_relevant_file_.wow", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/not_relevant_file_.wow", + "file_type": "wow" + }, + { + "name": "unknown_folder", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/unknown_folder", + "children": [ + { + "name": "unknown_child_folder", + "path": "20200122_1217_1-A1-B1-PAE12345_1234567a/unknown_folder/unknown_child_folder", + "children": [ + { + "name": "unknown_file_.new", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/unknown_folder/unknown_child_folder/unknown_file_.new", + "file_type": "new" + } + ] + }, + { + "name": "unknown_file_.new", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/unknown_folder/unknown_file_.new", + "file_type": "new" + } + ] + }, + { + "name": "fastq_pass", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fastq_pass", + "children": [ + { + "name": "QABCD001AB", + "path": "20200122_1217_1-A1-B1-PAE12345_1234567a/fastq_pass/QABCD001AB", + "children": [ + { + "name": "myfile3.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fastq_pass/QABCD001AB/myfile3.fastq.gz", + "file_type": "fastq.gz" + }, + { + "name": "myfile2.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fastq_pass/QABCD001AB/myfile2.fastq.gz", + "file_type": "fastq.gz" + }, + { + "name": "myfile4.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fastq_pass/QABCD001AB/myfile4.fastq.gz", + "file_type": "fastq.gz" + }, + { + "name": "myfile5.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fastq_pass/QABCD001AB/myfile5.fastq.gz", + "file_type": "fastq.gz" + }, + { + "name": "myfile1.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fastq_pass/QABCD001AB/myfile1.fastq.gz", + "file_type": "fastq.gz" + } + ] + }, + { + "name": "QABCD002CD", + "path": "20200122_1217_1-A1-B1-PAE12345_1234567a/fastq_pass/QABCD002CD", + "children": [ + { + "name": "myfile3.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fastq_pass/QABCD002CD/myfile3.fastq.gz", + "file_type": "fastq.gz" + }, + { + "name": "myfile2.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fastq_pass/QABCD002CD/myfile2.fastq.gz", + "file_type": "fastq.gz" + }, + { + "name": "myfile4.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fastq_pass/QABCD002CD/myfile4.fastq.gz", + "file_type": "fastq.gz" + }, + { + "name": "myfile5.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fastq_pass/QABCD002CD/myfile5.fastq.gz", + "file_type": "fastq.gz" + }, + { + "name": "myfile1.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fastq_pass/QABCD002CD/myfile1.fastq.gz", + "file_type": "fastq.gz" + } + ] + } + ] + }, + { + "name": "fastq_fail", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fastq_fail", + "children": [ + { + "name": "QABCD001AB", + "path": "20200122_1217_1-A1-B1-PAE12345_1234567a/fastq_fail/QABCD001AB", + "children": [ + { + "name": "myfile3.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fastq_fail/QABCD001AB/myfile3.fastq.gz", + "file_type": "fastq.gz" + }, + { + "name": "myfile2.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fastq_fail/QABCD001AB/myfile2.fastq.gz", + "file_type": "fastq.gz" + }, + { + "name": "myfile4.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fastq_fail/QABCD001AB/myfile4.fastq.gz", + "file_type": "fastq.gz" + }, + { + "name": "myfile5.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fastq_fail/QABCD001AB/myfile5.fastq.gz", + "file_type": "fastq.gz" + }, + { + "name": "myfile1.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fastq_fail/QABCD001AB/myfile1.fastq.gz", + "file_type": "fastq.gz" + } + ] + }, + { + "name": "QABCD002CD", + "path": "20200122_1217_1-A1-B1-PAE12345_1234567a/fastq_fail/QABCD002CD", + "children": [ + { + "name": "myfile3.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fastq_fail/QABCD002CD/myfile3.fastq.gz", + "file_type": "fastq.gz" + }, + { + "name": "myfile2.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fastq_fail/QABCD002CD/myfile2.fastq.gz", + "file_type": "fastq.gz" + }, + { + "name": "myfile4.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fastq_fail/QABCD002CD/myfile4.fastq.gz", + "file_type": "fastq.gz" + }, + { + "name": "myfile5.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fastq_fail/QABCD002CD/myfile5.fastq.gz", + "file_type": "fastq.gz" + }, + { + "name": "myfile1.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fastq_fail/QABCD002CD/myfile1.fastq.gz", + "file_type": "fastq.gz" + } + ] + } + ] + }, + { + "name": "fast5_fail", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_fail", + "children": [ + { + "name": "QABCD001AB", + "path": "20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_fail/QABCD001AB", + "children": [ + { + "name": "myfile3.fast5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_fail/QABCD001AB/myfile3.fast5", + "file_type": "fast5" + }, + { + "name": "myfile2.fast5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_fail/QABCD001AB/myfile2.fast5", + "file_type": "fast5" + }, + { + "name": "myfile4.fast5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_fail/QABCD001AB/myfile4.fast5", + "file_type": "fast5" + }, + { + "name": "myfile5.fast5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_fail/QABCD001AB/myfile5.fast5", + "file_type": "fast5" + }, + { + "name": "myfile1.fast5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_fail/QABCD001AB/myfile1.fast5", + "file_type": "fast5" + } + ] + }, + { + "name": "QABCD002CD", + "path": "20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_fail/QABCD002CD", + "children": [ + { + "name": "myfile3.fast5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_fail/QABCD002CD/myfile3.fast5", + "file_type": "fast5" + }, + { + "name": "myfile2.fast5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_fail/QABCD002CD/myfile2.fast5", + "file_type": "fast5" + }, + { + "name": "myfile4.fast5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_fail/QABCD002CD/myfile4.fast5", + "file_type": "fast5" + }, + { + "name": "myfile5.fast5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_fail/QABCD002CD/myfile5.fast5", + "file_type": "fast5" + }, + { + "name": "myfile1.fast5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_fail/QABCD002CD/myfile1.fast5", + "file_type": "fast5" + } + ] + } + ] + }, + { + "name": "fast5_pass", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_pass", + "children": [ + { + "name": "QABCD001AB", + "path": "20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_pass/QABCD001AB", + "children": [ + { + "name": "myfile3.fast5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_pass/QABCD001AB/myfile3.fast5", + "file_type": "fast5" + }, + { + "name": "myfile2.fast5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_pass/QABCD001AB/myfile2.fast5", + "file_type": "fast5" + }, + { + "name": "myfile4.fast5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_pass/QABCD001AB/myfile4.fast5", + "file_type": "fast5" + }, + { + "name": "myfile5.fast5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_pass/QABCD001AB/myfile5.fast5", + "file_type": "fast5" + }, + { + "name": "myfile1.fast5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_pass/QABCD001AB/myfile1.fast5", + "file_type": "fast5" + } + ] + }, + { + "name": "QABCD002CD", + "path": "20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_pass/QABCD002CD", + "children": [ + { + "name": "myfile3.fast5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_pass/QABCD002CD/myfile3.fast5", + "file_type": "fast5" + }, + { + "name": "myfile2.fast5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_pass/QABCD002CD/myfile2.fast5", + "file_type": "fast5" + }, + { + "name": "myfile4.fast5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_pass/QABCD002CD/myfile4.fast5", + "file_type": "fast5" + }, + { + "name": "myfile5.fast5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_pass/QABCD002CD/myfile5.fast5", + "file_type": "fast5" + }, + { + "name": "myfile1.fast5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_pass/QABCD002CD/myfile1.fast5", + "file_type": "fast5" + } + ] + } + ] + } + ] + } + ] +} diff --git a/src/test/resources/nanopore/valid-minimal-structure.json b/src/test/resources/nanopore/valid-minimal-structure.json new file mode 100644 index 0000000000..04fb0135e5 --- /dev/null +++ b/src/test/resources/nanopore/valid-minimal-structure.json @@ -0,0 +1,196 @@ +{ + "name": "QABCD001AB_E12A345a01_PAE12345", + "path": "./", + "children": [ + { + "name": "20200122_1217_1-A1-B1-PAE12345_1234567a", + "metadata": { + "adapter": "flongle", + "asic_temp": "32.631687", + "base_caller": "Guppy", + "base_caller_version": "3.2.8+bd67289", + "device_type": "promethion", + "flow_cell_id": "PAE26306", + "flow_cell_product_code": "FLO-PRO002", + "flow_cell_position": "2-A3-D3", + "hostname": "PCT0094", + "protocol": "sequencing/sequencing_PRO002_DNA:FLO-PRO002:SQK-LSK109:True", + "started": "2020-02-11T15:52:10.465982+01:00" + }, + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a", + "children": [ + { + "name": "report_.md", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/report_.md", + "file_type": "md" + }, + { + "name": "final_summary_.txt", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/final_summary_.txt", + "file_type": "txt" + }, + { + "name": "sequencing_summary_.txt", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/sequencing_summary_.txt", + "file_type": "txt" + }, + { + "name": "additional_file_.new", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/additional_file_.new", + "file_type": "new" + }, + { + "name": "not_relevant_file_.wow", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/not_relevant_file_.wow", + "file_type": "wow" + }, + { + "name": "unknown_folder", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/unknown_folder", + "children": [ + { + "name": "unknown_child_folder", + "path": "20200122_1217_1-A1-B1-PAE12345_1234567a/unknown_folder/unknown_child_folder", + "children": [ + { + "name": "unknown_file_.new", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/unknown_folder/unknown_child_folder/unknown_file_.new", + "file_type": "new" + } + ] + }, + { + "name": "unknown_file_.new", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/unknown_folder/unknown_file_.new", + "file_type": "new" + } + ] + }, + { + "name": "fastq_pass", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fastq_pass", + "children": [ + { + "name": "myfile3.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fastq_pass/myfile3.fastq.gz", + "file_type": "fastq.gz" + }, + { + "name": "myfile2.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fastq_pass/myfile2.fastq.gz", + "file_type": "fastq.gz" + }, + { + "name": "myfile4.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fastq_pass/myfile4.fastq.gz", + "file_type": "fastq.gz" + }, + { + "name": "myfile5.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fastq_pass/myfile5.fastq.gz", + "file_type": "fastq.gz" + }, + { + "name": "myfile1.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fastq_pass/myfile1.fastq.gz", + "file_type": "fastq.gz" + } + ] + }, + { + "name": "fastq_fail", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fastq_fail/", + "children": [ + { + "name": "myfile3.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fastq_fail/myfile3.fastq.gz", + "file_type": "fastq.gz" + }, + { + "name": "myfile2.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fastq_fail/myfile2.fastq.gz", + "file_type": "fastq.gz" + }, + { + "name": "myfile4.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fastq_fail/myfile4.fastq.gz", + "file_type": "fastq.gz" + }, + { + "name": "myfile5.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fastq_fail/myfile5.fastq.gz", + "file_type": "fastq.gz" + }, + { + "name": "myfile.fastq.gz", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fastq_fail/myfile.fastq.gz", + "file_type": "fastq.gz" + } + ] + }, + { + "name": "fast5_fail", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_fail/", + "children": [ + { + "name": "myfile2.fast5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_fail/myfile2.fast5", + "file_type": "fast5" + }, + { + "name": "myfile4.fast5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_fail/myfile4.fast5", + "file_type": "fast5" + }, + { + "name": "myfile3.fast5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_fail/myfile3.fast5", + "file_type": "fast5" + }, + { + "name": "myfile5.fast5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_fail/myfile5.fast5", + "file_type": "fast5" + }, + { + "name": "myfile.fast5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_fail/myfile.fast5", + "file_type": "fast5" + } + ] + }, + { + "name": "fast5_pass", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_pass/", + "children": [ + { + "name": "myfile2.fast5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_pass/myfile2.fast5", + "file_type": "fast5" + }, + { + "name": "myfile4.fast5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_pass/myfile4.fast5", + "file_type": "fast5" + }, + { + "name": "myfile3.fast5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_pass/myfile3.fast5", + "file_type": "fast5" + }, + { + "name": "myfile5.fast5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_pass/myfile5.fast5", + "file_type": "fast5" + }, + { + "name": "myfile.fast5", + "path": "./20200122_1217_1-A1-B1-PAE12345_1234567a/fast5_pass/myfile.fast5", + "file_type": "fast5" + } + ] + } + ] + } + ] +}