eva-sub-cli 0.4.4__py3-none-any.whl

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 (46) hide show
  1. eva_sub_cli/__init__.py +27 -0
  2. eva_sub_cli/_version.py +21 -0
  3. eva_sub_cli/auth.py +106 -0
  4. eva_sub_cli/date_utils.py +32 -0
  5. eva_sub_cli/etc/EVA_Submission_template.xlsx +0 -0
  6. eva_sub_cli/etc/eva-biosamples.json +174 -0
  7. eva_sub_cli/etc/eva_logo.png +0 -0
  8. eva_sub_cli/etc/eva_schema.json +369 -0
  9. eva_sub_cli/etc/spreadsheet2json_conf.yaml +135 -0
  10. eva_sub_cli/exceptions/__init__.py +0 -0
  11. eva_sub_cli/exceptions/invalid_file_type_exception.py +4 -0
  12. eva_sub_cli/exceptions/submission_not_found_exception.py +4 -0
  13. eva_sub_cli/exceptions/submission_status_exception.py +5 -0
  14. eva_sub_cli/executables/__init__.py +0 -0
  15. eva_sub_cli/executables/check_fasta_insdc.py +159 -0
  16. eva_sub_cli/executables/check_metadata_semantics.py +18 -0
  17. eva_sub_cli/executables/cli.py +115 -0
  18. eva_sub_cli/executables/samples_checker.py +144 -0
  19. eva_sub_cli/executables/trim_down.py +78 -0
  20. eva_sub_cli/executables/xlsx2json.py +335 -0
  21. eva_sub_cli/file_utils.py +117 -0
  22. eva_sub_cli/jinja_templates/fasta_check.html +86 -0
  23. eva_sub_cli/jinja_templates/file_validation.html +93 -0
  24. eva_sub_cli/jinja_templates/html_report.html +142 -0
  25. eva_sub_cli/jinja_templates/metadata_validation.html +52 -0
  26. eva_sub_cli/jinja_templates/project_details.html +37 -0
  27. eva_sub_cli/jinja_templates/sample_name_check.html +60 -0
  28. eva_sub_cli/jinja_templates/shallow_validation.html +29 -0
  29. eva_sub_cli/metadata_utils.py +46 -0
  30. eva_sub_cli/nextflow/validation.nf +303 -0
  31. eva_sub_cli/orchestrator.py +233 -0
  32. eva_sub_cli/report.py +40 -0
  33. eva_sub_cli/semantic_metadata.py +243 -0
  34. eva_sub_cli/submission_ws.py +61 -0
  35. eva_sub_cli/submit.py +111 -0
  36. eva_sub_cli/validators/__init__.py +0 -0
  37. eva_sub_cli/validators/docker_validator.py +205 -0
  38. eva_sub_cli/validators/native_validator.py +68 -0
  39. eva_sub_cli/validators/validation_results_parsers.py +210 -0
  40. eva_sub_cli/validators/validator.py +510 -0
  41. eva_sub_cli-0.4.4.dist-info/METADATA +164 -0
  42. eva_sub_cli-0.4.4.dist-info/RECORD +46 -0
  43. eva_sub_cli-0.4.4.dist-info/WHEEL +5 -0
  44. eva_sub_cli-0.4.4.dist-info/entry_points.txt +7 -0
  45. eva_sub_cli-0.4.4.dist-info/licenses/LICENSE +201 -0
  46. eva_sub_cli-0.4.4.dist-info/top_level.txt +1 -0
@@ -0,0 +1,142 @@
1
+ <!DOCTYPE html>
2
+ {% from 'project_details.html' import project_details_report %}
3
+ {% from 'file_validation.html' import file_validation_report %}
4
+ {% from 'sample_name_check.html' import sample_name_check_report %}
5
+ {% from 'fasta_check.html' import fasta_check_report %}
6
+ {% from 'metadata_validation.html' import metadata_validation_report %}
7
+ {% from 'shallow_validation.html' import optional_shallow_validation_report %}
8
+
9
+ <html lang="EN">
10
+ <head>
11
+ <meta charset="UTF-8">
12
+ <title>Validation Report</title>
13
+ <style>
14
+ body { font-family: Verdana, sans-serif; }
15
+
16
+ .logo { float: left; padding: 20px; }
17
+ .title { padding: 8px; }
18
+ header { overflow: auto; }
19
+ header::after { clear: both; }
20
+
21
+ section { padding: 20px 20px 10px 40px; }
22
+ .description { padding-bottom: 10px}
23
+ .error-description { padding: 10px 0px 10px}
24
+
25
+ .report-section { padding: 12px; border: 1px solid white; margin-bottom: 5px}
26
+ .active, .collapsible:hover.fail { background-color: #e6a3ae; }
27
+ .active, .collapsible:hover.info { background-color: #c0c0c0; }
28
+
29
+ table { border-collapse: collapse; padding: 10px; margin-bottom: 20px}
30
+ th, td { border: 1px solid black; padding: 8px; text-align: left; }
31
+ th { background-color: lightgrey; }
32
+ .fail { background-color: #FFB6C1; }
33
+ .pass { background-color: #90EE90; }
34
+ .info { background-color: #dadada; }
35
+ .error-list, .no-show { display: none; }
36
+ </style>
37
+ </head>
38
+ <body>
39
+
40
+ <header>
41
+ <div class="logo">
42
+ <img src="data:image/png;base64,{{ logo_data }}" width="100" height="100" alt="EVA logo">
43
+ </div>
44
+ <div class="title">
45
+ <h1>Validation Report</h1>
46
+ <h6>eva-sub-cli v{{cli_version}}</h6>
47
+ </div>
48
+ </header>
49
+
50
+ {{ optional_shallow_validation_report(validation_results) }}
51
+
52
+ <section>
53
+ <h2>Project Summary</h2>
54
+ <div class="description">
55
+ General details about the project
56
+ </div>
57
+ {{ project_details_report(project_title, validation_date, submission_dir, vcf_fasta_analysis_mapping) }}
58
+ </section>
59
+
60
+ <section>
61
+ <h2>Metadata validation results</h2>
62
+ <div class="description">
63
+ Ensures that required fields are present and values are formatted correctly.
64
+ For requirements, please refer to the <a href="https://www.ebi.ac.uk/eva/?Submit-Data" target=”_blank”>EVA website</a>.
65
+ </div>
66
+ {{ metadata_validation_report(validation_results) }}
67
+ </section>
68
+
69
+ <section>
70
+ <h2>VCF validation results</h2>
71
+ <div class="description">
72
+ Checks whether each file is compliant with the <a href="http://samtools.github.io/hts-specs/VCFv4.3.pdf" target=”_blank”>VCF specification</a>.
73
+ Also checks whether the variants' reference alleles match against the reference assembly.
74
+ </div>
75
+ {% for file_name in vcf_files %}
76
+ {% if file_name!= "pass"%}
77
+ <h3>{{ file_name }}</h3>
78
+ {{ file_validation_report(validation_results, file_name) }}
79
+ {% endif %}
80
+ {% endfor %}
81
+ </section>
82
+
83
+ <section>
84
+ <h2>Sample name concordance check</h2>
85
+ <div class="description">
86
+ Checks whether information in the metadata is concordant with that contained in the VCF files, in particular sample names.
87
+ </div>
88
+ {{ sample_name_check_report(validation_results)}}
89
+ </section>
90
+
91
+ <section>
92
+ <h2>Reference genome INSDC check</h2>
93
+ <div class="description">
94
+ Checks that the reference sequences in the FASTA file used to call the variants are accessioned in INSDC.
95
+ Also checks if the reference assembly accession in the metadata matches the one determined from the FASTA file.
96
+ </div>
97
+ {% for file_name in fasta_files %}
98
+ {% if file_name!= "pass"%}
99
+ <h3>{{ file_name }}</h3>
100
+ {{ fasta_check_report(validation_results, file_name) }}
101
+ {% endif %}
102
+ {% endfor %}
103
+ </section>
104
+
105
+ <script>
106
+ let collapsibles = document.querySelectorAll('.collapsible');
107
+ for (let collapsible of collapsibles) {
108
+ collapsible.addEventListener('click', function() {
109
+ this.classList.toggle('active');
110
+
111
+ let icon = this.querySelector('.expand_icon');
112
+ if (this.classList.contains('active')) {
113
+ icon.innerHTML = '&#9660;';
114
+ } else {
115
+ icon.innerHTML = '&#9654;';
116
+ }
117
+
118
+ let content = this.nextElementSibling;
119
+ if (content.style.display === 'block') {
120
+ content.style.display = 'none';
121
+ }
122
+ else {
123
+ content.style.display = 'block';
124
+ content.scrollIntoView({block: 'nearest'});
125
+ }
126
+ });
127
+ }
128
+
129
+ function toggleSampleAllErrorsList(id) {
130
+ var element = document.getElementById(id);
131
+ var link = document.querySelector(`a[onclick="toggleSampleAllErrorsList('${id}'); return false;"]`);
132
+ if (element.style.display === "none" || element.style.display === "") {
133
+ element.style.display = "block";
134
+ link.innerHTML = "Hide All Errors for Category";
135
+ } else {
136
+ element.style.display = "none";
137
+ link.innerHTML = "Show All Errors for Category";
138
+ }
139
+ }
140
+ </script>
141
+ </body>
142
+ </html>
@@ -0,0 +1,52 @@
1
+
2
+ {% macro metadata_validation_report(validation_results) -%}
3
+ {% set results = validation_results.get('metadata_check', {}) %}
4
+ {% set spreadsheet_errors = results.get('spreadsheet_errors', []) %}
5
+ {% set json_errors = results.get('json_errors', []) %}
6
+
7
+ {% set has_errors = spreadsheet_errors or json_errors %}
8
+ {% if has_errors %}
9
+ {% set expand_icon = "&#9654;" %}
10
+ {% set icon = "&#10060;" %}
11
+ {% set row_class = "report-section fail collapsible" %}
12
+ {% else %}
13
+ {% set icon = "&#10004;" %}
14
+ {% set row_class = "report-section pass" %}
15
+ {% endif %}
16
+ <div class='{{ row_class }}'><span class="expand_icon">{{ expand_icon }}</span> {{ icon }} Metadata validation check </div>
17
+ {% if spreadsheet_errors %}
18
+ <div class="error-list">
19
+ <div class="error-description"><strong>Full report:</strong> {{ results.get('spreadsheet_report_path', '') }}</div>
20
+ <table>
21
+ <tr>
22
+ <th>Sheet</th><th>Row</th><th>Column</th><th>Description</th>
23
+ </tr>
24
+ {% for error in spreadsheet_errors %}
25
+ <tr>
26
+ <td><strong>{{ error.get('sheet') }}</strong></td>
27
+ <td><strong>{{ error.get('row') }}</strong></td>
28
+ <td><strong>{{ error.get('column') }}</strong></td>
29
+ <td> {{ error.get('description') }}</td>
30
+ </tr>
31
+ {% endfor %}
32
+ </table>
33
+ </div>
34
+ {% endif %}
35
+
36
+ {% if json_errors %}
37
+ <div class="error-list">
38
+ <div class="error-description"><strong>Full report:</strong> {{ results.get('json_report_path', '') }}</div>
39
+ <table>
40
+ <tr>
41
+ <th>JSON Property</th><th>Error Description</th>
42
+ </tr>
43
+ {% for error in json_errors %}
44
+ <tr>
45
+ <td><strong>{{ error.get('property') }}</strong></td>
46
+ <td> {{ error.get('description') }}</td>
47
+ </tr>
48
+ {% endfor %}
49
+ </table>
50
+ </div>
51
+ {% endif %}
52
+ {%- endmacro %}
@@ -0,0 +1,37 @@
1
+ {% macro project_details_report(project_title, validation_date, submission_dir, vcf_fasta_analysis_mapping) -%}
2
+ <div class="report-section info">
3
+ {% if project_title %}
4
+ <p><b>Project Title:</b> {{ project_title }}</p>
5
+ {% else %}
6
+ <p><b>Project Title:</b> Not Found</p>
7
+ {% endif %}
8
+ <p><b>Validation Date:</b> {{ validation_date }}</p>
9
+ <p><b>Submission Directory:</b> {{ submission_dir }}</p>
10
+ </div>
11
+
12
+ {% if vcf_fasta_analysis_mapping %}
13
+ {% set row_class = "report-section info collapsible" %}
14
+ {% set expand_icon = "&#9654;" %}
15
+ <div class='{{ row_class }}'><span class="expand_icon">{{ expand_icon }}</span> Files mapping</div>
16
+ <div class="no-show">
17
+ <table>
18
+ <tr>
19
+ <th><strong>VCF File</strong></th>
20
+ <th><strong>Fasta File</strong></th>
21
+ <th><strong>Analysis</strong></th>
22
+ </tr>
23
+ {% for mapping in vcf_fasta_analysis_mapping %}
24
+ <tr>
25
+ <td>{{ mapping.vcf_file }}</td>
26
+ <td>{{ mapping.fasta_file }}</td>
27
+ <td>{{ mapping.analysis }}</td>
28
+ </tr>
29
+ {% endfor %}
30
+ </table>
31
+ </div>
32
+ </div>
33
+ {% endif %}
34
+
35
+
36
+
37
+ {%- endmacro %}
@@ -0,0 +1,60 @@
1
+
2
+ {% macro sample_name_check_report(validation_results) -%}
3
+ {% set results = validation_results.get('sample_check', {}) %}
4
+ {% macro format_error(error) %}
5
+ {{ error | replace(' ', '•') }}
6
+ {% endmacro %}
7
+
8
+ {% for analysis, results_for_analysis in results.get('results_per_analysis', {}).items() %}
9
+ {% if results_for_analysis.get('difference') %}
10
+ {% set expand_icon = "&#9654;" %}
11
+ {% set icon = "&#10060;" %}
12
+ {% set row_class = "report-section fail collapsible" %}
13
+ {% set text = "Sample names in metadata do not match with those in VCF files" %}
14
+ {% else %}
15
+ {% set icon = "&#10004;" %}
16
+ {% set row_class = "report-section pass" %}
17
+ {% set text = "Sample names in metadata match with those in VCF files" %}
18
+ {% endif %}
19
+ <div class='{{ row_class }}'><span class="expand_icon">{{ expand_icon }}</span> {{ icon }} <b>{{ analysis }}</b>: {{ text }} </div>
20
+ {% if results_for_analysis.get('difference') %}
21
+ <div class="error-list">
22
+ <table>
23
+ <tr>
24
+ <th>Category</th><th>First 5 Errors For Category</th><th>Link To View All Errors</th>
25
+ </tr>
26
+ <tr>
27
+ <td><strong>Samples described in the metadata but not in the VCF files</strong></td>
28
+ <td>{{ results_for_analysis.get('more_metadata_submitted_files')[:5]|join(", ") }}</td>
29
+ <td> <a href="#" onclick="toggleSampleAllErrorsList('metadata-{{ loop.index0 }}'); return false;">Show All Errors For Category</a></td>
30
+ </tr>
31
+ <tr>
32
+ <td><strong>Samples in the VCF files but not described in the metadata</strong></td>
33
+ <td>{{ results_for_analysis.get('more_submitted_files_metadata')[:5]|join(", ") }}</td>
34
+ <td> <a href="#" onclick="toggleSampleAllErrorsList('vcf-{{ loop.index0 }}'); return false;">Show All Errors For Category</a></td>
35
+ </tr>
36
+ </table>
37
+
38
+ <div id="metadata-{{ loop.index0 }}" class="description no-show">
39
+ <div class="error-description"><strong>All Errors For Category - Samples described in the metadata but not in the VCF files:</strong></div>
40
+ <ol>
41
+ {% for error in results_for_analysis.get('more_metadata_submitted_files') %}
42
+ <li>{{ format_error(error) }}</li>
43
+ {% endfor %}
44
+ </ol>
45
+ <a href="#" onclick="toggleSampleAllErrorsList('metadata-{{ loop.index0 }}'); return false;">Hide</a>
46
+ </div>
47
+
48
+ <div id="vcf-{{ loop.index0 }}" class="description no-show">
49
+ <div class="error-description"><strong>All Errors For Category - Samples in the VCF files but not described in the metadata:</strong></div>
50
+ <ol>
51
+ {% for error in results_for_analysis.get('more_submitted_files_metadata') %}
52
+ <li>{{ format_error(error) }}</li>
53
+ {% endfor %}
54
+ </ol>
55
+ <a href="#" onclick="toggleSampleAllErrorsList('vcf-{{ loop.index0 }}'); return false;">Hide</a>
56
+ </div>
57
+ </div>
58
+ {% endif %}
59
+ {% endfor %}
60
+ {%- endmacro %}
@@ -0,0 +1,29 @@
1
+
2
+ {% macro optional_shallow_validation_report(validation_results) -%}
3
+ {% set results = validation_results.get('shallow_validation', {}) %}
4
+
5
+ {% if results.get('required') %}
6
+ <section>
7
+ <div class="report-section fail collapsible"> <span class="expand_icon">&#9654;</span>
8
+ &#10060; <b>You requested to run the shallow validation, please run full validation before submitting the data</b>
9
+ </div>
10
+ <div class="no-show">
11
+ <table>
12
+ <tr>
13
+ <th><strong>VCF File</strong></th>
14
+ <th><strong>Variant lines validated in VCF</strong></th>
15
+ <th><strong>Entries used in Fasta</strong></th>
16
+ </tr>
17
+ {% for vcf_file in results.get('metrics') %}
18
+ <tr>
19
+ <td>{{ vcf_file }}</td>
20
+ <td>{{ results.get('metrics').get(vcf_file).get('trim_down_vcf_record') }}</td>
21
+ <td>{{ results.get('metrics').get(vcf_file).get('number_sequence_found') }}</td>
22
+ </tr>
23
+ {% endfor %}
24
+ </table>
25
+ </div>
26
+ </section>
27
+ {% endif %}
28
+
29
+ {%- endmacro %}
@@ -0,0 +1,46 @@
1
+ import os
2
+ from collections import defaultdict
3
+
4
+ from ebi_eva_common_pyutils.logger import logging_config
5
+
6
+ logger = logging_config.get_logger(__name__)
7
+
8
+
9
+ def get_samples_per_analysis(metadata):
10
+ """Returns mapping of analysis alias to sample names, based on metadata."""
11
+ samples_per_analysis = defaultdict(list)
12
+ for sample_info in metadata.get('sample', []):
13
+ for analysis_alias in sample_info.get('analysisAlias', []):
14
+ samples_per_analysis[analysis_alias].append(sample_info.get('sampleInVCF'))
15
+ return {
16
+ analysis_alias: set(samples)
17
+ for analysis_alias, samples in samples_per_analysis.items()
18
+ }
19
+
20
+
21
+ def get_files_per_analysis(metadata):
22
+ """Returns mapping of analysis alias to filenames, based on metadata."""
23
+ files_per_analysis = defaultdict(list)
24
+ for file_info in metadata.get('files', []):
25
+ files_per_analysis[file_info.get('analysisAlias')].append(file_info.get('fileName'))
26
+ return {
27
+ analysis_alias: set(filepaths)
28
+ for analysis_alias, filepaths in files_per_analysis.items()
29
+ }
30
+
31
+
32
+ def get_reference_assembly_for_analysis(metadata, analysis_alias):
33
+ """Returns the reference assembly for this analysis (does not validate format)."""
34
+ for analysis in metadata.get('analysis', []):
35
+ if analysis.get('analysisAlias') == analysis_alias:
36
+ return analysis.get('referenceGenome')
37
+ return None
38
+
39
+
40
+ def get_analysis_for_vcf_file(vcf_file, files_per_analysis):
41
+ """Returns list of analysis aliases associated with the vcf file path."""
42
+ if not os.path.exists(vcf_file):
43
+ raise FileNotFoundError(f'{vcf_file} cannot be resolved')
44
+ analysis_aliases = [analysis_alias for analysis_alias in files_per_analysis
45
+ if os.path.basename(vcf_file) in files_per_analysis[analysis_alias]]
46
+ return analysis_aliases
@@ -0,0 +1,303 @@
1
+ #!/usr/bin/env nextflow
2
+
3
+ nextflow.enable.dsl=2
4
+
5
+ def helpMessage() {
6
+ log.info"""
7
+ Validate a set of VCF files and metadata to check if they are valid to be submitted to EVA.
8
+
9
+ Inputs:
10
+ --vcf_files_mapping csv file with the mappings for vcf files, fasta and assembly report
11
+ --output_dir output_directory where the reports will be output
12
+ --metadata_json Json file describing the project, analysis, samples and files
13
+ --metadata_xlsx Excel file describing the project, analysis, samples and files
14
+ """
15
+ }
16
+
17
+ params.vcf_files_mapping = null
18
+ params.output_dir = null
19
+ params.metadata_json = null
20
+ params.metadata_xlsx = null
21
+
22
+ // executables - external tools
23
+ params.executable = [
24
+ "vcf_validator": "vcf_validator",
25
+ "vcf_assembly_checker": "vcf_assembly_checker",
26
+ "biovalidator": "biovalidator"
27
+ ]
28
+ // python scripts - installed as part of eva-sub-cli
29
+ params.python_scripts = [
30
+ "samples_checker": "samples_checker.py",
31
+ "fasta_checker": "check_fasta_insdc.py",
32
+ "xlsx2json": "xlsx2json.py",
33
+ "semantic_checker": "check_metadata_semantics.py",
34
+ "trim_down": "trim_down.py"
35
+ ]
36
+ // prefix to prepend to all provided path
37
+ params.base_dir = ""
38
+ // help
39
+ params.help = null
40
+ params.shallow_validation = false
41
+
42
+ // Show help message
43
+ if (params.help) exit 0, helpMessage()
44
+
45
+ // Test input files
46
+ if (!params.vcf_files_mapping || !params.output_dir || (!params.metadata_json && !params.metadata_xlsx)) {
47
+ if (!params.vcf_files_mapping) log.warn('Provide a csv file with the mappings (vcf, fasta, assembly report) --vcf_files_mapping')
48
+ if (!params.metadata_xlsx) log.warn('Provide a json file with the metadata description of the project and analysis --metadata_json')
49
+ if (!params.output_dir) log.warn('Provide an output directory where the reports will be copied using --output_dir')
50
+ if (!params.metadata_json && !params.metadata_xlsx) log.warn('Provide a json or Excel file with the metadata description of the project and analysis --metadata_json or --metadata_xlsx')
51
+ exit 1, helpMessage()
52
+ }
53
+
54
+ schema_dir = file(projectDir).parent + '/etc'
55
+ conversion_configuration = schema_dir + '/spreadsheet2json_conf.yaml'
56
+
57
+ def joinBasePath(path) {
58
+ if (path){
59
+ return params.base_dir + '/' + path
60
+ }
61
+ return 'NO_FILE'
62
+ }
63
+
64
+ output_dir = joinBasePath(params.output_dir)
65
+
66
+ workflow {
67
+ // Prepare the file path
68
+ vcf_and_ref_ch = Channel.fromPath(joinBasePath(params.vcf_files_mapping))
69
+ .splitCsv(header:true)
70
+ .map{row -> tuple(
71
+ file(joinBasePath(row.vcf)),
72
+ file(joinBasePath(row.fasta)),
73
+ file(joinBasePath(row.report))
74
+ )}
75
+ if (params.shallow_validation){
76
+ // create a smaller vcf and fasta then replace the channel
77
+ trim_down_vcf(vcf_and_ref_ch)
78
+ vcf_and_ref_ch = trim_down_vcf.out.vcf_and_ref
79
+ }
80
+ vcf_files = vcf_and_ref_ch.map{row -> row[0]}
81
+ fasta_to_vcfs = vcf_and_ref_ch.map{row -> tuple(row[1], row[0])}.groupTuple(by:0)
82
+ // VCF checks
83
+ check_vcf_valid(vcf_and_ref_ch)
84
+ check_vcf_reference(vcf_and_ref_ch)
85
+
86
+ generate_file_size_and_md5_digests(vcf_files)
87
+ collect_file_size_and_md5(generate_file_size_and_md5_digests.out.file_size_and_digest_info.collect())
88
+
89
+
90
+ // Metadata conversion
91
+ if (params.metadata_xlsx && !params.metadata_json){
92
+ convert_xlsx_2_json(joinBasePath(params.metadata_xlsx))
93
+ metadata_json = convert_xlsx_2_json.out.metadata_json
94
+ } else {
95
+ metadata_json = joinBasePath(params.metadata_json)
96
+ }
97
+ if (metadata_json) {
98
+ // Metadata checks and concordance checks
99
+ metadata_json_validation(metadata_json)
100
+ metadata_semantic_check(metadata_json)
101
+ sample_name_concordance(metadata_json, vcf_files.collect())
102
+ insdc_checker(metadata_json, fasta_to_vcfs)
103
+ }
104
+ }
105
+
106
+
107
+ process trim_down_vcf {
108
+ publishDir output_dir, overwrite: false, mode: "copy", pattern: "*.log"
109
+ publishDir output_dir, overwrite: false, mode: "copy", pattern: "*.yml"
110
+
111
+ input:
112
+ tuple path(vcf), path(fasta), path(report)
113
+
114
+ output:
115
+ tuple path("output/$vcf"), path("output/$fasta"), path(report), emit: vcf_and_ref
116
+ path "${vcf.getBaseName()}_trim_down.log", emit: trim_down_log
117
+ path "${vcf.getBaseName()}_trim_down.yml", emit: trim_down_metric
118
+
119
+ """
120
+ mkdir output
121
+ $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
122
+ # This is needed to ensure that a missing (NO_FILE) report can still be passed down to subsequent steps
123
+ touch $report
124
+ """
125
+
126
+ }
127
+
128
+ /*
129
+ * Validate the VCF file format
130
+ */
131
+ process check_vcf_valid {
132
+ publishDir output_dir,
133
+ overwrite: false,
134
+ mode: "copy"
135
+
136
+ input:
137
+ tuple path(vcf), path(fasta), path(report)
138
+
139
+ output:
140
+ path "vcf_format/*.errors.*.txt", emit: vcf_validation_txt
141
+ path "vcf_format/*.vcf_format.log", emit: vcf_validation_log
142
+
143
+ """
144
+ trap 'if [[ \$? == 1 ]]; then exit 0; fi' EXIT
145
+
146
+ mkdir -p vcf_format
147
+ $params.executable.vcf_validator -i $vcf -r text -o vcf_format --require-evidence > vcf_format/${vcf}.vcf_format.log 2>&1
148
+ """
149
+ }
150
+
151
+ /*
152
+ * Validate the VCF reference allele
153
+ */
154
+ process check_vcf_reference {
155
+ publishDir output_dir,
156
+ overwrite: true,
157
+ mode: "copy"
158
+
159
+ input:
160
+ tuple path(vcf), path(fasta), path(report)
161
+
162
+ output:
163
+ path "assembly_check/*text_assembly_report*", emit: assembly_check_report
164
+ path "assembly_check/*.assembly_check.log", emit: assembly_check_log
165
+
166
+ script:
167
+ def report_opt = report.name != 'NO_FILE' ? "-a $report" : ''
168
+
169
+ """
170
+ trap 'if [[ \$? == 1 || \$? == 139 ]]; then exit 0; fi' EXIT
171
+
172
+ mkdir -p assembly_check
173
+ $params.executable.vcf_assembly_checker -i $vcf -f $fasta $report_opt -r summary,text -o assembly_check --require-genbank > assembly_check/${vcf}.assembly_check.log 2>&1
174
+ """
175
+ }
176
+
177
+ process generate_file_size_and_md5_digests {
178
+ input:
179
+ path(vcf_file)
180
+
181
+ output:
182
+ path "${vcf_file}_file_size_and_digest_info.txt", emit: file_size_and_digest_info
183
+
184
+ script:
185
+ // Capture the realpath of the vcf to be able to resolve the file based on path instead of name
186
+ """
187
+ echo "\$(md5sum $vcf_file | awk '{print \$1}') \$(stat -c%s \$(readlink -f $vcf_file)) \$(readlink -f $vcf_file)" > ${vcf_file}_file_size_and_digest_info.txt
188
+ """
189
+ }
190
+
191
+ process collect_file_size_and_md5 {
192
+ publishDir output_dir,
193
+ overwrite: true,
194
+ mode: "copy"
195
+
196
+ input:
197
+ path(file_size_and_digests)
198
+
199
+ output:
200
+ path "file_info.txt", emit: file_info_log
201
+
202
+ script:
203
+ """
204
+ cat $file_size_and_digests > file_info.txt
205
+ """
206
+ }
207
+
208
+
209
+ process convert_xlsx_2_json {
210
+ publishDir output_dir,
211
+ overwrite: true,
212
+ mode: "copy"
213
+
214
+ input:
215
+ path(metadata_xlsx)
216
+
217
+ output:
218
+ path "metadata.json", emit: metadata_json, optional: true
219
+ path "metadata_conversion_errors.yml", emit: errors_yaml
220
+ path "xlsx2json.log", emit: xlsx2json_log
221
+
222
+ script:
223
+ metadata_json = metadata_xlsx.getBaseName() + '.json'
224
+
225
+ """
226
+ $params.python_scripts.xlsx2json --metadata_xlsx $metadata_xlsx --metadata_json metadata.json --errors_yaml metadata_conversion_errors.yml --conversion_configuration $conversion_configuration > xlsx2json.log 2>&1
227
+ """
228
+ }
229
+
230
+ process metadata_json_validation {
231
+ publishDir output_dir,
232
+ overwrite: true,
233
+ mode: "copy"
234
+
235
+ input:
236
+ path(metadata_json)
237
+
238
+ output:
239
+ path "metadata_validation.txt", emit: metadata_validation
240
+
241
+ script:
242
+ """
243
+ $params.executable.biovalidator --schema $schema_dir/eva_schema.json --ref $schema_dir/eva-biosamples.json --data $metadata_json > metadata_validation.txt 2>&1
244
+ """
245
+ }
246
+
247
+ process sample_name_concordance {
248
+ publishDir output_dir,
249
+ overwrite: true,
250
+ mode: "copy"
251
+
252
+ input:
253
+ path(metadata_json)
254
+ path(vcf_files)
255
+
256
+ output:
257
+ path "sample_checker.yml", emit: sample_checker_yml
258
+ path "sample_checker.log", emit: sample_checker_log
259
+
260
+ script:
261
+ """
262
+ $params.python_scripts.samples_checker --metadata_json $metadata_json --vcf_files $vcf_files --output_yaml sample_checker.yml > sample_checker.log 2>&1
263
+ """
264
+ }
265
+
266
+
267
+ process insdc_checker {
268
+ publishDir output_dir,
269
+ overwrite: true,
270
+ mode: "copy"
271
+
272
+ input:
273
+ path(metadata_json)
274
+ tuple(path(fasta_file), path(vcf_files))
275
+
276
+ output:
277
+ path "${fasta_file}_check.yml", emit: fasta_checker_yml
278
+ path "fasta_checker.log", emit: fasta_checker_log
279
+
280
+ script:
281
+ """
282
+ $params.python_scripts.fasta_checker --metadata_json $metadata_json --vcf_files $vcf_files --input_fasta $fasta_file --output_yaml ${fasta_file}_check.yml > fasta_checker.log 2>&1
283
+ """
284
+ }
285
+
286
+
287
+ process metadata_semantic_check {
288
+ publishDir output_dir,
289
+ overwrite: true,
290
+ mode: "copy"
291
+
292
+ input:
293
+ path(metadata_json)
294
+
295
+ output:
296
+ path "metadata_semantic_check.yml", emit: metadata_semantic_check_yml
297
+ path "semantic_checker.log", emit: semantic_checker_log
298
+
299
+ script:
300
+ """
301
+ $params.python_scripts.semantic_checker --metadata_json $metadata_json --output_yaml metadata_semantic_check.yml > semantic_checker.log 2>&1
302
+ """
303
+ }