opencloning 0.4.8__tar.gz → 0.5__tar.gz

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 (58) hide show
  1. {opencloning-0.4.8 → opencloning-0.5}/PKG-INFO +18 -8
  2. {opencloning-0.4.8 → opencloning-0.5}/README.md +15 -4
  3. {opencloning-0.4.8 → opencloning-0.5}/pyproject.toml +3 -4
  4. {opencloning-0.4.8 → opencloning-0.5}/src/opencloning/app_settings.py +7 -0
  5. {opencloning-0.4.8 → opencloning-0.5}/src/opencloning/batch_cloning/pombe/__init__.py +2 -2
  6. opencloning-0.5/src/opencloning/batch_cloning/pombe/pombe_clone.py +102 -0
  7. {opencloning-0.4.8 → opencloning-0.5}/src/opencloning/batch_cloning/pombe/pombe_summary.py +20 -8
  8. {opencloning-0.4.8 → opencloning-0.5}/src/opencloning/batch_cloning/ziqiang_et_al2024/__init__.py +8 -8
  9. {opencloning-0.4.8 → opencloning-0.5}/src/opencloning/batch_cloning/ziqiang_et_al2024/ziqiang_et_al2024.json +2 -9
  10. {opencloning-0.4.8 → opencloning-0.5}/src/opencloning/bug_fixing/backend_v0_3.py +13 -5
  11. opencloning-0.5/src/opencloning/catalogs/__init__.py +36 -0
  12. opencloning-0.5/src/opencloning/catalogs/igem2024.yaml +2172 -0
  13. opencloning-0.5/src/opencloning/catalogs/openDNA_collections.yaml +1161 -0
  14. opencloning-0.5/src/opencloning/catalogs/readme.txt +1 -0
  15. opencloning-0.5/src/opencloning/catalogs/seva.tsv +231 -0
  16. opencloning-0.5/src/opencloning/catalogs/snapgene.yaml +2837 -0
  17. {opencloning-0.4.8 → opencloning-0.5}/src/opencloning/dna_functions.py +155 -158
  18. {opencloning-0.4.8 → opencloning-0.5}/src/opencloning/dna_utils.py +45 -62
  19. {opencloning-0.4.8 → opencloning-0.5}/src/opencloning/ebic/primer_design.py +1 -1
  20. {opencloning-0.4.8 → opencloning-0.5}/src/opencloning/endpoints/annotation.py +9 -13
  21. opencloning-0.5/src/opencloning/endpoints/assembly.py +367 -0
  22. opencloning-0.5/src/opencloning/endpoints/endpoint_utils.py +52 -0
  23. {opencloning-0.4.8 → opencloning-0.5}/src/opencloning/endpoints/external_import.py +169 -124
  24. {opencloning-0.4.8 → opencloning-0.5}/src/opencloning/endpoints/no_assembly.py +23 -39
  25. opencloning-0.5/src/opencloning/endpoints/no_input.py +100 -0
  26. {opencloning-0.4.8 → opencloning-0.5}/src/opencloning/endpoints/other.py +1 -1
  27. {opencloning-0.4.8 → opencloning-0.5}/src/opencloning/endpoints/primer_design.py +2 -1
  28. {opencloning-0.4.8 → opencloning-0.5}/src/opencloning/http_client.py +2 -2
  29. opencloning-0.5/src/opencloning/ncbi_requests.py +203 -0
  30. {opencloning-0.4.8 → opencloning-0.5}/src/opencloning/primer_design.py +1 -1
  31. opencloning-0.5/src/opencloning/pydantic_models.py +75 -0
  32. {opencloning-0.4.8 → opencloning-0.5}/src/opencloning/request_examples.py +10 -22
  33. opencloning-0.5/src/opencloning/temp_functions.py +50 -0
  34. opencloning-0.4.8/src/opencloning/batch_cloning/pombe/pombe_clone.py +0 -183
  35. opencloning-0.4.8/src/opencloning/cre_lox.py +0 -116
  36. opencloning-0.4.8/src/opencloning/endpoints/assembly.py +0 -588
  37. opencloning-0.4.8/src/opencloning/endpoints/no_input.py +0 -115
  38. opencloning-0.4.8/src/opencloning/gateway.py +0 -154
  39. opencloning-0.4.8/src/opencloning/ncbi_requests.py +0 -137
  40. opencloning-0.4.8/src/opencloning/pydantic_models.py +0 -575
  41. {opencloning-0.4.8 → opencloning-0.5}/LICENSE +0 -0
  42. {opencloning-0.4.8 → opencloning-0.5}/src/opencloning/__init__.py +0 -0
  43. {opencloning-0.4.8 → opencloning-0.5}/src/opencloning/_version.py +0 -0
  44. {opencloning-0.4.8 → opencloning-0.5}/src/opencloning/api_config_utils.py +0 -0
  45. {opencloning-0.4.8 → opencloning-0.5}/src/opencloning/batch_cloning/__init__.py +0 -0
  46. {opencloning-0.4.8 → opencloning-0.5}/src/opencloning/batch_cloning/index.html +0 -0
  47. {opencloning-0.4.8 → opencloning-0.5}/src/opencloning/batch_cloning/pombe/index.html +0 -0
  48. {opencloning-0.4.8 → opencloning-0.5}/src/opencloning/batch_cloning/pombe/pombe_gather.py +0 -0
  49. {opencloning-0.4.8 → opencloning-0.5}/src/opencloning/batch_cloning/pombe/pombe_get_primers.py +0 -0
  50. {opencloning-0.4.8 → opencloning-0.5}/src/opencloning/batch_cloning/ziqiang_et_al2024/index.html +0 -0
  51. {opencloning-0.4.8 → opencloning-0.5}/src/opencloning/bug_fixing/README.md +0 -0
  52. {opencloning-0.4.8 → opencloning-0.5}/src/opencloning/bug_fixing/__init__.py +0 -0
  53. {opencloning-0.4.8 → opencloning-0.5}/src/opencloning/ebic/__init__.py +0 -0
  54. {opencloning-0.4.8 → opencloning-0.5}/src/opencloning/ebic/primer_design_settings.py +0 -0
  55. {opencloning-0.4.8 → opencloning-0.5}/src/opencloning/get_router.py +0 -0
  56. {opencloning-0.4.8 → opencloning-0.5}/src/opencloning/main.py +0 -0
  57. {opencloning-0.4.8 → opencloning-0.5}/src/opencloning/primer3_functions.py +0 -0
  58. {opencloning-0.4.8 → opencloning-0.5}/src/opencloning/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: opencloning
3
- Version: 0.4.8
3
+ Version: 0.5
4
4
  Summary: Backend of OpenCloning, a web application to generate molecular cloning strategies in json format, and share them with others.
5
5
  License: MIT
6
6
  License-File: LICENSE
@@ -17,14 +17,13 @@ Requires-Dist: beautifulsoup4 (>=4.11.1,<5.0.0)
17
17
  Requires-Dist: biopython (>=1.85,<2.0)
18
18
  Requires-Dist: fastapi
19
19
  Requires-Dist: httpx (>=0.28.1,<0.29.0)
20
- Requires-Dist: opencloning-linkml (==0.4.4)
21
20
  Requires-Dist: openpyxl (>=3.1.5,<4.0.0)
22
21
  Requires-Dist: packaging (>=25.0,<26.0)
23
22
  Requires-Dist: pairwise-alignments-to-msa (>=0.1.1,<0.2.0)
24
23
  Requires-Dist: pandas (>=2.2.3,<3.0.0)
25
- Requires-Dist: primer3-py (==2.2.0)
24
+ Requires-Dist: primer3-py (>=2.3,<3.0)
26
25
  Requires-Dist: pydantic (>=2.7.1,<3.0.0)
27
- Requires-Dist: pydna (==5.5.2)
26
+ Requires-Dist: pydna (>=5.5.5,<6.0.0)
28
27
  Requires-Dist: python-multipart
29
28
  Requires-Dist: pyyaml (>=6.0.2,<7.0.0)
30
29
  Requires-Dist: regex (>=2024.11.6,<2025.0.0)
@@ -47,9 +46,9 @@ Read [main project readme](https://github.com/manulera/OpenCloning) first.
47
46
 
48
47
  This API provides a series of entry points. The API documentation can be accessed [here](https://api.opencloning.org/docs). You can use the documentation page to try some request directly on the browser. Otherwise, the API is open for you to make requests from a python script or command line at: [https://api.opencloning.org/](https://api.opencloning.org/).
49
48
 
50
- ## Scripting
49
+ ## Scripting with pydna
51
50
 
52
- The API functions can also be used to write python scripts to automate cloning. See the [scripting examples](examples/scripting) for more information.
51
+ You can write python scripts to automate cloning using the python library [pydna](https://github.com/pydna-group/pydna), which is now integrated with the OpenCloning data model. See [the documentation](https://github.com/pydna-group/pydna/blob/master/docs/notebooks/history.ipynb) for how to get started.
53
52
 
54
53
  ## Migrating between model versions and fixing model bugs
55
54
 
@@ -117,7 +116,7 @@ docker run -d --name backendcontainer -p 8000:8000 manulera/opencloningbackend
117
116
 
118
117
  ```
119
118
 
120
- If you don't want to download the repository and build the image, you can fetch the latest image from dockerhub (same image that is used in [https://api.opencloning.org/](https://api.opencloning.org/))
119
+ If you don't want to download the repository and build the image, you can fetch the latest image from dockerhub.
121
120
 
122
121
  ```bash
123
122
  docker pull manulera/opencloningbackend
@@ -167,7 +166,7 @@ pytest -v -ks
167
166
  ### Ping a particular library version from github:
168
167
 
169
168
  ```
170
- poetry add git+https://github.com/BjornFJohansson/pydna#4fd760d075f77cceeb27969e017e04b42f6d0aa3
169
+ poetry add git+https://github.com/pydna-group/pydna#4fd760d075f77cceeb27969e017e04b42f6d0aa3
171
170
  ```
172
171
 
173
172
  When installing the last version, sometimes poetry may not be able to access the latest version
@@ -188,3 +187,14 @@ RECORD_STUBS=1 uvicorn opencloning.main:app --reload --reload-exclude='.venv'
188
187
 
189
188
  This will record the stubs (requests and responses) in the `stubs` folder.
190
189
 
190
+
191
+ ### Catalogs
192
+
193
+ Catalogs are used to map ids to urls for several plasmid collections. They are stored in the `src/opencloning/catalogs` folder.
194
+
195
+ To update the catalogs, run the following command:
196
+
197
+ ```bash
198
+ poetry run python scripts/update_catalogs.py
199
+ ```
200
+
@@ -13,9 +13,9 @@ Read [main project readme](https://github.com/manulera/OpenCloning) first.
13
13
 
14
14
  This API provides a series of entry points. The API documentation can be accessed [here](https://api.opencloning.org/docs). You can use the documentation page to try some request directly on the browser. Otherwise, the API is open for you to make requests from a python script or command line at: [https://api.opencloning.org/](https://api.opencloning.org/).
15
15
 
16
- ## Scripting
16
+ ## Scripting with pydna
17
17
 
18
- The API functions can also be used to write python scripts to automate cloning. See the [scripting examples](examples/scripting) for more information.
18
+ You can write python scripts to automate cloning using the python library [pydna](https://github.com/pydna-group/pydna), which is now integrated with the OpenCloning data model. See [the documentation](https://github.com/pydna-group/pydna/blob/master/docs/notebooks/history.ipynb) for how to get started.
19
19
 
20
20
  ## Migrating between model versions and fixing model bugs
21
21
 
@@ -83,7 +83,7 @@ docker run -d --name backendcontainer -p 8000:8000 manulera/opencloningbackend
83
83
 
84
84
  ```
85
85
 
86
- If you don't want to download the repository and build the image, you can fetch the latest image from dockerhub (same image that is used in [https://api.opencloning.org/](https://api.opencloning.org/))
86
+ If you don't want to download the repository and build the image, you can fetch the latest image from dockerhub.
87
87
 
88
88
  ```bash
89
89
  docker pull manulera/opencloningbackend
@@ -133,7 +133,7 @@ pytest -v -ks
133
133
  ### Ping a particular library version from github:
134
134
 
135
135
  ```
136
- poetry add git+https://github.com/BjornFJohansson/pydna#4fd760d075f77cceeb27969e017e04b42f6d0aa3
136
+ poetry add git+https://github.com/pydna-group/pydna#4fd760d075f77cceeb27969e017e04b42f6d0aa3
137
137
  ```
138
138
 
139
139
  When installing the last version, sometimes poetry may not be able to access the latest version
@@ -153,3 +153,14 @@ RECORD_STUBS=1 uvicorn opencloning.main:app --reload --reload-exclude='.venv'
153
153
  ```
154
154
 
155
155
  This will record the stubs (requests and responses) in the `stubs` folder.
156
+
157
+
158
+ ### Catalogs
159
+
160
+ Catalogs are used to map ids to urls for several plasmid collections. They are stored in the `src/opencloning/catalogs` folder.
161
+
162
+ To update the catalogs, run the following command:
163
+
164
+ ```bash
165
+ poetry run python scripts/update_catalogs.py
166
+ ```
@@ -8,7 +8,7 @@ authors = ["Manuel Lera-Ramirez <manulera14@gmail.com>"]
8
8
  description = "Backend of OpenCloning, a web application to generate molecular cloning strategies in json format, and share them with others."
9
9
  license = "MIT"
10
10
  name = "opencloning"
11
- version = "0.4.8"
11
+ version = "0.5"
12
12
  package-mode = true
13
13
  readme = "README.md"
14
14
  repository = "https://github.com/manulera/OpenCloning_backend"
@@ -20,17 +20,16 @@ httpx = "^0.28.1"
20
20
  python = "^3.11"
21
21
  python-multipart = "*"
22
22
  uvicorn = "*"
23
- pydna = "5.5.2"
24
23
  regex = "^2024.11.6"
25
24
  pydantic = "^2.7.1"
26
25
  pandas = "^2.2.3"
27
26
  openpyxl = "^3.1.5"
28
27
  pyyaml = "^6.0.2"
29
- opencloning-linkml = "0.4.4"
30
- primer3-py = "2.2.0"
28
+ primer3-py = "^2.3"
31
29
  biopython = "^1.85"
32
30
  packaging = "^25.0"
33
31
  pairwise-alignments-to-msa = "^0.1.1"
32
+ pydna = "^5.5.5"
34
33
 
35
34
  [tool.poetry.group.dev.dependencies]
36
35
  autopep8 = "^2.0.4"
@@ -22,6 +22,11 @@ if os.environ.get('ALLOWED_ORIGINS') is not None:
22
22
 
23
23
  # External services settings =================================
24
24
  NCBI_API_KEY = os.environ.get('NCBI_API_KEY')
25
+ NCBI_MAX_SEQUENCE_LENGTH = (
26
+ int(os.environ.get('NCBI_MAX_SEQUENCE_LENGTH'))
27
+ if os.environ.get('NCBI_MAX_SEQUENCE_LENGTH') is not None
28
+ else 500000
29
+ )
25
30
  PLANNOTATE_URL = os.environ['PLANNOTATE_URL'] if 'PLANNOTATE_URL' in os.environ else None
26
31
  PLANNOTATE_TIMEOUT = int(os.environ['PLANNOTATE_TIMEOUT']) if 'PLANNOTATE_TIMEOUT' in os.environ else 20
27
32
  # Handle trailing slash:
@@ -58,6 +63,7 @@ class Settings(BaseModel):
58
63
  BATCH_CLONING: bool
59
64
  RECORD_STUBS: bool
60
65
  NCBI_API_KEY: str | None
66
+ NCBI_MAX_SEQUENCE_LENGTH: int
61
67
  ALLOWED_ORIGINS: list[str]
62
68
  PLANNOTATE_URL: str | None
63
69
  PLANNOTATE_TIMEOUT: int
@@ -73,6 +79,7 @@ settings = Settings(
73
79
  BATCH_CLONING=BATCH_CLONING,
74
80
  RECORD_STUBS=RECORD_STUBS,
75
81
  NCBI_API_KEY=NCBI_API_KEY,
82
+ NCBI_MAX_SEQUENCE_LENGTH=NCBI_MAX_SEQUENCE_LENGTH,
76
83
  ALLOWED_ORIGINS=ALLOWED_ORIGINS,
77
84
  PLANNOTATE_URL=PLANNOTATE_URL,
78
85
  PLANNOTATE_TIMEOUT=PLANNOTATE_TIMEOUT,
@@ -70,8 +70,8 @@ async def post_batch_cloning(
70
70
  try:
71
71
  pombe_summary(temp_dir)
72
72
  pombe_gather(temp_dir)
73
- except Exception:
74
- raise HTTPException(status_code=400, detail='Summary failed')
73
+ except Exception as e:
74
+ raise HTTPException(status_code=400, detail=f'Summary failed: {e}')
75
75
 
76
76
  # zip the temp dir and return it
77
77
  zip_filename = f'{temp_dir}_archive'
@@ -0,0 +1,102 @@
1
+ import os
2
+ from pydna.assembly2 import homologous_recombination_integration, pcr_assembly
3
+ from opencloning.dna_functions import request_from_addgene
4
+ from opencloning.ncbi_requests import get_annotations_from_query, get_genome_region_from_annotation
5
+ import asyncio
6
+ from Bio import SeqIO
7
+ from pydna.primer import Primer
8
+ from pydna.opencloning_models import CloningStrategy
9
+ from fastapi.datastructures import UploadFile
10
+ from pydna.parsers import parse as pydna_parse
11
+
12
+
13
+ async def main(
14
+ gene: str,
15
+ assembly_accession: str,
16
+ output_dir: str,
17
+ plasmid_input: UploadFile | str = '19343',
18
+ padding: int = 1000,
19
+ ):
20
+ print(f"\033[92mCloning {gene}\033[0m")
21
+ # Parse primers =================================================================================
22
+ primers = [Primer(p) for p in SeqIO.parse(os.path.join(output_dir, gene, 'primers.fa'), 'fasta')]
23
+ common_primers = [Primer(p) for p in SeqIO.parse(os.path.join(output_dir, 'checking_primers.fa'), 'fasta')]
24
+
25
+ # Get plasmid sequence =================================================================================
26
+ if isinstance(plasmid_input, UploadFile):
27
+ file_content = (await plasmid_input.read()).decode()
28
+
29
+ plasmid = pydna_parse(file_content)[0]
30
+ else:
31
+ plasmid = await request_from_addgene(plasmid_input)
32
+
33
+ # Get genome region =====================================================================
34
+ annotations = await get_annotations_from_query(gene, assembly_accession)
35
+ if len(annotations) == 0:
36
+ raise ValueError(f'No annotations found for {gene}')
37
+
38
+ annotations = [a for a in annotations if gene.upper() in a['locus_tag'].upper()]
39
+ if len(annotations) != 1:
40
+ raise ValueError(f'No right annotation found for {gene}')
41
+
42
+ locus = await get_genome_region_from_annotation(annotations[0], 1000, 1000)
43
+
44
+ # PCR ================================================================================================
45
+ pcr_products = pcr_assembly(plasmid, primers[0], primers[1], limit=14, mismatches=0)
46
+ pcr_products[0].name = 'amplified_marker'
47
+ alleles = homologous_recombination_integration(locus, [pcr_products[0]], 40)
48
+ pcr_check1 = pcr_assembly(alleles[0], primers[2], common_primers[1], limit=14, mismatches=0)[0]
49
+ pcr_check1.name = 'check_pcr_left'
50
+ pcr_check2 = pcr_assembly(alleles[0], primers[3], common_primers[0], limit=14, mismatches=0)[0]
51
+ pcr_check2.name = 'check_pcr_right'
52
+
53
+ cs = CloningStrategy.from_dseqrecords([pcr_check1, pcr_check2])
54
+
55
+ if not os.path.exists(os.path.join(output_dir, gene)):
56
+ os.makedirs(os.path.join(output_dir, gene))
57
+
58
+ with open(os.path.join(output_dir, gene, 'cloning_strategy.json'), 'w') as f:
59
+ f.write(cs.model_dump_json(indent=2))
60
+
61
+
62
+ if __name__ == '__main__':
63
+ import argparse
64
+
65
+ parser = argparse.ArgumentParser(description='List of genes to delete from S. pombe')
66
+ parser.add_argument(
67
+ '--genes', type=str, required=True, help='Path to a file containing a list of genes, one per line'
68
+ )
69
+ args = parser.parse_args()
70
+
71
+ parser.add_argument(
72
+ '--assembly_accession',
73
+ type=str,
74
+ default='GCF_000002945.2',
75
+ help='Assembly accession for S. pombe genome (default: GCF_000002945.2)',
76
+ )
77
+
78
+ parser.add_argument(
79
+ '--output_dir',
80
+ type=str,
81
+ default='batch_cloning_output',
82
+ help='Directory to save the output files (default: batch_cloning_output)',
83
+ )
84
+
85
+ parser.add_argument(
86
+ '--plasmid',
87
+ type=str,
88
+ default='19343',
89
+ help='Addgene ID for the plasmid (default: 19343)',
90
+ )
91
+
92
+ args = parser.parse_args()
93
+ assembly_accession = args.assembly_accession
94
+
95
+ with open(args.genes, 'r') as f:
96
+ genes = [line.strip() for line in f if line.strip()]
97
+
98
+ if not os.path.exists(args.output_dir):
99
+ os.makedirs(args.output_dir)
100
+
101
+ for gene in genes:
102
+ asyncio.run(main(gene, assembly_accession, args.output_dir, args.plasmid))
@@ -1,10 +1,17 @@
1
- from ...pydantic_models import BaseCloningStrategy, PrimerModel, PCRSource, HomologousRecombinationSource
1
+ from pydna.utils import location_boundaries
2
+ from ...pydantic_models import BaseCloningStrategy
3
+ from opencloning_linkml.datamodel import (
4
+ Primer as PrimerModel,
5
+ PCRSource,
6
+ HomologousRecombinationSource,
7
+ )
2
8
  from pydna.parsers import parse as pydna_parse
3
9
  import os
4
10
  import json
5
11
  from pydna import tm
6
12
  from Bio.Seq import reverse_complement
7
13
  import argparse
14
+ from Bio.SeqFeature import Location
8
15
 
9
16
  chromosomes = {
10
17
  'NC_003424.3': 'I',
@@ -17,11 +24,11 @@ chromosomes = {
17
24
  def find_primer_aligned_sequence(pcr_sources: list[PCRSource], primer: PrimerModel) -> str:
18
25
  for source in pcr_sources:
19
26
  if source.input[0].sequence == primer.id:
20
- loc = source.input[0].right_location
21
- return str(primer.sequence[loc.start : loc.end])
27
+ loc = Location.fromstring(source.input[0].right_location)
28
+ return loc.extract(primer.sequence)
22
29
  if source.input[-1].sequence == primer.id:
23
- loc = source.input[-1].left_location
24
- return str(reverse_complement(primer.sequence)[loc.start : loc.end])
30
+ loc = Location.fromstring(source.input[-1].left_location)
31
+ return loc.extract(reverse_complement(primer.sequence))
25
32
  raise ValueError(f"Primer {primer.id} not found in any PCR source")
26
33
 
27
34
 
@@ -33,13 +40,18 @@ def process_folder(working_dir: str):
33
40
  # We do this to have action to .end and .start
34
41
  pcr_sources = [PCRSource.model_validate(s.model_dump()) for s in pcr_sources]
35
42
  locus_source = next(s for s in strategy.sources if s.type == 'GenomeCoordinatesSource')
43
+ locus_location = Location.fromstring(locus_source.coordinates)
36
44
  hrec_source = next(s for s in strategy.sources if s.type == 'HomologousRecombinationSource')
37
45
  # We do this to have action to .end and .start
38
46
  hrec_source: HomologousRecombinationSource = HomologousRecombinationSource.model_validate(hrec_source.model_dump())
39
47
 
40
- chromosome = chromosomes[locus_source.sequence_accession]
41
- insertion_start = locus_source.start + hrec_source.input[0].right_location.end
42
- insertion_end = locus_source.start + hrec_source.input[-1].left_location.start
48
+ chromosome = chromosomes[locus_source.repository_id]
49
+ insertion_start = (
50
+ locus_location.start + location_boundaries(Location.fromstring(hrec_source.input[0].right_location))[1]
51
+ )
52
+ insertion_end = (
53
+ locus_location.start + location_boundaries(Location.fromstring(hrec_source.input[-1].left_location))[0]
54
+ )
43
55
 
44
56
  # Write out the sequences in genbank format and extract some relevant info
45
57
  sequences = [pydna_parse(sequence.file_content)[0] for sequence in strategy.sequences]
@@ -6,10 +6,10 @@ from typing import Annotated
6
6
  from fastapi import Body, Query, HTTPException
7
7
 
8
8
  from ...get_router import get_router
9
- from ...pydantic_models import (
10
- BaseCloningStrategy,
11
- PrimerModel,
9
+ from ...pydantic_models import BaseCloningStrategy
10
+ from opencloning_linkml.datamodel import (
12
11
  TextFileSequence,
12
+ Primer as PrimerModel,
13
13
  PCRSource,
14
14
  RestrictionAndLigationSource,
15
15
  GatewaySource,
@@ -96,7 +96,7 @@ async def ziqiang_et_al2024_post(
96
96
  else:
97
97
  name = f'end_ps{i}_start_ps{i + 1}'
98
98
 
99
- pcr_source = PCRSource(id=0, output_name=name)
99
+ pcr_source = PCRSource(id=0, input=[], output_name=name)
100
100
  fwd_primer = next(p for p in cloning_strategy.primers if p.id == fwd_primer_id)
101
101
  rvs_primer = next(p for p in cloning_strategy.primers if p.id == rvs_primer_id)
102
102
 
@@ -112,18 +112,18 @@ async def ziqiang_et_al2024_post(
112
112
 
113
113
  # Make all input of a Golden gate assembly
114
114
  golden_gate_source = RestrictionAndLigationSource(
115
- id=0, output_name='golden_gate_assembly', restriction_enzymes=['BsaI']
115
+ id=0, input=[], output_name='golden_gate_assembly', restriction_enzymes=['BsaI']
116
116
  )
117
117
 
118
118
  # Make them
119
119
  input_sequences = [next(s for s in cloning_strategy.sequences if s.id == p) for p in pcr_product_ids]
120
- resp = await restriction_and_ligation(golden_gate_source, input_sequences, False, False)
120
+ resp = await restriction_and_ligation(golden_gate_source, input_sequences, False)
121
121
  golden_gate_product: TextFileSequence = TextFileSequence.model_validate(resp['sequences'][0])
122
122
  golden_gate_source: RestrictionAndLigationSource = RestrictionAndLigationSource.model_validate(resp['sources'][0])
123
123
  cloning_strategy.add_source_and_sequence(golden_gate_source, golden_gate_product)
124
124
 
125
125
  bp_target = next(s for s in cloning_strategy.sequences if s.id == 6)
126
- gateway_source = GatewaySource(id=0, output_name='entry_clone', reaction_type='BP', greedy=False)
126
+ gateway_source = GatewaySource(id=0, input=[], output_name='entry_clone', reaction_type='BP', greedy=False)
127
127
  resp = await gateway(gateway_source, [golden_gate_product, bp_target], circular_only=True, only_multi_site=True)
128
128
  gateway_product: TextFileSequence = TextFileSequence.model_validate(resp['sequences'][0])
129
129
  gateway_source: GatewaySource = GatewaySource.model_validate(resp['sources'][0])
@@ -141,7 +141,7 @@ async def ziqiang_et_al2024_post(
141
141
  all_input_ids = [s.sequence for s in all_inputs]
142
142
  sequences_to_clone = [s for s in cloning_strategy.sequences if s.id not in all_input_ids]
143
143
 
144
- gateway_source = GatewaySource(id=0, output_name='expression_clone', reaction_type='LR', greedy=False)
144
+ gateway_source = GatewaySource(id=0, input=[], output_name='expression_clone', reaction_type='LR', greedy=False)
145
145
  resp = await gateway(gateway_source, sequences_to_clone, circular_only=True, only_multi_site=True)
146
146
  index_of_product = next(i for i, s in enumerate(resp['sequences']) if '/label="Cas9"' in s.file_content)
147
147
  expression_clone: TextFileSequence = TextFileSequence.model_validate(resp['sequences'][index_of_product])
@@ -81,7 +81,6 @@
81
81
  "database_id": null,
82
82
  "input": [],
83
83
  "repository_id": "71287",
84
- "repository_name": "addgene",
85
84
  "sequence_file_url": "https://media.addgene.org/snapgene-media/v2.0.0/sequences/352460/6c8b0369-e549-4517-95da-8d07146ca49d/addgene-plasmid-71287-sequence-352460.gbk",
86
85
  "addgene_sequence_type": "addgene-full"
87
86
  },
@@ -92,7 +91,6 @@
92
91
  "database_id": null,
93
92
  "input": [],
94
93
  "repository_id": "71287",
95
- "repository_name": "addgene",
96
94
  "sequence_file_url": "https://media.addgene.org/snapgene-media/v2.0.0/sequences/352460/6c8b0369-e549-4517-95da-8d07146ca49d/addgene-plasmid-71287-sequence-352460.gbk",
97
95
  "addgene_sequence_type": "addgene-full"
98
96
  },
@@ -103,7 +101,6 @@
103
101
  "database_id": null,
104
102
  "input": [],
105
103
  "repository_id": "213912",
106
- "repository_name": "addgene",
107
104
  "sequence_file_url": "https://media.addgene.org/snapgene-media/v2.0.0/sequences/437264/8bd82b44-a1c3-4f81-8936-ebcc16d171e9/addgene-plasmid-213912-sequence-437264.gbk",
108
105
  "addgene_sequence_type": "addgene-full"
109
106
  },
@@ -114,7 +111,6 @@
114
111
  "database_id": null,
115
112
  "input": [],
116
113
  "repository_id": "213913",
117
- "repository_name": "addgene",
118
114
  "sequence_file_url": "https://media.addgene.org/snapgene-media/v2.0.0/sequences/437263/af62d64e-1f22-4dce-aafa-7935d1665700/addgene-plasmid-213913-sequence-437263.gbk",
119
115
  "addgene_sequence_type": "addgene-full"
120
116
  },
@@ -125,7 +121,6 @@
125
121
  "database_id": null,
126
122
  "input": [],
127
123
  "repository_id": "133748",
128
- "repository_name": "addgene",
129
124
  "sequence_file_url": "https://media.addgene.org/snapgene-media/v2.0.0/sequences/271264/c0ae5f43-46e6-4175-a0c1-9a00cbb92034/addgene-plasmid-133748-sequence-271264.gbk",
130
125
  "addgene_sequence_type": "addgene-full"
131
126
  },
@@ -135,8 +130,7 @@
135
130
  "output_name": "pDONR_P2r-P3",
136
131
  "database_id": null,
137
132
  "input": [],
138
- "repository_id": "gateway_cloning_vectors/pDONR_P2r-P3",
139
- "repository_name": "snapgene"
133
+ "repository_id": "gateway_cloning_vectors/pDONR_P2r-P3"
140
134
  },
141
135
  {
142
136
  "id": 7,
@@ -207,7 +201,6 @@
207
201
  "database_id": null,
208
202
  "input": [],
209
203
  "repository_id": "63143",
210
- "repository_name": "addgene",
211
204
  "sequence_file_url": "https://media.addgene.org/snapgene-media/v2.0.0/sequences/223326/8c81fe9a-fb57-40bc-93d6-e17d83502061/addgene-plasmid-63143-sequence-223326.gbk",
212
205
  "addgene_sequence_type": "addgene-full"
213
206
  }
@@ -258,7 +251,7 @@
258
251
  ],
259
252
  "description": "",
260
253
  "files": null,
261
- "schema_version": "0.4.3",
254
+ "schema_version": "0.4.9",
262
255
  "backend_version": null,
263
256
  "frontend_version": null
264
257
  }
@@ -4,10 +4,12 @@ See info in README.md
4
4
 
5
5
  from ..pydantic_models import (
6
6
  BaseCloningStrategy as CloningStrategy,
7
+ )
8
+ from pydna.opencloning_models import SequenceLocationStr
9
+ from opencloning_linkml.datamodel import (
7
10
  AssemblySource,
8
11
  TextFileSequence,
9
- PrimerModel,
10
- SequenceLocationStr,
12
+ Primer as PrimerModel,
11
13
  )
12
14
  from .._version import __version__
13
15
  import json
@@ -50,9 +52,15 @@ def fix_backend_v0_3(input_data: dict) -> CloningStrategy | None:
50
52
  primers = [PrimerModel.model_validate(p) for p in data['primers'] if p['id'] in primer_ids]
51
53
  input_seqs = [primers[0], input_seqs[0], primers[1]]
52
54
 
53
- assembly_plan = assembly_source.get_assembly_plan(input_seqs)
54
- for join in assembly_plan:
55
- if len(join[2]) != len(join[3]):
55
+ assembly_fragments = [a for a in assembly_source.input if a.type == 'AssemblyFragment']
56
+
57
+ for prev_f, next_f in zip(assembly_fragments, assembly_fragments[1:] + assembly_fragments[:1]):
58
+ left = prev_f.right_location
59
+ right = next_f.left_location
60
+ if (left is not None and right is not None) and (
61
+ len(SequenceLocationStr(left).to_biopython_location())
62
+ != len(SequenceLocationStr(right).to_biopython_location())
63
+ ):
56
64
  problematic_source_ids.add(source['id'])
57
65
  break
58
66
 
@@ -0,0 +1,36 @@
1
+ import os
2
+ import yaml
3
+
4
+
5
+ def get_seva_catalog():
6
+ seva_catalog_path = os.path.join(os.path.dirname(__file__), 'seva.tsv')
7
+ seva_catalog = dict()
8
+ with open(seva_catalog_path, 'r') as f:
9
+ for line in f:
10
+ name, genbank_link = line.strip().split('\t')
11
+ seva_catalog[name] = genbank_link
12
+ return seva_catalog
13
+
14
+
15
+ def get_snapgene_catalog():
16
+ snapgene_catalog_path = os.path.join(os.path.dirname(__file__), 'snapgene.yaml')
17
+ with open(snapgene_catalog_path, 'r') as f:
18
+ return yaml.safe_load(f)
19
+
20
+
21
+ def get_openDNA_collections_catalog():
22
+ catalog_path = os.path.join(os.path.dirname(__file__), 'openDNA_collections.yaml')
23
+ with open(catalog_path, 'r') as f:
24
+ return yaml.safe_load(f)
25
+
26
+
27
+ def get_iGEM2024_catalog():
28
+ catalog_path = os.path.join(os.path.dirname(__file__), 'igem2024.yaml')
29
+ with open(catalog_path, 'r') as f:
30
+ return yaml.safe_load(f)
31
+
32
+
33
+ seva_catalog = get_seva_catalog()
34
+ snapgene_catalog = get_snapgene_catalog()
35
+ openDNA_collections_catalog = get_openDNA_collections_catalog()
36
+ iGEM2024_catalog = get_iGEM2024_catalog()