eva-sub-cli 0.4.7__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.
Files changed (121) hide show
  1. eva_sub_cli-0.4.8/.github/workflows/trigger-eva-sub-cli-integration-tests.yml +16 -0
  2. {eva_sub_cli-0.4.7/eva_sub_cli.egg-info → eva_sub_cli-0.4.8}/PKG-INFO +1 -1
  3. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/eva_sub_cli/__init__.py +1 -1
  4. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/eva_sub_cli/_version.py +2 -2
  5. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/eva_sub_cli/etc/EVA_Submission_Example.xlsx +0 -0
  6. eva_sub_cli-0.4.8/eva_sub_cli/etc/EVA_Submission_template.xlsx +0 -0
  7. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/eva_sub_cli/etc/eva_schema.json +3 -2
  8. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/eva_sub_cli/executables/check_fasta_insdc.py +18 -25
  9. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/eva_sub_cli/executables/samples_checker.py +13 -19
  10. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/eva_sub_cli/executables/xlsx2json.py +3 -3
  11. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/eva_sub_cli/file_utils.py +5 -0
  12. eva_sub_cli-0.4.8/eva_sub_cli/metadata.py +95 -0
  13. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/eva_sub_cli/orchestrator.py +53 -52
  14. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/eva_sub_cli/validators/docker_validator.py +1 -1
  15. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/eva_sub_cli/validators/native_validator.py +4 -0
  16. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/eva_sub_cli/validators/validator.py +39 -43
  17. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8/eva_sub_cli.egg-info}/PKG-INFO +1 -1
  18. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/eva_sub_cli.egg-info/SOURCES.txt +4 -1
  19. eva_sub_cli-0.4.8/tests/resources/EVA_Submission_Docker_Test.xlsx +0 -0
  20. eva_sub_cli-0.4.8/tests/resources/EVA_Submission_test.xlsx +0 -0
  21. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/tests/resources/EVA_Submission_test_fails.xlsx +0 -0
  22. eva_sub_cli-0.4.8/tests/resources/EVA_Submission_test_version_missing.xlsx +0 -0
  23. eva_sub_cli-0.4.8/tests/resources/metadata_with_filename.json +19 -0
  24. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/tests/test_check_fasta_insdc.py +35 -3
  25. eva_sub_cli-0.4.8/tests/test_metadata.py +69 -0
  26. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/tests/test_orchestrator.py +27 -6
  27. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/tests/test_samples_checker.py +29 -2
  28. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/tests/test_validator.py +35 -0
  29. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/tests/test_xlsx2json.py +14 -0
  30. eva_sub_cli-0.4.7/eva_sub_cli/etc/EVA_Submission_template.xlsx +0 -0
  31. eva_sub_cli-0.4.7/eva_sub_cli/metadata_utils.py +0 -46
  32. eva_sub_cli-0.4.7/tests/resources/EVA_Submission_Docker_Test.xlsx +0 -0
  33. eva_sub_cli-0.4.7/tests/resources/EVA_Submission_test.xlsx +0 -0
  34. eva_sub_cli-0.4.7/tests/resources/EVA_Submission_test_version_missing.xlsx +0 -0
  35. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/.github/workflows/publish.yml +0 -0
  36. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/.github/workflows/publish_packages.yml +0 -0
  37. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/.github/workflows/tests.yml +0 -0
  38. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/.gitignore +0 -0
  39. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/LICENSE +0 -0
  40. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/README.md +0 -0
  41. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/conda/build.sh +0 -0
  42. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/conda/meta.yaml +0 -0
  43. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/docker/Dockerfile +0 -0
  44. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/docs/Getting_Started_with_eva_sub_cli.md +0 -0
  45. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/eva_sub_cli/auth.py +0 -0
  46. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/eva_sub_cli/date_utils.py +0 -0
  47. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/eva_sub_cli/etc/eva-biosamples.json +0 -0
  48. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/eva_sub_cli/etc/eva_logo.png +0 -0
  49. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/eva_sub_cli/etc/spreadsheet2json_conf.yaml +0 -0
  50. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/eva_sub_cli/exceptions/__init__.py +0 -0
  51. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/eva_sub_cli/exceptions/invalid_file_type_exception.py +0 -0
  52. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/eva_sub_cli/exceptions/metadata_template_version_exception.py +0 -0
  53. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/eva_sub_cli/exceptions/submission_not_found_exception.py +0 -0
  54. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/eva_sub_cli/exceptions/submission_status_exception.py +0 -0
  55. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/eva_sub_cli/executables/__init__.py +0 -0
  56. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/eva_sub_cli/executables/check_metadata_semantics.py +0 -0
  57. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/eva_sub_cli/executables/cli.py +0 -0
  58. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/eva_sub_cli/executables/trim_down.py +0 -0
  59. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/eva_sub_cli/jinja_templates/fasta_check.html +0 -0
  60. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/eva_sub_cli/jinja_templates/file_validation.html +0 -0
  61. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/eva_sub_cli/jinja_templates/html_report.html +0 -0
  62. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/eva_sub_cli/jinja_templates/metadata_validation.html +0 -0
  63. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/eva_sub_cli/jinja_templates/project_details.html +0 -0
  64. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/eva_sub_cli/jinja_templates/sample_name_check.html +0 -0
  65. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/eva_sub_cli/jinja_templates/shallow_validation.html +0 -0
  66. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/eva_sub_cli/nextflow/validation.nf +0 -0
  67. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/eva_sub_cli/report.py +0 -0
  68. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/eva_sub_cli/semantic_metadata.py +0 -0
  69. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/eva_sub_cli/submission_ws.py +0 -0
  70. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/eva_sub_cli/submit.py +0 -0
  71. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/eva_sub_cli/validators/__init__.py +0 -0
  72. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/eva_sub_cli/validators/validation_results_parsers.py +0 -0
  73. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/eva_sub_cli.egg-info/dependency_links.txt +0 -0
  74. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/eva_sub_cli.egg-info/entry_points.txt +0 -0
  75. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/eva_sub_cli.egg-info/requires.txt +0 -0
  76. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/eva_sub_cli.egg-info/top_level.txt +0 -0
  77. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/pyproject.toml +0 -0
  78. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/pytest.ini +0 -0
  79. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/requirements.txt +0 -0
  80. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/setup.cfg +0 -0
  81. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/setup.py +0 -0
  82. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/tests/build_and_test_docker_locally.py +0 -0
  83. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/tests/ignore_test_nextflow_validation.yml +0 -0
  84. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/tests/resources/EVA_Submission_test.json +0 -0
  85. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/tests/resources/EVA_Submission_test_with_asm_report.json +0 -0
  86. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/tests/resources/assembly_check/invalid.vcf.assembly_check.log +0 -0
  87. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/tests/resources/assembly_check/invalid.vcf.text_assembly_report.txt +0 -0
  88. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/tests/resources/assembly_reports/input_passed.txt +0 -0
  89. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/tests/resources/fasta_files/Saccharomyces_cerevisiae_I.fa +0 -0
  90. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/tests/resources/fasta_files/input_passed.fa +0 -0
  91. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/tests/resources/fasta_files/multi_seq.fa +0 -0
  92. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/tests/resources/sample_checker/example1.vcf.gz +0 -0
  93. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/tests/resources/sample_checker/example2.vcf +0 -0
  94. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/tests/resources/sample_checker/example3.vcf +0 -0
  95. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/tests/resources/sample_checker/metadata.json +0 -0
  96. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/tests/resources/validation_reports/expected_report_metadata_json.html +0 -0
  97. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/tests/resources/validation_reports/expected_report_metadata_xlsx.html +0 -0
  98. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/tests/resources/validation_reports/expected_shallow_metadata_xlsx_report.html +0 -0
  99. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/tests/resources/validation_reports/validation_output/assembly_check/input_passed.vcf.assembly_check.log +0 -0
  100. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/tests/resources/validation_reports/validation_output/assembly_check/input_passed.vcf.text_assembly_report.1680102476700.txt +0 -0
  101. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/tests/resources/validation_reports/validation_output/assembly_check/input_passed.vcf.valid_assembly_report.1680102476700.txt +0 -0
  102. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/tests/resources/validation_reports/validation_output/other_validations/input_passed.fa_check.yml +0 -0
  103. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/tests/resources/validation_reports/validation_output/other_validations/metadata_conversion_errors.yml +0 -0
  104. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/tests/resources/validation_reports/validation_output/other_validations/metadata_semantic_check.yml +0 -0
  105. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/tests/resources/validation_reports/validation_output/other_validations/metadata_validation.txt +0 -0
  106. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/tests/resources/validation_reports/validation_output/other_validations/sample_checker.yml +0 -0
  107. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/tests/resources/validation_reports/validation_output/vcf_format/input_passed.vcf.errors.1680102476854.db +0 -0
  108. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/tests/resources/validation_reports/validation_output/vcf_format/input_passed.vcf.errors.1680102476854.txt +0 -0
  109. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/tests/resources/validation_reports/validation_output/vcf_format/input_passed.vcf.vcf_format.log +0 -0
  110. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/tests/resources/vcf_files/example2.vcf.gz +0 -0
  111. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/tests/resources/vcf_files/input_passed.vcf +0 -0
  112. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/tests/test_auth.py +0 -0
  113. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/tests/test_cli.py +0 -0
  114. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/tests/test_docker_validator.py +0 -0
  115. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/tests/test_file_utils.py +0 -0
  116. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/tests/test_report.py +0 -0
  117. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/tests/test_semantic_metadata.py +0 -0
  118. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/tests/test_submit.py +0 -0
  119. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/tests/test_trim_down.py +0 -0
  120. {eva_sub_cli-0.4.7 → eva_sub_cli-0.4.8}/tests/test_utils.py +0 -0
  121. {eva_sub_cli-0.4.7 → 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.7
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
@@ -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
- DEFAULT_METADATA_XLSX_TEMPLATE_VERSION = '1.1.6'
9
+ MINIMUM_METADATA_XLSX_TEMPLATE_VERSION = '2.0.0'
10
10
 
11
11
  SUB_CLI_CONFIG_FILE = ".eva_sub_cli_config.yml"
12
12
 
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '0.4.7'
21
- __version_tuple__ = version_tuple = (0, 4, 7)
20
+ __version__ = version = '0.4.8'
21
+ __version_tuple__ = version_tuple = (0, 4, 8)
@@ -310,9 +310,10 @@
310
310
  "type": "string",
311
311
  "description": "Sample Name used in the VCF file"
312
312
  },
313
- "sampleAccession": {
313
+ "bioSampleAccession": {
314
314
  "type": "string",
315
- "description": "Accession of the sample (BioSamples, ENA or EGA)"
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.metadata_utils import get_files_per_analysis, get_analysis_for_vcf_file, \
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 |= _get_containing_assemblies_paged(response_data['_links']['next'])
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 (ConnectionError, HTTPError) as e:
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
- with open(json_file) as open_json:
125
- metadata = json.load(open_json)
126
- files_per_analysis = get_files_per_analysis(metadata)
127
- # Get all analyses associated with all vcf files that are linked with a single fasta file
128
- all_analyses = set()
129
- for vcf_file in vcf_files_for_fasta:
130
- analysis_aliases = get_analysis_for_vcf_file(vcf_file, files_per_analysis)
131
- if len(analysis_aliases) != 1:
132
- logger.error(f'Could not determine analysis associated with VCF file: {vcf_file}')
133
- else:
134
- all_analyses.add(analysis_aliases[0])
135
- # Get (single) assembly associated with these analyses
136
- assemblies = [get_reference_assembly_for_analysis(metadata, analysis) for analysis in all_analyses]
137
- if len(assemblies) != 1:
138
- logger.error(f'Could not determine assembly accession to check against fasta file, out of: {assemblies}')
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.metadata_utils import get_samples_per_analysis, get_files_per_analysis, get_analysis_for_vcf_file
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 Sample names found in file {vcf_file}")
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(samples_per_analysis, files_per_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 read_metadata_json(json_file):
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, files_per_analysis)
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
- samples_per_analysis, files_per_analysis = read_metadata_json(metadata_json)
127
- file_path_per_analysis = associate_vcf_path_with_analysis(vcf_files, files_per_analysis)
128
- overall_differences, results_per_analysis_alias = compare_all_analysis(samples_per_analysis, file_path_per_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
- return [XlsxParser.trim_value(v)
112
- for v in value.split(',') if v]
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
  """
@@ -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)
@@ -1,6 +1,5 @@
1
1
  #!/usr/bin/env python
2
2
  import csv
3
- import json
4
3
  import os
5
4
  import re
6
5
  from collections import defaultdict
@@ -14,13 +13,15 @@ from requests import HTTPError
14
13
  from retry import retry
15
14
 
16
15
  import eva_sub_cli
17
- from eva_sub_cli import DEFAULT_METADATA_XLSX_TEMPLATE_VERSION
16
+ from eva_sub_cli import MINIMUM_METADATA_XLSX_TEMPLATE_VERSION
18
17
  from eva_sub_cli import SUB_CLI_CONFIG_FILE, __version__
19
18
  from eva_sub_cli.exceptions.invalid_file_type_exception import InvalidFileTypeError
20
19
  from eva_sub_cli.exceptions.metadata_template_version_exception import MetadataTemplateVersionException, \
21
20
  MetadataTemplateVersionNotFoundException
22
21
  from eva_sub_cli.exceptions.submission_not_found_exception import SubmissionNotFoundException
23
22
  from eva_sub_cli.exceptions.submission_status_exception import SubmissionStatusException
23
+ from eva_sub_cli.file_utils import is_vcf_file
24
+ from eva_sub_cli.metadata import EvaMetadataJson
24
25
  from eva_sub_cli.submission_ws import SubmissionWSClient
25
26
  from eva_sub_cli.submit import StudySubmitter, SUB_CLI_CONFIG_KEY_SUBMISSION_ID, \
26
27
  SUB_CLI_CONFIG_KEY_SUBMISSION_UPLOAD_URL
@@ -76,6 +77,8 @@ def get_project_title_and_create_vcf_files_mapping(submission_dir, vcf_files, re
76
77
  elif metadata_xlsx:
77
78
  project_title, vcf_files_mapping = get_project_and_vcf_fasta_mapping_from_metadata_xlsx(metadata_xlsx, True)
78
79
 
80
+ # Filter out non-vcf files
81
+ vcf_files_mapping = [(vcf, fasta, report) for vcf, fasta, report in vcf_files_mapping if is_vcf_file(vcf)]
79
82
  validate_vcf_mapping(vcf_files_mapping)
80
83
  for mapping in vcf_files_mapping:
81
84
  writer.writerow(mapping)
@@ -103,27 +106,24 @@ def validate_vcf_mapping(vcf_mapping):
103
106
 
104
107
 
105
108
  def get_project_and_vcf_fasta_mapping_from_metadata_json(metadata_json, mapping_req=False):
106
- with open(metadata_json) as file:
107
- json_metadata = json.load(file)
108
-
109
- if 'project' in json_metadata:
110
- if 'title' in json_metadata['project']:
111
- project_title = json_metadata['project']['title']
112
-
113
- vcf_fasta_report_mapping = []
114
- if mapping_req:
115
- analysis_alias_dict = defaultdict(dict)
116
- for analysis in json_metadata['analysis']:
117
- analysis_alias_dict[analysis['analysisAlias']]['referenceFasta'] = analysis['referenceFasta']
118
- analysis_alias_dict[analysis['analysisAlias']]['assemblyReport'] = analysis['assemblyReport'] \
119
- if 'assemblyReport' in analysis else ''
120
-
121
- for file_dict in json_metadata['files']:
122
- reference_fasta = analysis_alias_dict[file_dict['analysisAlias']]['referenceFasta']
123
- assembly_report = analysis_alias_dict[file_dict['analysisAlias']]['assemblyReport']
124
- vcf_fasta_report_mapping.append([os.path.abspath(file_dict['fileName']),
125
- os.path.abspath(reference_fasta),
126
- os.path.abspath(assembly_report) if assembly_report else ''])
109
+ metadata = EvaMetadataJson(metadata_json)
110
+
111
+ project_title = metadata.project.get('title')
112
+
113
+ vcf_fasta_report_mapping = []
114
+ if mapping_req:
115
+ analysis_alias_dict = defaultdict(dict)
116
+ for analysis in metadata.analyses:
117
+ analysis_alias_dict[analysis['analysisAlias']]['referenceFasta'] = analysis['referenceFasta']
118
+ analysis_alias_dict[analysis['analysisAlias']]['assemblyReport'] = analysis['assemblyReport'] \
119
+ if 'assemblyReport' in analysis else ''
120
+
121
+ for file_dict in metadata.resolved_files:
122
+ reference_fasta = analysis_alias_dict[file_dict['analysisAlias']]['referenceFasta']
123
+ assembly_report = analysis_alias_dict[file_dict['analysisAlias']]['assemblyReport']
124
+ vcf_fasta_report_mapping.append([os.path.abspath(file_dict['fileName']),
125
+ os.path.abspath(reference_fasta),
126
+ os.path.abspath(assembly_report) if assembly_report else ''])
127
127
 
128
128
  return project_title, vcf_fasta_report_mapping
129
129
 
@@ -225,35 +225,36 @@ def get_project_and_vcf_fasta_mapping_from_metadata_xlsx(metadata_xlsx, mapping_
225
225
 
226
226
  def check_validation_required(tasks, sub_config, username=None, password=None):
227
227
  # Validation is mandatory so if submit is requested then VALIDATE must have run before or be requested as well
228
- if SUBMIT in tasks:
229
- if not sub_config.get(READY_FOR_SUBMISSION_TO_EVA, False):
230
- return True
231
- # If we are working with an existing submission check its status to see if it was submitted and failed before.
232
- submission_id = sub_config.get(SUB_CLI_CONFIG_KEY_SUBMISSION_ID, None)
233
- if submission_id:
234
- try:
235
- submission_status = SubmissionWSClient(username, password).get_submission_status(submission_id)
236
- if submission_status == 'FAILED':
237
- # Reset the submission_id which will force the creation of a new one
238
- sub_config.set(SUB_CLI_CONFIG_KEY_SUBMISSION_ID, value=None)
239
- sub_config.set(SUB_CLI_CONFIG_KEY_SUBMISSION_UPLOAD_URL, value=None)
240
- return True
241
- else:
242
- return False
243
- except requests.HTTPError as ex:
244
- if ex.response.status_code == 404:
245
- logger.error(
246
- f'Submission with id {submission_id} could not be found: '
247
- f'status code: {ex.response.status_code} response: {ex.response.text}')
248
- raise SubmissionNotFoundException(f'Submission with id {submission_id} could not be found')
249
- else:
250
- logger.error(f'Error occurred while getting status of the submission with Id {submission_id}: '
251
- f'status code: {ex.response.status_code} response: {ex.response.text}')
252
- raise SubmissionStatusException(f'Error occurred while getting status of the submission '
253
- f'with Id {submission_id}')
254
-
255
- logger.debug(f'submission id not found in config. This might be the first time user is submitting')
228
+ if SUBMIT not in tasks:
256
229
  return False
230
+ if not sub_config.get(READY_FOR_SUBMISSION_TO_EVA, False):
231
+ return True
232
+ # If we are working with an existing submission check its status to see if it was submitted and failed before.
233
+ submission_id = sub_config.get(SUB_CLI_CONFIG_KEY_SUBMISSION_ID, None)
234
+ if submission_id:
235
+ try:
236
+ submission_status = SubmissionWSClient(username, password).get_submission_status(submission_id)
237
+ if submission_status == 'FAILED':
238
+ # Reset the submission_id which will force the creation of a new one
239
+ sub_config.set(SUB_CLI_CONFIG_KEY_SUBMISSION_ID, value=None)
240
+ sub_config.set(SUB_CLI_CONFIG_KEY_SUBMISSION_UPLOAD_URL, value=None)
241
+ return True
242
+ else:
243
+ return False
244
+ except requests.HTTPError as ex:
245
+ if ex.response.status_code == 404:
246
+ logger.error(
247
+ f'Submission with id {submission_id} could not be found: '
248
+ f'status code: {ex.response.status_code} response: {ex.response.text}')
249
+ raise SubmissionNotFoundException(f'Submission with id {submission_id} could not be found')
250
+ else:
251
+ logger.error(f'Error occurred while getting status of the submission with Id {submission_id}: '
252
+ f'status code: {ex.response.status_code} response: {ex.response.text}')
253
+ raise SubmissionStatusException(f'Error occurred while getting status of the submission '
254
+ f'with Id {submission_id}')
255
+
256
+ logger.debug(f'submission id not found in config. This might be the first time user is submitting')
257
+ return False
257
258
 
258
259
 
259
260
  def orchestrate_process(submission_dir, vcf_files, reference_fasta, metadata_json, metadata_xlsx,
@@ -272,7 +273,7 @@ def orchestrate_process(submission_dir, vcf_files, reference_fasta, metadata_jso
272
273
  if metadata_xlsx:
273
274
  metadata_xlsx = os.path.abspath(metadata_xlsx)
274
275
  # check metadata xlsx version is not lower than the required min metadata template version
275
- verify_metadata_xlsx_version(metadata_xlsx, DEFAULT_METADATA_XLSX_TEMPLATE_VERSION)
276
+ verify_metadata_xlsx_version(metadata_xlsx, MINIMUM_METADATA_XLSX_TEMPLATE_VERSION)
276
277
 
277
278
  # Get the provided Project Title and VCF files mapping (VCF, Fasta and Report)
278
279
  project_title, vcf_files_mapping = get_project_title_and_create_vcf_files_mapping(
@@ -12,7 +12,7 @@ from eva_sub_cli.validators.validator import Validator
12
12
  logger = logging_config.get_logger(__name__)
13
13
 
14
14
  default_container_image = 'ebivariation/eva-sub-cli'
15
- default_container_tag = 'v0.0.3'
15
+ default_container_tag = 'v0.0.4.dev0'
16
16
  container_validation_dir = '/opt/vcf_validation'
17
17
  container_validation_output_dir = 'vcf_validation_output'
18
18
 
@@ -22,6 +22,10 @@ class NativeValidator(Validator):
22
22
  self.assembly_checker_path = assembly_checker_path
23
23
  self.biovalidator_path = biovalidator_path
24
24
 
25
+ @staticmethod
26
+ def _validation_file_path_for(file_path):
27
+ return file_path
28
+
25
29
  def _validate(self):
26
30
  self.run_validator()
27
31
 
@@ -12,6 +12,7 @@ from ebi_eva_common_pyutils.config import WritableConfig
12
12
 
13
13
  from eva_sub_cli import ETC_DIR, SUB_CLI_CONFIG_FILE, __version__
14
14
  from eva_sub_cli.file_utils import backup_file_or_directory, resolve_single_file_path
15
+ from eva_sub_cli.metadata import EvaMetadataJson
15
16
  from eva_sub_cli.report import generate_html_report
16
17
  from ebi_eva_common_pyutils.logger import logging_config, AppLogger
17
18
 
@@ -119,7 +120,7 @@ class Validator(AppLogger):
119
120
 
120
121
  @staticmethod
121
122
  def _validation_file_path_for(file_path):
122
- return file_path
123
+ raise NotImplementedError
123
124
 
124
125
  def verify_files_present(self):
125
126
  # verify mapping file exists
@@ -431,29 +432,25 @@ class Validator(AppLogger):
431
432
  f"Cannot locate file_info.txt at {os.path.join(self.output_dir, 'other_validations', 'file_info.txt')}"
432
433
  )
433
434
  if self.metadata_json_post_validation:
434
- with open(self.metadata_json_post_validation) as open_file:
435
- try:
436
- json_data = json.load(open_file)
437
- file_rows = []
438
- files_from_metadata = json_data.get('files', [])
439
- if files_from_metadata:
440
- for file_dict in json_data.get('files', []):
441
- file_path = self._validation_file_path_for(file_dict.get('fileName'))
442
- file_dict['md5'] = file_path_2_md5.get(file_path) or \
443
- file_name_2_md5.get(file_dict.get('fileName')) or ''
444
- file_dict['fileSize'] = file_path_2_file_size.get(file_path) or \
445
- file_name_2_file_size.get(file_dict.get('fileName')) or ''
446
- file_rows.append(file_dict)
447
- else:
448
- self.error('No file found in metadata and multiple analysis alias exist: '
449
- 'cannot infer the relationship between files and analysis alias')
450
- json_data['files'] = file_rows
451
- except Exception as e:
452
- # Skip adding the md5
453
- self.error('Error while loading or parsing metadata json: ' + str(e))
454
- if json_data:
455
- with open(self.metadata_json_post_validation, 'w') as open_file:
456
- json.dump(json_data, open_file)
435
+ metadata = EvaMetadataJson(self.metadata_json_post_validation)
436
+ try:
437
+ file_rows = []
438
+ if metadata.files:
439
+ for file_dict in metadata.files:
440
+ file_path = self._validation_file_path_for(file_dict.get('fileName'))
441
+ file_dict['md5'] = file_path_2_md5.get(file_path) or \
442
+ file_name_2_md5.get(file_dict.get('fileName')) or ''
443
+ file_dict['fileSize'] = file_path_2_file_size.get(file_path) or \
444
+ file_name_2_file_size.get(file_dict.get('fileName')) or ''
445
+ file_rows.append(file_dict)
446
+ else:
447
+ self.error('No file found in metadata and multiple analysis alias exist: '
448
+ 'cannot infer the relationship between files and analysis alias')
449
+ metadata.set_files(file_rows)
450
+ except Exception as e:
451
+ # Skip adding the md5
452
+ self.error('Error while loading or parsing metadata json: ' + str(e))
453
+ metadata.write(self.metadata_json_post_validation)
457
454
  else:
458
455
  self.error(f'Cannot locate the metadata in JSON format in {os.path.join(self.output_dir, "metadata.json")}')
459
456
 
@@ -479,27 +476,26 @@ class Validator(AppLogger):
479
476
  vcf_fasta_analysis_mapping.append({'vcf_file': row['vcf'], 'fasta_file': row['fasta']})
480
477
 
481
478
  if self.metadata_json_post_validation:
482
- with open(self.metadata_json_post_validation) as open_file:
483
- try:
484
- vcf_analysis_dict = {}
485
- json_data = json.load(open_file)
486
- if json_data.get('files', []):
487
- for file in json_data.get('files', []):
488
- if file.get('fileName', []) and file.get('analysisAlias', []):
489
- vcf_analysis_dict[file.get('fileName')] = file.get('analysisAlias')
490
-
491
- for vcf_fasta_mapping in vcf_fasta_analysis_mapping:
492
- vcf_file = vcf_fasta_mapping.get('vcf_file')
493
- if vcf_file in vcf_analysis_dict:
494
- vcf_fasta_mapping.update({'analysis': vcf_analysis_dict.get(vcf_file)})
495
- else:
496
- vcf_fasta_mapping.update({'analysis': 'Could not be linked'})
497
-
498
- return vcf_fasta_analysis_mapping
499
- except Exception as e:
500
- self.error('Error building Validation Report : Error getting info from metadata file' + str(e))
479
+ metadata = EvaMetadataJson(self.metadata_json_post_validation)
480
+ try:
481
+ vcf_analysis_dict = {}
482
+ for file in metadata.resolved_files:
483
+ if file.get('fileName', []) and file.get('analysisAlias', []):
484
+ vcf_analysis_dict[file.get('fileName')] = file.get('analysisAlias')
485
+
486
+ for vcf_fasta_mapping in vcf_fasta_analysis_mapping:
487
+ vcf_file = vcf_fasta_mapping.get('vcf_file')
488
+ if vcf_file in vcf_analysis_dict:
489
+ vcf_fasta_mapping.update({'analysis': vcf_analysis_dict.get(vcf_file)})
490
+ else:
491
+ vcf_fasta_mapping.update({'analysis': 'Could not be linked'})
492
+
493
+ return vcf_fasta_analysis_mapping
494
+ except Exception as e:
495
+ self.error('Error building Validation Report : Error getting info from metadata file' + str(e))
501
496
  else:
502
497
  self.error('Error building validation report : Metadata file not present')
498
+ return []
503
499
 
504
500
  def create_reports(self):
505
501
  report_html = generate_html_report(self.results, self.validation_date, self.submission_dir,