opencloning 0.1.0__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 (37) hide show
  1. opencloning-0.1.0/LICENSE +7 -0
  2. opencloning-0.1.0/PKG-INFO +168 -0
  3. opencloning-0.1.0/README.md +138 -0
  4. opencloning-0.1.0/pyproject.toml +52 -0
  5. opencloning-0.1.0/src/opencloning/__init__.py +0 -0
  6. opencloning-0.1.0/src/opencloning/api_config_utils.py +97 -0
  7. opencloning-0.1.0/src/opencloning/app_settings.py +50 -0
  8. opencloning-0.1.0/src/opencloning/assembly2.py +1347 -0
  9. opencloning-0.1.0/src/opencloning/batch_cloning/__init__.py +10 -0
  10. opencloning-0.1.0/src/opencloning/batch_cloning/index.html +54 -0
  11. opencloning-0.1.0/src/opencloning/batch_cloning/pombe/__init__.py +86 -0
  12. opencloning-0.1.0/src/opencloning/batch_cloning/pombe/index.html +191 -0
  13. opencloning-0.1.0/src/opencloning/batch_cloning/pombe/pombe_all.sh +9 -0
  14. opencloning-0.1.0/src/opencloning/batch_cloning/pombe/pombe_clone.py +191 -0
  15. opencloning-0.1.0/src/opencloning/batch_cloning/pombe/pombe_gather.py +66 -0
  16. opencloning-0.1.0/src/opencloning/batch_cloning/pombe/pombe_get_primers.py +114 -0
  17. opencloning-0.1.0/src/opencloning/batch_cloning/pombe/pombe_summary.py +104 -0
  18. opencloning-0.1.0/src/opencloning/batch_cloning/ziqiang_et_al2024/__init__.py +179 -0
  19. opencloning-0.1.0/src/opencloning/batch_cloning/ziqiang_et_al2024/index.html +132 -0
  20. opencloning-0.1.0/src/opencloning/batch_cloning/ziqiang_et_al2024/ziqiang_et_al2024.json +280 -0
  21. opencloning-0.1.0/src/opencloning/dna_functions.py +341 -0
  22. opencloning-0.1.0/src/opencloning/dna_utils.py +144 -0
  23. opencloning-0.1.0/src/opencloning/endpoints/annotation.py +64 -0
  24. opencloning-0.1.0/src/opencloning/endpoints/assembly.py +524 -0
  25. opencloning-0.1.0/src/opencloning/endpoints/external_import.py +371 -0
  26. opencloning-0.1.0/src/opencloning/endpoints/no_assembly.py +103 -0
  27. opencloning-0.1.0/src/opencloning/endpoints/no_input.py +108 -0
  28. opencloning-0.1.0/src/opencloning/endpoints/other.py +64 -0
  29. opencloning-0.1.0/src/opencloning/endpoints/primer_design.py +224 -0
  30. opencloning-0.1.0/src/opencloning/gateway.py +174 -0
  31. opencloning-0.1.0/src/opencloning/get_router.py +10 -0
  32. opencloning-0.1.0/src/opencloning/main.py +104 -0
  33. opencloning-0.1.0/src/opencloning/ncbi_requests.py +130 -0
  34. opencloning-0.1.0/src/opencloning/primer_design.py +214 -0
  35. opencloning-0.1.0/src/opencloning/pydantic_models.py +393 -0
  36. opencloning-0.1.0/src/opencloning/request_examples.py +102 -0
  37. opencloning-0.1.0/src/opencloning/utils.py +57 -0
@@ -0,0 +1,7 @@
1
+ Copyright 2022 Manuel Lera Ramirez
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,168 @@
1
+ Metadata-Version: 2.3
2
+ Name: opencloning
3
+ Version: 0.1.0
4
+ Summary: Backend of OpenCloning, a web application to generate molecular cloning strategies in json format, and share them with others.
5
+ License: MIT
6
+ Author: Manuel Lera-Ramirez
7
+ Author-email: manulera14@gmail.com
8
+ Requires-Python: >=3.11,<4.0
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Programming Language :: Python :: 3.13
14
+ Requires-Dist: beautifulsoup4 (>=4.11.1,<5.0.0)
15
+ Requires-Dist: fastapi
16
+ Requires-Dist: httpx (>=0.25.0,<0.26.0)
17
+ Requires-Dist: opencloning-linkml (>=0.2a0,<0.3)
18
+ Requires-Dist: openpyxl (>=3.1.5,<4.0.0)
19
+ Requires-Dist: pandas (>=2.2.3,<3.0.0)
20
+ Requires-Dist: pydantic (>=2.7.1,<3.0.0)
21
+ Requires-Dist: pydna (>=5.3,<6.0)
22
+ Requires-Dist: python-multipart
23
+ Requires-Dist: pyyaml (>=6.0.2,<7.0.0)
24
+ Requires-Dist: regex (>=2023.10.3,<2024.0.0)
25
+ Requires-Dist: requests (>=2.31.0,<3.0.0)
26
+ Requires-Dist: uvicorn
27
+ Project-URL: Repository, https://github.com/manulera/OpenCloning_backend
28
+ Description-Content-Type: text/markdown
29
+
30
+ [![Python tests](https://github.com/manulera/OpenCloning_backend/actions/workflows/ci.yml/badge.svg)](https://github.com/manulera/OpenCloning_backend/actions/workflows/ci.yml)
31
+ [![codecov](https://codecov.io/gh/manulera/OpenCloning_backend/graph/badge.svg?token=CFIB2H6WMO)](https://codecov.io/gh/manulera/OpenCloning_backend)
32
+
33
+ # OpenCloning Backend API
34
+
35
+ This API is part of a bigger application, before going further, please go to the [main project readme](https://github.com/manulera/OpenCloning), where you can find an introduction.
36
+
37
+ This python API is built with [FastAPI](https://fastapi.tiangolo.com/) and is for *in silico* cloning.
38
+
39
+ ## Summary
40
+
41
+ Read [main project readme](https://github.com/manulera/OpenCloning) first.
42
+
43
+ This API provides a series of entry points. The API documentation can be accessed [here](https://OpenCloning.api.genestorian.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://opencloning.api.genestorian.org/](https://opencloning.api.genestorian.org/).
44
+
45
+ ## Getting started
46
+
47
+ If you want to quickly set up a local instance of the frontend and backend of the application, check [getting started in 5 minutes](https://github.com/manulera/OpenCloning#timer_clock-getting-started-in-5-minutes) in the main repository.
48
+
49
+ ### Running locally
50
+
51
+ You can install this as a python package:
52
+
53
+ ```bash
54
+ # Create a virtual environment
55
+ python -m venv .venv
56
+ # Activate the virtual environment
57
+ source .venv/bin/activate
58
+ # Install the package from github (will be in pypi at some point)
59
+ pip install opencloning
60
+ # Run the API (uvicorn should be installed in the virtual environment)
61
+ uvicorn opencloning.main:app
62
+ ```
63
+
64
+ ### Running locally if you want to contribute
65
+
66
+ For the management of the dependencies `poetry` is used, if you don't have it, visit https://python-poetry.org/.
67
+
68
+ In the project directory:
69
+
70
+ ```bash
71
+ # This should install the dependencies and create a virtual environment
72
+ poetry install
73
+
74
+ # Install the pre-commit hooks
75
+ pre-commit install
76
+
77
+ # Activate the virtual environment
78
+ poetry shell
79
+
80
+ ```
81
+
82
+ The virtual environment is installed in the project folder. This is convenient if you are using an IDE for development. For settings of vscode see the folder `.vscode`.
83
+
84
+ Now you should be able to run the api by running:
85
+
86
+ ```bash
87
+ # The --reload argument will reload the API if you make changes to the code
88
+ uvicorn opencloning.main:app --reload --reload-exclude='.venv'
89
+ ```
90
+
91
+ Then you should be able to open the API docs at [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs) to know that your API is working.
92
+
93
+ ### Running locally with docker :whale:
94
+
95
+ If you want to serve the full site (backend and frontend) with docker, check [getting started in 5 minutes](https://github.com/manulera/OpenCloning#timer_clock-getting-started-in-5-minutes) in the main repository.
96
+
97
+ If you want to serve only the backend from a docker container, an image is available at [manulera/opencloningbackend](https://hub.docker.com/r/manulera/opencloningbackend). The image is built from the Dockerfile in the root of this repository and exposes the port 3000. To run it:
98
+
99
+ ```bash
100
+ docker build -t manulera/opencloningbackend .
101
+ docker run -d --name backendcontainer -p 8000:8000 manulera/opencloningbackend
102
+
103
+ ```
104
+
105
+ 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://opencloning.api.genestorian.org/](https://opencloning.api.genestorian.org/))
106
+
107
+ ```bash
108
+ docker pull manulera/opencloningbackend
109
+ docker run -d --name backendcontainer -p 8000:8000 manulera/opencloningbackend
110
+ ```
111
+
112
+ The api will be running at `http://localhost:8000`, so you should be able to access the docs at [http://localhost:8000/docs](http://localhost:8000/docs).
113
+
114
+ ### Connecting to the frontend
115
+
116
+ If you want to receive requests from the [frontend](https://github.com/manulera/OpenCloning_frontend), or from another web application you may have to include the url of the frontend application in the CORS exceptions. By default, if you run the dev server with `uvicorn opencloning.main:app --reload --reload-exclude='.venv'`, the backend will accept requests coming from `http://localhost:3000`, which is the default address of the frontend dev server (ran with `yarn start`).
117
+
118
+ If you want to change the allowed origins, you can do so via env variables (comma-separated). e.g.:
119
+
120
+ ```
121
+ ALLOWED_ORIGINS=http://localhost:3000,http://localhost:3001 uvicorn opencloning.main:app --reload --reload-exclude='.venv'
122
+ ```
123
+
124
+ Similarly, the frontend should be configured to send requests to the backend address, [see here](https://github.com/manulera/OpenCloning_frontend#connecting-to-the-backend).
125
+
126
+ #### Serving the frontend from the backend
127
+
128
+ You may prefer to handle everything from a single server. You can do so by:
129
+ * Build the [frontend](https://github.com/manulera/OpenCloning_frontend) with `yarn build`.
130
+ * Copy the folder `build` from the frontend to the root directory of the backend, and rename it to `frontend`.
131
+ * Set the environment variable `SERVE_FRONTEND=1` when running the backend. By default this will remove all allowed origins, but you can still set them with `ALLOWED_ORIGINS`.
132
+ * Set the value of `backendUrl` in `frontend/config.js` to `/`.
133
+ * Now, when you go to the root of the backend (e.g. `http://localhost:8000`), you should receive the frontend instead of the greeting page of the API.
134
+
135
+ You can see how this is done in this [docker image](https://github.com/manulera/OpenCloning/blob/master/Dockerfile) and [docker-compose file](https://github.com/manulera/OpenCloning/blob/master/docker-compose.yml).
136
+
137
+ ## Contributing :hammer_and_wrench:
138
+
139
+ Check [contribution guidelines in the main repository](https://github.com/manulera/OpenCloning/blob/master/CONTRIBUTING.md) for general guidelines.
140
+
141
+ For more specific tasks:
142
+ * Creating a new type of source: follow the [new source issue template](.github/ISSUE_TEMPLATE/new-source.md). You can create an issue like that [here](https://github.com/manulera/OpenCloning_backend/issues/new?assignees=&labels=new-source&projects=&template=new-source.md&title=New+source%3A+%3Cname-of-source%3E).
143
+
144
+ ## Running the tests locally
145
+
146
+ ```
147
+ pytest -v -ks
148
+ ```
149
+
150
+ ## Notes
151
+
152
+ ### Ping a particular library version from github:
153
+
154
+ ```
155
+ poetry add git+https://github.com/BjornFJohansson/pydna#4fd760d075f77cceeb27969e017e04b42f6d0aa3
156
+ ```
157
+
158
+ ### Generating API stubs
159
+
160
+ For the frontend, it may be useful to produce stubs (I use them for writing the tests). See how this is implemented
161
+ by looking at the `RecordStubRoute` class in `api_config_utils.py`. To run the dev server and record stubs:
162
+
163
+ ```bash
164
+ RECORD_STUBS=1 uvicorn opencloning.main:app --reload --reload-exclude='.venv'
165
+ ```
166
+
167
+ This will record the stubs (requests and responses) in the `stubs` folder.
168
+
@@ -0,0 +1,138 @@
1
+ [![Python tests](https://github.com/manulera/OpenCloning_backend/actions/workflows/ci.yml/badge.svg)](https://github.com/manulera/OpenCloning_backend/actions/workflows/ci.yml)
2
+ [![codecov](https://codecov.io/gh/manulera/OpenCloning_backend/graph/badge.svg?token=CFIB2H6WMO)](https://codecov.io/gh/manulera/OpenCloning_backend)
3
+
4
+ # OpenCloning Backend API
5
+
6
+ This API is part of a bigger application, before going further, please go to the [main project readme](https://github.com/manulera/OpenCloning), where you can find an introduction.
7
+
8
+ This python API is built with [FastAPI](https://fastapi.tiangolo.com/) and is for *in silico* cloning.
9
+
10
+ ## Summary
11
+
12
+ Read [main project readme](https://github.com/manulera/OpenCloning) first.
13
+
14
+ This API provides a series of entry points. The API documentation can be accessed [here](https://OpenCloning.api.genestorian.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://opencloning.api.genestorian.org/](https://opencloning.api.genestorian.org/).
15
+
16
+ ## Getting started
17
+
18
+ If you want to quickly set up a local instance of the frontend and backend of the application, check [getting started in 5 minutes](https://github.com/manulera/OpenCloning#timer_clock-getting-started-in-5-minutes) in the main repository.
19
+
20
+ ### Running locally
21
+
22
+ You can install this as a python package:
23
+
24
+ ```bash
25
+ # Create a virtual environment
26
+ python -m venv .venv
27
+ # Activate the virtual environment
28
+ source .venv/bin/activate
29
+ # Install the package from github (will be in pypi at some point)
30
+ pip install opencloning
31
+ # Run the API (uvicorn should be installed in the virtual environment)
32
+ uvicorn opencloning.main:app
33
+ ```
34
+
35
+ ### Running locally if you want to contribute
36
+
37
+ For the management of the dependencies `poetry` is used, if you don't have it, visit https://python-poetry.org/.
38
+
39
+ In the project directory:
40
+
41
+ ```bash
42
+ # This should install the dependencies and create a virtual environment
43
+ poetry install
44
+
45
+ # Install the pre-commit hooks
46
+ pre-commit install
47
+
48
+ # Activate the virtual environment
49
+ poetry shell
50
+
51
+ ```
52
+
53
+ The virtual environment is installed in the project folder. This is convenient if you are using an IDE for development. For settings of vscode see the folder `.vscode`.
54
+
55
+ Now you should be able to run the api by running:
56
+
57
+ ```bash
58
+ # The --reload argument will reload the API if you make changes to the code
59
+ uvicorn opencloning.main:app --reload --reload-exclude='.venv'
60
+ ```
61
+
62
+ Then you should be able to open the API docs at [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs) to know that your API is working.
63
+
64
+ ### Running locally with docker :whale:
65
+
66
+ If you want to serve the full site (backend and frontend) with docker, check [getting started in 5 minutes](https://github.com/manulera/OpenCloning#timer_clock-getting-started-in-5-minutes) in the main repository.
67
+
68
+ If you want to serve only the backend from a docker container, an image is available at [manulera/opencloningbackend](https://hub.docker.com/r/manulera/opencloningbackend). The image is built from the Dockerfile in the root of this repository and exposes the port 3000. To run it:
69
+
70
+ ```bash
71
+ docker build -t manulera/opencloningbackend .
72
+ docker run -d --name backendcontainer -p 8000:8000 manulera/opencloningbackend
73
+
74
+ ```
75
+
76
+ 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://opencloning.api.genestorian.org/](https://opencloning.api.genestorian.org/))
77
+
78
+ ```bash
79
+ docker pull manulera/opencloningbackend
80
+ docker run -d --name backendcontainer -p 8000:8000 manulera/opencloningbackend
81
+ ```
82
+
83
+ The api will be running at `http://localhost:8000`, so you should be able to access the docs at [http://localhost:8000/docs](http://localhost:8000/docs).
84
+
85
+ ### Connecting to the frontend
86
+
87
+ If you want to receive requests from the [frontend](https://github.com/manulera/OpenCloning_frontend), or from another web application you may have to include the url of the frontend application in the CORS exceptions. By default, if you run the dev server with `uvicorn opencloning.main:app --reload --reload-exclude='.venv'`, the backend will accept requests coming from `http://localhost:3000`, which is the default address of the frontend dev server (ran with `yarn start`).
88
+
89
+ If you want to change the allowed origins, you can do so via env variables (comma-separated). e.g.:
90
+
91
+ ```
92
+ ALLOWED_ORIGINS=http://localhost:3000,http://localhost:3001 uvicorn opencloning.main:app --reload --reload-exclude='.venv'
93
+ ```
94
+
95
+ Similarly, the frontend should be configured to send requests to the backend address, [see here](https://github.com/manulera/OpenCloning_frontend#connecting-to-the-backend).
96
+
97
+ #### Serving the frontend from the backend
98
+
99
+ You may prefer to handle everything from a single server. You can do so by:
100
+ * Build the [frontend](https://github.com/manulera/OpenCloning_frontend) with `yarn build`.
101
+ * Copy the folder `build` from the frontend to the root directory of the backend, and rename it to `frontend`.
102
+ * Set the environment variable `SERVE_FRONTEND=1` when running the backend. By default this will remove all allowed origins, but you can still set them with `ALLOWED_ORIGINS`.
103
+ * Set the value of `backendUrl` in `frontend/config.js` to `/`.
104
+ * Now, when you go to the root of the backend (e.g. `http://localhost:8000`), you should receive the frontend instead of the greeting page of the API.
105
+
106
+ You can see how this is done in this [docker image](https://github.com/manulera/OpenCloning/blob/master/Dockerfile) and [docker-compose file](https://github.com/manulera/OpenCloning/blob/master/docker-compose.yml).
107
+
108
+ ## Contributing :hammer_and_wrench:
109
+
110
+ Check [contribution guidelines in the main repository](https://github.com/manulera/OpenCloning/blob/master/CONTRIBUTING.md) for general guidelines.
111
+
112
+ For more specific tasks:
113
+ * Creating a new type of source: follow the [new source issue template](.github/ISSUE_TEMPLATE/new-source.md). You can create an issue like that [here](https://github.com/manulera/OpenCloning_backend/issues/new?assignees=&labels=new-source&projects=&template=new-source.md&title=New+source%3A+%3Cname-of-source%3E).
114
+
115
+ ## Running the tests locally
116
+
117
+ ```
118
+ pytest -v -ks
119
+ ```
120
+
121
+ ## Notes
122
+
123
+ ### Ping a particular library version from github:
124
+
125
+ ```
126
+ poetry add git+https://github.com/BjornFJohansson/pydna#4fd760d075f77cceeb27969e017e04b42f6d0aa3
127
+ ```
128
+
129
+ ### Generating API stubs
130
+
131
+ For the frontend, it may be useful to produce stubs (I use them for writing the tests). See how this is implemented
132
+ by looking at the `RecordStubRoute` class in `api_config_utils.py`. To run the dev server and record stubs:
133
+
134
+ ```bash
135
+ RECORD_STUBS=1 uvicorn opencloning.main:app --reload --reload-exclude='.venv'
136
+ ```
137
+
138
+ This will record the stubs (requests and responses) in the `stubs` folder.
@@ -0,0 +1,52 @@
1
+ [tool.poetry]
2
+ authors = ["Manuel Lera-Ramirez <manulera14@gmail.com>"]
3
+ description = "Backend of OpenCloning, a web application to generate molecular cloning strategies in json format, and share them with others."
4
+ license = "MIT"
5
+ name = "opencloning"
6
+ version = "0.1.0"
7
+ package-mode = true
8
+ readme = "README.md"
9
+ repository = "https://github.com/manulera/OpenCloning_backend"
10
+
11
+ [tool.poetry.dependencies]
12
+ beautifulsoup4 = "^4.11.1"
13
+ fastapi = "*"
14
+ httpx = "^0.25.0"
15
+ python = "^3.11"
16
+ python-multipart = "*"
17
+ uvicorn = "*"
18
+ pydna = "^5.3"
19
+ requests = "^2.31.0"
20
+ regex = "^2023.10.3"
21
+ pydantic = "^2.7.1"
22
+ pandas = "^2.2.3"
23
+ openpyxl = "^3.1.5"
24
+ pyyaml = "^6.0.2"
25
+ opencloning-linkml = "^0.2a0"
26
+
27
+ [tool.poetry.group.dev.dependencies]
28
+ autopep8 = "^2.0.4"
29
+ flake8-bugbear = "^24.2.6"
30
+ black = "^24.2.0"
31
+ pre-commit = "^3.6.2"
32
+ watchfiles = "^0.21.0"
33
+
34
+ [tool.poetry.group.test.dependencies]
35
+ pytest = "8.3.4"
36
+ pre-commit = "^3.6.2"
37
+ pytest-cov = "^4.1.0"
38
+ pytest-rerunfailures = "^14.0"
39
+ respx = "^0.21.1"
40
+
41
+
42
+ [tool.poetry.group.ipython.dependencies]
43
+ ipython = "^8.20.0"
44
+ ipykernel = "^6.28.0"
45
+
46
+ [build-system]
47
+ build-backend = "poetry.core.masonry.api"
48
+ requires = ["poetry-core>=1.0.0"]
49
+
50
+ [tool.black]
51
+ skip-string-normalization = true
52
+ line-length = 119
File without changes
@@ -0,0 +1,97 @@
1
+ import json
2
+ import datetime
3
+ from fastapi.exceptions import RequestValidationError, HTTPException
4
+ from typing import Callable
5
+ from fastapi.routing import APIRoute
6
+ from fastapi import Request, Response
7
+ from fastapi.responses import JSONResponse
8
+ import os
9
+ import glob
10
+ from fastapi.middleware.cors import CORSMiddleware
11
+
12
+
13
+ class RecordStubRoute(APIRoute):
14
+ """Subclass of APIRoute that stores the request and response of a route in the folder `stubs`"""
15
+
16
+ def get_route_handler(self) -> Callable:
17
+ original_route_handler = super().get_route_handler()
18
+
19
+ async def custom_route_handler(request: Request) -> Response:
20
+ if request.method != 'POST':
21
+ return await original_route_handler(request)
22
+ formatted_request = {
23
+ 'path': request.url.path,
24
+ 'method': request.method,
25
+ 'body': await request.json(),
26
+ 'headers': dict(request.headers),
27
+ }
28
+ try:
29
+ response: JSONResponse = await original_route_handler(request)
30
+ except RequestValidationError as exc:
31
+ errors = exc.errors()
32
+ for e in errors:
33
+ if 'ctx' in e:
34
+ # This is a ValueError Object that cannot be serialized
35
+ del e['ctx']
36
+ response = JSONResponse(content=errors, status_code=422)
37
+ except HTTPException as exc:
38
+ print('>>exception', exc)
39
+ detail = {'detail': exc.detail}
40
+ response = JSONResponse(content=detail, status_code=exc.status_code)
41
+
42
+ if type(response) is JSONResponse:
43
+ formatted_response = {
44
+ 'statusCode': response.status_code,
45
+ 'body': json.loads(response.body),
46
+ 'headers': dict(response.headers),
47
+ }
48
+
49
+ formatted_time = datetime.datetime.now().strftime('%Y_%m_%d-%H_%M_%S')
50
+ stub_folder = f'stubs{formatted_request["path"]}/{formatted_time}'
51
+ existing_folders = glob.glob(f'{stub_folder}_*')
52
+ stub_folder = f'{stub_folder}_{len(existing_folders)}'
53
+ if not os.path.exists(stub_folder):
54
+ os.makedirs(stub_folder)
55
+ with open(f'{stub_folder}/request.json', 'w') as f:
56
+ json.dump(formatted_request, f, indent=4)
57
+ with open(f'{stub_folder}/response.json', 'w') as f:
58
+ json.dump(formatted_response, f, indent=4)
59
+ with open(f'{stub_folder}/response_body.json', 'w') as f:
60
+ json.dump(formatted_response['body'], f, indent=4)
61
+
62
+ print(9 * ' ', '> stub written to', stub_folder)
63
+ return response
64
+
65
+ return custom_route_handler
66
+
67
+
68
+ # Workaround for internal server errors: https://github.com/tiangolo/fastapi/discussions/7847#discussioncomment-5144709
69
+ async def custom_http_exception_handler(request: Request, exc: Exception, app, allow_origins):
70
+
71
+ response = JSONResponse(content={'message': 'internal server error'}, status_code=500)
72
+
73
+ origin = request.headers.get('origin')
74
+
75
+ if origin:
76
+ # Have the middleware do the heavy lifting for us to parse
77
+ # all the config, then update our response headers
78
+ cors = CORSMiddleware(
79
+ app=app, allow_origins=allow_origins, allow_credentials=True, allow_methods=['*'], allow_headers=['*']
80
+ )
81
+ # Logic directly from Starlette's CORSMiddleware:
82
+ # https://github.com/encode/starlette/blob/master/starlette/middleware/cors.py#L152
83
+
84
+ response.headers.update(cors.simple_headers)
85
+ has_cookie = 'cookie' in request.headers
86
+ # If request includes any cookie headers, then we must respond
87
+ # with the specific origin instead of '*'.
88
+ if cors.allow_all_origins and has_cookie:
89
+ response.headers['Access-Control-Allow-Origin'] = origin
90
+
91
+ # If we only allow specific origins, then we have to mirror back
92
+ # the Origin header in the response.
93
+ elif not cors.allow_all_origins and cors.is_allowed_origin(origin=origin):
94
+ response.headers['Access-Control-Allow-Origin'] = origin
95
+ response.headers.add_vary_header('Origin')
96
+
97
+ return response
@@ -0,0 +1,50 @@
1
+ """
2
+ This module contains the settings for the app that can be set via environment variables.
3
+ """
4
+
5
+ import os
6
+ from pydantic import BaseModel
7
+
8
+
9
+ def parse_bool(value: str) -> bool:
10
+ return value in {'1', 'TRUE', 'true', 'True'}
11
+
12
+
13
+ # API settings ===============================================
14
+ SERVE_FRONTEND = parse_bool(os.environ['SERVE_FRONTEND']) if 'SERVE_FRONTEND' in os.environ else False
15
+ BATCH_CLONING = parse_bool(os.environ['BATCH_CLONING']) if 'BATCH_CLONING' in os.environ else True
16
+ RECORD_STUBS = parse_bool(os.environ['RECORD_STUBS']) if 'RECORD_STUBS' in os.environ else False
17
+ ALLOWED_ORIGINS = ['http://localhost:3000', 'http://localhost:5173']
18
+ if os.environ.get('ALLOWED_ORIGINS') is not None:
19
+ # Remove trailing slash from each origin if ends with one
20
+ ALLOWED_ORIGINS = [origin.rstrip('/') for origin in os.environ['ALLOWED_ORIGINS'].split(',')]
21
+
22
+
23
+ # External services settings =================================
24
+ NCBI_API_KEY = os.environ.get('NCBI_API_KEY')
25
+ PLANNOTATE_URL = os.environ['PLANNOTATE_URL'] if 'PLANNOTATE_URL' in os.environ else None
26
+ PLANNOTATE_TIMEOUT = int(os.environ['PLANNOTATE_TIMEOUT']) if 'PLANNOTATE_TIMEOUT' in os.environ else 20
27
+ # Handle trailing slash:
28
+ if PLANNOTATE_URL is not None and not PLANNOTATE_URL.endswith('/'):
29
+ PLANNOTATE_URL += '/'
30
+
31
+
32
+ class Settings(BaseModel):
33
+ SERVE_FRONTEND: bool
34
+ BATCH_CLONING: bool
35
+ RECORD_STUBS: bool
36
+ NCBI_API_KEY: str | None
37
+ ALLOWED_ORIGINS: list[str]
38
+ PLANNOTATE_URL: str | None
39
+ PLANNOTATE_TIMEOUT: int
40
+
41
+
42
+ settings = Settings(
43
+ SERVE_FRONTEND=SERVE_FRONTEND,
44
+ BATCH_CLONING=BATCH_CLONING,
45
+ RECORD_STUBS=RECORD_STUBS,
46
+ NCBI_API_KEY=NCBI_API_KEY,
47
+ ALLOWED_ORIGINS=ALLOWED_ORIGINS,
48
+ PLANNOTATE_URL=PLANNOTATE_URL,
49
+ PLANNOTATE_TIMEOUT=PLANNOTATE_TIMEOUT,
50
+ )