eva-sub-cli 0.4.6__tar.gz → 0.4.8__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.8/.github/workflows/trigger-eva-sub-cli-integration-tests.yml +16 -0
- {eva_sub_cli-0.4.6/eva_sub_cli.egg-info → eva_sub_cli-0.4.8}/PKG-INFO +4 -2
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/eva_sub_cli/__init__.py +1 -1
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/eva_sub_cli/_version.py +2 -2
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/eva_sub_cli/etc/EVA_Submission_Example.xlsx +0 -0
- eva_sub_cli-0.4.8/eva_sub_cli/etc/EVA_Submission_template.xlsx +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/eva_sub_cli/etc/eva_schema.json +10 -7
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/eva_sub_cli/executables/check_fasta_insdc.py +18 -25
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/eva_sub_cli/executables/samples_checker.py +13 -19
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/eva_sub_cli/executables/trim_down.py +21 -11
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/eva_sub_cli/executables/xlsx2json.py +3 -3
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/eva_sub_cli/file_utils.py +8 -3
- eva_sub_cli-0.4.8/eva_sub_cli/metadata.py +95 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/eva_sub_cli/nextflow/validation.nf +1 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/eva_sub_cli/orchestrator.py +53 -52
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/eva_sub_cli/semantic_metadata.py +42 -5
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/eva_sub_cli/validators/docker_validator.py +15 -11
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/eva_sub_cli/validators/native_validator.py +4 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/eva_sub_cli/validators/validator.py +44 -45
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8/eva_sub_cli.egg-info}/PKG-INFO +4 -2
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/eva_sub_cli.egg-info/SOURCES.txt +8 -1
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/eva_sub_cli.egg-info/requires.txt +3 -1
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/requirements.txt +3 -1
- eva_sub_cli-0.4.8/tests/build_and_test_docker_locally.py +293 -0
- eva_sub_cli-0.4.8/tests/resources/EVA_Submission_Docker_Test.xlsx +0 -0
- eva_sub_cli-0.4.8/tests/resources/EVA_Submission_test.xlsx +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/tests/resources/EVA_Submission_test_fails.xlsx +0 -0
- eva_sub_cli-0.4.8/tests/resources/EVA_Submission_test_version_missing.xlsx +0 -0
- eva_sub_cli-0.4.8/tests/resources/fasta_files/multi_seq.fa +8 -0
- eva_sub_cli-0.4.8/tests/resources/metadata_with_filename.json +19 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/tests/resources/validation_reports/validation_output/other_validations/metadata_validation.txt +2 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/tests/test_check_fasta_insdc.py +35 -3
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/tests/test_cli.py +5 -0
- eva_sub_cli-0.4.8/tests/test_metadata.py +69 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/tests/test_orchestrator.py +27 -6
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/tests/test_samples_checker.py +29 -2
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/tests/test_semantic_metadata.py +41 -17
- eva_sub_cli-0.4.8/tests/test_trim_down.py +108 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/tests/test_validator.py +46 -2
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/tests/test_xlsx2json.py +41 -0
- eva_sub_cli-0.4.6/eva_sub_cli/etc/EVA_Submission_template.xlsx +0 -0
- eva_sub_cli-0.4.6/eva_sub_cli/metadata_utils.py +0 -46
- eva_sub_cli-0.4.6/tests/resources/EVA_Submission_test.xlsx +0 -0
- eva_sub_cli-0.4.6/tests/resources/EVA_Submission_test_version_missing.xlsx +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/.github/workflows/publish.yml +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/.github/workflows/publish_packages.yml +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/.github/workflows/tests.yml +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/.gitignore +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/LICENSE +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/README.md +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/conda/build.sh +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/conda/meta.yaml +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/docker/Dockerfile +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/docs/Getting_Started_with_eva_sub_cli.md +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/eva_sub_cli/auth.py +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/eva_sub_cli/date_utils.py +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/eva_sub_cli/etc/eva-biosamples.json +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/eva_sub_cli/etc/eva_logo.png +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/eva_sub_cli/etc/spreadsheet2json_conf.yaml +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/eva_sub_cli/exceptions/__init__.py +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/eva_sub_cli/exceptions/invalid_file_type_exception.py +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/eva_sub_cli/exceptions/metadata_template_version_exception.py +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/eva_sub_cli/exceptions/submission_not_found_exception.py +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/eva_sub_cli/exceptions/submission_status_exception.py +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/eva_sub_cli/executables/__init__.py +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/eva_sub_cli/executables/check_metadata_semantics.py +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/eva_sub_cli/executables/cli.py +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/eva_sub_cli/jinja_templates/fasta_check.html +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/eva_sub_cli/jinja_templates/file_validation.html +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/eva_sub_cli/jinja_templates/html_report.html +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/eva_sub_cli/jinja_templates/metadata_validation.html +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/eva_sub_cli/jinja_templates/project_details.html +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/eva_sub_cli/jinja_templates/sample_name_check.html +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/eva_sub_cli/jinja_templates/shallow_validation.html +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/eva_sub_cli/report.py +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/eva_sub_cli/submission_ws.py +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/eva_sub_cli/submit.py +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/eva_sub_cli/validators/__init__.py +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/eva_sub_cli/validators/validation_results_parsers.py +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/eva_sub_cli.egg-info/dependency_links.txt +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/eva_sub_cli.egg-info/entry_points.txt +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/eva_sub_cli.egg-info/top_level.txt +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/pyproject.toml +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/pytest.ini +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/setup.cfg +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/setup.py +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/tests/ignore_test_nextflow_validation.yml +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/tests/resources/EVA_Submission_test.json +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/tests/resources/EVA_Submission_test_with_asm_report.json +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/tests/resources/assembly_check/invalid.vcf.assembly_check.log +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/tests/resources/assembly_check/invalid.vcf.text_assembly_report.txt +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/tests/resources/assembly_reports/input_passed.txt +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/tests/resources/fasta_files/Saccharomyces_cerevisiae_I.fa +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/tests/resources/fasta_files/input_passed.fa +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/tests/resources/sample_checker/example1.vcf.gz +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/tests/resources/sample_checker/example2.vcf +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/tests/resources/sample_checker/example3.vcf +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/tests/resources/sample_checker/metadata.json +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/tests/resources/validation_reports/expected_report_metadata_json.html +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/tests/resources/validation_reports/expected_report_metadata_xlsx.html +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/tests/resources/validation_reports/expected_shallow_metadata_xlsx_report.html +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/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.8}/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.8}/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.8}/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.8}/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.8}/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.8}/tests/resources/validation_reports/validation_output/other_validations/sample_checker.yml +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/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.8}/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.8}/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.8}/tests/resources/vcf_files/example2.vcf.gz +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/tests/resources/vcf_files/input_passed.vcf +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/tests/test_auth.py +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/tests/test_docker_validator.py +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/tests/test_file_utils.py +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/tests/test_report.py +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/tests/test_submit.py +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/tests/test_utils.py +0 -0
- {eva_sub_cli-0.4.6 → eva_sub_cli-0.4.8}/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.8
|
|
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
|
|
@@ -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
|
|
@@ -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
|
},
|
|
@@ -308,9 +310,10 @@
|
|
|
308
310
|
"type": "string",
|
|
309
311
|
"description": "Sample Name used in the VCF file"
|
|
310
312
|
},
|
|
311
|
-
"
|
|
313
|
+
"bioSampleAccession": {
|
|
312
314
|
"type": "string",
|
|
313
|
-
"
|
|
315
|
+
"pattern": "^SAM(E|D|N)[A-Z]?[0-9]+$",
|
|
316
|
+
"description": "BioSample accession of pre-existing sample. Only use for preregistered samples"
|
|
314
317
|
},
|
|
315
318
|
"bioSampleObject": {
|
|
316
319
|
"type": "object",
|
|
@@ -319,7 +322,7 @@
|
|
|
319
322
|
"$ref": "eva-biosamples.json"
|
|
320
323
|
},
|
|
321
324
|
{
|
|
322
|
-
|
|
325
|
+
"$ref": "https://www.ebi.ac.uk/biosamples/schema/store/registry/schemas/ERC000011"
|
|
323
326
|
},
|
|
324
327
|
{
|
|
325
328
|
"$ref": "https://www.ebi.ac.uk/biosamples/schemas/certification/plant-miappe.json"
|
|
@@ -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
|
|
|
@@ -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
|
|
|
@@ -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
|
"""
|
|
@@ -46,12 +51,12 @@ def backup_file_or_directory(file_name, max_backups=None):
|
|
|
46
51
|
os.rename(file_name, file_name + '.1')
|
|
47
52
|
|
|
48
53
|
|
|
49
|
-
def open_gzip_if_required(input_file):
|
|
54
|
+
def open_gzip_if_required(input_file, mode='r'):
|
|
50
55
|
"""Open a file in read mode using gzip if the file extension says .gz"""
|
|
51
56
|
if input_file.endswith('.gz'):
|
|
52
|
-
return gzip.open(input_file, '
|
|
57
|
+
return gzip.open(input_file, mode + 't')
|
|
53
58
|
else:
|
|
54
|
-
return open(input_file,
|
|
59
|
+
return open(input_file, mode)
|
|
55
60
|
|
|
56
61
|
|
|
57
62
|
def fasta_iter(input_fasta):
|
|
@@ -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)
|
|
@@ -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
|