retuve.app.routes.models

The Models API for Retuve.

  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"""
 16The Models API for Retuve.
 17"""
 18
 19import shutil
 20import tempfile
 21import traceback
 22from typing import Optional
 23
 24from fastapi import APIRouter, File, Form, HTTPException, Request, UploadFile
 25
 26from retuve import __version__ as retuve_version
 27from retuve.app.classes import Metric2D, Metric3D, ModelResponse
 28from retuve.app.helpers import (
 29    RESULTS_DIR,
 30    RESULTS_URL_ACCESS,
 31    analyze_validation,
 32)
 33from retuve.funcs import retuve_run
 34from retuve.keyphrases.config import Config
 35
 36router = APIRouter()
 37
 38
 39@router.post(
 40    "/api/model/",
 41    response_model=ModelResponse,
 42    responses={
 43        400: {"description": "Invalid file type. Expected a DICOM file."},
 44        500: {"description": "Internal Server Error"},
 45    },
 46)
 47def analyse_image(
 48    request: Request,
 49    file: UploadFile = File(...),
 50    keyphrase: str = Form(...),
 51    api_token: Optional[str] = Form(None),
 52):
 53    """
 54    Analyze a file with a Retuve Model based on the keyphrase provided.
 55
 56    :param request: The request object.
 57    :param file: The file to be analyzed.
 58    :param keyphrase: The keyphrase to be used for analysis.
 59    :param api_token: The API token for the keyphrase.
 60
 61    :raises HTTPException: If the file or keyphrase is invalid.
 62    """
 63
 64    try:
 65        config = Config.get_config(keyphrase)
 66
 67        hippa_logger = request.app.config[config.name].hippa_logger
 68
 69        analyze_validation(file, keyphrase, api_token)
 70
 71        hippa_logger.info(
 72            f"Validated and Uploaded {file.filename} "
 73            f"with keyphrase {keyphrase} from host: {request.client.host}."
 74        )
 75
 76        file_name = file.filename.split("/")[-1]
 77
 78        # save file in tempfile with same name
 79        with tempfile.NamedTemporaryFile(
 80            mode="wb", delete=False, suffix=file_name, prefix=""
 81        ) as temp_file:
 82            # save the file
 83            shutil.copyfileobj(file.file, temp_file)
 84            temp_file.flush()
 85
 86            # get the file location
 87            temp_file = temp_file.name
 88
 89            result = retuve_run(
 90                hip_mode=config.batch.hip_mode,
 91                config=config,
 92                modes_func=config.batch.mode_func,
 93                modes_func_kwargs_dict=config.batch.mode_func_args,
 94                file=temp_file,
 95            )
 96
 97        notes = ""
 98        metrics3d = []
 99        metrics2d = []
100        video_path = None
101        figure_path = None
102        img_path = None
103
104        filename = file.filename.split("/")[-1].split(".")[0]
105
106        if result.video_clip:
107            video_path = f"{RESULTS_DIR}/{filename}.mp4"
108            result.video_clip.write_videofile(video_path)
109            video_path = video_path.replace(
110                RESULTS_DIR, f"{RESULTS_URL_ACCESS}/{config.name}"
111            )
112
113        if result.visual_3d:
114            figure_path = f"{RESULTS_DIR}/{filename}.html"
115            result.visual_3d.write_html(figure_path)
116            figure_path = figure_path.replace(
117                RESULTS_DIR, f"{RESULTS_URL_ACCESS}/{config.name}"
118            )
119
120        if result.image:
121            img_path = f"{RESULTS_DIR}/{filename}.jpg"
122            result.image.save(img_path)
123            img_path = img_path.replace(
124                RESULTS_DIR, f"{RESULTS_URL_ACCESS}/{config.name}"
125            )
126
127        if result.hip_datas:
128            for i, metric in enumerate(result.hip_datas.metrics):
129                metrics3d.append(
130                    Metric3D(
131                        name=metric.name,
132                        post=0 if metric.post == "N/A" else metric.post,
133                        graf=0 if metric.graf == "N/A" else metric.graf,
134                        ant=0 if metric.ant == "N/A" else metric.ant,
135                        full=0 if metric.full == "N/A" else metric.full,
136                    )
137                )
138
139            notes = str(result.hip_datas.recorded_error)
140
141        if result.image:
142            for i, metric in enumerate(result.hip.metrics):
143                metrics2d.append(
144                    Metric2D(name=metric.name, value=metric.value)
145                )
146
147        # Mark success in hippa logs
148        hippa_logger.info(
149            f"Successfully processed {file.filename} with keyphrase {keyphrase} "
150            f"from host: {request.client.host}."
151        )
152
153        # Mock response for demonstration
154        return ModelResponse(
155            notes=notes,
156            metrics_3d=metrics3d,
157            video_url=f"{config.api.url}{video_path}" if video_path else "",
158            figure_url=f"{config.api.url}{figure_path}" if figure_path else "",
159            img_url=f"{config.api.url}{img_path}" if img_path else "",
160            retuve_version=retuve_version,
161            keyphrase_name=config.name,
162            metrics_2d=metrics2d,
163        )
164    except Exception as e:
165        # print traceback to hippa logs
166        hippa_logger.error(
167            f"Error in processing {file.filename} with keyphrase {keyphrase} "
168            f"from host: {request.client.host}."
169        )
170        hippa_logger.error(traceback.format_exc())
171        raise HTTPException(
172            status_code=500, detail="Internal Server Error, check logs."
173        )
router = <fastapi.routing.APIRouter object>
@router.post('/api/model/', response_model=ModelResponse, responses={400: {'description': 'Invalid file type. Expected a DICOM file.'}, 500: {'description': 'Internal Server Error'}})
def analyse_image( request: starlette.requests.Request, file: fastapi.datastructures.UploadFile = File(PydanticUndefined), keyphrase: str = Form(PydanticUndefined), api_token: Optional[str] = Form(None)):
 40@router.post(
 41    "/api/model/",
 42    response_model=ModelResponse,
 43    responses={
 44        400: {"description": "Invalid file type. Expected a DICOM file."},
 45        500: {"description": "Internal Server Error"},
 46    },
 47)
 48def analyse_image(
 49    request: Request,
 50    file: UploadFile = File(...),
 51    keyphrase: str = Form(...),
 52    api_token: Optional[str] = Form(None),
 53):
 54    """
 55    Analyze a file with a Retuve Model based on the keyphrase provided.
 56
 57    :param request: The request object.
 58    :param file: The file to be analyzed.
 59    :param keyphrase: The keyphrase to be used for analysis.
 60    :param api_token: The API token for the keyphrase.
 61
 62    :raises HTTPException: If the file or keyphrase is invalid.
 63    """
 64
 65    try:
 66        config = Config.get_config(keyphrase)
 67
 68        hippa_logger = request.app.config[config.name].hippa_logger
 69
 70        analyze_validation(file, keyphrase, api_token)
 71
 72        hippa_logger.info(
 73            f"Validated and Uploaded {file.filename} "
 74            f"with keyphrase {keyphrase} from host: {request.client.host}."
 75        )
 76
 77        file_name = file.filename.split("/")[-1]
 78
 79        # save file in tempfile with same name
 80        with tempfile.NamedTemporaryFile(
 81            mode="wb", delete=False, suffix=file_name, prefix=""
 82        ) as temp_file:
 83            # save the file
 84            shutil.copyfileobj(file.file, temp_file)
 85            temp_file.flush()
 86
 87            # get the file location
 88            temp_file = temp_file.name
 89
 90            result = retuve_run(
 91                hip_mode=config.batch.hip_mode,
 92                config=config,
 93                modes_func=config.batch.mode_func,
 94                modes_func_kwargs_dict=config.batch.mode_func_args,
 95                file=temp_file,
 96            )
 97
 98        notes = ""
 99        metrics3d = []
100        metrics2d = []
101        video_path = None
102        figure_path = None
103        img_path = None
104
105        filename = file.filename.split("/")[-1].split(".")[0]
106
107        if result.video_clip:
108            video_path = f"{RESULTS_DIR}/{filename}.mp4"
109            result.video_clip.write_videofile(video_path)
110            video_path = video_path.replace(
111                RESULTS_DIR, f"{RESULTS_URL_ACCESS}/{config.name}"
112            )
113
114        if result.visual_3d:
115            figure_path = f"{RESULTS_DIR}/{filename}.html"
116            result.visual_3d.write_html(figure_path)
117            figure_path = figure_path.replace(
118                RESULTS_DIR, f"{RESULTS_URL_ACCESS}/{config.name}"
119            )
120
121        if result.image:
122            img_path = f"{RESULTS_DIR}/{filename}.jpg"
123            result.image.save(img_path)
124            img_path = img_path.replace(
125                RESULTS_DIR, f"{RESULTS_URL_ACCESS}/{config.name}"
126            )
127
128        if result.hip_datas:
129            for i, metric in enumerate(result.hip_datas.metrics):
130                metrics3d.append(
131                    Metric3D(
132                        name=metric.name,
133                        post=0 if metric.post == "N/A" else metric.post,
134                        graf=0 if metric.graf == "N/A" else metric.graf,
135                        ant=0 if metric.ant == "N/A" else metric.ant,
136                        full=0 if metric.full == "N/A" else metric.full,
137                    )
138                )
139
140            notes = str(result.hip_datas.recorded_error)
141
142        if result.image:
143            for i, metric in enumerate(result.hip.metrics):
144                metrics2d.append(
145                    Metric2D(name=metric.name, value=metric.value)
146                )
147
148        # Mark success in hippa logs
149        hippa_logger.info(
150            f"Successfully processed {file.filename} with keyphrase {keyphrase} "
151            f"from host: {request.client.host}."
152        )
153
154        # Mock response for demonstration
155        return ModelResponse(
156            notes=notes,
157            metrics_3d=metrics3d,
158            video_url=f"{config.api.url}{video_path}" if video_path else "",
159            figure_url=f"{config.api.url}{figure_path}" if figure_path else "",
160            img_url=f"{config.api.url}{img_path}" if img_path else "",
161            retuve_version=retuve_version,
162            keyphrase_name=config.name,
163            metrics_2d=metrics2d,
164        )
165    except Exception as e:
166        # print traceback to hippa logs
167        hippa_logger.error(
168            f"Error in processing {file.filename} with keyphrase {keyphrase} "
169            f"from host: {request.client.host}."
170        )
171        hippa_logger.error(traceback.format_exc())
172        raise HTTPException(
173            status_code=500, detail="Internal Server Error, check logs."
174        )

Analyze a file with a Retuve Model based on the keyphrase provided.

Parameters
  • request: The request object.
  • file: The file to be analyzed.
  • keyphrase: The keyphrase to be used for analysis.
  • api_token: The API token for the keyphrase.
Raises
  • HTTPException: If the file or keyphrase is invalid.