retuve.app.routes.api
API routes for the Retuve App.
1# Copyright 2024 Adam McArthur 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15""" 16API routes for the Retuve App. 17""" 18 19import json 20import os 21import shutil 22 23from fastapi import APIRouter, File, Request, UploadFile 24from fastapi.responses import JSONResponse 25 26from retuve.app.classes import FeedbackRequest, SystemFiles 27from retuve.app.helpers import RESULTS_DIR 28from retuve.app.routes.live import dicom_processing_queue 29from retuve.app.utils import validate_api_token 30from retuve.keyphrases.config import Config 31from retuve.logs import ulogger 32from retuve.trak.data import extract_files 33 34router = APIRouter() 35 36 37@router.post("/api/store_feedback/{keyphrase}") 38async def store_feedback( 39 request: Request, feedback_request: FeedbackRequest, keyphrase: str 40): 41 """ 42 Store feedback on a result. 43 44 :param feedback_request: The feedback request. 45 :param keyphrase: The keyphrase to get right config. 46 47 :return: The status of the feedback storage. 48 """ 49 50 api_token = request.cookies.get("api_token") 51 validate_api_token(api_token) 52 53 config = Config.get_config(keyphrase) 54 file_id = feedback_request.file_id 55 feedback = feedback_request.feedback 56 57 feedback_dir = f"{config.api.savedir}/{file_id}" 58 feedback_file = f"{feedback_dir}/feedback.json" 59 os.makedirs(feedback_dir, exist_ok=True) 60 try: 61 if os.path.exists(feedback_file): 62 with open(feedback_file, "r") as file: 63 feedback_list = json.load(file) 64 else: 65 feedback_list = [] 66 feedback_list.append({"comment": feedback}) 67 with open(feedback_file, "w") as file: 68 json.dump(feedback_list, file) 69 return { 70 "status": "success", 71 "message": "Feedback stored successfully.", 72 } 73 except Exception as e: 74 ulogger.info(e) 75 return {"status": "error", "message": str(e)} 76 77 78@router.get("/api/get_feedback/{keyphrase}") 79async def get_feedback(file_id: str, keyphrase: str, request: Request): 80 """ 81 Get feedback on a result. 82 83 :param file_id: The file id. 84 :param keyphrase: The keyphrase to get right config. 85 86 :return: The feedback on the result. 87 """ 88 89 api_token = request.cookies.get("api_token") 90 validate_api_token(api_token) 91 92 config = Config.get_config(keyphrase) 93 feedback_file = f"{config.api.savedir}/{file_id}/feedback.json" 94 if os.path.exists(feedback_file): 95 with open(feedback_file, "r") as file: 96 feedback_list = json.load(file) 97 return {"status": "success", "feedback": feedback_list} 98 return {"status": "error", "message": "Feedback not found."} 99 100 101@router.get("/api/get_metrics/{keyphrase}") 102async def get_metrics(file_id: str, keyphrase: str, request: Request): 103 """ 104 Get metrics on a result. 105 106 :param file_id: The file id. 107 :param keyphrase: The keyphrase to get right config. 108 """ 109 110 api_token = request.cookies.get("api_token") 111 validate_api_token(api_token) 112 113 config = Config.get_config(keyphrase) 114 metrics_file = f"{config.api.savedir}/{file_id}/metrics.json" 115 if os.path.exists(metrics_file): 116 with open(metrics_file, "r") as file: 117 metrics_list = json.load(file) 118 metrics_list["status"] = "success" 119 return metrics_list 120 return {"status": "error", "message": "metrics not found."} 121 122 123@router.post("/api/upload/{keyphrase}") 124async def handle_upload( 125 request: Request, keyphrase: str, file: UploadFile = File(...) 126): 127 """ 128 Handle file uploads. 129 130 :param keyphrase: The keyphrase to get right config. 131 :param file: The file to upload. 132 """ 133 134 api_token = request.cookies.get("api_token") 135 validate_api_token(api_token) 136 137 config = Config.get_config(keyphrase) 138 139 os.makedirs(config.api.upload_dir, exist_ok=True) 140 file_location = f"{config.api.upload_dir}/{file.filename}" 141 with open(file_location, "wb+") as file_object: 142 shutil.copyfileobj(file.file, file_object) 143 144 if config.api.zero_trust: 145 live_savedir = RESULTS_DIR 146 else: 147 live_savedir = config.api.savedir 148 149 # check if the file is a zip, and extract it 150 if file.filename.endswith(".zip"): 151 # create a temp folder in the upload dir 152 temp_dir = f"{config.api.upload_dir}/temp" 153 154 shutil.unpack_archive(file_location, temp_dir) 155 156 zip_name = file.filename.split(".")[0] 157 158 # add the zips name to each extracted file 159 for root, dirs, files in os.walk(temp_dir): 160 for _file in files: 161 os.rename( 162 os.path.join(root, _file), 163 os.path.join(root, f"{zip_name}_{_file}"), 164 ) 165 166 # move the extracted files to the upload dir 167 for root, dirs, files in os.walk(temp_dir): 168 for _file in files: 169 shutil.move( 170 os.path.join(root, _file), 171 os.path.join(config.api.upload_dir, _file), 172 ) 173 174 dicom_id = _file.split(".")[0] 175 await dicom_processing_queue.put( 176 (dicom_id, None, config, live_savedir) 177 ) 178 179 # delete the temp folder 180 shutil.rmtree(temp_dir) 181 182 # delete the zip file 183 os.remove(file_location) 184 return { 185 "info": f"file '{file.filename}' extracted at '{config.api.upload_dir}'" 186 } 187 188 dicom_id = file.filename.split(".")[0] 189 await dicom_processing_queue.put((dicom_id, None, config, live_savedir)) 190 191 return {"info": f"file '{file.filename}' saved at '{file_location}'"} 192 193 194@router.post( 195 "/api/states/{keyphrase}", 196 response_model=SystemFiles, 197 responses={400: {"description": "Error"}}, 198) 199@router.get( 200 "/api/states/{keyphrase}", 201 response_model=SystemFiles, 202 responses={400: {"description": "Error"}}, 203) 204async def get_states(keyphrase: str, request: Request): 205 """ 206 Get the states from the database. 207 208 :param keyphrase: The keyphrase to get right config. 209 210 :return: The states from the database. 211 """ 212 213 api_token = request.cookies.get("api_token") 214 validate_api_token(api_token) 215 216 config = Config.get_config(keyphrase) 217 states = extract_files(config.api.db_path) 218 output_states = SystemFiles(states=states, length=len(states)) 219 220 return output_states 221 222 223@router.get("/api/keyphrases") 224async def keyphrases(request: Request): 225 """ 226 Get all valid keyphrases. 227 228 :return: The valid keyphrases. 229 """ 230 231 api_token = request.cookies.get("api_token") 232 validate_api_token(api_token) 233 234 keyphrases = [keyphrase for keyphrase, _ in Config.get_configs()] 235 return JSONResponse(content={"keyphrases": keyphrases})
router =
<fastapi.routing.APIRouter object>
@router.post('/api/store_feedback/{keyphrase}')
async def
store_feedback( request: starlette.requests.Request, feedback_request: retuve.app.classes.FeedbackRequest, keyphrase: str):
38@router.post("/api/store_feedback/{keyphrase}") 39async def store_feedback( 40 request: Request, feedback_request: FeedbackRequest, keyphrase: str 41): 42 """ 43 Store feedback on a result. 44 45 :param feedback_request: The feedback request. 46 :param keyphrase: The keyphrase to get right config. 47 48 :return: The status of the feedback storage. 49 """ 50 51 api_token = request.cookies.get("api_token") 52 validate_api_token(api_token) 53 54 config = Config.get_config(keyphrase) 55 file_id = feedback_request.file_id 56 feedback = feedback_request.feedback 57 58 feedback_dir = f"{config.api.savedir}/{file_id}" 59 feedback_file = f"{feedback_dir}/feedback.json" 60 os.makedirs(feedback_dir, exist_ok=True) 61 try: 62 if os.path.exists(feedback_file): 63 with open(feedback_file, "r") as file: 64 feedback_list = json.load(file) 65 else: 66 feedback_list = [] 67 feedback_list.append({"comment": feedback}) 68 with open(feedback_file, "w") as file: 69 json.dump(feedback_list, file) 70 return { 71 "status": "success", 72 "message": "Feedback stored successfully.", 73 } 74 except Exception as e: 75 ulogger.info(e) 76 return {"status": "error", "message": str(e)}
Store feedback on a result.
Parameters
- feedback_request: The feedback request.
- keyphrase: The keyphrase to get right config.
Returns
The status of the feedback storage.
@router.get('/api/get_feedback/{keyphrase}')
async def
get_feedback(file_id: str, keyphrase: str, request: starlette.requests.Request):
79@router.get("/api/get_feedback/{keyphrase}") 80async def get_feedback(file_id: str, keyphrase: str, request: Request): 81 """ 82 Get feedback on a result. 83 84 :param file_id: The file id. 85 :param keyphrase: The keyphrase to get right config. 86 87 :return: The feedback on the result. 88 """ 89 90 api_token = request.cookies.get("api_token") 91 validate_api_token(api_token) 92 93 config = Config.get_config(keyphrase) 94 feedback_file = f"{config.api.savedir}/{file_id}/feedback.json" 95 if os.path.exists(feedback_file): 96 with open(feedback_file, "r") as file: 97 feedback_list = json.load(file) 98 return {"status": "success", "feedback": feedback_list} 99 return {"status": "error", "message": "Feedback not found."}
Get feedback on a result.
Parameters
- file_id: The file id.
- keyphrase: The keyphrase to get right config.
Returns
The feedback on the result.
@router.get('/api/get_metrics/{keyphrase}')
async def
get_metrics(file_id: str, keyphrase: str, request: starlette.requests.Request):
102@router.get("/api/get_metrics/{keyphrase}") 103async def get_metrics(file_id: str, keyphrase: str, request: Request): 104 """ 105 Get metrics on a result. 106 107 :param file_id: The file id. 108 :param keyphrase: The keyphrase to get right config. 109 """ 110 111 api_token = request.cookies.get("api_token") 112 validate_api_token(api_token) 113 114 config = Config.get_config(keyphrase) 115 metrics_file = f"{config.api.savedir}/{file_id}/metrics.json" 116 if os.path.exists(metrics_file): 117 with open(metrics_file, "r") as file: 118 metrics_list = json.load(file) 119 metrics_list["status"] = "success" 120 return metrics_list 121 return {"status": "error", "message": "metrics not found."}
Get metrics on a result.
Parameters
- file_id: The file id.
- keyphrase: The keyphrase to get right config.
@router.post('/api/upload/{keyphrase}')
async def
handle_upload( request: starlette.requests.Request, keyphrase: str, file: fastapi.datastructures.UploadFile = File(PydanticUndefined)):
124@router.post("/api/upload/{keyphrase}") 125async def handle_upload( 126 request: Request, keyphrase: str, file: UploadFile = File(...) 127): 128 """ 129 Handle file uploads. 130 131 :param keyphrase: The keyphrase to get right config. 132 :param file: The file to upload. 133 """ 134 135 api_token = request.cookies.get("api_token") 136 validate_api_token(api_token) 137 138 config = Config.get_config(keyphrase) 139 140 os.makedirs(config.api.upload_dir, exist_ok=True) 141 file_location = f"{config.api.upload_dir}/{file.filename}" 142 with open(file_location, "wb+") as file_object: 143 shutil.copyfileobj(file.file, file_object) 144 145 if config.api.zero_trust: 146 live_savedir = RESULTS_DIR 147 else: 148 live_savedir = config.api.savedir 149 150 # check if the file is a zip, and extract it 151 if file.filename.endswith(".zip"): 152 # create a temp folder in the upload dir 153 temp_dir = f"{config.api.upload_dir}/temp" 154 155 shutil.unpack_archive(file_location, temp_dir) 156 157 zip_name = file.filename.split(".")[0] 158 159 # add the zips name to each extracted file 160 for root, dirs, files in os.walk(temp_dir): 161 for _file in files: 162 os.rename( 163 os.path.join(root, _file), 164 os.path.join(root, f"{zip_name}_{_file}"), 165 ) 166 167 # move the extracted files to the upload dir 168 for root, dirs, files in os.walk(temp_dir): 169 for _file in files: 170 shutil.move( 171 os.path.join(root, _file), 172 os.path.join(config.api.upload_dir, _file), 173 ) 174 175 dicom_id = _file.split(".")[0] 176 await dicom_processing_queue.put( 177 (dicom_id, None, config, live_savedir) 178 ) 179 180 # delete the temp folder 181 shutil.rmtree(temp_dir) 182 183 # delete the zip file 184 os.remove(file_location) 185 return { 186 "info": f"file '{file.filename}' extracted at '{config.api.upload_dir}'" 187 } 188 189 dicom_id = file.filename.split(".")[0] 190 await dicom_processing_queue.put((dicom_id, None, config, live_savedir)) 191 192 return {"info": f"file '{file.filename}' saved at '{file_location}'"}
Handle file uploads.
Parameters
- keyphrase: The keyphrase to get right config.
- file: The file to upload.
@router.post('/api/states/{keyphrase}', response_model=SystemFiles, responses={400: {'description': 'Error'}})
@router.get('/api/states/{keyphrase}', response_model=SystemFiles, responses={400: {'description': 'Error'}})
async def
get_states(keyphrase: str, request: starlette.requests.Request):
195@router.post( 196 "/api/states/{keyphrase}", 197 response_model=SystemFiles, 198 responses={400: {"description": "Error"}}, 199) 200@router.get( 201 "/api/states/{keyphrase}", 202 response_model=SystemFiles, 203 responses={400: {"description": "Error"}}, 204) 205async def get_states(keyphrase: str, request: Request): 206 """ 207 Get the states from the database. 208 209 :param keyphrase: The keyphrase to get right config. 210 211 :return: The states from the database. 212 """ 213 214 api_token = request.cookies.get("api_token") 215 validate_api_token(api_token) 216 217 config = Config.get_config(keyphrase) 218 states = extract_files(config.api.db_path) 219 output_states = SystemFiles(states=states, length=len(states)) 220 221 return output_states
Get the states from the database.
Parameters
- keyphrase: The keyphrase to get right config.
Returns
The states from the database.
@router.get('/api/keyphrases')
async def
keyphrases(request: starlette.requests.Request):
224@router.get("/api/keyphrases") 225async def keyphrases(request: Request): 226 """ 227 Get all valid keyphrases. 228 229 :return: The valid keyphrases. 230 """ 231 232 api_token = request.cookies.get("api_token") 233 validate_api_token(api_token) 234 235 keyphrases = [keyphrase for keyphrase, _ in Config.get_configs()] 236 return JSONResponse(content={"keyphrases": keyphrases})
Get all valid keyphrases.
Returns
The valid keyphrases.