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.
- eva_sub_cli/__init__.py +27 -0
- eva_sub_cli/_version.py +21 -0
- eva_sub_cli/auth.py +106 -0
- eva_sub_cli/date_utils.py +32 -0
- eva_sub_cli/etc/EVA_Submission_template.xlsx +0 -0
- eva_sub_cli/etc/eva-biosamples.json +174 -0
- eva_sub_cli/etc/eva_logo.png +0 -0
- eva_sub_cli/etc/eva_schema.json +369 -0
- eva_sub_cli/etc/spreadsheet2json_conf.yaml +135 -0
- eva_sub_cli/exceptions/__init__.py +0 -0
- eva_sub_cli/exceptions/invalid_file_type_exception.py +4 -0
- eva_sub_cli/exceptions/submission_not_found_exception.py +4 -0
- eva_sub_cli/exceptions/submission_status_exception.py +5 -0
- eva_sub_cli/executables/__init__.py +0 -0
- eva_sub_cli/executables/check_fasta_insdc.py +159 -0
- eva_sub_cli/executables/check_metadata_semantics.py +18 -0
- eva_sub_cli/executables/cli.py +115 -0
- eva_sub_cli/executables/samples_checker.py +144 -0
- eva_sub_cli/executables/trim_down.py +78 -0
- eva_sub_cli/executables/xlsx2json.py +335 -0
- eva_sub_cli/file_utils.py +117 -0
- eva_sub_cli/jinja_templates/fasta_check.html +86 -0
- eva_sub_cli/jinja_templates/file_validation.html +93 -0
- eva_sub_cli/jinja_templates/html_report.html +142 -0
- eva_sub_cli/jinja_templates/metadata_validation.html +52 -0
- eva_sub_cli/jinja_templates/project_details.html +37 -0
- eva_sub_cli/jinja_templates/sample_name_check.html +60 -0
- eva_sub_cli/jinja_templates/shallow_validation.html +29 -0
- eva_sub_cli/metadata_utils.py +46 -0
- eva_sub_cli/nextflow/validation.nf +303 -0
- eva_sub_cli/orchestrator.py +233 -0
- eva_sub_cli/report.py +40 -0
- eva_sub_cli/semantic_metadata.py +243 -0
- eva_sub_cli/submission_ws.py +61 -0
- eva_sub_cli/submit.py +111 -0
- eva_sub_cli/validators/__init__.py +0 -0
- eva_sub_cli/validators/docker_validator.py +205 -0
- eva_sub_cli/validators/native_validator.py +68 -0
- eva_sub_cli/validators/validation_results_parsers.py +210 -0
- eva_sub_cli/validators/validator.py +510 -0
- eva_sub_cli-0.4.4.dist-info/METADATA +164 -0
- eva_sub_cli-0.4.4.dist-info/RECORD +46 -0
- eva_sub_cli-0.4.4.dist-info/WHEEL +5 -0
- eva_sub_cli-0.4.4.dist-info/entry_points.txt +7 -0
- eva_sub_cli-0.4.4.dist-info/licenses/LICENSE +201 -0
- 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
|
|
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
|
+
|