eva-sub-cli 0.4.7__tar.gz → 0.4.9__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- eva_sub_cli-0.4.9/.github/workflows/trigger-eva-sub-cli-integration-tests.yml +16 -0
- {eva_sub_cli-0.4.7/eva_sub_cli.egg-info → eva_sub_cli-0.4.9}/PKG-INFO +2 -2
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/eva_sub_cli/__init__.py +1 -1
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/eva_sub_cli/_version.py +2 -2
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/eva_sub_cli/etc/EVA_Submission_Example.xlsx +0 -0
- eva_sub_cli-0.4.9/eva_sub_cli/etc/EVA_Submission_template.xlsx +0 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/eva_sub_cli/etc/eva_schema.json +3 -2
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/eva_sub_cli/executables/check_fasta_insdc.py +18 -25
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/eva_sub_cli/executables/samples_checker.py +13 -19
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/eva_sub_cli/executables/xlsx2json.py +3 -3
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/eva_sub_cli/file_utils.py +5 -0
- eva_sub_cli-0.4.7/eva_sub_cli/jinja_templates/html_report.html → eva_sub_cli-0.4.9/eva_sub_cli/jinja_templates/html/report.html +3 -3
- eva_sub_cli-0.4.9/eva_sub_cli/jinja_templates/text/fasta_check.txt +46 -0
- eva_sub_cli-0.4.9/eva_sub_cli/jinja_templates/text/file_validation.txt +58 -0
- eva_sub_cli-0.4.9/eva_sub_cli/jinja_templates/text/metadata_validation.txt +33 -0
- eva_sub_cli-0.4.9/eva_sub_cli/jinja_templates/text/project_details.txt +21 -0
- eva_sub_cli-0.4.9/eva_sub_cli/jinja_templates/text/report.txt +59 -0
- eva_sub_cli-0.4.9/eva_sub_cli/jinja_templates/text/sample_name_check.txt +24 -0
- eva_sub_cli-0.4.9/eva_sub_cli/jinja_templates/text/shallow_validation.txt +16 -0
- eva_sub_cli-0.4.9/eva_sub_cli/metadata.py +95 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/eva_sub_cli/orchestrator.py +53 -52
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/eva_sub_cli/report.py +13 -4
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/eva_sub_cli/validators/docker_validator.py +1 -1
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/eva_sub_cli/validators/native_validator.py +4 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/eva_sub_cli/validators/validator.py +53 -49
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9/eva_sub_cli.egg-info}/PKG-INFO +2 -2
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/eva_sub_cli.egg-info/SOURCES.txt +21 -8
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/eva_sub_cli.egg-info/requires.txt +1 -1
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/requirements.txt +1 -1
- eva_sub_cli-0.4.9/tests/resources/EVA_Submission_Docker_Test.xlsx +0 -0
- eva_sub_cli-0.4.9/tests/resources/EVA_Submission_test.xlsx +0 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/tests/resources/EVA_Submission_test_fails.xlsx +0 -0
- eva_sub_cli-0.4.9/tests/resources/EVA_Submission_test_version_missing.xlsx +0 -0
- eva_sub_cli-0.4.9/tests/resources/metadata_with_filename.json +19 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/tests/resources/validation_reports/expected_report_metadata_json.html +1 -1
- eva_sub_cli-0.4.9/tests/resources/validation_reports/expected_report_metadata_json.txt +132 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/tests/resources/validation_reports/expected_report_metadata_xlsx.html +1 -1
- eva_sub_cli-0.4.9/tests/resources/validation_reports/expected_report_metadata_xlsx.txt +126 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/tests/resources/validation_reports/expected_shallow_metadata_xlsx_report.html +1 -1
- eva_sub_cli-0.4.9/tests/resources/validation_reports/expected_shallow_metadata_xlsx_report.txt +135 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/tests/test_check_fasta_insdc.py +35 -3
- eva_sub_cli-0.4.9/tests/test_metadata.py +69 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/tests/test_orchestrator.py +27 -6
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/tests/test_report.py +96 -30
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/tests/test_samples_checker.py +29 -2
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/tests/test_validator.py +40 -3
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/tests/test_xlsx2json.py +14 -0
- eva_sub_cli-0.4.7/eva_sub_cli/etc/EVA_Submission_template.xlsx +0 -0
- eva_sub_cli-0.4.7/eva_sub_cli/metadata_utils.py +0 -46
- eva_sub_cli-0.4.7/tests/resources/EVA_Submission_Docker_Test.xlsx +0 -0
- eva_sub_cli-0.4.7/tests/resources/EVA_Submission_test.xlsx +0 -0
- eva_sub_cli-0.4.7/tests/resources/EVA_Submission_test_version_missing.xlsx +0 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/.github/workflows/publish.yml +0 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/.github/workflows/publish_packages.yml +0 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/.github/workflows/tests.yml +0 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/.gitignore +0 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/LICENSE +0 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/README.md +0 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/conda/build.sh +0 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/conda/meta.yaml +0 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/docker/Dockerfile +0 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/docs/Getting_Started_with_eva_sub_cli.md +0 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/eva_sub_cli/auth.py +0 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/eva_sub_cli/date_utils.py +0 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/eva_sub_cli/etc/eva-biosamples.json +0 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/eva_sub_cli/etc/eva_logo.png +0 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/eva_sub_cli/etc/spreadsheet2json_conf.yaml +0 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/eva_sub_cli/exceptions/__init__.py +0 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/eva_sub_cli/exceptions/invalid_file_type_exception.py +0 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/eva_sub_cli/exceptions/metadata_template_version_exception.py +0 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/eva_sub_cli/exceptions/submission_not_found_exception.py +0 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/eva_sub_cli/exceptions/submission_status_exception.py +0 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/eva_sub_cli/executables/__init__.py +0 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/eva_sub_cli/executables/check_metadata_semantics.py +0 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/eva_sub_cli/executables/cli.py +0 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/eva_sub_cli/executables/trim_down.py +0 -0
- {eva_sub_cli-0.4.7/eva_sub_cli/jinja_templates → eva_sub_cli-0.4.9/eva_sub_cli/jinja_templates/html}/fasta_check.html +0 -0
- {eva_sub_cli-0.4.7/eva_sub_cli/jinja_templates → eva_sub_cli-0.4.9/eva_sub_cli/jinja_templates/html}/file_validation.html +0 -0
- {eva_sub_cli-0.4.7/eva_sub_cli/jinja_templates → eva_sub_cli-0.4.9/eva_sub_cli/jinja_templates/html}/metadata_validation.html +0 -0
- {eva_sub_cli-0.4.7/eva_sub_cli/jinja_templates → eva_sub_cli-0.4.9/eva_sub_cli/jinja_templates/html}/project_details.html +0 -0
- {eva_sub_cli-0.4.7/eva_sub_cli/jinja_templates → eva_sub_cli-0.4.9/eva_sub_cli/jinja_templates/html}/sample_name_check.html +0 -0
- {eva_sub_cli-0.4.7/eva_sub_cli/jinja_templates → eva_sub_cli-0.4.9/eva_sub_cli/jinja_templates/html}/shallow_validation.html +0 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/eva_sub_cli/nextflow/validation.nf +0 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/eva_sub_cli/semantic_metadata.py +0 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/eva_sub_cli/submission_ws.py +0 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/eva_sub_cli/submit.py +0 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/eva_sub_cli/validators/__init__.py +0 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/eva_sub_cli/validators/validation_results_parsers.py +0 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/eva_sub_cli.egg-info/dependency_links.txt +0 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/eva_sub_cli.egg-info/entry_points.txt +0 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/eva_sub_cli.egg-info/top_level.txt +0 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/pyproject.toml +0 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/pytest.ini +0 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/setup.cfg +0 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/setup.py +0 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/tests/build_and_test_docker_locally.py +0 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/tests/ignore_test_nextflow_validation.yml +0 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/tests/resources/EVA_Submission_test.json +0 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/tests/resources/EVA_Submission_test_with_asm_report.json +0 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/tests/resources/assembly_check/invalid.vcf.assembly_check.log +0 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/tests/resources/assembly_check/invalid.vcf.text_assembly_report.txt +0 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/tests/resources/assembly_reports/input_passed.txt +0 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/tests/resources/fasta_files/Saccharomyces_cerevisiae_I.fa +0 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/tests/resources/fasta_files/input_passed.fa +0 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/tests/resources/fasta_files/multi_seq.fa +0 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/tests/resources/sample_checker/example1.vcf.gz +0 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/tests/resources/sample_checker/example2.vcf +0 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/tests/resources/sample_checker/example3.vcf +0 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/tests/resources/sample_checker/metadata.json +0 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/tests/resources/validation_reports/validation_output/assembly_check/input_passed.vcf.assembly_check.log +0 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/tests/resources/validation_reports/validation_output/assembly_check/input_passed.vcf.text_assembly_report.1680102476700.txt +0 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/tests/resources/validation_reports/validation_output/assembly_check/input_passed.vcf.valid_assembly_report.1680102476700.txt +0 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/tests/resources/validation_reports/validation_output/other_validations/input_passed.fa_check.yml +0 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/tests/resources/validation_reports/validation_output/other_validations/metadata_conversion_errors.yml +0 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/tests/resources/validation_reports/validation_output/other_validations/metadata_semantic_check.yml +0 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/tests/resources/validation_reports/validation_output/other_validations/metadata_validation.txt +0 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/tests/resources/validation_reports/validation_output/other_validations/sample_checker.yml +0 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/tests/resources/validation_reports/validation_output/vcf_format/input_passed.vcf.errors.1680102476854.db +0 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/tests/resources/validation_reports/validation_output/vcf_format/input_passed.vcf.errors.1680102476854.txt +0 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/tests/resources/validation_reports/validation_output/vcf_format/input_passed.vcf.vcf_format.log +0 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/tests/resources/vcf_files/example2.vcf.gz +0 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/tests/resources/vcf_files/input_passed.vcf +0 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/tests/test_auth.py +0 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/tests/test_cli.py +0 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/tests/test_docker_validator.py +0 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/tests/test_file_utils.py +0 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/tests/test_semantic_metadata.py +0 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/tests/test_submit.py +0 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/tests/test_trim_down.py +0 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/tests/test_utils.py +0 -0
- {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.9}/tests/test_validaton_results_parsers.py +0 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
name: Trigger Eva Sub Cli Integration Tests
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main, test-trigger-integration-tests]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
trigger-integration-tests:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
steps:
|
|
11
|
+
- name: Trigger eva sub cli integration tests
|
|
12
|
+
uses: peter-evans/repository-dispatch@v2
|
|
13
|
+
with:
|
|
14
|
+
token: ${{ secrets.TRIGGER_INTEGRATION_TESTS }}
|
|
15
|
+
repository: EBIvariation/eva-integration-tests
|
|
16
|
+
event-type: trigger-eva-sub-cli-tests
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: eva_sub_cli
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.9
|
|
4
4
|
Summary: EBI EVA - validation and submission command line tool
|
|
5
5
|
Classifier: Development Status :: 5 - Production/Stable
|
|
6
6
|
Classifier: Environment :: Console
|
|
@@ -19,7 +19,7 @@ Classifier: License :: OSI Approved :: Apache Software License
|
|
|
19
19
|
Requires-Python: >=3.6
|
|
20
20
|
Description-Content-Type: text/markdown
|
|
21
21
|
License-File: LICENSE
|
|
22
|
-
Requires-Dist: ebi_eva_common_pyutils==0.*,>=0.7.
|
|
22
|
+
Requires-Dist: ebi_eva_common_pyutils==0.*,>=0.7.3
|
|
23
23
|
Requires-Dist: jinja2==3.*,>=3.0.0
|
|
24
24
|
Requires-Dist: jsonschema==4.*
|
|
25
25
|
Requires-Dist: openpyxl==3.*
|
|
@@ -6,7 +6,7 @@ PACKAGE_DIR = os.path.dirname(os.path.abspath(__file__))
|
|
|
6
6
|
NEXTFLOW_DIR = os.path.join(PACKAGE_DIR, 'nextflow')
|
|
7
7
|
ETC_DIR = os.path.join(PACKAGE_DIR, 'etc')
|
|
8
8
|
LSRI_CLIENT_ID = "aa0fcc42-096a-4f9d-b871-aceb1a97d174"
|
|
9
|
-
|
|
9
|
+
MINIMUM_METADATA_XLSX_TEMPLATE_VERSION = '2.0.0'
|
|
10
10
|
|
|
11
11
|
SUB_CLI_CONFIG_FILE = ".eva_sub_cli_config.yml"
|
|
12
12
|
|
|
Binary file
|
|
Binary file
|
|
@@ -310,9 +310,10 @@
|
|
|
310
310
|
"type": "string",
|
|
311
311
|
"description": "Sample Name used in the VCF file"
|
|
312
312
|
},
|
|
313
|
-
"
|
|
313
|
+
"bioSampleAccession": {
|
|
314
314
|
"type": "string",
|
|
315
|
-
"
|
|
315
|
+
"pattern": "^SAM(E|D|N)[A-Z]?[0-9]+$",
|
|
316
|
+
"description": "BioSample accession of pre-existing sample. Only use for preregistered samples"
|
|
316
317
|
},
|
|
317
318
|
"bioSampleObject": {
|
|
318
319
|
"type": "object",
|
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
import argparse
|
|
2
|
-
import gzip
|
|
3
2
|
import hashlib
|
|
4
|
-
import json
|
|
5
|
-
from itertools import groupby
|
|
6
3
|
|
|
7
4
|
import requests
|
|
8
5
|
import yaml
|
|
@@ -11,8 +8,7 @@ from requests import HTTPError
|
|
|
11
8
|
from retry import retry
|
|
12
9
|
|
|
13
10
|
from eva_sub_cli.file_utils import fasta_iter
|
|
14
|
-
from eva_sub_cli.
|
|
15
|
-
get_reference_assembly_for_analysis
|
|
11
|
+
from eva_sub_cli.metadata import EvaMetadataJson
|
|
16
12
|
|
|
17
13
|
REFGET_SERVER = 'https://www.ebi.ac.uk/ena/cram'
|
|
18
14
|
CONTIG_ALIAS_SERVER = 'https://www.ebi.ac.uk/eva/webservices/contig-alias/v1/chromosomes/md5checksum'
|
|
@@ -52,7 +48,7 @@ def _get_containing_assemblies_paged(url):
|
|
|
52
48
|
results.add(contigEntity['assembly']['insdcAccession'])
|
|
53
49
|
if '_links' in response_data and 'next' in response_data['_links']:
|
|
54
50
|
# Add results from next page if needed
|
|
55
|
-
results
|
|
51
|
+
results = results.union(_get_containing_assemblies_paged(response_data['_links']['next']['href']))
|
|
56
52
|
return results
|
|
57
53
|
return set()
|
|
58
54
|
|
|
@@ -94,7 +90,7 @@ def assess_fasta(input_fasta, analyses, assembly_in_metadata):
|
|
|
94
90
|
possible_assemblies = containing_assemblies
|
|
95
91
|
else:
|
|
96
92
|
possible_assemblies &= containing_assemblies
|
|
97
|
-
except
|
|
93
|
+
except Exception as e:
|
|
98
94
|
# Server errors from either ENA refget or EVA contig alias will halt the check prematurely.
|
|
99
95
|
# Report the error but do not return from the method, so that incomplete results can be reported
|
|
100
96
|
# (i.e. any sequences found to be INSDC and any compatible assemblies so far)
|
|
@@ -121,23 +117,21 @@ def get_analyses_and_reference_genome_from_metadata(vcf_files_for_fasta, json_fi
|
|
|
121
117
|
single assembly accession
|
|
122
118
|
:param json_file: JSON file of the metadata
|
|
123
119
|
"""
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
return all_analyses, None
|
|
140
|
-
return all_analyses, assemblies[0]
|
|
120
|
+
metadata = EvaMetadataJson(json_file)
|
|
121
|
+
# Get all analyses associated with all vcf files that are linked with a single fasta file
|
|
122
|
+
all_analyses = set()
|
|
123
|
+
for vcf_file in vcf_files_for_fasta:
|
|
124
|
+
analysis_aliases = metadata.get_analysis_for_vcf_file(vcf_file)
|
|
125
|
+
if len(analysis_aliases) != 1:
|
|
126
|
+
logger.error(f'Could not determine analysis associated with VCF file: {vcf_file}')
|
|
127
|
+
else:
|
|
128
|
+
all_analyses.add(analysis_aliases[0])
|
|
129
|
+
# Get (single) assembly associated with these analyses
|
|
130
|
+
assemblies = [metadata.get_reference_assembly_for_analysis(analysis) for analysis in all_analyses]
|
|
131
|
+
if len(assemblies) != 1:
|
|
132
|
+
logger.error(f'Could not determine assembly accession to check against fasta file, out of: {assemblies}')
|
|
133
|
+
return all_analyses, None
|
|
134
|
+
return all_analyses, assemblies[0]
|
|
141
135
|
|
|
142
136
|
|
|
143
137
|
def main():
|
|
@@ -156,4 +150,3 @@ def main():
|
|
|
156
150
|
analyses, metadata_insdc = get_analyses_and_reference_genome_from_metadata(args.vcf_files, args.metadata_json)
|
|
157
151
|
results = assess_fasta(args.input_fasta, analyses, metadata_insdc)
|
|
158
152
|
write_result_yaml(args.output_yaml, results)
|
|
159
|
-
|
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
import argparse
|
|
2
|
-
import gzip
|
|
3
|
-
import json
|
|
4
2
|
import os
|
|
5
3
|
|
|
6
4
|
from ebi_eva_common_pyutils.logger import logging_config
|
|
@@ -8,7 +6,7 @@ from ebi_eva_common_pyutils.logger import logging_config
|
|
|
8
6
|
import yaml
|
|
9
7
|
|
|
10
8
|
from eva_sub_cli.file_utils import open_gzip_if_required
|
|
11
|
-
from eva_sub_cli.
|
|
9
|
+
from eva_sub_cli.metadata import EvaMetadataJson
|
|
12
10
|
|
|
13
11
|
logger = logging_config.get_logger(__name__)
|
|
14
12
|
|
|
@@ -24,8 +22,10 @@ def get_samples_from_vcf(vcf_file):
|
|
|
24
22
|
if len(sp_line) > 9:
|
|
25
23
|
return sp_line[9:]
|
|
26
24
|
else:
|
|
27
|
-
logger.warning(f"No
|
|
25
|
+
logger.warning(f"No sample names found in file {vcf_file}")
|
|
28
26
|
return []
|
|
27
|
+
logger.warning(f"No sample names found in file {vcf_file}")
|
|
28
|
+
return []
|
|
29
29
|
|
|
30
30
|
|
|
31
31
|
def compare_names_in_files_and_samples(sample_name_in_analysis, sample_name_per_file):
|
|
@@ -54,12 +54,12 @@ def compare_names_in_files_and_samples(sample_name_in_analysis, sample_name_per_
|
|
|
54
54
|
more_metadata_submitted_files)
|
|
55
55
|
|
|
56
56
|
|
|
57
|
-
def compare_all_analysis(
|
|
57
|
+
def compare_all_analysis(metadata, files_per_analysis):
|
|
58
58
|
overall_differences = False
|
|
59
59
|
results_per_analysis_alias = {}
|
|
60
|
-
all_analysis_alias = set(samples_per_analysis) | set(files_per_analysis)
|
|
60
|
+
all_analysis_alias = set(metadata.samples_per_analysis) | set(files_per_analysis)
|
|
61
61
|
for analysis_alias in all_analysis_alias:
|
|
62
|
-
sample_name_in_analysis = samples_per_analysis.get(analysis_alias, [])
|
|
62
|
+
sample_name_in_analysis = metadata.samples_per_analysis.get(analysis_alias, [])
|
|
63
63
|
sample_name_per_file = {
|
|
64
64
|
file_path: get_samples_from_vcf(file_path)
|
|
65
65
|
for file_path in files_per_analysis.get(analysis_alias, [])
|
|
@@ -78,13 +78,7 @@ def compare_all_analysis(samples_per_analysis, files_per_analysis):
|
|
|
78
78
|
return overall_differences, results_per_analysis_alias
|
|
79
79
|
|
|
80
80
|
|
|
81
|
-
def
|
|
82
|
-
with open(json_file) as open_json:
|
|
83
|
-
metadata = json.load(open_json)
|
|
84
|
-
return get_samples_per_analysis(metadata), get_files_per_analysis(metadata)
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
def associate_vcf_path_with_analysis(vcf_files, files_per_analysis):
|
|
81
|
+
def associate_vcf_path_with_analysis(metadata, vcf_files):
|
|
88
82
|
"""
|
|
89
83
|
Match the files names associated with analysis provided in the metadata with the file path given on the command
|
|
90
84
|
line.
|
|
@@ -93,10 +87,10 @@ def associate_vcf_path_with_analysis(vcf_files, files_per_analysis):
|
|
|
93
87
|
:returns dictionary of analysis and their associated vcf file path
|
|
94
88
|
"""
|
|
95
89
|
result_files_per_analysis = dict()
|
|
96
|
-
for analysis in files_per_analysis:
|
|
90
|
+
for analysis in metadata.files_per_analysis:
|
|
97
91
|
result_files_per_analysis[analysis] = []
|
|
98
92
|
for vcf_file in vcf_files:
|
|
99
|
-
analysis_aliases = get_analysis_for_vcf_file(vcf_file
|
|
93
|
+
analysis_aliases = metadata.get_analysis_for_vcf_file(vcf_file)
|
|
100
94
|
if len(analysis_aliases) == 1:
|
|
101
95
|
result_files_per_analysis[analysis_aliases[0]].append(vcf_file)
|
|
102
96
|
elif len(analysis_aliases) == 0:
|
|
@@ -123,9 +117,9 @@ def check_sample_name_concordance(metadata_json, vcf_files, output_yaml):
|
|
|
123
117
|
Take the metadata following EVA standard and formatted in JSON then compare the sample names in it to the ones
|
|
124
118
|
found in the VCF files
|
|
125
119
|
"""
|
|
126
|
-
|
|
127
|
-
file_path_per_analysis = associate_vcf_path_with_analysis(
|
|
128
|
-
overall_differences, results_per_analysis_alias = compare_all_analysis(
|
|
120
|
+
metadata = EvaMetadataJson(metadata_json)
|
|
121
|
+
file_path_per_analysis = associate_vcf_path_with_analysis(metadata, vcf_files)
|
|
122
|
+
overall_differences, results_per_analysis_alias = compare_all_analysis(metadata, file_path_per_analysis)
|
|
129
123
|
write_result_yaml(output_yaml, overall_differences, results_per_analysis_alias)
|
|
130
124
|
|
|
131
125
|
|
|
@@ -105,11 +105,11 @@ class XlsxParser:
|
|
|
105
105
|
if type_name == 'string':
|
|
106
106
|
return str(value)
|
|
107
107
|
if type_name == 'boolean':
|
|
108
|
-
return str(value).lower() in ['true', '1', 't', 'y', 'yes']
|
|
108
|
+
return str(value).lower() in ['true', '1', "'1'", 't', 'y', 'yes']
|
|
109
109
|
if type_name == 'list':
|
|
110
110
|
# split and remove empty values
|
|
111
|
-
|
|
112
|
-
|
|
111
|
+
split_values = [XlsxParser.trim_value(v) for v in value.split(',') if v]
|
|
112
|
+
return [v for v in split_values if v]
|
|
113
113
|
if type_name == 'date':
|
|
114
114
|
return XlsxParser.serialize(value)
|
|
115
115
|
return value
|
|
@@ -23,6 +23,11 @@ def is_submission_dir_writable(submission_dir):
|
|
|
23
23
|
return False
|
|
24
24
|
return True
|
|
25
25
|
|
|
26
|
+
def is_vcf_file(file_path):
|
|
27
|
+
if file_path:
|
|
28
|
+
file_path = file_path.strip().lower()
|
|
29
|
+
return file_path.endswith('.vcf') or file_path.endswith('.vcf.gz')
|
|
30
|
+
return False
|
|
26
31
|
|
|
27
32
|
def backup_file_or_directory(file_name, max_backups=None):
|
|
28
33
|
"""
|
|
@@ -69,11 +69,11 @@
|
|
|
69
69
|
<section>
|
|
70
70
|
<h2>VCF validation results</h2>
|
|
71
71
|
<div class="description">
|
|
72
|
-
Checks whether each file is compliant with the <a href="http://samtools.github.io/hts-specs/VCFv4.
|
|
72
|
+
Checks whether each file is compliant with the <a href="http://samtools.github.io/hts-specs/VCFv4.4.pdf" target=”_blank”>VCF specification</a>.
|
|
73
73
|
Also checks whether the variants' reference alleles match against the reference assembly.
|
|
74
74
|
</div>
|
|
75
75
|
{% for file_name in vcf_files %}
|
|
76
|
-
{% if file_name!= "pass"%}
|
|
76
|
+
{% if file_name != "pass"%}
|
|
77
77
|
<h3>{{ file_name }}</h3>
|
|
78
78
|
{{ file_validation_report(validation_results, file_name) }}
|
|
79
79
|
{% endif %}
|
|
@@ -95,7 +95,7 @@
|
|
|
95
95
|
Also checks if the reference assembly accession in the metadata matches the one determined from the FASTA file.
|
|
96
96
|
</div>
|
|
97
97
|
{% for file_name in fasta_files %}
|
|
98
|
-
{% if file_name!= "pass"%}
|
|
98
|
+
{% if file_name != "pass"%}
|
|
99
99
|
<h3>{{ file_name }}</h3>
|
|
100
100
|
{{ fasta_check_report(validation_results, file_name) }}
|
|
101
101
|
{% endif %}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{% macro fasta_check_report(validation_results, file_name) -%}
|
|
2
|
+
|
|
3
|
+
{% set results_for_fasta = validation_results['fasta_check'][file_name] %}
|
|
4
|
+
|
|
5
|
+
{% if results_for_fasta.get('connection_error') %}
|
|
6
|
+
Warning: The following results may be incomplete due to problems with external services.
|
|
7
|
+
Please try again later for complete results.
|
|
8
|
+
Error message: {{ results_for_fasta.get('connection_error') }}
|
|
9
|
+
{% endif %}
|
|
10
|
+
|
|
11
|
+
{% if results_for_fasta.get('all_insdc') %}
|
|
12
|
+
{{ "\u2714" }} All sequences are INSDC accessioned.
|
|
13
|
+
{% else %}
|
|
14
|
+
{{ "\u274C" }} Some sequences are not INSDC accessioned
|
|
15
|
+
First 10 sequences not in INSDC. Full report: {{ results_for_fasta.get('report_path', '') }}
|
|
16
|
+
{% set sequence_info_list = results_for_fasta.get('sequences', [])|rejectattr("insdc")|list %}
|
|
17
|
+
{% for sequence_info in sequence_info_list[:10] %}
|
|
18
|
+
Sequence name: {{ sequence_info.get('sequence_name') }}
|
|
19
|
+
Refget md5: {{ sequence_info.get('sequence_md5') }}
|
|
20
|
+
---
|
|
21
|
+
{% endfor %}
|
|
22
|
+
{% endif %}
|
|
23
|
+
|
|
24
|
+
{% if 'possible_assemblies' in results_for_fasta %}
|
|
25
|
+
{% if 'metadata_assembly_compatible' in results_for_fasta %}
|
|
26
|
+
{% set analysis_text = results_for_fasta.get('associated_analyses')|join(", ") %}
|
|
27
|
+
{% if results_for_fasta.get('metadata_assembly_compatible') %}
|
|
28
|
+
{% set icon = "\u2714" %}
|
|
29
|
+
{% set text = analysis_text + ": Assembly accession in metadata is compatible" %}
|
|
30
|
+
{% else %}
|
|
31
|
+
{% set icon = "\u274C" %}
|
|
32
|
+
{% set text = analysis_text + ": Assembly accession in metadata is not compatible" %}
|
|
33
|
+
{% endif %}
|
|
34
|
+
{% else %}
|
|
35
|
+
{% set icon = "\u274C" %}
|
|
36
|
+
{% set text = "No assembly accession found in metadata" %}
|
|
37
|
+
{% endif %}
|
|
38
|
+
{{ icon }} {{ text }}
|
|
39
|
+
{% if 'metadata_assembly_compatible' not in results_for_fasta or not results_for_fasta['metadata_assembly_compatible'] %}
|
|
40
|
+
Full report: {{ results_for_fasta.get('report_path', '') }}
|
|
41
|
+
Assembly accession found in metadata: {{ results_for_fasta.get('assembly_in_metadata', 'Not found') }}
|
|
42
|
+
Assembly accession(s) compatible with FASTA: {{ results_for_fasta.get('possible_assemblies')|join(", ") }}
|
|
43
|
+
{% endif %}
|
|
44
|
+
{% endif %}
|
|
45
|
+
|
|
46
|
+
{%- endmacro %}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
|
|
2
|
+
{% macro file_validation_report(validation_results, file_name) -%}
|
|
3
|
+
{% for check_type, check_per_file in validation_results.items() %}
|
|
4
|
+
{% set result = check_per_file.get(file_name, {}) %}
|
|
5
|
+
{% if check_type == "assembly_check" %}
|
|
6
|
+
{{ assembly_check(result) }}
|
|
7
|
+
{% elif check_type == "vcf_check" %}
|
|
8
|
+
{{ vcf_check(result) }}
|
|
9
|
+
{% endif %}
|
|
10
|
+
{% endfor %}
|
|
11
|
+
{%- endmacro %}
|
|
12
|
+
|
|
13
|
+
{% macro vcf_check(vcf_check_result) %}
|
|
14
|
+
{% set critical_count = vcf_check_result.get("critical_count", 0) %}
|
|
15
|
+
{% set error_count = vcf_check_result.get("error_count", 0) %}
|
|
16
|
+
{% if critical_count > 0 or error_count > 0 %}
|
|
17
|
+
{% set icon = "\u274C" %}
|
|
18
|
+
{% else %}
|
|
19
|
+
{% set icon = "\u2714" %}
|
|
20
|
+
{% endif %}
|
|
21
|
+
{{ icon }} VCF check: {{ critical_count }} critical errors, {{ error_count }} non-critical errors
|
|
22
|
+
|
|
23
|
+
{% set critical_list = vcf_check_result.get("critical_list") %}
|
|
24
|
+
{% set error_list = vcf_check_result.get("error_list") %}
|
|
25
|
+
{% if critical_list or error_list%}
|
|
26
|
+
First 10 errors per category are below. Full report: {{ vcf_check_result.get('report_path', '') }}
|
|
27
|
+
{% for error in critical_list[:10] %}
|
|
28
|
+
Critical error: {{ error }}
|
|
29
|
+
{% endfor %}
|
|
30
|
+
{% for error in error_list[:10] %}
|
|
31
|
+
Non-critical error: {{ error }}
|
|
32
|
+
{% endfor %}
|
|
33
|
+
{% endif %}
|
|
34
|
+
{%- endmacro %}
|
|
35
|
+
|
|
36
|
+
{% macro assembly_check(assembly_check_result) %}
|
|
37
|
+
{% set nb_match = assembly_check_result.get("match", 0) %}
|
|
38
|
+
{% set nb_total = assembly_check_result.get("total", 0) %}
|
|
39
|
+
{% set match_percentage = nb_match / nb_total * 100 if nb_total else 0 %}
|
|
40
|
+
{% if assembly_check_result.get("nb_mismatch", 0) > 0 or nb_total == 0 %}
|
|
41
|
+
{% set icon = "\u274C" %}
|
|
42
|
+
{% else %}
|
|
43
|
+
{% set icon = "\u2714" %}
|
|
44
|
+
{% endif %}
|
|
45
|
+
{{ icon }} Assembly check: {{ nb_match }}/{{ nb_total }} ({{ match_percentage|round(2) }}%)
|
|
46
|
+
|
|
47
|
+
{% set mismatch_list = assembly_check_result.get("mismatch_list") %}
|
|
48
|
+
{% set error_list = assembly_check_result.get("error_list") %}
|
|
49
|
+
{% if mismatch_list or error_list %}
|
|
50
|
+
First 10 errors per category are below. Full report: {{ assembly_check_result.get('report_path', '') }}
|
|
51
|
+
{% for error in error_list[:10] %}
|
|
52
|
+
Parsing error: {{ error }}
|
|
53
|
+
{% endfor %}
|
|
54
|
+
{% for error in mismatch_list[:10] %}
|
|
55
|
+
Mismatch error: {{ error }}
|
|
56
|
+
{% endfor %}
|
|
57
|
+
{% endif %}
|
|
58
|
+
{%- endmacro %}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
|
|
2
|
+
{% macro metadata_validation_report(validation_results) -%}
|
|
3
|
+
|
|
4
|
+
{% set results = validation_results.get('metadata_check', {}) %}
|
|
5
|
+
{% set spreadsheet_errors = results.get('spreadsheet_errors', []) %}
|
|
6
|
+
{% set json_errors = results.get('json_errors', []) %}
|
|
7
|
+
{% set has_errors = spreadsheet_errors or json_errors %}
|
|
8
|
+
{% if has_errors %}
|
|
9
|
+
{% set icon = "\u274C" %}
|
|
10
|
+
{% else %}
|
|
11
|
+
{% set icon = "\u2714" %}
|
|
12
|
+
{% endif %}
|
|
13
|
+
{{ icon }} Metadata validation check
|
|
14
|
+
|
|
15
|
+
{% if spreadsheet_errors %}
|
|
16
|
+
Full report: {{ results.get('spreadsheet_report_path', '') }}
|
|
17
|
+
{% for error in spreadsheet_errors %}
|
|
18
|
+
Sheet: {{ error.get('sheet') }} | Row: {{ error.get('row') }} | Column: {{ error.get('column') }}
|
|
19
|
+
Error: {{ error.get('description') }}
|
|
20
|
+
---
|
|
21
|
+
{% endfor %}
|
|
22
|
+
{% endif %}
|
|
23
|
+
|
|
24
|
+
{% if json_errors %}
|
|
25
|
+
Full report: {{ results.get('json_report_path', '') }}
|
|
26
|
+
{% for error in json_errors %}
|
|
27
|
+
JSON Property: {{ error.get('property') }}
|
|
28
|
+
Error: {{ error.get('description') }}
|
|
29
|
+
---
|
|
30
|
+
{% endfor %}
|
|
31
|
+
{% endif %}
|
|
32
|
+
|
|
33
|
+
{%- endmacro %}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{% macro project_details_report(project_title, validation_date, submission_dir, vcf_fasta_analysis_mapping) -%}
|
|
2
|
+
|
|
3
|
+
{% if project_title %}
|
|
4
|
+
Project Title: {{ project_title }}
|
|
5
|
+
{% else %}
|
|
6
|
+
Project Title: Not Found
|
|
7
|
+
{% endif %}
|
|
8
|
+
Validation Date: {{ validation_date }}
|
|
9
|
+
Submission Directory: {{ submission_dir }}
|
|
10
|
+
|
|
11
|
+
{% if vcf_fasta_analysis_mapping %}
|
|
12
|
+
Files mapping:
|
|
13
|
+
{% for mapping in vcf_fasta_analysis_mapping %}
|
|
14
|
+
---
|
|
15
|
+
VCF File: {{ mapping.vcf_file }}
|
|
16
|
+
Fasta File: {{ mapping.fasta_file }}
|
|
17
|
+
Analysis: {{ mapping.analysis }}
|
|
18
|
+
{% endfor %}
|
|
19
|
+
{% endif %}
|
|
20
|
+
|
|
21
|
+
{%- endmacro %}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{% from 'project_details.txt' import project_details_report %}
|
|
2
|
+
{% from 'file_validation.txt' import file_validation_report %}
|
|
3
|
+
{% from 'sample_name_check.txt' import sample_name_check_report %}
|
|
4
|
+
{% from 'fasta_check.txt' import fasta_check_report %}
|
|
5
|
+
{% from 'metadata_validation.txt' import metadata_validation_report %}
|
|
6
|
+
{% from 'shallow_validation.txt' import optional_shallow_validation_report %}
|
|
7
|
+
|
|
8
|
+
VALIDATION REPORT
|
|
9
|
+
eva-sub-cli v{{cli_version}}
|
|
10
|
+
|
|
11
|
+
{{ optional_shallow_validation_report(validation_results) }}
|
|
12
|
+
|
|
13
|
+
-
|
|
14
|
+
|
|
15
|
+
PROJECT SUMMARY
|
|
16
|
+
General details about the project
|
|
17
|
+
|
|
18
|
+
{{ project_details_report(project_title, validation_date, submission_dir, vcf_fasta_analysis_mapping) }}
|
|
19
|
+
|
|
20
|
+
-
|
|
21
|
+
|
|
22
|
+
METADATA VALIDATION RESULTS
|
|
23
|
+
Ensures that required fields are present and values are formatted correctly.
|
|
24
|
+
For requirements, please refer to the EVA website (https://www.ebi.ac.uk/eva/?Submit-Data).
|
|
25
|
+
|
|
26
|
+
{{ metadata_validation_report(validation_results) }}
|
|
27
|
+
|
|
28
|
+
-
|
|
29
|
+
|
|
30
|
+
VCF VALIDATION RESULTS
|
|
31
|
+
Checks whether each file is compliant with the VCF specification (http://samtools.github.io/hts-specs/VCFv4.4.pdf).
|
|
32
|
+
Also checks whether the variants' reference alleles match against the reference assembly.
|
|
33
|
+
|
|
34
|
+
{% for file_name in vcf_files %}
|
|
35
|
+
{% if file_name != "pass"%}
|
|
36
|
+
{{ file_name }}
|
|
37
|
+
{{ file_validation_report(validation_results, file_name) }}
|
|
38
|
+
{% endif %}
|
|
39
|
+
{% endfor %}
|
|
40
|
+
|
|
41
|
+
-
|
|
42
|
+
|
|
43
|
+
SAMPLE NAME CONCORDANCE CHECK
|
|
44
|
+
Checks whether information in the metadata is concordant with that contained in the VCF files, in particular sample names.
|
|
45
|
+
|
|
46
|
+
{{ sample_name_check_report(validation_results)}}
|
|
47
|
+
|
|
48
|
+
-
|
|
49
|
+
|
|
50
|
+
REFERENCE GENOME INSDC CHECK
|
|
51
|
+
Checks that the reference sequences in the FASTA file used to call the variants are accessioned in INSDC.
|
|
52
|
+
Also checks if the reference assembly accession in the metadata matches the one determined from the FASTA file.
|
|
53
|
+
|
|
54
|
+
{% for file_name in fasta_files %}
|
|
55
|
+
{% if file_name != "pass"%}
|
|
56
|
+
{{ file_name }}
|
|
57
|
+
{{ fasta_check_report(validation_results, file_name) }}
|
|
58
|
+
{% endif %}
|
|
59
|
+
{% endfor %}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
|
|
2
|
+
{% macro sample_name_check_report(validation_results) -%}
|
|
3
|
+
{% set results = validation_results.get('sample_check', {}) %}
|
|
4
|
+
{% macro format_error(error) %}
|
|
5
|
+
{{ error | replace(' ', '•') }}
|
|
6
|
+
{% endmacro %}
|
|
7
|
+
|
|
8
|
+
{% for analysis, results_for_analysis in results.get('results_per_analysis', {}).items() %}
|
|
9
|
+
{% if results_for_analysis.get('difference') %}
|
|
10
|
+
{% set icon = "\u274C" %}
|
|
11
|
+
{% set text = "Sample names in metadata do not match with those in VCF files" %}
|
|
12
|
+
{% else %}
|
|
13
|
+
{% set icon = "\u2714" %}
|
|
14
|
+
{% set text = "Sample names in metadata match with those in VCF files" %}
|
|
15
|
+
{% endif %}
|
|
16
|
+
{{ icon }} {{ analysis }}: {{ text }}
|
|
17
|
+
|
|
18
|
+
{% if results_for_analysis.get('difference') %}
|
|
19
|
+
First 10 errors per category are below. Full report: {{ results.get('report_path', '') }}
|
|
20
|
+
Samples described in the metadata but not in the VCF files: {{ results_for_analysis.get('more_metadata_submitted_files')[:10]|join(", ") }}
|
|
21
|
+
Samples in the VCF files but not described in the metadata: {{ results_for_analysis.get('more_submitted_files_metadata')[:10]|join(", ") }}
|
|
22
|
+
{% endif %}
|
|
23
|
+
{% endfor %}
|
|
24
|
+
{%- endmacro %}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{% macro optional_shallow_validation_report(validation_results) -%}
|
|
2
|
+
|
|
3
|
+
{% set results = validation_results.get('shallow_validation', {}) %}
|
|
4
|
+
{% if results.get('required') %}
|
|
5
|
+
|
|
6
|
+
{{ "\u274C" }} You requested to run the shallow validation, please run full validation before submitting the data
|
|
7
|
+
{% for vcf_file in results.get('metrics') %}
|
|
8
|
+
---
|
|
9
|
+
VCF File: {{ vcf_file }}
|
|
10
|
+
Variant lines validated in VCF: {{ results.get('metrics').get(vcf_file).get('trim_down_vcf_record') }}
|
|
11
|
+
Entries used in Fasta: {{ results.get('metrics').get(vcf_file).get('number_sequence_found') }}
|
|
12
|
+
{% endfor %}
|
|
13
|
+
|
|
14
|
+
{% endif %}
|
|
15
|
+
|
|
16
|
+
{%- endmacro %}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import os
|
|
3
|
+
from collections import defaultdict
|
|
4
|
+
from copy import deepcopy
|
|
5
|
+
from functools import cached_property
|
|
6
|
+
|
|
7
|
+
from ebi_eva_common_pyutils.logger import AppLogger
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class EvaMetadataJson(AppLogger):
|
|
11
|
+
|
|
12
|
+
def __init__(self, path_to_json):
|
|
13
|
+
with open(path_to_json) as open_json:
|
|
14
|
+
self.content = json.load(open_json)
|
|
15
|
+
|
|
16
|
+
@property
|
|
17
|
+
def project(self):
|
|
18
|
+
return self.content.get('project', {})
|
|
19
|
+
|
|
20
|
+
@property
|
|
21
|
+
def submitter_details(self):
|
|
22
|
+
return self.content.get('submitterDetails', {})
|
|
23
|
+
|
|
24
|
+
@property
|
|
25
|
+
def analyses(self):
|
|
26
|
+
return self.content.get('analysis', [])
|
|
27
|
+
|
|
28
|
+
@property
|
|
29
|
+
def samples(self):
|
|
30
|
+
return self.content.get('sample', [])
|
|
31
|
+
|
|
32
|
+
@property
|
|
33
|
+
def files(self):
|
|
34
|
+
return self.content.get('files', [])
|
|
35
|
+
|
|
36
|
+
@cached_property
|
|
37
|
+
def resolved_files(self):
|
|
38
|
+
"""Returns list of files with fileName resolved to an absolute path."""
|
|
39
|
+
mod_files = deepcopy(self.content.get('files', []))
|
|
40
|
+
for file_info in mod_files:
|
|
41
|
+
if 'fileName' in file_info:
|
|
42
|
+
file_info['fileName'] = os.path.abspath(file_info['fileName'])
|
|
43
|
+
return mod_files
|
|
44
|
+
|
|
45
|
+
def set_files(self, file_list):
|
|
46
|
+
assert isinstance(file_list, list)
|
|
47
|
+
self.content['files'] = file_list
|
|
48
|
+
# invalidate the cached property
|
|
49
|
+
if self.resolved_files:
|
|
50
|
+
del self.resolved_files
|
|
51
|
+
|
|
52
|
+
@cached_property
|
|
53
|
+
def samples_per_analysis(self):
|
|
54
|
+
"""Returns mapping of analysis alias to sample names, based on metadata."""
|
|
55
|
+
samples_per_analysis = defaultdict(list)
|
|
56
|
+
for sample_info in self.samples:
|
|
57
|
+
for analysis_alias in sample_info.get('analysisAlias', []):
|
|
58
|
+
samples_per_analysis[analysis_alias].append(sample_info.get('sampleInVCF'))
|
|
59
|
+
return {
|
|
60
|
+
analysis_alias: set(samples)
|
|
61
|
+
for analysis_alias, samples in samples_per_analysis.items()
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
@cached_property
|
|
65
|
+
def files_per_analysis(self):
|
|
66
|
+
"""Returns mapping of analysis alias to filenames, based on metadata."""
|
|
67
|
+
files_per_analysis = defaultdict(list)
|
|
68
|
+
for file_info in self.resolved_files:
|
|
69
|
+
files_per_analysis[file_info.get('analysisAlias')].append(file_info.get('fileName'))
|
|
70
|
+
return {
|
|
71
|
+
analysis_alias: set(filepaths)
|
|
72
|
+
for analysis_alias, filepaths in files_per_analysis.items()
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
def get_reference_assembly_for_analysis(self, analysis_alias):
|
|
76
|
+
"""Returns the reference assembly for this analysis (does not validate format)."""
|
|
77
|
+
for analysis in self.analyses:
|
|
78
|
+
if analysis.get('analysisAlias') == analysis_alias:
|
|
79
|
+
return analysis.get('referenceGenome')
|
|
80
|
+
return None
|
|
81
|
+
|
|
82
|
+
def get_analysis_for_vcf_file(self, vcf_file):
|
|
83
|
+
"""Returns list of analysis aliases associated with the vcf file path."""
|
|
84
|
+
if not os.path.exists(vcf_file):
|
|
85
|
+
raise FileNotFoundError(f'{vcf_file} cannot be resolved')
|
|
86
|
+
analysis_aliases = [analysis_alias for analysis_alias in self.files_per_analysis
|
|
87
|
+
if vcf_file in self.files_per_analysis[analysis_alias]
|
|
88
|
+
or os.path.basename(vcf_file) in [
|
|
89
|
+
os.path.basename(p) for p in self.files_per_analysis[analysis_alias]
|
|
90
|
+
]]
|
|
91
|
+
return analysis_aliases
|
|
92
|
+
|
|
93
|
+
def write(self, output_path):
|
|
94
|
+
with open(output_path, 'w') as open_file:
|
|
95
|
+
json.dump(self.content, open_file)
|