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,369 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "version": "1.0.0",
4
+ "$id": "EVA_metadata_schema",
5
+ "author": "EVA",
6
+ "type": "object",
7
+ "title": "EVA metadata schema",
8
+ "description": "",
9
+ "required": [
10
+ "submitterDetails",
11
+ "project",
12
+ "sample",
13
+ "analysis",
14
+ "files"
15
+ ],
16
+ "properties": {
17
+ "submitterDetails": {
18
+ "type": "array",
19
+ "description": "Contact details of the submitter",
20
+ "items": {
21
+ "type": "object",
22
+ "additionalProperties": false,
23
+ "required": [
24
+ "lastName",
25
+ "firstName",
26
+ "email",
27
+ "laboratory",
28
+ "centre"
29
+ ],
30
+ "properties": {
31
+ "lastName": {
32
+ "type": "string",
33
+ "description": "Last name of the contact"
34
+ },
35
+ "firstName": {
36
+ "type": "string",
37
+ "description": "First name of the contact"
38
+ },
39
+ "telephone": {
40
+ "type": "string",
41
+ "description": "Contact's phone number"
42
+ },
43
+ "email": {
44
+ "type": "string",
45
+ "format": "email",
46
+ "description": "Contact's Email",
47
+ "examples": [
48
+ "user@domain.com"
49
+ ]
50
+ },
51
+ "laboratory": {
52
+ "description": "Name of the laboratory or department of the contact",
53
+ "type": "string"
54
+ },
55
+ "centre": {
56
+ "description": "Name of the Centre, company or institute the contact belongs to",
57
+ "type": "string"
58
+ },
59
+ "address": {
60
+ "description": "Address of the Centre, company or institute the contact belongs to",
61
+ "type": "string"
62
+ }
63
+ }
64
+ }
65
+ },
66
+ "project": {
67
+ "type": "object",
68
+ "description": "General information about the Project",
69
+ "required": [
70
+ "title",
71
+ "description",
72
+ "taxId",
73
+ "centre"
74
+ ],
75
+ "properties": {
76
+ "title": {
77
+ "type": "string",
78
+ "description": "Title of the project"
79
+ },
80
+ "description": {
81
+ "type": "string",
82
+ "description": "Description of the project"
83
+ },
84
+ "centre": {
85
+ "type": "string",
86
+ "description": "Centre, institute or company submitting the project"
87
+ },
88
+ "taxId": {
89
+ "type": "number",
90
+ "description": "Taxonomy ID of the species associated with the project (found here: http://www.ncbi.nlm.nih.gov/taxonomy)"
91
+ },
92
+ "publications": {
93
+ "type": "array",
94
+ "items": true
95
+ },
96
+ "parentProject": {
97
+ "$ref": "#/definitions/projectAccession"
98
+ },
99
+ "childProjects": {
100
+ "type": "array",
101
+ "items": {
102
+ "$ref": "#/definitions/projectAccession"
103
+ }
104
+ },
105
+ "peerProjects": {
106
+ "type": "array",
107
+ "items": {
108
+ "$ref": "#/definitions/projectAccession"
109
+ }
110
+ },
111
+ "links": {
112
+ "type": "array",
113
+ "items": {
114
+ "type": "string"
115
+ }
116
+ },
117
+ "holdDate": {
118
+ "type": "string",
119
+ "format": "date",
120
+ "examples": [
121
+ "2018-11-13"
122
+ ]
123
+ }
124
+ }
125
+ },
126
+ "analysis": {
127
+ "type": "array",
128
+ "minItems": 1,
129
+ "items": {
130
+ "type": "object",
131
+ "required": [
132
+ "analysisTitle",
133
+ "analysisAlias",
134
+ "description",
135
+ "experimentType",
136
+ "referenceGenome"
137
+ ],
138
+ "properties": {
139
+ "analysisTitle": {
140
+ "type": "string",
141
+ "description": "Title of the analysis"
142
+ },
143
+ "analysisAlias": {
144
+ "type": "string",
145
+ "description": "Shortened identifier for the analysis"
146
+ },
147
+ "description": {
148
+ "type": "string",
149
+ "description": "Description of the analysis"
150
+ },
151
+ "experimentType": {
152
+ "type": "string",
153
+ "description": "Type of experiment",
154
+ "enum": [
155
+ "Whole genome sequencing",
156
+ "Whole transcriptome sequencing",
157
+ "Exome sequencing",
158
+ "Genotyping by array",
159
+ "Curation",
160
+ "Genotyping by sequencing",
161
+ "Target sequencing",
162
+ "Transcriptomics"
163
+ ]
164
+ },
165
+ "referenceGenome": {
166
+ "type": "string",
167
+ "description": "Reference against which the analysis was performed. ENA or NCBI accession (starting with GCA) accepted."
168
+ },
169
+ "referenceFasta": {
170
+ "type": "string",
171
+ "description": "The path to the reference in fasta format that was used to create the VCF. (This is a local path)"
172
+ },
173
+ "assemblyReport": {
174
+ "type": "string",
175
+ "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
+ },
177
+ "platform": {
178
+ "description": "Platform used to generate the raw data.",
179
+ "type": "string",
180
+ "examples": [
181
+ "454 GS",
182
+ "454 GS 20",
183
+ "454 GS FLX",
184
+ "454 GS FLX Titanium",
185
+ "454 GS FLX+",
186
+ "454 GS Junior",
187
+ "AB 3130 Genetic Analyzer",
188
+ "AB 3300 Genetic Analyzer",
189
+ "AB 3730xl",
190
+ "AB 5500 Genetic Analyzer",
191
+ "AB 5500xl Genetic Analyzer",
192
+ "AB SOLiD 3 Plus System",
193
+ "AB SOLiD 4 System",
194
+ "AB SOLiD 4hq System",
195
+ "AB SOLiD PI System",
196
+ "AB SOLiD System",
197
+ "AB SOLiD System 2.0",
198
+ "AB SOLiD System 3.0",
199
+ "ABI 3500 Genetic Analyzer",
200
+ "ABI PRISM 310 Genetic Analyzer",
201
+ "Affymetrix",
202
+ "BGISEQ-500",
203
+ "Bio-Rad CFX96",
204
+ "Complete Genomics",
205
+ "Illumina",
206
+ "Illumina CanineHD",
207
+ "Illumina Genome Analyzer",
208
+ "Illumina Genome Analyzer II",
209
+ "Illumina Genome Analyzer IIx",
210
+ "Illumina HiScanSQ",
211
+ "Illumina HiSeq 1000",
212
+ "Illumina HiSeq 2000",
213
+ "Illumina HiSeq 2500",
214
+ "Illumina HiSeq 3500",
215
+ "Illumina HiSeq 4000",
216
+ "Illumina HiSeq X Ten",
217
+ "Illumina Hiseq Xten",
218
+ "Illumina iScan",
219
+ "Illumina MiniSeq",
220
+ "Illumina MiSeq",
221
+ "Illumina NextSeq 500",
222
+ "Illumina NovaSeq 6000",
223
+ "Ion Personal Genome Machine (PGM) System v2",
224
+ "Ion S5XL",
225
+ "Ion Torrent PGM",
226
+ "Ion Torrent Proton",
227
+ "MGISEQ-2000",
228
+ "Nimblegen 4.2M Probe Custom DNA Microarray",
229
+ "Oxford Nanopore PromethION",
230
+ "unspecified"
231
+ ]
232
+ },
233
+ "software": {
234
+ "description": "Software used to generate the analysis.",
235
+ "type": "array",
236
+ "items": {
237
+ "type": "string"
238
+ }
239
+ },
240
+ "pipelineDescriptions": {
241
+ "type": "string"
242
+ },
243
+ "imputation": {
244
+ "description": "Specify if imputation was used to generate the variants",
245
+ "type": "boolean"
246
+ },
247
+ "phasing": {
248
+ "description": "Specify if the variants have been phased",
249
+ "type": "boolean"
250
+ },
251
+ "centre": {
252
+ "description": "Centre, institute or company where the analysis was performed (if different from the project's centre).",
253
+ "type": "string"
254
+ },
255
+ "date": {
256
+ "description": "Date in which the analysis was performed",
257
+ "type": "string"
258
+ },
259
+ "links": {
260
+ "description": "Link(s) to external resources related to this analysis in the form DB:ID:LABEL.",
261
+ "type": "array",
262
+ "items": {
263
+ "type": "string"
264
+ }
265
+ },
266
+ "runAccessions": {
267
+ "type": "array",
268
+ "items": {
269
+ "type": "string",
270
+ "pattern": "^(E|D|S)RR[0-9]{6,}$"
271
+ },
272
+ "description": "List of run accessions linking to the raw data used in this analysis if applicable (e.g. SRR576651, SRR576652)"
273
+ }
274
+ }
275
+ }
276
+ },
277
+ "sample": {
278
+ "type": "array",
279
+ "minItems": 1,
280
+ "items": {
281
+ "type": "object",
282
+ "oneOf": [
283
+ {
284
+ "required": [
285
+ "analysisAlias",
286
+ "sampleInVCF",
287
+ "bioSampleAccession"
288
+ ]
289
+ },
290
+ {
291
+ "required": [
292
+ "analysisAlias",
293
+ "sampleInVCF",
294
+ "bioSampleObject"
295
+ ]
296
+ }
297
+ ],
298
+ "properties": {
299
+ "analysisAlias": {
300
+ "type": "array",
301
+ "description": "Aliases of the analyses performed on this sample",
302
+ "minItems": 1,
303
+ "items": {
304
+ "type": "string"
305
+ }
306
+ },
307
+ "sampleInVCF": {
308
+ "type": "string",
309
+ "description": "Sample Name used in the VCF file"
310
+ },
311
+ "sampleAccession": {
312
+ "type": "string",
313
+ "description": "Accession of the sample (BioSamples, ENA or EGA)"
314
+ },
315
+ "bioSampleObject": {
316
+ "type": "object",
317
+ "anyOf": [
318
+ {
319
+ "$ref": "eva-biosamples.json"
320
+ },
321
+ {
322
+ "$ref": "https://www.ebi.ac.uk/biosamples/schema/store/registry/schemas/ERC000011"
323
+ },
324
+ {
325
+ "$ref": "https://www.ebi.ac.uk/biosamples/schemas/certification/plant-miappe.json"
326
+ }
327
+ ]
328
+ }
329
+ }
330
+ }
331
+ },
332
+ "files": {
333
+ "type": "array",
334
+ "minItems": 1,
335
+ "items": {
336
+ "type": "object",
337
+ "required": [
338
+ "analysisAlias",
339
+ "fileName"
340
+ ],
341
+ "properties": {
342
+ "analysisAlias": {
343
+ "type": "string",
344
+ "description": "Alias of the analysis that produced the file"
345
+ },
346
+ "fileName": {
347
+ "type": "string",
348
+ "description": "File name"
349
+ },
350
+ "fileSize": {
351
+ "type": "number",
352
+ "description": "Size of file in bytes"
353
+ },
354
+ "md5": {
355
+ "type": "string",
356
+ "description": "MD5 hash of the file"
357
+ }
358
+ }
359
+ }
360
+ }
361
+ },
362
+ "definitions": {
363
+ "projectAccession": {
364
+ "type": "string",
365
+ "description": "ENA project accession",
366
+ "pattern": "^PRJ(EB|NA)\\d+$"
367
+ }
368
+ }
369
+ }
@@ -0,0 +1,135 @@
1
+ #Configuration for defining which fields in which worksheets to be parsed in an Excel file
2
+ #
3
+ #At the top level, "worksheets" defines a list of worksheets (titles) to be parsed.
4
+ #Then for each worksheet, you define which fields to be parsed from this worksheet.
5
+ #These fields are either "required", "optional" and/or header_row. "required" fields must be found
6
+ #in the worksheet so that this worksheet is valid. Otherwise this worksheet is not
7
+ #valid. The field names must match those values in the header row in the worksheet.
8
+ #
9
+ #This is a YAML file
10
+
11
+ # List of worksheets to be parsed.
12
+ worksheets:
13
+ Submitter Details: submitterDetails
14
+ Project: project
15
+ Analysis: analysis
16
+ Sample: sample
17
+ Files: files
18
+
19
+ Submitter Details:
20
+ required:
21
+ Last Name: lastName
22
+ First Name: firstName
23
+ Email Address: email
24
+ Laboratory: laboratory
25
+ Center: centre
26
+ optional:
27
+ Address: address
28
+
29
+ # List of field for Projects
30
+ Project:
31
+ required:
32
+ Project Title: title
33
+ Description: description
34
+ Center: centre
35
+ Tax ID: taxId
36
+ optional:
37
+ Publication(s): publications
38
+ Parent Project: parentProject
39
+ Child Project(s): childProjects
40
+ Peer Project(s): peerProjects
41
+ Link(s): links
42
+ Hold Date: holdDate
43
+ Collaborator(s): collaborators
44
+ Strain: strain
45
+ Breed: breed
46
+ Broker: broker
47
+ cast:
48
+ Publication(s): list
49
+ Child Project(s): list
50
+ Peer Project(s): list
51
+ Link(s): list
52
+ Hold Date: date
53
+
54
+ Analysis:
55
+ required:
56
+ Analysis Title: analysisTitle
57
+ Analysis Alias: analysisAlias
58
+ Description: description
59
+ Experiment Type: experimentType
60
+ Reference: referenceGenome
61
+ optional:
62
+ Reference Fasta Path: referenceFasta
63
+ Platform: platform
64
+ Software: software
65
+ Pipeline Description: pipelineDescriptions
66
+ Imputation: imputation
67
+ Phasing: phasing
68
+ Centre: centre
69
+ Date: date
70
+ Link(s): links
71
+ Run Accession(s): runAccessions
72
+ cast:
73
+ Run Accession(s): list
74
+ Imputation: boolean
75
+ Phasing: boolean
76
+ Date: date
77
+
78
+ Sample:
79
+ header_row: 3
80
+ required:
81
+ Analysis Alias: analysisAlias
82
+ Sample Name in VCF: sampleInVCF
83
+ optional:
84
+ Sample Accession: bioSampleAccession
85
+ BioSample Name: bioSampleName
86
+ Title: title
87
+ Description: description
88
+ Unique Name: uniqueName
89
+ Prefix: prefix
90
+ Subject: subject
91
+ Derived From: derivedFrom
92
+ Tax Id: taxId
93
+ Scientific Name: scientificName
94
+ Common Name: commonName
95
+ mating_type: matingType
96
+ sex: sex
97
+ population: population
98
+ cell_type: cellType
99
+ dev_stage: devStage
100
+ germline: germline
101
+ tissue_lib: tissueLib
102
+ tissue_type: tissueType
103
+ bio_material: BioMaterial
104
+ culture_collection: cultureCollection
105
+ specimen_voucher: specimenVoucher
106
+ collected_by: collectedBy
107
+ collection_date: collectionDate
108
+ geographic location (country and/or sea): geographicLocationCountrySea
109
+ geographic location (region and locality): geographicLocationRegion
110
+ host: host
111
+ identified_by: identifiedBy
112
+ isolation_source: isolationSource
113
+ lat_lon: latLon
114
+ lab_host: LabHost
115
+ environmental_sample: environmentalSample
116
+ cultivar: cultivar
117
+ ecotype: ecotype
118
+ isolate: isolate
119
+ strain: strain
120
+ sub_species: subSpecies
121
+ variety: variety
122
+ sub_strain: subStrain
123
+ cell_line: cellType
124
+ serotype: serotype
125
+ serovar: serovar
126
+ cast:
127
+ Sample ID: string
128
+ BioSample Name: string
129
+ collection_date: date
130
+
131
+
132
+ Files:
133
+ required:
134
+ Analysis Alias: analysisAlias
135
+ File Name: fileName
File without changes
@@ -0,0 +1,4 @@
1
+ class InvalidFileTypeError(Exception):
2
+ def __init__(self, message):
3
+ self.message = message
4
+ super().__init__(self.message)
@@ -0,0 +1,4 @@
1
+ class SubmissionNotFoundException(Exception):
2
+ def __init__(self, message):
3
+ self.message = message
4
+ super().__init__(self.message)
@@ -0,0 +1,5 @@
1
+
2
+ class SubmissionStatusException(Exception):
3
+ def __init__(self, message):
4
+ self.message = message
5
+ super().__init__(self.message)
File without changes
@@ -0,0 +1,159 @@
1
+ import argparse
2
+ import gzip
3
+ import hashlib
4
+ import json
5
+ from itertools import groupby
6
+
7
+ import requests
8
+ import yaml
9
+ from ebi_eva_common_pyutils.logger import logging_config
10
+ from requests import HTTPError
11
+ from retry import retry
12
+
13
+ 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
16
+
17
+ REFGET_SERVER = 'https://www.ebi.ac.uk/ena/cram'
18
+ CONTIG_ALIAS_SERVER = 'https://www.ebi.ac.uk/eva/webservices/contig-alias/v1/chromosomes/md5checksum'
19
+
20
+ logger = logging_config.get_logger(__name__)
21
+
22
+
23
+ def write_result_yaml(output_yaml, results):
24
+ with open(output_yaml, 'w') as open_yaml:
25
+ yaml.safe_dump(data=results, stream=open_yaml)
26
+
27
+
28
+ def refget_md5_digest(sequence):
29
+ return hashlib.md5(sequence.upper().encode('utf-8')).hexdigest()
30
+
31
+ @retry(exceptions=(HTTPError,), tries=3, delay=2, backoff=1.2, jitter=(1, 3))
32
+ def get_refget_metadata(md5_digest):
33
+ response = requests.get(f'{REFGET_SERVER}/sequence/{md5_digest}/metadata')
34
+ if 500 <= response.status_code < 600:
35
+ raise HTTPError(f"{response.status_code} Server Error: {response.reason} for url: {response.url}", response=response)
36
+ if 200 <= response.status_code < 300:
37
+ return response.json()
38
+ return None
39
+
40
+
41
+ @retry(exceptions=(HTTPError,), tries=3, delay=2, backoff=1.2, jitter=(1, 3))
42
+ def _get_containing_assemblies_paged(url):
43
+ response = requests.get(url)
44
+ if 500 <= response.status_code < 600:
45
+ raise HTTPError(f"{response.status_code} Server Error: {response.reason} for url: {response.url}",
46
+ response=response)
47
+ if 200 <= response.status_code < 300:
48
+ results = set()
49
+ response_data = response.json()
50
+ if '_embedded' in response_data and 'chromosomeEntities' in response_data['_embedded']:
51
+ for contigEntity in response_data['_embedded']['chromosomeEntities']:
52
+ results.add(contigEntity['assembly']['insdcAccession'])
53
+ if '_links' in response_data and 'next' in response_data['_links']:
54
+ # Add results from next page if needed
55
+ results |= _get_containing_assemblies_paged(response_data['_links']['next'])
56
+ return results
57
+ return set()
58
+
59
+
60
+ def get_containing_assemblies(md5_digest):
61
+ # Wrapper method to handle pagination
62
+ url = f'{CONTIG_ALIAS_SERVER}/{md5_digest}'
63
+ return _get_containing_assemblies_paged(url)
64
+
65
+
66
+ def assess_fasta(input_fasta, analyses, assembly_in_metadata):
67
+ """
68
+ Check whether all sequences in fasta file are INSDC, and if so whether the INSDC accession provided in the metadata
69
+ is compatible.
70
+ :param input_fasta: path to fasta file
71
+ :param analyses: aliases of all analyses associated with this fasta (used only for reporting)
72
+ :param assembly_in_metadata: INSDC accession from metadata (if None will only do the first check but will report
73
+ compatible assemblies)
74
+ :returns: dict of results
75
+ """
76
+ results = {'sequences': []}
77
+ all_insdc = True
78
+ possible_assemblies = set()
79
+ try:
80
+ for header, sequence in fasta_iter(input_fasta):
81
+ # Check sequence is INSDC
82
+ name = header.split()[0]
83
+ md5_digest = refget_md5_digest(sequence)
84
+ sequence_metadata = get_refget_metadata(md5_digest)
85
+ is_insdc = bool(sequence_metadata)
86
+ results['sequences'].append({'sequence_name': name, 'sequence_md5': md5_digest, 'insdc': is_insdc})
87
+ all_insdc = all_insdc and is_insdc
88
+ # Get possible assemblies for sequence
89
+ containing_assemblies = get_containing_assemblies(md5_digest)
90
+ if len(containing_assemblies) == 0:
91
+ if is_insdc:
92
+ logger.warning(f'Sequence with this MD5 is INSDC but not found in contig alias: {md5_digest}')
93
+ elif len(possible_assemblies) == 0:
94
+ possible_assemblies = containing_assemblies
95
+ else:
96
+ possible_assemblies &= containing_assemblies
97
+ except (ConnectionError, HTTPError) as e:
98
+ # Server errors from either ENA refget or EVA contig alias will halt the check prematurely.
99
+ # Report the error but do not return from the method, so that incomplete results can be reported
100
+ # (i.e. any sequences found to be INSDC and any compatible assemblies so far)
101
+ results['connection_error'] = str(e)
102
+
103
+ # Always report whether everything is INSDC
104
+ results['all_insdc'] = all_insdc
105
+ # Only report compatible assembly accessions if any were found in contig alias
106
+ if possible_assemblies:
107
+ results['possible_assemblies'] = possible_assemblies
108
+ # Only report on metadata concordance if we found compatible assemblies and found a single assembly accession in
109
+ # the metadata to compare against this FASTA file
110
+ if possible_assemblies and assembly_in_metadata:
111
+ results['metadata_assembly_compatible'] = (assembly_in_metadata in possible_assemblies)
112
+ results['associated_analyses'] = analyses
113
+ results['assembly_in_metadata'] = assembly_in_metadata
114
+ return results
115
+
116
+
117
+ def get_analyses_and_reference_genome_from_metadata(vcf_files_for_fasta, json_file):
118
+ """
119
+ Get analysis aliases and associated reference genome from the metadata.
120
+ :param vcf_files_for_fasta: list of VCF file paths, assumed to be associated with a single FASTA file and thus a
121
+ single assembly accession
122
+ :param json_file: JSON file of the metadata
123
+ """
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]
141
+
142
+
143
+ def main():
144
+ arg_parser = argparse.ArgumentParser(
145
+ description="Calculate each sequence's Refget MD5 digest and compare these against INSDC Refget server.")
146
+ arg_parser.add_argument('--input_fasta', required=True, dest='input_fasta',
147
+ help='Fasta file that contains the sequence to be checked')
148
+ arg_parser.add_argument('--vcf_files', dest='vcf_files', nargs='+',
149
+ help='VCF files associated with this fasta file (used to connect fasta file to '
150
+ 'assembly accession in metadata via analysis)')
151
+ arg_parser.add_argument('--metadata_json', required=True, dest='metadata_json', help='EVA metadata json file')
152
+ arg_parser.add_argument('--output_yaml', required=True, dest='output_yaml',
153
+ help='Path to the location of the results')
154
+ args = arg_parser.parse_args()
155
+ logging_config.add_stdout_handler()
156
+ analyses, metadata_insdc = get_analyses_and_reference_genome_from_metadata(args.vcf_files, args.metadata_json)
157
+ results = assess_fasta(args.input_fasta, analyses, metadata_insdc)
158
+ write_result_yaml(args.output_yaml, results)
159
+