eva-sub-cli 0.4.6__tar.gz → 0.4.7__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.6/eva_sub_cli.egg-info → eva_sub_cli-0.4.7}/PKG-INFO +4 -2
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/eva_sub_cli/_version.py +2 -2
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/eva_sub_cli/etc/eva_schema.json +7 -5
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/eva_sub_cli/executables/trim_down.py +21 -11
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/eva_sub_cli/file_utils.py +3 -3
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/eva_sub_cli/nextflow/validation.nf +1 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/eva_sub_cli/semantic_metadata.py +42 -5
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/eva_sub_cli/validators/docker_validator.py +15 -11
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/eva_sub_cli/validators/validator.py +5 -2
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7/eva_sub_cli.egg-info}/PKG-INFO +4 -2
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/eva_sub_cli.egg-info/SOURCES.txt +4 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/eva_sub_cli.egg-info/requires.txt +3 -1
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/requirements.txt +3 -1
- eva_sub_cli-0.4.7/tests/build_and_test_docker_locally.py +293 -0
- eva_sub_cli-0.4.7/tests/resources/EVA_Submission_Docker_Test.xlsx +0 -0
- eva_sub_cli-0.4.7/tests/resources/fasta_files/multi_seq.fa +8 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/tests/resources/validation_reports/validation_output/other_validations/metadata_validation.txt +2 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/tests/test_cli.py +5 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/tests/test_semantic_metadata.py +41 -17
- eva_sub_cli-0.4.7/tests/test_trim_down.py +108 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/tests/test_validator.py +11 -2
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/tests/test_xlsx2json.py +27 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/.github/workflows/publish.yml +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/.github/workflows/publish_packages.yml +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/.github/workflows/tests.yml +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/.gitignore +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/LICENSE +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/README.md +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/conda/build.sh +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/conda/meta.yaml +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/docker/Dockerfile +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/docs/Getting_Started_with_eva_sub_cli.md +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/eva_sub_cli/__init__.py +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/eva_sub_cli/auth.py +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/eva_sub_cli/date_utils.py +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/eva_sub_cli/etc/EVA_Submission_Example.xlsx +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/eva_sub_cli/etc/EVA_Submission_template.xlsx +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/eva_sub_cli/etc/eva-biosamples.json +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/eva_sub_cli/etc/eva_logo.png +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/eva_sub_cli/etc/spreadsheet2json_conf.yaml +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/eva_sub_cli/exceptions/__init__.py +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/eva_sub_cli/exceptions/invalid_file_type_exception.py +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/eva_sub_cli/exceptions/metadata_template_version_exception.py +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/eva_sub_cli/exceptions/submission_not_found_exception.py +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/eva_sub_cli/exceptions/submission_status_exception.py +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/eva_sub_cli/executables/__init__.py +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/eva_sub_cli/executables/check_fasta_insdc.py +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/eva_sub_cli/executables/check_metadata_semantics.py +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/eva_sub_cli/executables/cli.py +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/eva_sub_cli/executables/samples_checker.py +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/eva_sub_cli/executables/xlsx2json.py +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/eva_sub_cli/jinja_templates/fasta_check.html +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/eva_sub_cli/jinja_templates/file_validation.html +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/eva_sub_cli/jinja_templates/html_report.html +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/eva_sub_cli/jinja_templates/metadata_validation.html +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/eva_sub_cli/jinja_templates/project_details.html +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/eva_sub_cli/jinja_templates/sample_name_check.html +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/eva_sub_cli/jinja_templates/shallow_validation.html +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/eva_sub_cli/metadata_utils.py +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/eva_sub_cli/orchestrator.py +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/eva_sub_cli/report.py +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/eva_sub_cli/submission_ws.py +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/eva_sub_cli/submit.py +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/eva_sub_cli/validators/__init__.py +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/eva_sub_cli/validators/native_validator.py +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/eva_sub_cli/validators/validation_results_parsers.py +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/eva_sub_cli.egg-info/dependency_links.txt +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/eva_sub_cli.egg-info/entry_points.txt +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/eva_sub_cli.egg-info/top_level.txt +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/pyproject.toml +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/pytest.ini +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/setup.cfg +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/setup.py +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/tests/ignore_test_nextflow_validation.yml +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/tests/resources/EVA_Submission_test.json +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/tests/resources/EVA_Submission_test.xlsx +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/tests/resources/EVA_Submission_test_fails.xlsx +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/tests/resources/EVA_Submission_test_version_missing.xlsx +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/tests/resources/EVA_Submission_test_with_asm_report.json +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/tests/resources/assembly_check/invalid.vcf.assembly_check.log +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/tests/resources/assembly_check/invalid.vcf.text_assembly_report.txt +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/tests/resources/assembly_reports/input_passed.txt +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/tests/resources/fasta_files/Saccharomyces_cerevisiae_I.fa +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/tests/resources/fasta_files/input_passed.fa +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/tests/resources/sample_checker/example1.vcf.gz +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/tests/resources/sample_checker/example2.vcf +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/tests/resources/sample_checker/example3.vcf +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/tests/resources/sample_checker/metadata.json +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/tests/resources/validation_reports/expected_report_metadata_json.html +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/tests/resources/validation_reports/expected_report_metadata_xlsx.html +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/tests/resources/validation_reports/expected_shallow_metadata_xlsx_report.html +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/tests/resources/validation_reports/validation_output/assembly_check/input_passed.vcf.assembly_check.log +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/tests/resources/validation_reports/validation_output/assembly_check/input_passed.vcf.text_assembly_report.1680102476700.txt +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/tests/resources/validation_reports/validation_output/assembly_check/input_passed.vcf.valid_assembly_report.1680102476700.txt +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/tests/resources/validation_reports/validation_output/other_validations/input_passed.fa_check.yml +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/tests/resources/validation_reports/validation_output/other_validations/metadata_conversion_errors.yml +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/tests/resources/validation_reports/validation_output/other_validations/metadata_semantic_check.yml +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/tests/resources/validation_reports/validation_output/other_validations/sample_checker.yml +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/tests/resources/validation_reports/validation_output/vcf_format/input_passed.vcf.errors.1680102476854.db +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/tests/resources/validation_reports/validation_output/vcf_format/input_passed.vcf.errors.1680102476854.txt +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/tests/resources/validation_reports/validation_output/vcf_format/input_passed.vcf.vcf_format.log +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/tests/resources/vcf_files/example2.vcf.gz +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/tests/resources/vcf_files/input_passed.vcf +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/tests/test_auth.py +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/tests/test_check_fasta_insdc.py +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/tests/test_docker_validator.py +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/tests/test_file_utils.py +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/tests/test_orchestrator.py +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/tests/test_report.py +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/tests/test_samples_checker.py +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/tests/test_submit.py +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/tests/test_utils.py +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.7}/tests/test_validaton_results_parsers.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: eva_sub_cli
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.7
|
|
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,12 +19,14 @@ 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
|
|
22
|
+
Requires-Dist: ebi_eva_common_pyutils==0.*,>=0.7.1
|
|
23
23
|
Requires-Dist: jinja2==3.*,>=3.0.0
|
|
24
24
|
Requires-Dist: jsonschema==4.*
|
|
25
25
|
Requires-Dist: openpyxl==3.*
|
|
26
26
|
Requires-Dist: pyyaml==6.*
|
|
27
27
|
Requires-Dist: requests==2.*
|
|
28
|
+
Requires-Dist: packaging==24.*
|
|
29
|
+
Requires-Dist: setuptools-scm
|
|
28
30
|
Dynamic: license-file
|
|
29
31
|
|
|
30
32
|
# eva-sub-cli
|
|
@@ -75,11 +75,13 @@
|
|
|
75
75
|
"properties": {
|
|
76
76
|
"title": {
|
|
77
77
|
"type": "string",
|
|
78
|
-
"description": "Title of the project"
|
|
78
|
+
"description": "Title of the project",
|
|
79
|
+
"maxLength": 500
|
|
79
80
|
},
|
|
80
81
|
"description": {
|
|
81
82
|
"type": "string",
|
|
82
|
-
"description": "Description of the project"
|
|
83
|
+
"description": "Description of the project",
|
|
84
|
+
"maxLength": 5000
|
|
83
85
|
},
|
|
84
86
|
"centre": {
|
|
85
87
|
"type": "string",
|
|
@@ -166,11 +168,11 @@
|
|
|
166
168
|
"type": "string",
|
|
167
169
|
"description": "Reference against which the analysis was performed. ENA or NCBI accession (starting with GCA) accepted."
|
|
168
170
|
},
|
|
169
|
-
|
|
171
|
+
"referenceFasta": {
|
|
170
172
|
"type": "string",
|
|
171
173
|
"description": "The path to the reference in fasta format that was used to create the VCF. (This is a local path)"
|
|
172
174
|
},
|
|
173
|
-
|
|
175
|
+
"assemblyReport": {
|
|
174
176
|
"type": "string",
|
|
175
177
|
"description": "The path to the assembly report associated with the assembly as found in NCBI assemblies. (This is a local path and only applicable for GCAs)"
|
|
176
178
|
},
|
|
@@ -319,7 +321,7 @@
|
|
|
319
321
|
"$ref": "eva-biosamples.json"
|
|
320
322
|
},
|
|
321
323
|
{
|
|
322
|
-
|
|
324
|
+
"$ref": "https://www.ebi.ac.uk/biosamples/schema/store/registry/schemas/ERC000011"
|
|
323
325
|
},
|
|
324
326
|
{
|
|
325
327
|
"$ref": "https://www.ebi.ac.uk/biosamples/schemas/certification/plant-miappe.json"
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import argparse
|
|
2
2
|
import os
|
|
3
3
|
|
|
4
|
+
import sys
|
|
4
5
|
import yaml
|
|
5
6
|
from ebi_eva_common_pyutils.logger import logging_config
|
|
6
7
|
from eva_sub_cli.file_utils import open_gzip_if_required, fasta_iter
|
|
@@ -8,15 +9,16 @@ from eva_sub_cli.file_utils import open_gzip_if_required, fasta_iter
|
|
|
8
9
|
logger = logging_config.get_logger(__name__)
|
|
9
10
|
|
|
10
11
|
|
|
11
|
-
|
|
12
|
+
MAX_NB_LINES = 10000
|
|
12
13
|
|
|
13
14
|
|
|
14
|
-
def trim_down_vcf(vcf_file, output_vcf):
|
|
15
|
+
def trim_down_vcf(vcf_file, output_vcf, max_nb_lines=MAX_NB_LINES):
|
|
15
16
|
"""
|
|
16
17
|
Produce a smaller vcf files containing a maximum of 10000 records
|
|
17
18
|
"""
|
|
18
|
-
with open_gzip_if_required(vcf_file) as vcf_in,
|
|
19
|
+
with open_gzip_if_required(vcf_file) as vcf_in, open_gzip_if_required(output_vcf, 'w') as vcf_out:
|
|
19
20
|
line_count = 0
|
|
21
|
+
trimmed_down = False
|
|
20
22
|
ref_seq_names = set()
|
|
21
23
|
for line in vcf_in:
|
|
22
24
|
if line.startswith('#') or line_count < max_nb_lines:
|
|
@@ -25,10 +27,12 @@ def trim_down_vcf(vcf_file, output_vcf):
|
|
|
25
27
|
line_count += 1
|
|
26
28
|
ref_seq_names.add(line.split('\t')[0])
|
|
27
29
|
else:
|
|
30
|
+
trimmed_down = True
|
|
28
31
|
break
|
|
29
32
|
if line_count != max_nb_lines:
|
|
30
33
|
logger.warning(f'Only {line_count} found in the source VCF {vcf_file} ')
|
|
31
|
-
|
|
34
|
+
|
|
35
|
+
return line_count, trimmed_down, ref_seq_names
|
|
32
36
|
|
|
33
37
|
|
|
34
38
|
def trim_down_fasta(fasta_file, output_fasta, ref_seq_names):
|
|
@@ -46,10 +50,9 @@ def trim_down_fasta(fasta_file, output_fasta, ref_seq_names):
|
|
|
46
50
|
print(sequence[i:i+80], file=fasta_out)
|
|
47
51
|
return found_sequences
|
|
48
52
|
|
|
49
|
-
|
|
50
|
-
def main():
|
|
53
|
+
def parse_args(cmd_line_args):
|
|
51
54
|
arg_parser = argparse.ArgumentParser(
|
|
52
|
-
description=f'Take a VCF file and only keep {
|
|
55
|
+
description=f'Take a VCF file and only keep {MAX_NB_LINES} lines and remove unused fasta sequence from the '
|
|
53
56
|
f'associated reference genome')
|
|
54
57
|
arg_parser.add_argument('--vcf_file', dest='vcf_file', required=True,
|
|
55
58
|
help='Path to the vcf file to be trimmed down')
|
|
@@ -63,16 +66,23 @@ def main():
|
|
|
63
66
|
help='Path to the yaml file containing the trim down metrics')
|
|
64
67
|
|
|
65
68
|
args = arg_parser.parse_args()
|
|
69
|
+
return args
|
|
70
|
+
|
|
71
|
+
def main():
|
|
72
|
+
args = parse_args(sys.argv[1:])
|
|
73
|
+
|
|
66
74
|
logging_config.add_stdout_handler()
|
|
67
75
|
|
|
68
|
-
line_count, ref_sequence = trim_down_vcf(args.vcf_file, args.output_vcf_file)
|
|
76
|
+
line_count, trimmed_down, ref_sequence = trim_down_vcf(args.vcf_file, args.output_vcf_file)
|
|
69
77
|
sequence_found = trim_down_fasta(args.fasta_file, args.output_fasta_file, ref_sequence)
|
|
70
|
-
trim_down_metrics = {'trim_down_vcf_record': line_count, 'number_sequence_found': sequence_found,
|
|
71
|
-
'trim_down_required':
|
|
78
|
+
trim_down_metrics = {'trim_down_vcf_record': line_count, 'number_sequence_found': len(sequence_found),
|
|
79
|
+
'trim_down_required': trimmed_down}
|
|
72
80
|
if len(sequence_found) != len(ref_sequence):
|
|
73
81
|
logger.warning(f'Not all sequences were found in the fasta file. Cancelling trimming down of fasta file')
|
|
82
|
+
if os.path.exists(args.output_fasta_file):
|
|
83
|
+
os.remove(args.output_fasta_file)
|
|
74
84
|
os.link(args.fasta_file, args.output_fasta_file)
|
|
75
85
|
trim_down_metrics.pop('number_sequence_found')
|
|
76
|
-
with open(args.output_yaml_file) as open_file:
|
|
86
|
+
with open(args.output_yaml_file, 'w') as open_file:
|
|
77
87
|
yaml.safe_dump(trim_down_metrics, open_file)
|
|
78
88
|
|
|
@@ -46,12 +46,12 @@ def backup_file_or_directory(file_name, max_backups=None):
|
|
|
46
46
|
os.rename(file_name, file_name + '.1')
|
|
47
47
|
|
|
48
48
|
|
|
49
|
-
def open_gzip_if_required(input_file):
|
|
49
|
+
def open_gzip_if_required(input_file, mode='r'):
|
|
50
50
|
"""Open a file in read mode using gzip if the file extension says .gz"""
|
|
51
51
|
if input_file.endswith('.gz'):
|
|
52
|
-
return gzip.open(input_file, '
|
|
52
|
+
return gzip.open(input_file, mode + 't')
|
|
53
53
|
else:
|
|
54
|
-
return open(input_file,
|
|
54
|
+
return open(input_file, mode)
|
|
55
55
|
|
|
56
56
|
|
|
57
57
|
def fasta_iter(input_fasta):
|
|
@@ -116,6 +116,7 @@ process trim_down_vcf {
|
|
|
116
116
|
path "${vcf.getBaseName()}_trim_down.log", emit: trim_down_log
|
|
117
117
|
path "${vcf.getBaseName()}_trim_down.yml", emit: trim_down_metric
|
|
118
118
|
|
|
119
|
+
script:
|
|
119
120
|
"""
|
|
120
121
|
mkdir output
|
|
121
122
|
$params.python_scripts.trim_down --vcf_file $vcf --output_vcf_file output/$vcf --fasta_file $fasta --output_fasta_file output/$fasta --output_yaml_file ${vcf.getBaseName()}_trim_down.yml > ${vcf.getBaseName()}_trim_down.log
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import json
|
|
2
2
|
from collections import Counter
|
|
3
3
|
from copy import deepcopy
|
|
4
|
+
from datetime import datetime
|
|
4
5
|
|
|
5
6
|
import yaml
|
|
6
7
|
|
|
@@ -26,6 +27,8 @@ SCI_NAME_KEYS = ['species', 'Species', 'organism', 'Organism']
|
|
|
26
27
|
ANALYSIS_ALIAS_KEY = 'analysisAlias'
|
|
27
28
|
ANALYSIS_RUNS_KEY = 'runAccessions'
|
|
28
29
|
|
|
30
|
+
# Samples created before this date are not required to have collection date or geographic location
|
|
31
|
+
threshold_2023 = datetime(2023,1,1)
|
|
29
32
|
|
|
30
33
|
def cast_list(l, type_to_cast=str):
|
|
31
34
|
for e in l:
|
|
@@ -147,8 +150,12 @@ class SemanticMetadataChecker(AppLogger):
|
|
|
147
150
|
"""
|
|
148
151
|
self.errors.append({'property': property, 'description': description})
|
|
149
152
|
|
|
150
|
-
def _get_biosample(self, sample_accession):
|
|
151
|
-
|
|
153
|
+
def _get_biosample(self, sample_accession, include_curation=False):
|
|
154
|
+
if include_curation:
|
|
155
|
+
append_to_url = sample_accession
|
|
156
|
+
else:
|
|
157
|
+
append_to_url = sample_accession + '?curationdomain='
|
|
158
|
+
return self.communicator.follows_link('samples', join_url=append_to_url)
|
|
152
159
|
|
|
153
160
|
def check_existing_biosamples(self):
|
|
154
161
|
"""Check that existing BioSamples are accessible and contain the required attributes."""
|
|
@@ -184,15 +191,45 @@ class SemanticMetadataChecker(AppLogger):
|
|
|
184
191
|
# Successful response returns the sample object, error response has a list of error objects.
|
|
185
192
|
if isinstance(response, list):
|
|
186
193
|
for error_dict in response:
|
|
194
|
+
error_property = ''
|
|
195
|
+
# Retrieve the property the error applied to in the datapath
|
|
196
|
+
if 'dataPath' in error_dict:
|
|
197
|
+
if error_dict['dataPath'].startswith('/characteristics/'):
|
|
198
|
+
error_property = error_dict['dataPath'].split('/')[2]
|
|
199
|
+
else:
|
|
200
|
+
error_property = error_dict['dataPath']
|
|
201
|
+
# Define the error
|
|
202
|
+
error_list = []
|
|
187
203
|
if accession:
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
204
|
+
error_list.append(f'Error validating existing sample {accession}:')
|
|
205
|
+
if error_property and error_property not in error_dict['errors'][0]:
|
|
206
|
+
error_list.append(error_property)
|
|
207
|
+
error_list.append(error_dict['errors'][0])
|
|
208
|
+
error = ' '.join(error_list)
|
|
209
|
+
if self._should_bypass_error(sample_data, error):
|
|
210
|
+
self.debug(f'bypass error {error} from {json_path}')
|
|
211
|
+
continue
|
|
212
|
+
self.add_error(json_path, error)
|
|
213
|
+
|
|
191
214
|
finally:
|
|
192
215
|
self.communicator.acceptable_code = pre_acceptable_code
|
|
193
216
|
|
|
217
|
+
def _should_bypass_error(self, sample_data, error):
|
|
218
|
+
try:
|
|
219
|
+
created_dated = datetime.strptime(sample_data['create'],'%Y-%m-%dT%H:%M:%S.%fZ')
|
|
220
|
+
if created_dated < threshold_2023 and (
|
|
221
|
+
'collection date' in error or
|
|
222
|
+
'geographic location (country and/or sea)' in error
|
|
223
|
+
):
|
|
224
|
+
return True
|
|
225
|
+
except Exception:
|
|
226
|
+
pass
|
|
227
|
+
return False
|
|
228
|
+
|
|
194
229
|
def _local_validate_existing_biosample(self, sample_data, json_path, accession):
|
|
195
230
|
"""Check if the existing sample has the expected fields present"""
|
|
231
|
+
if self._should_bypass_error(sample_data, 'collection date'):
|
|
232
|
+
return
|
|
196
233
|
found_collection_date = False
|
|
197
234
|
for key in ['collection_date', 'collection date']:
|
|
198
235
|
if key in sample_data['characteristics'] and check_date(sample_data['characteristics'][key][0]['text']):
|
|
@@ -11,8 +11,8 @@ from eva_sub_cli.validators.validator import Validator
|
|
|
11
11
|
|
|
12
12
|
logger = logging_config.get_logger(__name__)
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
default_container_image = 'ebivariation/eva-sub-cli'
|
|
15
|
+
default_container_tag = 'v0.0.3'
|
|
16
16
|
container_validation_dir = '/opt/vcf_validation'
|
|
17
17
|
container_validation_output_dir = 'vcf_validation_output'
|
|
18
18
|
|
|
@@ -20,13 +20,16 @@ container_validation_output_dir = 'vcf_validation_output'
|
|
|
20
20
|
class DockerValidator(Validator):
|
|
21
21
|
|
|
22
22
|
def __init__(self, mapping_file, submission_dir, project_title, metadata_json=None,
|
|
23
|
-
metadata_xlsx=None, shallow_validation=False, docker_path='docker', submission_config=None
|
|
23
|
+
metadata_xlsx=None, shallow_validation=False, docker_path='docker', submission_config=None,
|
|
24
|
+
container_image=None, container_tag=None, container_name=None):
|
|
24
25
|
super().__init__(mapping_file, submission_dir, project_title,
|
|
25
26
|
metadata_json=metadata_json, metadata_xlsx=metadata_xlsx,
|
|
26
27
|
shallow_validation=shallow_validation, submission_config=submission_config)
|
|
27
28
|
self.docker_path = docker_path
|
|
28
29
|
submission_basename = re.sub('[^a-zA-Z0-9]', '', os.path.basename(submission_dir))
|
|
29
|
-
self.
|
|
30
|
+
self.container_image = container_image or default_container_image
|
|
31
|
+
self.container_tag = container_tag or default_container_tag
|
|
32
|
+
self.container_name = container_name or f'{self.container_image.split("/")[1]}.{self.container_tag}.{submission_basename}'
|
|
30
33
|
|
|
31
34
|
def _validate(self):
|
|
32
35
|
self.run_docker_validator()
|
|
@@ -130,11 +133,11 @@ class DockerValidator(Validator):
|
|
|
130
133
|
f"{self.docker_path} images",
|
|
131
134
|
return_process_output=True
|
|
132
135
|
)
|
|
133
|
-
if container_images_cmd_ouptut is not None and re.search(container_image + r'\s+' + container_tag, container_images_cmd_ouptut):
|
|
134
|
-
logger.debug(f"Container ({container_image}) image is available locally")
|
|
136
|
+
if container_images_cmd_ouptut is not None and re.search(self.container_image + r'\s+' + self.container_tag, container_images_cmd_ouptut):
|
|
137
|
+
logger.debug(f"Container ({self.container_image}) image is available locally")
|
|
135
138
|
return True
|
|
136
139
|
else:
|
|
137
|
-
logger.debug(f"Container ({container_image}) image is not available locally")
|
|
140
|
+
logger.debug(f"Container ({self.container_image}) image is not available locally")
|
|
138
141
|
return False
|
|
139
142
|
|
|
140
143
|
def run_container_if_required(self):
|
|
@@ -149,7 +152,7 @@ class DockerValidator(Validator):
|
|
|
149
152
|
try:
|
|
150
153
|
self._run_quiet_command(
|
|
151
154
|
"Running container",
|
|
152
|
-
f"{self.docker_path} run -it --rm -d --name {self.container_name} {container_image}:{container_tag}"
|
|
155
|
+
f"{self.docker_path} run -it --rm -d --name {self.container_name} {self.container_image}:{self.container_tag}"
|
|
153
156
|
)
|
|
154
157
|
# Wait to give some time to container to get up and running
|
|
155
158
|
time.sleep(5)
|
|
@@ -169,12 +172,13 @@ class DockerValidator(Validator):
|
|
|
169
172
|
@retry(RuntimeError, tries=3, delay=5, backoff=1, jitter=2, logger=logger)
|
|
170
173
|
def download_container_image_if_needed(self):
|
|
171
174
|
if not self.verify_image_available_locally():
|
|
172
|
-
logger.debug(f"Pulling container ({container_image}) image")
|
|
175
|
+
logger.debug(f"Pulling container ({self.container_image}) image")
|
|
173
176
|
try:
|
|
174
|
-
self._run_quiet_command("pull container image",
|
|
177
|
+
self._run_quiet_command("pull container image",
|
|
178
|
+
f"{self.docker_path} pull {self.container_image}:{self.container_tag}")
|
|
175
179
|
except subprocess.CalledProcessError as ex:
|
|
176
180
|
logger.error(ex)
|
|
177
|
-
raise RuntimeError(f"Cannot pull container ({container_image}) image")
|
|
181
|
+
raise RuntimeError(f"Cannot pull container ({self.container_image}) image")
|
|
178
182
|
# Give the pull command some time to complete
|
|
179
183
|
time.sleep(5)
|
|
180
184
|
|
|
@@ -371,15 +371,18 @@ class Validator(AppLogger):
|
|
|
371
371
|
sheet = convert_metadata_sheet(sheet_json, xls2json_conf)
|
|
372
372
|
row = convert_metadata_row(sheet, row_json, xls2json_conf)
|
|
373
373
|
column = convert_metadata_attribute(sheet, attribute_json, xls2json_conf)
|
|
374
|
+
|
|
374
375
|
if row_json is None and attribute_json is None and sheet is not None:
|
|
375
376
|
new_description = f'Sheet "{sheet}" is missing'
|
|
376
377
|
elif row_json is None:
|
|
377
|
-
|
|
378
|
+
missing_property_error = f" have required property '{sheet_json}'"
|
|
379
|
+
if not error['description'].endswith(missing_property_error):
|
|
378
380
|
new_description = error['description']
|
|
379
381
|
else:
|
|
380
382
|
new_description = f'Column "{column}" is not populated'
|
|
381
383
|
elif attribute_json and column:
|
|
382
|
-
|
|
384
|
+
missing_property_error = f" have required property '{attribute_json}'"
|
|
385
|
+
if not error['description'].endswith(missing_property_error):
|
|
383
386
|
new_description = error['description']
|
|
384
387
|
else:
|
|
385
388
|
new_description = f'Column "{column}" is not populated'
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: eva_sub_cli
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.7
|
|
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,12 +19,14 @@ 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
|
|
22
|
+
Requires-Dist: ebi_eva_common_pyutils==0.*,>=0.7.1
|
|
23
23
|
Requires-Dist: jinja2==3.*,>=3.0.0
|
|
24
24
|
Requires-Dist: jsonschema==4.*
|
|
25
25
|
Requires-Dist: openpyxl==3.*
|
|
26
26
|
Requires-Dist: pyyaml==6.*
|
|
27
27
|
Requires-Dist: requests==2.*
|
|
28
|
+
Requires-Dist: packaging==24.*
|
|
29
|
+
Requires-Dist: setuptools-scm
|
|
28
30
|
Dynamic: license-file
|
|
29
31
|
|
|
30
32
|
# eva-sub-cli
|
|
@@ -61,6 +61,7 @@ eva_sub_cli/validators/docker_validator.py
|
|
|
61
61
|
eva_sub_cli/validators/native_validator.py
|
|
62
62
|
eva_sub_cli/validators/validation_results_parsers.py
|
|
63
63
|
eva_sub_cli/validators/validator.py
|
|
64
|
+
tests/build_and_test_docker_locally.py
|
|
64
65
|
tests/ignore_test_nextflow_validation.yml
|
|
65
66
|
tests/test_auth.py
|
|
66
67
|
tests/test_check_fasta_insdc.py
|
|
@@ -72,10 +73,12 @@ tests/test_report.py
|
|
|
72
73
|
tests/test_samples_checker.py
|
|
73
74
|
tests/test_semantic_metadata.py
|
|
74
75
|
tests/test_submit.py
|
|
76
|
+
tests/test_trim_down.py
|
|
75
77
|
tests/test_utils.py
|
|
76
78
|
tests/test_validaton_results_parsers.py
|
|
77
79
|
tests/test_validator.py
|
|
78
80
|
tests/test_xlsx2json.py
|
|
81
|
+
tests/resources/EVA_Submission_Docker_Test.xlsx
|
|
79
82
|
tests/resources/EVA_Submission_test.json
|
|
80
83
|
tests/resources/EVA_Submission_test.xlsx
|
|
81
84
|
tests/resources/EVA_Submission_test_fails.xlsx
|
|
@@ -86,6 +89,7 @@ tests/resources/assembly_check/invalid.vcf.text_assembly_report.txt
|
|
|
86
89
|
tests/resources/assembly_reports/input_passed.txt
|
|
87
90
|
tests/resources/fasta_files/Saccharomyces_cerevisiae_I.fa
|
|
88
91
|
tests/resources/fasta_files/input_passed.fa
|
|
92
|
+
tests/resources/fasta_files/multi_seq.fa
|
|
89
93
|
tests/resources/sample_checker/example1.vcf.gz
|
|
90
94
|
tests/resources/sample_checker/example2.vcf
|
|
91
95
|
tests/resources/sample_checker/example3.vcf
|