Ejemplo n.º 1
0
    def schema(
        self,
        engine_fqdn: str,
        namespace: str,
        study_uid: str,
        extended: Optional[bool] = None,
        attachments_only: Optional[bool] = None,
        phi_namespace: Optional[str] = None,
    ) -> Box:
        """Get the schema of study.

        URL: /study/{namespace}/{studyUid}/schema?sid={sid}&phi_namespace={phi_namespace}&extended={1,0}&attachments_only={0,1}  # NOQA E501

        :param engine_fqdn: Engine FQDN (Required).
        :param namespace: Namespace (Required).
        :param study_uid: Study uid (Required).
        :param extended: is an bool, when set to 1 will include
            the optional phi_version and transfer_attributes
            name/value pairs in the response.
        :param attachments_only: is an bool, when set to 1
            will only include a list of
            the attachments in the study.
        :param phi_namespace: A string, set to the UUID of the
            namespace where the file was attached if it was
            attached to a shared instance of the study
            outside of the original storage namespace

        :returns: study schema
        """
        extended: int = bool_to_int(extended)  # type: ignore
        attachemtns_onlynt: int = bool_to_int(attachments_only)  # type: ignore
        url_template = '/study/{namespace}/{study_uid}/schema'
        url_arg_names = {
            'engine_fqdn',
            'namespace',
            'study_uid',
        }
        request_arg_names = {'phi_namespace', 'extended', 'attachments_only'}
        url, request_data = self._storage.get_url_and_request(
            url_template,
            url_arg_names,
            request_arg_names,
            locals(),
        )
        response = self._storage.get(url, params=request_data)
        response = check_response(response, url_arg_names=url_arg_names)
        return Box(response.json())
Ejemplo n.º 2
0
    def _dicom_payload(
        self,
        engine_fqdn: str,
        namespace: str,
        study_uid: str,
        image_uid: str,
        # In external api this is a hash parameter
        image_version: str,
        phi_namespace: Optional[str] = None,
        pretranscode: Optional[bool] = None,
        transfer_syntax: Optional[str] = None,
    ) -> PreparedRequest:
        """Gets dicom payload.

        URL: {namespace}/{studyUid}/image/{imageUid}/version/{hash}

        :param engine_fqdn: Engine FQDN (Required).
        :param namespace: Namespace (Required).
        :param study_uid: Study uid (Required).
        :param image_uid: Image uid (Required).
        :param image_version: image version (Required).
        :param phi_namespace: A string, set to the UUID
            If set, specifies the phi namespace from which to pull PHI.
            Will overlay the values onto the phiSource.
        :param pretranscode: Get pretranscoded dicom.
        :param transfer_syntax: Transfer syntax.

        :returns: dicom.
        """
        pretranscode: int = bool_to_int(  # type: ignore
            pretranscode, )
        url_template = '/study/{namespace}/{study_uid}/image/{image_uid}/version/{image_version}'

        url_arg_names = {
            'engine_fqdn',
            'namespace',
            'study_uid',
            'image_uid',
            'image_version',
        }
        request_arg_names = {
            'phi_namespace',
            'pretranscode',
            'transfer_syntax',
        }
        url, request_data = self._storage.get_url_and_request(
            url_template,
            url_arg_names,
            request_arg_names,
            locals(),
        )
        return PreparedRequest(
            storage=self._storage,
            method=StorageMethod.get,
            url=url,
            params=request_data,
            stream=True,
        )
Ejemplo n.º 3
0
    def count(
        self,
        engine_fqdn: str,
        namespace: str,
        study_uid: str,
        image_uid: str,
        images_only: Optional[bool] = None,
        attachments_only: Optional[bool] = None,
        count_files: Optional[bool] = None,
    ) -> Box:
        """Gets study file count.

        URL: /study/{namespace}/{studyUid}/count?sid={sid}&images_only={1,0}&attachments_only={1,0}&count_files={1,0}

        :param engine_fqdn: Engine FQDN (Required).
        :param namespace: Namespace (Required).
        :param study_uid: Study uid (Required).
        :param image_uid: Image uid (Required).
        :param images_only: an integer, zero or 1, returns the number
            of images in the study with superseded images by way of study update not counted.
        :param attachments_only: an integer, zero or 1, returns
            the number of attachments which have been added to this study.
        :param count_files: if present and set to 1 will count files stored
            on-disk for images and/or attachments,
            instead of counting from (possibly cached) meta data.

        :returns: count obj
        """
        images_only: int = bool_to_int(images_only)  # type: ignore
        attachments_only: int = bool_to_int(attachments_only)  # type: ignore
        count_files: int = bool_to_int(count_files)  # type: ignore
        url_template = '/study/{namespace}/{study_uid}/count'
        url_arg_names = {'engine_fqdn', 'namespace', 'study_uid'}
        request_arg_names = {'images_only', 'attachments_only', 'count_files'}
        url, request_data = self._storage.get_url_and_request(
            url_template,
            url_arg_names,
            request_arg_names,
            locals(),
        )
        response = self._storage.get(url, params=request_data)
        response = check_response(response, url_arg_names=url_arg_names)
        return Box(response.json())
Ejemplo n.º 4
0
    def video(
        self,
        engine_fqdn: str,
        namespace: str,
        study_uid: str,
        image_uid: str,
        image_version: str,
        reencode_video: Optional[bool] = None,
    ) -> Response:
        """Gets a study encapsulated video.

        URL: /study/{namespace}/{studyUid}/image/{imageUid}/version/{imageVersion}/video?sid={sid}&reencode_video={0,1}

        :param engine_fqdn: Engine FQDN (Required).
        :param namespace: Namespace (Required).
        :param study_uid: Study uid (Required).
        :param image_uid: Image uid (Required).
        :param image_version: Image version (Required).
        :param reencode_video: An integer of value 1 or 0.

        :returns: Video response
        """
        reencode_video: int = bool_to_int(reencode_video)  # type: ignore

        url_template = '/study/{namespace}/{study_uid}/image/{image_uid}/version/{image_version}/video'
        url_arg_names = {
            'engine_fqdn',
            'namespace',
            'study_uid',
            'image_uid',
            'image_version',
        }
        request_arg_names = {'reencode_video'}
        url, request_data = self._storage.get_url_and_request(
            url_template,
            url_arg_names,
            request_arg_names,
            locals(),
        )

        errors_mapping = {
            415:
            UnsupportedMediaType(
                'Video was not found encapsulated in the DICOM file.'
            )  # NOQA: 997
        }
        response: Response = self._storage.get(
            url,
            params=request_data,
            stream=True,
        )
        response = check_response(response, url_arg_names=url_arg_names)
        return response
Ejemplo n.º 5
0
    def wrap(
        self,
        engine_fqdn: str,
        namespace: str,
        opened_file: BufferedReader,
        tags: Optional[str] = None,
        render_wrapped_pdf: Optional[bool] = None,
    ) -> Box:
        """Upload a non DICOM image.

        URL: /namespace/{namespace}/wrap?sid={sid}&render_wrapped_pdf={0,1}

        :param engine_fqdn: Engine FQDN (Required).
        :param namespace: Namespace (Required).
        :param tags: Any DICOM tags to be overwrite or added should be provided as a form-data field.
        :param opened_file: The multipart file to be uploaded should be provided as a form-data field.
        :param render_wrapped_pdf: An integer value of either 0 or 1.

        :returns: image object attributes
        """
        render_wrapped_pdf: int = bool_to_int(render_wrapped_pdf)  # type: ignore
        url_template = '/namespace/{namespace}/wrap'
        url_arg_names = {'engine_fqdn', 'namespace'}
        request_arg_names: Set[str] = set()
        url, request_data = self._storage.get_url_and_request(
            url_template,
            url_arg_names,
            request_arg_names,
            locals(),
        )
        files = {
            'file': opened_file,
        }
        if tags is not None:
            post_data = {
                'tags': tags,
            }
            response = self._storage.post(
                url,
                params=request_data,
                files=files,
                data=post_data,
            )
        else:
            response = self._storage.post(
                url,
                params=request_data,
                files=files,
            )
        response = check_response(response, url_arg_names=url_arg_names)
        return Box(response.json())
Ejemplo n.º 6
0
    def delete(
        self,
        engine_fqdn: str,
        namespace: str,
        study_uid: str,
        keep_attachments: Optional[bool] = None,
    ):
        """Deletes a study.

        URL: /study/{namespace}/{study_uid}?sid={sid}&keep_attachments={1,0}

        :param engine_fqdn: Engine FQDN (Required).
        :param namespace: Namespace (Required).
        :param study_uid: Study uid (Required).
        :param keep_attachments: An integer value of 1 or 0.

        If the optional parameter keep_attachments is set to 1, then:
            all DICOM images will be deleted.
            reports and attachments related to the study will be kept.
        """
        keep_attachments: int = bool_to_int(keep_attachments)  # type: ignore

        url_template = '/study/{namespace}/{study_uid}'
        url_arg_names = {
            'engine_fqdn',
            'namespace',
            'study_uid',
        }
        request_arg_names = {'keep_attachments'}
        url, request_data = self._storage.get_url_and_request(
            url_template,
            url_arg_names,
            request_arg_names,
            locals(),
        )
        response = self._storage.delete(url, params=request_data)
        check_response(response, url_arg_names=url_arg_names)
Ejemplo n.º 7
0
    def _wrap(
        self,
        engine_fqdn: str,
        namespace: str,
        opened_file: RequestsFileType,
        tags: Optional[str] = None,
        render_wrapped_pdf: Optional[bool] = None,
        x_ambrahealth_job_id: Optional[str] = None,
    ) -> PreparedRequest:
        """Upload a non DICOM image.

        URL: /namespace/{namespace}/wrap?sid={sid}&render_wrapped_pdf={0,1}

        :param engine_fqdn: Engine FQDN (Required).
        :param namespace: Namespace (Required).
        :param tags: Any DICOM tags to be overwrite or added should be provided as a form-data field.
        :param opened_file: The multipart file to be uploaded should be
            provided as a form-data field.
            File object, or may be 2-tuples (filename, fileobj),
            3-tuples (filename, fileobj, contentype) or
            4-tuples (filename, fileobj, contentype, custom_headers).

        :param render_wrapped_pdf: An integer value of either 0 or 1.
        :param x_ambrahealth_job_id: X-AmbraHealth-Job-Id headers argument

        :raises ValueError: Multifiles uploading for wrap
        :returns: image object attributes
        """
        render_wrapped_pdf: int = bool_to_int(  # type: ignore
            render_wrapped_pdf, )
        url_template = '/namespace/{namespace}/wrap'
        url_arg_names = {'engine_fqdn', 'namespace'}
        request_arg_names = {
            'tags',
            'render_wrapped_pdf',
        }
        url, request_data = self._storage.get_url_and_request(
            url_template,
            url_arg_names,
            request_arg_names,
            locals(),
        )
        if isinstance(opened_file, tuple):
            fp = opened_file[1]
        elif isinstance(opened_file, FormData):
            form_data_fields = opened_file._fields  # NOQA:WPS437
            if len(form_data_fields) != 1:
                raise ValueError('Multifiles uploading for wrap.')
            files_info = form_data_fields[0]
            fp = files_info[2]
        else:
            fp = opened_file
        file_size = os.fstat(fp.fileno()).st_size
        if isinstance(opened_file, FormData):
            files = opened_file
            data_name = files._fields[0][0]['name']  # NOQA:WPS437
            if data_name != 'file':
                # In this case we have 404 error from storage
                raise ValueError(
                    'For FormData use "file" field name. '
                    'Example f = fd.add_field("file", ...)', )
        else:
            files = {
                'file': opened_file,
            }
        headers = {'X-File-Size': str(file_size)}

        if x_ambrahealth_job_id is not None:
            headers['X-AmbraHealth-Job-Id'] = x_ambrahealth_job_id

        if tags is not None:
            post_data = {
                'tags': tags,
            }
            prepared_request = PreparedRequest(
                storage=self._storage,
                method=StorageMethod.post,
                url=url,
                params=request_data,
                files=files,
                headers=headers,
                data=post_data,
            )
        else:
            prepared_request = PreparedRequest(
                storage=self._storage,
                method=StorageMethod.post,
                url=url,
                params=request_data,
                files=files,
                headers=headers,
            )
        return prepared_request
Ejemplo n.º 8
0
    def download(
        self,
        engine_fqdn: str,
        namespace: str,
        study_uid: str,
        bundle: str,
        phi_namespace: Optional[str] = None,
        series_uid: Optional[str] = None,
        image_uid: Optional[str] = None,
        include_wrapped_dicoms: Optional[bool] = None,
        stop_on_failure: Optional[bool] = None,
        exclude_viewer: Optional[bool] = None,
    ) -> Response:
        """Downloads a study ZIP file.

        URL: /study/{namespace}/{studyUid}/download?sid={sid}&phi_namespace={phi_namespace}&bundle={iso,dicom,osx,win}&include_wrapped_dicoms={0,1}&series_uid={series_uid[,series_uid...]}&image_uid={image_uid[,image_uid...]}

        :param engine_fqdn: Engine FQDN (Required).
        :param namespace: Namespace (Required).
        :param study_uid: Study uid (Required).
        :param bundle: A string with value "dicom", "iso", "osx" or "win" (Required).

        :param phi_namespace: The shared-to namespace UUID
            of a shared instance of a study (Optional)
        :param include_wrapped_dicoms: When "1", will also
            include the DICOM file of a wrapped DICOM.
            Default is to only include the unwrapped file.
        :param series_uid: One or more Series Instance UIDs,
            comma-separated. Download will only include images
            from these series.
        :param image_uid: One or more SOP Instance UIDs,
            comma-separated. Download will only include these images.
        :param stop_on_failure: If "1", will not include the viewer
            app if there are any errors generating the download.
        :param exclude_viewer: If "1", viewer app will not be
            included in the "iso", "osx", and "win" bundle.
            The viewer app can be retrieved separately via /download/viewer

        :raises ValueError: Wrong bundle value

        :returns: study zip file response
        """
        if bundle not in {'dicom', 'iso', 'osx', 'win'}:
            raise ValueError('Boundle not in ("dicom", "iso", "osx", "win")')

        include_wrapped_dicoms: int = bool_to_int(
            include_wrapped_dicoms)  # type: ignore
        stop_on_failure: int = bool_to_int(stop_on_failure)  # type: ignore
        exclude_viewer: int = bool_to_int(exclude_viewer)  # type: ignore

        url_template = '/study/{namespace}/{study_uid}/download'
        url_arg_names = {
            'engine_fqdn',
            'namespace',
            'study_uid',
        }

        request_arg_names = {
            'bundle',
            'phi_namespace',
            'series_uid',
            'image_uid',
            'include_wrapped_dicoms',
            'stop_on_failure',
            'exclude_viewer',
        }
        url, request_data = self._storage.get_url_and_request(
            url_template,
            url_arg_names,
            request_arg_names,
            locals(),
        )
        response: Response = self._storage.get(
            url,
            params=request_data,
            stream=True,
        )
        response = check_response(response, url_arg_names=url_arg_names)
        return response
Ejemplo n.º 9
0
    def post_attachment(
        self,
        engine_fqdn: str,
        namespace: str,
        study_uid: str,
        opened_file: BufferedReader,
        phi_namespace: Optional[str] = None,
        wrap_images: Optional[bool] = None,
        return_html: Optional[bool] = None,
        synchronous_wrap: Optional[bool] = None,
        static_ids: Optional[bool] = None,
    ) -> Box:
        """Posts an attachment to a study.

        URL: /study/{namespace}/{studyUid}/attachment?sid={sid}&phi_namespace={phi_namespace}&wrap_images={0,1}&return_html={0,1}&synchronous_wrap={0,1}&static_ids={0,1}

        :param engine_fqdn: Engine FQDN (Required).
        :param namespace: Namespace (Required).
        :param study_uid: Study uid (Required).
        :param opened_file: Opened file (Required).
        :param phi_namespace: A string, set to the UUID of
            the namespace where the file was attached if it
            was attached to a shared instance of the study
            outside of the original storage namespace
        :param wrap_images: An integer of value 1 or 0.
            For attachments that are images, or can be rendered
            to an image, generate a new DICOM image in the study
            containing (a rendered image of) the attachment
            (also controlled by namespace setting auto_wrap_images).
        :param synchronous_wrap: An integer of value 1 or 0.
            If 1, do all processing for image wrapping before returning,
            including Services notifications.
        :param static_ids: An integer of value 1 or 0. If 1,
            the attachment, series and any images rendered from PDF
            are assigned (u)uids based on a hash of the attachment,
            which will be the same if the attachment is re-uploaded.
            The attachment is also allowed to be re-uploaded when set to 1.
        :param return_html: An integer of value 1 or 0. Return results as
            Content Type text html, instead of
            application json (required for certain browsers)

        :returns: posts attachments response

        The request entity must contain
        a single multipart/form-data element named
        data containing the content to be attached.

        If the optional parameter wrap_images or synchronous_wrap
        is set to 1, or the uploading account has the setting
        auto_wrap_images set to 1, then:
        attachments of type image/jpeg or image/bmp are, asynchronously
        (unless synchronous_wrap=1) to the Store Attachment request,
        posted to the Wrap Attachment service, to be stored as a single
        DICOM image in a new series within the specified study.
        attachments of type application/pdf are, asynchronously
        (unless synchronous_wrap=1) to the Store Attachment request,
        rendered to DICOM images at a resolution of 200 ppi,
        one image per page of the original PDF. The resulting are
        stored in a new series within the specified study.
        UIDs assigned to the series and images will be random unless static_ids=1
        """
        wrap_images: int = bool_to_int(wrap_images)  # type: ignore
        synchronous_wrap: int = bool_to_int(synchronous_wrap)  # type: ignore
        static_ids: int = bool_to_int(static_ids)  # type: ignore
        return_html: int = bool_to_int(return_html)  # type: ignore

        files = {'data': opened_file}

        url_template = '/study/{namespace}/{study_uid}/attachment'
        url_arg_names = {
            'engine_fqdn',
            'namespace',
            'study_uid',
        }
        request_arg_names = {
            'phi_namespace',
            'wrap_images',
            'return_html',
            'synchronous_wrap',
            'static_ids',
        }
        url, request_data = self._storage.get_url_and_request(
            url_template,
            url_arg_names,
            request_arg_names,
            locals(),
        )
        response = self._storage.post(url, params=request_data, files=files)
        check_response(response, url_arg_names=url_arg_names)
        return Box(response.json())