XspecT 0.2.7__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 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, get_xspect_tmp_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
- download_path = get_xspect_tmp_path() / "models.zip"
14
- extract_path = get_xspect_tmp_path() / "extracted_models"
15
-
16
- r = requests.get(url, allow_redirects=True, timeout=10)
17
- with open(download_path, "wb") as f:
18
- f.write(r.content)
19
-
20
- shutil.unpack_archive(
21
- download_path,
22
- extract_path,
23
- "zip",
24
- )
25
-
26
- shutil.copytree(
27
- extract_path,
28
- get_xspect_model_path(),
29
- dirs_exist_ok=True,
30
- )
31
-
32
- os.remove(download_path)
33
- shutil.rmtree(extract_path)
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 datetime
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.models.result import StepType
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("https://xspect2.s3.eu-central-1.amazonaws.com/models.zip")
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
- path = get_xspect_upload_path() / file
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
- species_filtering_step = PipelineStep(
35
- StepType.FILTERING, genus, 0.7, species_execution
36
- )
37
- genus_execution = ModelExecution(
38
- genus.lower() + "-genus", sparse_sampling_step=step
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
- genus_execution.add_pipeline_step(species_filtering_step)
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
- return run.to_dict()
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(train_ncbi, genus, svm_steps)
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
- os.mkdir(unzipped_path)
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")