XspecT 0.2.6__py3-none-any.whl → 0.4.0__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.
Potentially problematic release.
This version of XspecT might be problematic. Click here for more details.
- xspect/definitions.py +0 -7
- xspect/download_models.py +25 -24
- xspect/fastapi.py +23 -26
- xspect/file_io.py +86 -2
- xspect/main.py +333 -98
- xspect/mlst_feature/mlst_helper.py +5 -7
- xspect/model_management.py +6 -0
- xspect/models/probabilistic_filter_model.py +16 -5
- xspect/models/probabilistic_filter_svm_model.py +33 -18
- xspect/models/probabilistic_single_filter_model.py +8 -1
- xspect/models/result.py +15 -61
- xspect/ncbi.py +265 -0
- xspect/train.py +258 -247
- {XspecT-0.2.6.dist-info → xspect-0.4.0.dist-info}/METADATA +14 -21
- xspect-0.4.0.dist-info/RECORD +24 -0
- {XspecT-0.2.6.dist-info → xspect-0.4.0.dist-info}/WHEEL +1 -1
- XspecT-0.2.6.dist-info/RECORD +0 -34
- xspect/pipeline.py +0 -201
- xspect/run.py +0 -38
- xspect/train_filter/__init__.py +0 -0
- xspect/train_filter/create_svm.py +0 -45
- xspect/train_filter/extract_and_concatenate.py +0 -124
- xspect/train_filter/html_scrap.py +0 -114
- xspect/train_filter/ncbi_api/__init__.py +0 -0
- xspect/train_filter/ncbi_api/download_assemblies.py +0 -31
- xspect/train_filter/ncbi_api/ncbi_assembly_metadata.py +0 -110
- xspect/train_filter/ncbi_api/ncbi_children_tree.py +0 -53
- xspect/train_filter/ncbi_api/ncbi_taxon_metadata.py +0 -55
- {XspecT-0.2.6.dist-info → xspect-0.4.0.dist-info}/entry_points.txt +0 -0
- {XspecT-0.2.6.dist-info → xspect-0.4.0.dist-info/licenses}/LICENSE +0 -0
- {XspecT-0.2.6.dist-info → xspect-0.4.0.dist-info}/top_level.txt +0 -0
xspect/definitions.py
CHANGED
|
@@ -21,13 +21,6 @@ def get_xspect_model_path():
|
|
|
21
21
|
return model_path
|
|
22
22
|
|
|
23
23
|
|
|
24
|
-
def get_xspect_tmp_path():
|
|
25
|
-
"""Return the path to the XspecT temporary files."""
|
|
26
|
-
tmp_path = get_xspect_root_path() / "tmp"
|
|
27
|
-
tmp_path.mkdir(exist_ok=True, parents=True)
|
|
28
|
-
return tmp_path
|
|
29
|
-
|
|
30
|
-
|
|
31
24
|
def get_xspect_upload_path():
|
|
32
25
|
"""Return the path to the XspecT upload directory."""
|
|
33
26
|
upload_path = get_xspect_root_path() / "uploads"
|
xspect/download_models.py
CHANGED
|
@@ -1,33 +1,34 @@
|
|
|
1
1
|
"""Download filters from public repository."""
|
|
2
2
|
|
|
3
|
-
import os
|
|
4
3
|
import shutil
|
|
4
|
+
from tempfile import TemporaryDirectory
|
|
5
|
+
from pathlib import Path
|
|
5
6
|
import requests
|
|
6
7
|
|
|
7
|
-
from xspect.definitions import get_xspect_model_path
|
|
8
|
+
from xspect.definitions import get_xspect_model_path
|
|
8
9
|
|
|
9
10
|
|
|
10
11
|
def download_test_models(url):
|
|
11
12
|
"""Download models."""
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
13
|
+
with TemporaryDirectory() as tmp_dir:
|
|
14
|
+
tmp_dir = Path(tmp_dir)
|
|
15
|
+
download_path = tmp_dir / "models.zip"
|
|
16
|
+
extract_path = tmp_dir / "extracted_models"
|
|
17
|
+
|
|
18
|
+
r = requests.get(url, allow_redirects=True, timeout=10)
|
|
19
|
+
with open(download_path, "wb") as f:
|
|
20
|
+
f.write(r.content)
|
|
21
|
+
|
|
22
|
+
shutil.unpack_archive(
|
|
23
|
+
download_path,
|
|
24
|
+
extract_path,
|
|
25
|
+
"zip",
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
shutil.copytree(
|
|
29
|
+
extract_path,
|
|
30
|
+
get_xspect_model_path(),
|
|
31
|
+
dirs_exist_ok=True,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
shutil.rmtree(extract_path)
|
xspect/fastapi.py
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
"""FastAPI application for XspecT."""
|
|
2
2
|
|
|
3
|
-
import
|
|
3
|
+
from uuid import uuid4
|
|
4
4
|
from pathlib import Path
|
|
5
5
|
from shutil import copyfileobj
|
|
6
6
|
from fastapi import FastAPI, UploadFile, BackgroundTasks
|
|
7
7
|
from xspect.definitions import get_xspect_runs_path, get_xspect_upload_path
|
|
8
8
|
from xspect.download_models import download_test_models
|
|
9
|
+
from xspect.file_io import filter_sequences
|
|
9
10
|
import xspect.model_management as mm
|
|
10
|
-
from xspect.
|
|
11
|
-
from xspect.pipeline import ModelExecution, Pipeline, PipelineStep
|
|
12
|
-
from xspect.train import train_ncbi
|
|
11
|
+
from xspect.train import train_from_ncbi
|
|
13
12
|
|
|
14
13
|
app = FastAPI()
|
|
15
14
|
|
|
@@ -17,43 +16,41 @@ app = FastAPI()
|
|
|
17
16
|
@app.get("/download-filters")
|
|
18
17
|
def download_filters():
|
|
19
18
|
"""Download filters."""
|
|
20
|
-
download_test_models("
|
|
19
|
+
download_test_models("http://assets.adrianromberg.com/xspect-models.zip")
|
|
21
20
|
|
|
22
21
|
|
|
23
22
|
@app.get("/classify")
|
|
24
23
|
def classify(genus: str, file: str, meta: bool = False, step: int = 500):
|
|
25
24
|
"""Classify uploaded sample."""
|
|
26
25
|
|
|
27
|
-
|
|
26
|
+
input_path = get_xspect_upload_path() / file
|
|
27
|
+
|
|
28
|
+
uuid = str(uuid4())
|
|
28
29
|
|
|
29
|
-
pipeline = Pipeline(genus + " classification", "Test Author", "test@example.com")
|
|
30
|
-
species_execution = ModelExecution(
|
|
31
|
-
genus.lower() + "-species", sparse_sampling_step=step
|
|
32
|
-
)
|
|
33
30
|
if meta:
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
)
|
|
37
|
-
|
|
38
|
-
|
|
31
|
+
genus_model = mm.get_genus_model(genus)
|
|
32
|
+
genus_result = genus_model.predict(input_path, step=step)
|
|
33
|
+
included_ids = genus_result.get_filtered_subsequence_labels(genus)
|
|
34
|
+
if not included_ids:
|
|
35
|
+
return {"message": "No sequences found for the given genus."}
|
|
36
|
+
filtered_path = get_xspect_runs_path() / f"filtered_{uuid}.fasta"
|
|
37
|
+
filter_sequences(
|
|
38
|
+
Path(input_path),
|
|
39
|
+
Path(filtered_path),
|
|
40
|
+
included_ids=included_ids,
|
|
39
41
|
)
|
|
40
|
-
|
|
41
|
-
pipeline.add_pipeline_step(genus_execution)
|
|
42
|
-
else:
|
|
43
|
-
pipeline.add_pipeline_step(species_execution)
|
|
44
|
-
|
|
45
|
-
run = pipeline.run(Path(path))
|
|
46
|
-
time_str = datetime.datetime.now().strftime("%Y-%m-%d-%H-%M-%S")
|
|
47
|
-
save_path = get_xspect_runs_path() / f"run_{time_str}.json"
|
|
48
|
-
run.save(save_path)
|
|
42
|
+
input_path = filtered_path
|
|
49
43
|
|
|
50
|
-
|
|
44
|
+
species_model = mm.get_species_model(genus)
|
|
45
|
+
species_result = species_model.predict(input_path, step=step)
|
|
46
|
+
species_result.save(get_xspect_runs_path() / f"result_{uuid}.json")
|
|
47
|
+
return species_result.to_dict()
|
|
51
48
|
|
|
52
49
|
|
|
53
50
|
@app.post("/train")
|
|
54
51
|
def train(genus: str, background_tasks: BackgroundTasks, svm_steps: int = 1):
|
|
55
52
|
"""Train NCBI model."""
|
|
56
|
-
background_tasks.add_task(
|
|
53
|
+
background_tasks.add_task(train_from_ncbi, genus, svm_steps)
|
|
57
54
|
|
|
58
55
|
return {"message": "Training started."}
|
|
59
56
|
|
xspect/file_io.py
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
File IO module.
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
+
from json import loads
|
|
5
6
|
import os
|
|
6
7
|
from pathlib import Path
|
|
7
8
|
import zipfile
|
|
@@ -18,10 +19,10 @@ def delete_zip_files(dir_path):
|
|
|
18
19
|
os.remove(file_path)
|
|
19
20
|
|
|
20
21
|
|
|
21
|
-
def extract_zip(zip_path, unzipped_path):
|
|
22
|
+
def extract_zip(zip_path: Path, unzipped_path: Path):
|
|
22
23
|
"""Extracts all files from a directory with zip files."""
|
|
23
24
|
# Make new directory.
|
|
24
|
-
|
|
25
|
+
unzipped_path.mkdir(parents=True, exist_ok=True)
|
|
25
26
|
|
|
26
27
|
file_names = os.listdir(zip_path)
|
|
27
28
|
for file in file_names:
|
|
@@ -85,3 +86,86 @@ def get_records_by_id(file: Path, ids: list[str]):
|
|
|
85
86
|
"""Return records with the specified ids."""
|
|
86
87
|
records = get_record_iterator(file)
|
|
87
88
|
return [record for record in records if record.id in ids]
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def concatenate_species_fasta_files(input_folders: list[Path], output_directory: Path):
|
|
92
|
+
"""Concatenate fasta files from different species into one file per species.
|
|
93
|
+
|
|
94
|
+
Args:
|
|
95
|
+
input_species_folders (list[Path]): List of paths to species folders.
|
|
96
|
+
output_directory (Path): Path to the output directory.
|
|
97
|
+
"""
|
|
98
|
+
for species_folder in input_folders:
|
|
99
|
+
species_name = species_folder.name
|
|
100
|
+
fasta_files = [
|
|
101
|
+
f for ending in fasta_endings for f in species_folder.glob(f"*.{ending}")
|
|
102
|
+
]
|
|
103
|
+
if len(fasta_files) == 0:
|
|
104
|
+
raise ValueError(f"no fasta files found in {species_folder}")
|
|
105
|
+
|
|
106
|
+
# concatenate fasta files
|
|
107
|
+
concatenated_fasta = output_directory / f"{species_name}.fasta"
|
|
108
|
+
with open(concatenated_fasta, "w", encoding="utf-8") as f:
|
|
109
|
+
for fasta_file in fasta_files:
|
|
110
|
+
with open(fasta_file, "r", encoding="utf-8") as f_in:
|
|
111
|
+
f.write(f_in.read())
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def concatenate_metagenome(fasta_dir: Path, meta_path: Path):
|
|
115
|
+
"""Concatenate all fasta files in a directory into one file.
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
fasta_dir (Path): Path to the directory with the fasta files.
|
|
119
|
+
meta_path (Path): Path to the output file.
|
|
120
|
+
"""
|
|
121
|
+
with open(meta_path, "w", encoding="utf-8") as meta_file:
|
|
122
|
+
for fasta_file in fasta_dir.glob("*.fasta"):
|
|
123
|
+
with open(fasta_file, "r", encoding="utf-8") as f_in:
|
|
124
|
+
meta_file.write(f_in.read())
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def get_ncbi_dataset_accession_paths(
|
|
128
|
+
ncbi_dataset_path: Path,
|
|
129
|
+
) -> dict[str, Path]:
|
|
130
|
+
"""Get the paths of the NCBI dataset accessions.
|
|
131
|
+
|
|
132
|
+
Args:
|
|
133
|
+
ncbi_dataset_path (Path): Path to the NCBI dataset directory.
|
|
134
|
+
|
|
135
|
+
Returns:
|
|
136
|
+
dict[str, Path]: Dictionary with the accession as key and the path as value.
|
|
137
|
+
"""
|
|
138
|
+
data_path = ncbi_dataset_path / "ncbi_dataset" / "data"
|
|
139
|
+
if not data_path.exists():
|
|
140
|
+
raise ValueError(f"Path {data_path} does not exist.")
|
|
141
|
+
|
|
142
|
+
accession_paths = {}
|
|
143
|
+
with open(data_path / "dataset_catalog.json", "r", encoding="utf-8") as f:
|
|
144
|
+
res = loads(f.read())
|
|
145
|
+
for assembly in res["assemblies"][1:]: # the first item is the data report
|
|
146
|
+
accession = assembly["accession"]
|
|
147
|
+
assembly_path = data_path / assembly["files"][0]["filePath"]
|
|
148
|
+
accession_paths[accession] = assembly_path
|
|
149
|
+
return accession_paths
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def filter_sequences(
|
|
153
|
+
input_file: Path,
|
|
154
|
+
output_file: Path,
|
|
155
|
+
included_ids: list[str],
|
|
156
|
+
):
|
|
157
|
+
"""Filter sequences by IDs from an input file and save them to an output file.
|
|
158
|
+
|
|
159
|
+
Args:
|
|
160
|
+
input_file (Path): Path to the input file.
|
|
161
|
+
output_file (Path): Path to the output file.
|
|
162
|
+
included_ids (list[str], optional): List of IDs to include. If None, no output file is created.
|
|
163
|
+
"""
|
|
164
|
+
if not included_ids:
|
|
165
|
+
print("No IDs provided, no output file will be created.")
|
|
166
|
+
return
|
|
167
|
+
|
|
168
|
+
with open(output_file, "w", encoding="utf-8") as out_f:
|
|
169
|
+
for record in get_record_iterator(input_file):
|
|
170
|
+
if record.id in included_ids:
|
|
171
|
+
SeqIO.write(record, out_f, "fasta")
|